diff options
author | Petr Vobornik <pvoborni@redhat.com> | 2012-11-26 14:28:32 +0100 |
---|---|---|
committer | Petr Vobornik <pvoborni@redhat.com> | 2013-01-18 15:10:36 +0100 |
commit | b9ef6ab0c412913234f05f788b3fcd3c3277eb69 (patch) | |
tree | 2af9ef49ce74fd152c4c7b6f0aad543b4793ba59 /install/ui/src | |
parent | 8f8e790d9468245c031320d6a506a420f486638f (diff) | |
download | freeipa-b9ef6ab0c412913234f05f788b3fcd3c3277eb69.tar.gz freeipa-b9ef6ab0c412913234f05f788b3fcd3c3277eb69.tar.xz freeipa-b9ef6ab0c412913234f05f788b3fcd3c3277eb69.zip |
Move of core Web UI files to AMD directory
SSIA
https://fedorahosted.org/freeipa/ticket/112
Diffstat (limited to 'install/ui/src')
35 files changed, 26997 insertions, 0 deletions
diff --git a/install/ui/src/freeipa/aci.js b/install/ui/src/freeipa/aci.js new file mode 100644 index 000000000..bd7de19ab --- /dev/null +++ b/install/ui/src/freeipa/aci.js @@ -0,0 +1,897 @@ +/*jsl:import ipa.js */ + +/* Authors: + * Adam Young <ayoung@redhat.com> + * Endi S. Dewata <edewata@redhat.com> + * + * Copyright (C) 2010 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; version 2 only + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* REQUIRES: ipa.js, details.js, search.js, add.js, facet.js, entity.js */ + +IPA.aci = {}; + +IPA.aci.permission_entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + that.entity_init(); + + that.builder.facet_groups(['settings', 'privilege']). + search_facet({ + columns: [ 'cn' ] + }). + details_facet({ + factory: IPA.aci.permission_details_facet, + fields: [ + { + name:'cn', + widget: 'identity.cn' + }, + { + type: 'rights', + name: 'permissions', + widget: 'rights.permissions' + }, + { + type: 'select', + name: 'target', + widget: 'target.target', + enabled: false + }, + { + name: 'filter', + widget: 'target.filter', + enabled: false + }, + { + type: 'entity_select', + name: 'memberof', + widget: 'target.memberof', + enabled: false + }, + { + name: 'subtree', + widget: 'target.subtree', + enabled: false + }, + { + type: 'entity_select', + name: 'targetgroup', + widget: 'target.targetgroup', + enabled: false + }, + { + type: 'select', + name: 'type', + widget: 'target.type', + enabled: false + }, + { + name: 'attrs', + widget: 'target.attrs', + enabled: false + }, + { + name: 'attrs_multi', + param: 'attrs', + type: 'multivalued', + widget: 'target.attrs_multi', + enabled: false + } + ], + widgets: [ + { + type: 'details_table_section', + name: 'identity', + label: IPA.messages.objects.permission.identity, + widgets: [ + 'cn' + ] + }, + { + type: 'details_table_section', + name: 'rights', + label: IPA.messages.objects.permission.rights, + widgets: [ + { + type: 'rights', + name: 'permissions' + } + ] + }, + { + type: 'permission_target', + container_factory: IPA.details_table_section, + label: IPA.messages.objects.permission.target, + name: 'target', + show_target: false + } + ], + policies: [ + IPA.permission_target_policy('target') + ] + }). + association_facet({ + name: 'member_privilege', + facet_group: 'privilege' + }). + adder_dialog({ + height: 450, + fields: [ + { + name:'cn', + widget: 'general.cn' + }, + { + type: 'rights', + name: 'permissions', + widget: 'general.permissions' + }, + { + type: 'select', + name: 'target', + widget: 'target.target', + enabled: false + }, + { + name: 'filter', + widget: 'target.filter', + enabled: false + }, + { + type: 'entity_select', + name: 'memberof', + widget: 'target.memberof', + enabled: false + }, + { + name: 'subtree', + widget: 'target.subtree', + enabled: false + }, + { + type: 'entity_select', + name: 'targetgroup', + widget: 'target.targetgroup', + enabled: false + }, + { + type: 'select', + name: 'type', + widget: 'target.type', + enabled: false + }, + { + name: 'attrs', + widget: 'target.attrs', + enabled: false + }, + { + name: 'attrs_multi', + type: 'multivalued', + param: 'attrs', + widget: 'target.attrs_multi', + enabled: false + } + ], + widgets: [ + { + type: 'details_table_section_nc', + name: 'general', + widgets: [ + 'cn', + { + type: 'rights', + name: 'permissions' + } + ] + }, + { + type: 'permission_target', + name:'target', + show_target: true + } + ], + policies: [ + IPA.permission_target_policy('target') + ] + }); + }; + + return that; +}; + +IPA.aci.permission_details_facet = function(spec) { + + var that = IPA.details_facet(spec); + + that.get_refresh_command_name = function() { + return that.entity.name+'_show_'+that.pkey; + }; + + return that; +}; + +IPA.aci.privilege_entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + that.entity_init(); + + that.builder.facet_groups(['permission', 'settings', 'role']). + search_facet({ + columns: [ + 'cn', + 'description' + ] + }). + details_facet({ + sections: [ + { + name: 'identity', + label: IPA.messages.details.identity, + fields: [ + 'cn', + { + type: 'textarea', + name: 'description' + } + ] + } + ] + }). + association_facet({ + name: 'member_role', + facet_group: 'role', + add_method: 'add_privilege', + remove_method: 'remove_privilege', + associator: IPA.serial_associator + }). + association_facet({ + name: 'memberof_permission', + facet_group: 'permission', + add_method: 'add_permission', + remove_method: 'remove_permission' + }). + standard_association_facets(). + adder_dialog({ + fields: [ + 'cn', + { + type: 'textarea', + name: 'description' + } + ] + }); + }; + + return that; +}; + +IPA.aci.role_entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + that.entity_init(); + + that.builder.facet_groups(['member', 'privilege', 'settings']). + search_facet({ + columns: [ + 'cn', + 'description' + ] + }). + details_facet({ + sections: [ + { + name: 'identity', + label: IPA.messages.objects.role.identity, + fields: [ + 'cn', + { + type: 'textarea', + name: 'description' + } + ] + } + ] + }). + association_facet({ + name: 'memberof_privilege', + facet_group: 'privilege', + add_method: 'add_privilege', + remove_method: 'remove_privilege' + }). + standard_association_facets(). + adder_dialog({ + fields: [ + 'cn', + { + type: 'textarea', + name: 'description' + } + ] + }); + }; + + return that; +}; + +IPA.aci.selfservice_entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + that.entity_init(); + + that.builder.search_facet({ + columns: [ 'aciname' ], + pagination: false + }). + details_facet({ + check_rights: false, + sections: [ + { + name: 'general', + label: IPA.messages.details.general, + fields: [ + 'aciname', + { + type: 'attributes', + object_type: 'user', + name: 'attrs' + } + ] + } + ] + }). + adder_dialog({ + fields: [ + 'aciname', + { + type: 'attributes', + object_type: 'user', + name: 'attrs' + } + ] + }); + }; + + return that; +}; + +IPA.aci.delegation_entity = function(spec) { + + var that = IPA.entity(spec); + + that.group_entity = IPA.get_entity(spec.group_entity || 'group'); + + that.init = function() { + that.entity_init(); + + that.builder.search_facet({ + columns: [ 'aciname' ], + pagination: false + }). + details_facet({ + check_rights: false, + sections: [ + { + name: 'general', + label: IPA.messages.details.general, + fields: [ + 'aciname', + { + type: 'checkboxes', + name: 'permissions', + required: true, + options: IPA.create_options(['read', 'write']) + }, + { + type: 'entity_select', + name: 'group', + other_entity: that.group_entity, + other_field: 'cn' + }, + { + type: 'entity_select', + name: 'memberof', + other_entity: that.group_entity, + other_field: 'cn' + }, + { + type: 'attributes', + name: 'attrs', + object_type: 'user' + } + ] + } + ] + }). + standard_association_facets(). + adder_dialog({ + fields: [ + 'aciname', + { + type: 'checkboxes', + name: 'permissions', + options: IPA.create_options(['read', 'write']) + }, + { + type: 'entity_select', + name: 'group', + other_entity: that.group_entity, + other_field: 'cn' + }, + { + type: 'entity_select', + name: 'memberof', + other_entity: that.group_entity, + other_field: 'cn' + }, + { + type: 'attributes', + name: 'attrs', + object_type: 'user' + } + ] + }); + }; + + return that; +}; + + +IPA.attributes_widget = function(spec) { + + spec = spec || {}; + + var that = IPA.checkboxes_widget(spec); + + that.object_type = spec.object_type; + that.skip_unmatched = spec.skip_unmatched === undefined ? false : spec.skip_unmatched; + + var id = spec.name; + + that.create = function(container) { + that.container = container; + + var attr_container = $('<div/>', { + 'class': 'aci-attribute-table-container' + }).appendTo(container); + + that.table = $('<table/>', { + id:id, + 'class':'search-table aci-attribute-table scrollable' + }). + append('<thead/>'). + append('<tbody/>'). + appendTo(attr_container); + + var tr = $('<tr></tr>').appendTo($('thead', that.table)); + + tr.append($('<th/>', { + html: $('<input/>', { + type: "checkbox", + click: function() { + $('.aci-attribute', that.table). + prop('checked', $(this).prop('checked')); + that.value_changed.notify([], that); + } + }) + })).append($('<th/>', { + 'class': 'aci-attribute-column', + html: IPA.messages.objects.aci.attribute + })); + + if (that.undo) { + that.create_undo(container); + } + + if (that.object_type) { + that.populate(that.object_type); + } + + that.create_error_link(container); + }; + + that.create_options = function(options) { + var tbody = $('tbody', that.table); + + for (var i=0; i<options.length ; i++){ + var value = options[i].toLowerCase(); + var tr = $('<tr/>').appendTo(tbody); + + var td = $('<td/>').appendTo(tr); + td.append($('<input/>',{ + type: 'checkbox', + name: that.name, + value: value, + 'class': 'aci-attribute', + change: function() { + that.value_changed.notify([], that); + } + })); + td = $('<td/>').appendTo(tr); + td.append($('<label/>',{ + text: value + })); + } + }; + + that.update = function(values) { + + that.values = []; + + values = values || []; + for (var i=0; i<values.length; i++) { + + var value = values[i]; + + if (!value || value === '') continue; + + value = value.toLowerCase(); + that.values.push(value); + } + + that.populate(that.object_type); + that.append(); + that.checkboxes_update(values); + }; + + that.populate = function(object_type) { + + $('tbody tr', that.table).remove(); + + if (!object_type || object_type === '') return; + + var metadata = IPA.metadata.objects[object_type]; + if (!metadata) return; + + var aciattrs = metadata.aciattrs; + + that.create_options(aciattrs); + }; + + that.append = function() { + + if (!that.values) return; + + var unmatched = []; + + for (var i=0; i<that.values.length; i++) { + var input = $('input[name="'+that.name+'"]'+ + '[value="'+that.values[i]+'"]', that.container); + if (!input.length) { + unmatched.push(that.values[i]); + } + } + + if (unmatched.length > 0 && !that.skip_unmatched) { + that.create_options(unmatched); + } + }; + + that.show_undo = function() { + $(that.undo_span).css('display', 'inline-block'); + }; + + return that; +}; + +IPA.widget_factories['attributes'] = IPA.attributes_widget; +IPA.field_factories['attributes'] = IPA.checkboxes_field; + +IPA.rights_widget = function(spec) { + + var that = IPA.checkboxes_widget(spec); + + that.rights = ['write', 'add', 'delete']; + for (var i=0; i<that.rights.length; i++) { + var right = that.rights[i]; + that.add_option({label: right, value: right}); + } + + return that; +}; + +IPA.widget_factories['rights'] = IPA.rights_widget; +IPA.field_factories['rights'] = IPA.checkboxes_field; + +IPA.permission_target_widget = function(spec) { + + spec = spec || {}; + + var factory = spec.container_factory || IPA.details_table_section_nc; + + var that = factory(spec); + + that.group_entity = IPA.get_entity(spec.group_entity || 'group'); + + that.targets = [ 'filter', 'subtree', 'targetgroup', 'type' ]; + that.target = that.targets[0]; + that.show_target = spec.show_target; + + var init = function() { + + that.target_select = IPA.select_widget({ + entity: that.entity, + name: 'target', + label: IPA.messages.objects.permission.target, + hidden: !that.show_target + }); + + for (var i=0; i<that.targets.length; i++) { + var target = that.targets[i]; + var target_param = IPA.get_entity_param('permission', target); + + that.target_select.options.push({ + label: target_param.label, + value: target + }); + } + + that.widgets.add_widget(that.target_select); + + + that.memberof_select = IPA.entity_select_widget({ + entity: that.entity, + name: 'memberof', + other_entity: that.group_entity, + other_field: 'cn', + hidden: true + }); + + that.widgets.add_widget(that.memberof_select); + + that.filter_text = IPA.text_widget({ + entity: that.entity, + name: 'filter', + hidden: true + }); + + that.widgets.add_widget(that.filter_text); + + that.subtree_textarea = IPA.textarea_widget({ + entity: that.entity, + name: 'subtree', + hidden: true + }); + + that.widgets.add_widget(that.subtree_textarea); + + that.group_select = IPA.entity_select_widget({ + entity: that.entity, + name: 'targetgroup', + other_entity: that.group_entity, + other_field: 'cn', + hidden: true + }); + + that.widgets.add_widget(that.group_select); + + that.type_select = IPA.select_widget({ + entity: that.entity, + name: 'type', + hidden: true + }); + + var type_param = IPA.get_entity_param('permission', 'type'); + + for (var j=0; j<type_param.values.length; j++) { + var type_name = type_param.values[j]; + var type_label = IPA.metadata.objects[type_name].label_singular; + + that.type_select.options.push({ + label: type_label, + value: type_name + }); + } + + that.widgets.add_widget(that.type_select); + + that.attribute_table = IPA.attributes_widget({ + entity: that.entity, + name: 'attrs', + object_type: type_param.values[0], + hidden: true + }); + + that.widgets.add_widget(that.attribute_table); + + that.attribute_multivalued = IPA.multivalued_widget({ + entity: that.entity, + name: 'attrs_multi', + hidden: true + }); + + that.widgets.add_widget(that.attribute_multivalued); + }; + + init(); + + return that; +}; + +IPA.permission_target_policy = function (widget_name) { + + var that = IPA.facet_policy(); + + that.init = function() { + + that.permission_target = that.container.widgets.get_widget(widget_name); + var widgets = that.permission_target.widgets; + + var target_select = widgets.get_widget('target'); + target_select.value_changed.attach(function() { + var target = target_select.save()[0]; + that.select_target(target); + }); + + var type_select = widgets.get_widget('type'); + + type_select.value_changed.attach(function() { + var type = type_select.save()[0]; + that.set_attrs_type(type, true); + }); + + type_select.undo_clicked.attach(function() { + var type = type_select.save()[0]; + that.set_attrs_type(type, true); + }); + }; + + that.set_attrs_type = function(type, skip_unmatched) { + var attribute_field = that.container.fields.get_field('attrs'); + var attribute_table = that.permission_target.widgets.get_widget('attrs'); + var skip_unmatched_org = attribute_table.skip_unmatched; + attribute_table.object_type = type; + // skip values which don't belong to new type. Bug #2617 + attribute_table.skip_unmatched = skip_unmatched || skip_unmatched_org; + attribute_field.reset(); + // force value_change to update dirty status if some unmatched values were skipped + attribute_table.value_changed.notify([], attribute_table); + attribute_table.skip_unmatched = skip_unmatched_org; + }; + + that.update_attrs = function() { + + var type_select = that.permission_target.widgets.get_widget('type'); + var type = type_select.save()[0]; + that.set_attrs_type(type, false); + }; + + that.post_create = function() { + that.select_target(that.permission_target.targets[0]); + }; + + that.post_load = function(data) { + + var displayed_target; + + for (var target in that.target_mapping) { + + if (data.result.result[target]) { + displayed_target = target; + } else { + that.set_target_visible(target, false); + } + } + + if (displayed_target) { + that.permission_target.target = displayed_target; + that.set_target_visible(displayed_target, true); + } + }; + + that.select_target = function(target) { + that.set_target_visible(that.permission_target.target, false); + that.permission_target.target = target; + that.set_target_visible(that.permission_target.target, true); + }; + + that.set_target_visible = function(target, visible) { + + var target_info = that.target_mapping[target]; + that.set_target_visible_core(target_info, visible); + }; + + that.set_target_visible_core = function(target_info, visible) { + var widget = that.permission_target.widgets.get_widget(target_info.name); + var field = that.container.fields.get_field(target_info.name); + that.permission_target.set_row_visible(target_info.name, visible); + field.enabled = visible; + field.set_required(visible && target_info.required); + widget.hidden = !visible; + + if (target_info.additional) { + for (var i=0; i<target_info.additional.length; i++) { + var nested_info = target_info.additional[i]; + that.set_target_visible_core(nested_info, visible); + } + } + + if (target_info.action) target_info.action(); + }; + + + that.target_mapping = { + filter: { + name: 'filter', + required: true, + additional: [ + { + name: 'attrs_multi' + } + ] + }, + subtree: { + name: 'subtree', + required: true, + additional: [ + { + name: 'memberof' + }, + { + name: 'attrs_multi' + } + ] + }, + targetgroup: { + name: 'targetgroup', + required: true, + additional: [ + { + name: 'attrs' + } + ], + action: function() { + that.set_attrs_type('group', false); + } + }, + type: { + name: 'type', + additional: [ + { + name: 'memberof' + }, + { + name: 'attrs' + } + ], + action: function() { + that.update_attrs(); + } + } + }; + + + return that; +}; + +IPA.widget_factories['permission_target'] = IPA.permission_target_widget; + + +IPA.register('permission', IPA.aci.permission_entity); +IPA.register('privilege', IPA.aci.privilege_entity); +IPA.register('role', IPA.aci.role_entity); +IPA.register('selfservice', IPA.aci.selfservice_entity); +IPA.register('delegation', IPA.aci.delegation_entity); diff --git a/install/ui/src/freeipa/add.js b/install/ui/src/freeipa/add.js new file mode 100644 index 000000000..6d3f454be --- /dev/null +++ b/install/ui/src/freeipa/add.js @@ -0,0 +1,207 @@ +/*jsl:import ipa.js */ + +/* Authors: + * Pavel Zuna <pzuna@redhat.com> + * Endi Sukma Dewata <edewata@redhat.com> + * + * Copyright (C) 2010 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/>. +*/ + +/* REQUIRES: ipa.js */ + +IPA.entity_adder_dialog = function(spec) { + + spec = spec || {}; + + spec.name = spec.name || 'entity_adder_dialog'; + + var that = IPA.dialog(spec); + + IPA.confirm_mixin().apply(that); + + that.method = spec.method || 'add'; + that.on_error = spec.on_error ; + that.retry = typeof spec.retry !== 'undefined' ? spec.retry : true; + that.command = null; + that.added = IPA.observer(); + that.subject = spec.subject || that.entity.metadata.label_singular; + + that.show_edit_page = spec.show_edit_page || show_edit_page; + + var init = function() { + that.create_button({ + name: 'add', + label: IPA.messages.buttons.add, + click: function() { + that.on_add(); + } + }); + + that.create_button({ + name: 'add_and_add_another', + label: IPA.messages.buttons.add_and_add_another, + click: function() { + that.hide_message(); + that.add( + function(data, text_status, xhr) { + that.added.notify(); + that.show_message(that.get_success_message(data)); + var facet = IPA.current_entity.get_facet(); + facet.refresh(); + that.reset(); + that.focus_first_element(); + }, + that.on_error); + } + }); + + that.create_button({ + name: 'add_and_edit', + label: IPA.messages.buttons.add_and_edit, + click: function() { + that.hide_message(); + that.add( + function(data, text_status, xhr) { + that.added.notify(); + that.close(); + var result = data.result.result; + that.show_edit_page(that.entity, result); + that.notify_success(data); + }, + that.on_error); + } + }); + + that.create_button({ + name: 'cancel', + label: IPA.messages.buttons.cancel, + click: function() { + that.hide_message(); + that.close(); + } + }); + }; + + that.on_add = function() { + + that.hide_message(); + that.add( + function(data, text_status, xhr) { + that.added.notify(); + var facet = IPA.current_entity.get_facet(); + facet.refresh(); + that.close(); + that.notify_success(data); + }, + that.on_error); + }; + + that.on_confirm = function() { + that.on_add(); + }; + + that.get_success_message = function(data) { + var message = IPA.messages.dialogs.add_confirmation; + return message.replace('${entity}', that.subject); + }; + + that.notify_success = function(data) { + IPA.notify_success(that.get_success_message(data)); + }; + + function show_edit_page(entity,result) { + var pkey_name = entity.metadata.primary_key; + var pkey = result[pkey_name]; + if (pkey instanceof Array) { + pkey = pkey[0]; + } + IPA.nav.show_entity_page(that.entity, 'default', pkey); + } + + that.create_add_command = function(record) { + + var pkey_name = that.entity.metadata.primary_key; + + var command = IPA.command({ + entity: that.entity.name, + method: that.method, + retry: that.retry + }); + + command.add_args(that.entity.get_primary_key_prefix()); + + var fields = that.fields.get_fields(); + for (var j=0; j<fields.length; j++) { + var field = fields[j]; + + var values = record[field.param]; + if (!values || values.length === 0) continue; + if (field.flags.indexOf('no_command') > -1) continue; + + if (field.param === pkey_name) { + command.add_arg(values[0]); + } else if (values.length === 1) { + command.set_option(field.param, values[0]); + } else { + command.set_option(field.param, values); + } + } + + return command; + }; + + that.add = function(on_success, on_error) { + + if (!that.validate()) return; + + var record = {}; + that.save(record); + + that.command = that.create_add_command(record); + that.command.on_success = on_success; + that.command.on_error = on_error; + + that.command.execute(); + }; + + that.create = function() { + that.dialog_create(); + + var div = $('<div/>', { + }).appendTo(that.container); + + $('<span/>', { + 'class': 'required-indicator', + text: IPA.required_indicator + }).appendTo(div); + + div.append(' '); + + $('<span/>', { + text: IPA.messages.widget.validation.required + }).appendTo(div); + }; + + // methods that should be invoked by subclasses + that.entity_adder_dialog_create = that.create; + that.entity_adder_dialog_create_add_command = that.create_add_command; + that.entity_adder_dialog_get_success_message = that.get_success_message; + + init(); + + return that; +}; diff --git a/install/ui/src/freeipa/association.js b/install/ui/src/freeipa/association.js new file mode 100644 index 000000000..70b8e9f1e --- /dev/null +++ b/install/ui/src/freeipa/association.js @@ -0,0 +1,1389 @@ +/*jsl:import ipa.js */ + +/* Authors: + * Adam Young <ayoung@redhat.com> + * Petr Vobornik <pvoborni@redhat.com> + * + * Copyright (C) 2010 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/>. +*/ + +/* REQUIRES: ipa.js */ +/* CURRENTLY ALSO REQUIRES search.js, because it reuses it's code to create + * the AssociationList elements; IT NEEDS IT'S OWN CODE! */ + +IPA.associator = function (spec) { + + spec = spec || {}; + + var that = {}; + + that.entity = IPA.get_entity(spec.entity); + that.pkey = spec.pkey; + + that.other_entity = IPA.get_entity(spec.other_entity); + that.values = spec.values; + + that.method = spec.method; + + that.on_success = spec.on_success; + that.on_error = spec.on_error; + + that.execute = function() { + }; + + return that; +}; + + +/** + * This associator is built for the case where each association requires a separate rpc + */ +IPA.serial_associator = function(spec) { + + spec = spec || {}; + + var that = IPA.associator(spec); + + that.execute = function() { + + if (!that.values || !that.values.length) { + that.on_success(); + return; + } + + var batch = IPA.batch_command({ + on_success: that.on_success, + on_error: that.on_error + }); + + var args, options, command; + + for(var i=0; i < that.values.length; i++) { + args = [that.values[i]]; + options = {}; + options[that.entity.name] = that.pkey; + + command = IPA.command({ + entity: that.other_entity.name, + method: that.method, + args: args, + options: options + }); + + batch.add_command(command); + } + //alert(JSON.stringify(command.to_json())); + + batch.execute(); + }; + + return that; +}; + +/** + * This associator is for the common case where all the asociations can be sent + * in a single rpc + */ +IPA.bulk_associator = function(spec) { + + spec = spec || {}; + + var that = IPA.associator(spec); + + that.execute = function() { + + if (!that.values || !that.values.length) { + that.on_success(); + return; + } + + var command = IPA.command({ + entity: that.entity.name, + method: that.method, + args: [that.pkey], + options: { 'all': true }, + on_success: that.on_success, + on_error: that.on_error + }); + + command.set_option(that.other_entity.name, that.values); + + //alert(JSON.stringify(command.to_json())); + + command.execute(); + }; + + return that; +}; + +/** + * This dialog is for adding value of multivalued attribute which behaves like + * association attribute. + */ +IPA.attribute_adder_dialog = function(spec) { + + spec = spec || {}; + + spec.name = spec.name || 'attr_adder_dialog'; + spec.method = spec.method || 'add_member'; + + var metadata = IPA.get_command_option(spec.entity.name+'_'+spec.method, spec.attribute); + + spec.fields = spec.fields || [ + { + name: spec.attribute, + metadata: metadata, + required: true + } + ]; + spec.title = spec.title || IPA.messages.dialogs.add_title.replace('${entity}', metadata.label); + spec.subject = metadata.label; + + var that = IPA.entity_adder_dialog(spec); + + that.create_add_command = function(record) { + + var command = that.entity_adder_dialog_create_add_command(record); + + command.add_args(that.entity.get_primary_key()); + + return command; + }; + + that.create_buttons = function() { + + that.buttons.remove('add_and_edit'); + }; + + that.create_buttons(); + + return that; +}; + +/** + * This dialog is used for adding associations between two entities. + */ +IPA.association_adder_dialog = function(spec) { + + spec = spec || {}; + + var that = IPA.adder_dialog(spec); + + that.entity = IPA.get_entity(spec.entity); + that.pkey = spec.pkey; + + that.other_entity = IPA.get_entity(spec.other_entity); + that.attribute_member = spec.attribute_member; + + that.exclude = spec.exclude || []; + + var init = function() { + if (!that.get_columns().length) { + var pkey_name = that.other_entity.metadata.primary_key; + that.create_column({ + entity: that.entity, + name: pkey_name, + label: that.other_entity.metadata.label, + primary_key: true, + width: '600px' + }); + } + }; + + that.search = function() { + function on_success(data, text_status, xhr) { + + that.clear_available_values(); + + var pkey_attr = that.other_entity.metadata.primary_key; + + var selected = that.get_selected_values(); + + var results = data.result; + var same_entity = that.entity === that.other_entity; + for (var i=0; i<results.count; i++) { + var result = results.result[i]; + var pkey = result[pkey_attr][0]; + + if (same_entity && pkey === that.pkey) continue; + if (that.exclude.indexOf(pkey) >= 0) continue; + if (selected.indexOf(pkey) >= 0) continue; + + that.add_available_value(result); + } + } + + var options = { all: true }; + var relationships = that.other_entity.metadata.relationships; + + /* TODO: better generic handling of different relationships! */ + var other_attribute_member = ''; + if (that.attribute_member == 'member') + other_attribute_member = 'memberof'; + else if (that.attribute_member == 'memberuser') + other_attribute_member = 'memberof'; + else if (that.attribute_member == 'memberhost') + other_attribute_member = 'memberof'; + else if (that.attribute_member == 'memberof') + other_attribute_member = 'member'; + else if (that.attribute_member == 'managedby') + other_attribute_member = 'managing'; + + var relationship = relationships[other_attribute_member]; + if (relationship) { + var param_name = relationship[2] + that.entity.name; + var cmd_opt = IPA.get_command_option(that.other_entity.name + '_find', + param_name); + if (cmd_opt) { + options[param_name] = that.pkey; + } + } + + IPA.command({ + entity: that.other_entity.name, + method: 'find', + args: [that.get_filter()], + options: options, + on_success: on_success + }).execute(); + }; + + init(); + + return that; +}; + + +/** + * This dialog is used for removing associations between two entities. + */ +IPA.association_deleter_dialog = function (spec) { + + spec = spec || {}; + + var that = IPA.deleter_dialog(spec); + + that.entity = IPA.get_entity(spec.entity); + that.pkey = spec.pkey; + + that.other_entity = IPA.get_entity(spec.other_entity); + that.values = spec.values; + + that.associator = spec.associator; + that.method = spec.method || 'remove_member'; + + that.on_success = spec.on_success; + that.on_error = spec.on_error; + + that.execute = function() { + + var associator = that.associator({ + entity: that.entity, + pkey: that.pkey, + other_entity: that.other_entity, + values: that.values, + method: that.method, + on_success: that.on_success, + on_error: that.on_error + }); + + associator.execute(); + }; + + return that; +}; + + +IPA.association_config = function (spec) { + + spec = spec || {}; + + var that = {}; + + that.name = spec.name; + that.associator = spec.associator; + that.add_method = spec.add_method; + that.remove_method = spec.remove_method; + + return that; +}; + +IPA.association_table_widget = function (spec) { + + spec = spec || {}; + + var index = spec.name.indexOf('_'); + spec.attribute_member = spec.attribute_member || spec.name.substring(0, index); + spec.other_entity = spec.other_entity || spec.name.substring(index+1); + + spec.managed_entity = IPA.get_entity(spec.other_entity); + + var that = IPA.table_widget(spec); + + that.other_entity = IPA.get_entity(spec.other_entity); + that.attribute_member = spec.attribute_member; + + that.associator = spec.associator || IPA.bulk_associator; + that.add_method = spec.add_method || 'add_member'; + that.remove_method = spec.remove_method || 'remove_member'; + + that.add_title = spec.add_title || IPA.messages.association.add.member; + that.remove_title = spec.remove_title || IPA.messages.association.remove.member; + + that.adder_columns = $.ordered_map(); + + that.needs_refresh = IPA.observer(); + + that.get_adder_column = function(name) { + return that.adder_columns.get(name); + }; + + that.add_adder_column = function(column) { + that.adder_columns.put(column.name, column); + }; + + that.create_adder_column = function(spec) { + spec.entity = that.other_entity; + var column = IPA.column(spec); + that.add_adder_column(column); + return column; + }; + + that.create_columns = function() { + // create a column if none defined + if (!that.columns.length) { + that.create_column({ + name: that.name, + label: that.label, + entity: that.other_entity, + primary_key: true, + link: true + }); + } + }; + + that.init_columns = function() { + var column; + var columns = that.columns.values; + for (var i=0; i<columns.length; i++) { + column = columns[i]; + column.entity = that.other_entity; + + if (column.link) { + column.link_handler = function(value) { + IPA.nav.show_page(that.other_entity.name, 'default', value); + return false; + }; + } + } + }; + + that.init_adder_columns = function() { + var column; + var adder_columns = that.adder_columns.values; + for (var j=0; j<adder_columns.length; j++) { + column = adder_columns[j]; + column.entity = that.other_entity; + } + }; + + that.init = function() { + + that.create_columns(); + that.init_columns(); + that.init_adder_columns(); + }; + + that.create = function(container) { + + that.init(); + + that.table_create(container); + + that.remove_button = IPA.action_button({ + name: 'remove', + label: IPA.messages.buttons.remove, + icon: 'remove-icon', + 'class': 'action-button-disabled', + click: function() { + if (!that.remove_button.hasClass('action-button-disabled')) { + that.remove_handler(); + } + return false; + } + }).appendTo(that.buttons); + + that.add_button = IPA.action_button({ + name: 'add', + label: IPA.messages.buttons.add, + icon: 'add-icon', + click: function() { + if (!that.add_button.hasClass('action-button-disabled')) { + that.add_handler(); + } + return false; + } + }).appendTo(that.buttons); + }; + + that.add_handler = function() { + var facet = that.entity.get_facet(); + + if (facet.is_dirty()) { + var dialog = IPA.dirty_dialog({ + entity:that.entity, + facet: facet + }); + + dialog.callback = function() { + that.show_add_dialog(); + }; + + dialog.open(that.container); + + } else { + that.show_add_dialog(); + } + }; + + that.remove_handler = function() { + var facet = that.entity.get_facet(); + + if (facet.is_dirty()) { + var dialog = IPA.dirty_dialog({ + entity:that.entity, + facet: facet + }); + + dialog.callback = function() { + that.show_remove_dialog(); + }; + + dialog.open(that.container); + + } else { + that.show_remove_dialog(); + } + }; + + that.set_enabled = function(enabled) { + that.table_set_enabled(enabled); + if (enabled) { + if(that.add_button) { + that.add_button.removeClass('action-button-disabled'); + } + } else { + $('.action-button', that.table).addClass('action-button-disabled'); + that.unselect_all(); + } + that.enabled = enabled; + }; + + that.select_changed = function() { + + var values = that.get_selected_values(); + + if (that.remove_button) { + if (values.length === 0) { + that.remove_button.addClass('action-button-disabled'); + } else { + that.remove_button.removeClass('action-button-disabled'); + } + } + }; + + that.load = function(result) { + that.values = result[that.name] || []; + that.update(); + that.unselect_all(); + }; + + that.update = function(values) { + + if (values) that.values = values; + + that.empty(); + + var i; + var columns = that.columns.values; + if (columns.length == 1) { // show pkey only + var name = columns[0].name; + for (i=0; i<that.values.length; i++) { + var record = {}; + record[name] = that.values[i]; + that.add_record(record); + } + } else { + for (i=0; i<that.values.length; i++) { + that.add_record(that.values[i]); + } + } + }; + + that.create_add_dialog = function() { + + var entity_label = that.entity.metadata.label_singular; + var pkey = IPA.nav.get_state(that.entity.name+'-pkey'); + var other_entity_label = that.other_entity.metadata.label; + + var title = that.add_title; + title = title.replace('${entity}', entity_label); + title = title.replace('${primary_key}', pkey); + title = title.replace('${other_entity}', other_entity_label); + + return IPA.association_adder_dialog({ + title: title, + entity: that.entity, + pkey: pkey, + other_entity: that.other_entity, + attribute_member: that.attribute_member, + method: that.add_method, + exclude: that.values + }); + }; + + that.show_add_dialog = function() { + + var dialog = that.create_add_dialog({entity:that.entity}); + + var columns = that.adder_columns.values; + if (columns.length) { + dialog.set_columns(columns); + } + + dialog.execute = function() { + that.add( + dialog.get_selected_values(), + function() { + that.refresh(); + dialog.close(); + IPA.notify_success(IPA.messages.association.added); + }, + function() { + that.refresh(); + dialog.close(); + } + ); + }; + + dialog.open(that.container); + }; + + that.add = function(values, on_success, on_error) { + + var pkey = IPA.nav.get_state(that.entity.name+'-pkey'); + + var command = IPA.command({ + entity: that.entity.name, + method: that.add_method, + args: [pkey], + on_success: on_success, + on_error: on_error + }); + command.set_option(that.other_entity.name, values); + + command.execute(); + }; + + that.show_remove_dialog = function() { + + var selected_values = that.get_selected_values(); + + if (!selected_values.length) { + var message = IPA.messages.dialogs.remove_empty; + alert(message); + return; + } + + var entity_label = that.entity.metadata.label_singular; + var pkey = IPA.nav.get_state(that.entity.name+'-pkey'); + var other_entity_label = that.other_entity.metadata.label; + + var title = that.remove_title; + title = title.replace('${entity}', entity_label); + title = title.replace('${primary_key}', pkey); + title = title.replace('${other_entity}', other_entity_label); + + var dialog = IPA.association_deleter_dialog({ + title: title, + entity: that.entity, + pkey: pkey, + other_entity: that.other_entity, + values: selected_values, + method: that.remove_method + }); + + dialog.execute = function() { + that.remove( + selected_values, + function() { + that.refresh(); + IPA.notify_success(IPA.messages.association.removed); + }, + function() { + that.refresh(); + } + ); + }; + + + dialog.open(that.container); + }; + + that.remove = function(values, on_success, on_error) { + + var pkey = IPA.nav.get_state(that.entity.name+'-pkey'); + + var command = IPA.command({ + entity: that.entity.name, + method: that.remove_method, + args: [pkey], + on_success: on_success, + on_error: on_error + }); + + command.set_option(that.other_entity.name, values); + + command.execute(); + }; + + that.refresh = function() { + + that.needs_refresh.notify([], that); + }; + + /*initialization code*/ + /*this is duplicated in the facet... should be unified*/ + var i; + if (spec.columns){ + for (i = 0; i < spec.columns.length; i+= 1){ + spec.columns[i].entity = spec.columns[i].entity || that.other_entity; + that.create_column(spec.columns[i]); + } + } + if (spec.adder_columns){ + for (i = 0; i < spec.adder_columns.length; i+= 1){ + that.create_adder_column(spec.adder_columns[i]); + } + } + + // methods that should be invoked by subclasses + that.association_table_widget_create_columns = that.create_columns; + that.association_table_widget_show_add_dialog = that.show_add_dialog; + that.association_table_widget_show_remove_dialog = that.show_remove_dialog; + + return that; +}; + +IPA.association_table_field = function (spec) { + + spec = spec || {}; + + var that = IPA.field(spec); + + that.refresh = function() { + + function on_success(data, text_status, xhr) { + that.load(data.result.result); + } + + function on_error(xhr, text_status, error_thrown) { + that.widget.summary.text(error_thrown.name+': '+error_thrown.message); + } + + var pkey = IPA.nav.get_state(that.entity.name+'-pkey'); + IPA.command({ + entity: that.entity.name, + method: 'show', + args: [pkey], + options: { all: true, rights: true }, + on_success: on_success, + on_error: on_error + }).execute(); + }; + + that.widgets_created = function() { + + that.field_widgets_created(); + that.widget.needs_refresh.attach(that.refresh); + }; + + return that; +}; + +IPA.widget_factories['association_table'] = IPA.association_table_widget; +IPA.field_factories['association_table'] = IPA.association_table_field; + + +IPA.association_facet = function (spec, no_init) { + + spec = spec || {}; + + /* + Link parameter is used to turn off the links in selfservice mode. + Default it to true if not set so that facets that would not otherwise + link by default get links set. + + link must be set before the call to the base class, to affect the table. + */ + spec.link = spec.link === undefined ? true : spec.link; + spec.managed_entity = IPA.get_entity(spec.other_entity); + + + //default buttons and their actions + spec.actions = spec.actions || []; + spec.actions.unshift( + IPA.refresh_action, + { + name: 'remove', + hide_cond: ['read-only'], + show_cond: ['direct'], + enable_cond: ['item-selected'], + handler: function(facet) { + facet.show_remove_dialog(); + } + }, + { + name: 'add', + hide_cond: ['read-only'], + show_cond: ['direct'], + handler: function(facet) { + facet.show_add_dialog(); + } + } + ); + + spec.control_buttons = spec.control_buttons || []; + spec.control_buttons.unshift( + { + name: 'refresh', + label: IPA.messages.buttons.refresh, + icon: 'reset-icon' + }, + { + name: 'remove', + label: IPA.messages.buttons.remove, + icon: 'remove-icon' + }, + { + name: 'add', + label: IPA.messages.buttons.add, + icon: 'add-icon' + }); + + spec.state = spec.state || {}; + spec.state.evaluators = spec.state.evaluators || []; + spec.state.evaluators.push( + IPA.selected_state_evaluator, + IPA.association_type_state_evaluator, + IPA.read_only_state_evaluator); + + var that = IPA.table_facet(spec, true); + + that.attribute_member = spec.attribute_member; + that.indirect_attribute_member = spec.indirect_attribute_member; + + that.other_entity = IPA.get_entity(spec.other_entity); + + that.association_type = 'direct'; + that.facet_group = spec.facet_group; + + that.read_only = spec.read_only; + + that.associator = spec.associator || IPA.bulk_associator; + that.add_method = spec.add_method || 'add_member'; + that.remove_method = spec.remove_method || 'remove_member'; + + that.add_title = spec.add_title || IPA.messages.association.add.member; + that.remove_title = spec.remove_title || IPA.messages.association.remove.member; + + that.adder_columns = $.ordered_map(); + + that.get_adder_column = function(name) { + return that.adder_columns.get(name); + }; + + that.add_adder_column = function(column) { + column.entity = that.other_entity; + that.adder_columns.put(column.name, column); + }; + + /*TODO try to reuse the association_table_widget in association_facet*/ + that.create_adder_column = function(spec) { + var column; + var factory; + if (spec instanceof Object) { + factory = spec.factory || IPA.column; + } else { + factory = IPA.column; + spec = { name: spec }; + } + spec.entity = that.other_entity; + column = factory(spec); + that.add_adder_column(column); + return column; + }; + + var init = function() { + + var column; + var i; + + var pkey_name; + if (that.other_entity) { + pkey_name = that.other_entity.metadata.primary_key; + } + + if (!that.columns.length){ + that.create_column({ + name: pkey_name + }); + } + + var columns = that.columns.values; + for (i=0; i<columns.length; i++) { + column = columns[i]; + column.link = spec.link; + } + + that.init_table(that.other_entity); + + var adder_columns = spec.adder_columns || []; + for (i=0; i<adder_columns.length; i++) { + that.create_adder_column(adder_columns[i]); + } + + if (!that.adder_columns.length) { + that.create_adder_column({ + name: pkey_name, + primary_key: true + }); + } + + adder_columns = that.adder_columns.values; + for (i=0; i<adder_columns.length; i++) { + column = adder_columns[i]; + column.entity = that.other_entity; + } + }; + + that.get_records_command_name = function() { + return that.entity.name+'_'+that.get_attribute_name(); + }; + + that.create_header = function(container) { + + that.facet_create_header(container); + + if (that.indirect_attribute_member) { + + var div = $('<div/>', { + 'class': 'right-aligned-facet-controls' + }).appendTo(that.controls); + + div.append(IPA.messages.association.show_results); + div.append(' '); + + var name = that.entity.name+'-'+that.attribute_member+'-'+that.other_entity.name+'-type-radio'; + var direct_id = name + '-direct'; + + that.direct_radio = $('<input/>', { + id: direct_id, + type: 'radio', + name: name, + value: 'direct', + click: function() { + that.association_type = $(this).val(); + that.refresh(); + return true; + } + }).appendTo(div); + + $('<label/>', { + text: IPA.messages.association.direct_membership, + 'for': direct_id + }).appendTo(div); + + div.append(' '); + + var indirect_id = name + '-indirect'; + + that.indirect_radio = $('<input/>', { + id: indirect_id, + type: 'radio', + name: name, + value: 'indirect', + click: function() { + that.association_type = $(this).val(); + that.refresh(); + return true; + } + }).appendTo(div); + + $('<label/>', { + text: IPA.messages.association.indirect_membership, + 'for': indirect_id + }).appendTo(div); + } + + that.create_control_buttons(that.controls); + }; + + that.get_attribute_name = function() { + if (that.association_type == 'direct') { + return that.attribute_member+'_'+that.other_entity.name; + } else { + return that.indirect_attribute_member+'_'+that.other_entity.name; + } + }; + + that.show = function() { + that.facet_show(); + + that.pkey = IPA.nav.get_state(that.entity.name+'-pkey'); + that.header.set_pkey(that.pkey); + }; + + that.show_add_dialog = function() { + + var entity_label = that.entity.metadata.label_singular; + var pkey = IPA.nav.get_state(that.entity.name+'-pkey'); + var other_entity_label = that.other_entity.metadata.label; + + var title = that.add_title; + title = title.replace('${entity}', entity_label); + title = title.replace('${primary_key}', pkey); + title = title.replace('${other_entity}', other_entity_label); + + var pkeys = that.data.result.result[that.get_attribute_name()]; + + var dialog = IPA.association_adder_dialog({ + title: title, + entity: that.entity, + pkey: pkey, + other_entity: that.other_entity, + attribute_member: that.attribute_member, + exclude: pkeys + }); + + var adder_columns = that.adder_columns.values; + if (adder_columns.length) { + dialog.set_columns(adder_columns); + } + + dialog.execute = function() { + + var pkey = IPA.nav.get_state(that.entity.name+'-pkey'); + + var associator = that.associator({ + entity: that.entity, + pkey: pkey, + other_entity: that.other_entity, + values: dialog.get_selected_values(), + method: that.add_method, + on_success: function() { + that.refresh(); + dialog.close(); + IPA.notify_success(IPA.messages.association.added); + }, + on_error: function() { + that.refresh(); + dialog.close(); + } + }); + + associator.execute(); + }; + + dialog.open(that.container); + }; + + that.show_remove_dialog = function() { + + var values = that.table.get_selected_values(); + + if (!values.length) { + var message = IPA.messages.dialogs.remove_empty; + alert(message); + return; + } + + var entity_label = that.entity.metadata.label_singular; + var pkey = IPA.nav.get_state(that.entity.name+'-pkey'); + var other_entity_label = that.other_entity.metadata.label; + + var title = that.remove_title; + title = title.replace('${entity}', entity_label); + title = title.replace('${primary_key}', pkey); + title = title.replace('${other_entity}', other_entity_label); + + var dialog = IPA.association_deleter_dialog({ + title: title, + entity: that.entity, + pkey: pkey, + other_entity: that.other_entity, + values: values + }); + + dialog.execute = function() { + + var associator = that.associator({ + entity: that.entity, + pkey: pkey, + other_entity: that.other_entity, + values: values, + method: that.remove_method, + on_success: function() { + that.refresh(); + IPA.notify_success(IPA.messages.association.removed); + }, + on_error: function() { + that.refresh(); + } + }); + + associator.execute(); + }; + + dialog.open(that.container); + }; + + that.get_records_map = function(data) { + + var records_map = $.ordered_map(); + var association_name = that.get_attribute_name(); + var pkey_name = that.managed_entity.metadata.primary_key; + + var pkeys = data.result.result[association_name]; + for (var i=0; pkeys && i<pkeys.length; i++) { + var pkey = pkeys[i]; + var record = {}; + record[pkey_name] = pkey; + records_map.put(pkey, record); + } + + return records_map; + }; + + that.refresh = function() { + + if (that.association_type == 'direct') { + if (that.direct_radio) that.direct_radio.prop('checked', true); + } else { + if (that.indirect_radio) that.indirect_radio.prop('checked', true); + } + + var pkey = that.entity.get_primary_key(); + + var command = IPA.command({ + entity: that.entity.name, + method: 'show', + args: pkey + }); + + command.on_success = function(data, text_status, xhr) { + that.load(data); + that.show_content(); + }; + + command.on_error = function(xhr, text_status, error_thrown) { + that.redirect_error(error_thrown); + that.report_error(error_thrown); + }; + + command.execute(); + }; + + that.clear = function() { + that.header.clear(); + that.table.clear(); + }; + + that.needs_update = function() { + if (that._needs_update !== undefined) return that._needs_update; + + var pkey = IPA.nav.get_state(that.entity.name+'-pkey'); + if (that.pkey !== pkey) return true; + + var page = parseInt(IPA.nav.get_state(that.entity.name+'-page'), 10) || 1; + if (that.table.current_page !== page) return true; + + return that.facet_needs_update(); + }; + + that.init_association_facet = function() { + + that.init_facet(); + that.init_table_columns(); + init(); + }; + + if (!no_init) that.init_association_facet(); + + + return that; +}; + +IPA.attribute_facet = function(spec, no_init) { + + spec = spec || {}; + + //default buttons and their actions + spec.actions = spec.actions || []; + spec.actions.unshift( + IPA.refresh_action, + { + name: 'remove', + hide_cond: ['read-only'], + enable_cond: ['item-selected'], + handler: function(facet) { + facet.show_remove_dialog(); + } + }, + { + name: 'add', + hide_cond: ['read-only'], + handler: function(facet) { + facet.show_add_dialog(); + } + } + ); + + spec.control_buttons = spec.control_buttons || []; + spec.control_buttons.unshift( + { + name: 'refresh', + label: IPA.messages.buttons.refresh, + icon: 'reset-icon' + }, + { + name: 'remove', + label: IPA.messages.buttons.remove, + icon: 'remove-icon' + }, + { + name: 'add', + label: IPA.messages.buttons.add, + icon: 'add-icon' + }); + + spec.state = spec.state || {}; + spec.state.evaluators = spec.state.evaluators || []; + spec.state.evaluators.push( + IPA.selected_state_evaluator, + IPA.read_only_state_evaluator, + { + factory: IPA.attr_read_only_evaluator, + attribute: spec.attribute + }); + + spec.columns = spec.columns || [ spec.attribute ]; + spec.table_name = spec.table_name || spec.attribute; + + var that = IPA.table_facet(spec, true); + + that.attribute = spec.attribute; + + that.add_method = spec.add_method || 'add_member'; + that.remove_method = spec.remove_method || 'remove_member'; + + that.create_header = function(container) { + + that.facet_create_header(container); + that.create_control_buttons(that.controls); + }; + + that.show = function() { + that.facet_show(); + + that.pkey = IPA.nav.get_state(that.entity.name+'-pkey'); + that.header.set_pkey(that.pkey); + }; + + that.get_records_map = function(data) { + + var records_map = $.ordered_map(); + var pkeys = data.result.result[that.attribute]; + + for (var i=0; pkeys && i<pkeys.length; i++) { + var pkey = pkeys[i]; + var record = {}; + record[that.attribute] = pkey; + records_map.put(pkey, record); + } + + return records_map; + }; + + that.refresh = function() { + + var pkey = that.entity.get_primary_key(); + + var command = IPA.command({ + entity: that.entity.name, + method: 'show', + args: pkey + }); + + if (command.check_option('all')) { + command.set_option('all', true); + } + if (command.check_option('rights')) { + command.set_option('rights', true); + } + + command.on_success = function(data, text_status, xhr) { + that.load(data); + that.show_content(); + }; + + command.on_error = function(xhr, text_status, error_thrown) { + that.redirect_error(error_thrown); + that.report_error(error_thrown); + }; + + command.execute(); + }; + + that.clear = function() { + that.header.clear(); + that.table.clear(); + }; + + that.needs_update = function() { + if (that._needs_update !== undefined) return that._needs_update; + + var pkey = IPA.nav.get_state(that.entity.name+'-pkey'); + if (that.pkey !== pkey) return true; + + var page = parseInt(IPA.nav.get_state(that.entity.name+'-page'), 10) || 1; + if (that.table.current_page !== page) return true; + + return that.facet_needs_update(); + }; + + that.show_add_dialog = function() { + + var dialog = IPA.attribute_adder_dialog({ + attribute: spec.attribute, + entity: that.entity + }); + + dialog.open(that.container); + }; + + that.show_remove_dialog = function() { + + var selected_values = that.get_selected_values(); + + if (!selected_values.length) { + var message = IPA.messages.dialogs.remove_empty; + alert(message); + return; + } + + var dialog = IPA.deleter_dialog({ + entity: that.entity, + values: selected_values + }); + + dialog.execute = function() { + that.remove( + selected_values, + function(data) { + that.load(data); + that.show_content(); + IPA.notify_success(IPA.messages.association.removed); + }, + function() { + that.refresh(); + } + ); + }; + + + dialog.open(that.container); + }; + + that.remove = function(values, on_success, on_error) { + + var pkey = that.entity.get_primary_key(); + + var command = IPA.command({ + entity: that.entity.name, + method: that.remove_method, + args: pkey, + on_success: on_success, + on_error: on_error + }); + + command.set_option(that.attribute, values); + + if (command.check_option('all')) { + command.set_option('all', true); + } + if (command.check_option('rights')) { + command.set_option('rights', true); + } + + command.execute(); + }; + + + that.init_attribute_facet = function() { + + that.init_facet(); + that.init_table_columns(); + that.init_table(that.entity); + }; + + if (!no_init) that.init_attribute_facet(); + + return that; +}; + +IPA.attr_read_only_evaluator = function(spec) { + + spec.name = spec.name || 'attr_read_only_evaluator'; + spec.event = spec.event || 'post_load'; + + var that = IPA.state_evaluator(spec); + that.attribute = spec.attribute; + + that.on_event = function(data) { + + var old_state, record, rights, i, state; + + old_state = that.state; + record = data.result.result; + + // ignore loads without --rights + if (!record.attributelevelrights) return; + + that.state = []; + + rights = record.attributelevelrights[that.attribute]; + + if (!rights || rights.indexOf('w') === -1) { + that.state.push('read-only'); + } + + that.notify_on_change(old_state); + }; + + return that; +};
\ No newline at end of file diff --git a/install/ui/src/freeipa/automember.js b/install/ui/src/freeipa/automember.js new file mode 100644 index 000000000..6f8eba956 --- /dev/null +++ b/install/ui/src/freeipa/automember.js @@ -0,0 +1,679 @@ +/*jsl:import ipa.js */ + +/* Authors: + * Petr Vobornik <pvoborni@redhat.com> + * + * Copyright (C) 2012 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/>. + */ + +/* REQUIRES: ipa.js, details.js, search.js, add.js, facet.js, entity.js */ + +IPA.automember = {}; + +IPA.automember.entity = function(spec) { + + //HACK: Automember takes_params is missing a cn attribute. This hack + //copies cn from mod command. Also it is set as pkey. + var pkey_attr = IPA.metadata.commands.automember_mod.takes_args[0]; + pkey_attr.primary_key = true; + IPA.metadata.objects.automember.takes_params.push(pkey_attr); + IPA.metadata.objects.automember.primary_key = pkey_attr.name; + + spec = spec || {}; + + spec.policies = spec.policies || [ + IPA.facet_update_policy({ + source_facet: 'usergrouprule', + dest_facet: 'searchgroup' + }), + IPA.facet_update_policy({ + source_facet: 'hostgrouprule', + dest_facet: 'searchhostgroup' + }) + ]; + + var that = IPA.entity(spec); + + that.init = function() { + + that.entity_init(); + + that.builder.search_facet({ + factory: IPA.automember.rule_search_facet, + name: 'searchgroup', + group_type: 'group', + label: IPA.messages.objects.automember.usergrouprules, + details_facet: 'usergrouprule', + pagination: false, + columns: [ + 'cn', + 'description' + ] + }). + search_facet({ + factory: IPA.automember.rule_search_facet, + name: 'searchhostgroup', + group_type: 'hostgroup', + label: IPA.messages.objects.automember.hostgrouprules, + details_facet: 'hostgrouprule', + pagination: false, + columns: [ + 'cn', + 'description' + ] + }). + details_facet({ + factory: IPA.automember.rule_details_facet, + name: 'usergrouprule', + group_type: 'group', + label: IPA.messages.objects.automember.usergrouprule, + disable_facet_tabs: true, + check_rights: false, + redirect_info: { tab: 'amgroup' } + }). + details_facet({ + factory: IPA.automember.rule_details_facet, + name: 'hostgrouprule', + group_type: 'hostgroup', + label: IPA.messages.objects.automember.hostgrouprule, + disable_facet_tabs: true, + check_rights: false, + redirect_info: { tab: 'amhostgroup' } + }). + adder_dialog({ + factory: IPA.automember.rule_adder_dialog, + title: IPA.messages.objects.automember.add_rule, + fields: [ + { + type: 'entity_select', + name: 'cn', + other_entity: 'group', + other_field: 'cn' + } + ], + height: '300' + }). + deleter_dialog({ + factory: IPA.automember.rule_deleter_dialog + }); + }; + + return that; +}; + + +IPA.automember.rule_search_facet = function(spec) { + + spec = spec || {}; + + var that = IPA.search_facet(spec); + + that.group_type = spec.group_type; + + var init = function() { + + that.default_group_widget = IPA.automember.default_group_widget({ + entity: that.entity, + group_type: that.group_type + }); + }; + + that.refresh = function() { + + that.search_facet_refresh(); + that.default_group_widget.refresh(); + }; + + + that.get_records_command_name = function() { + return that.managed_entity.name + that.group_type+'_get_records'; + }; + + that.get_search_command_name = function() { + var name = that.managed_entity.name + that.group_type + '_find'; + if (that.pagination && !that.search_all_entries) { + name += '_pkeys'; + } + return name; + }; + + that.create_get_records_command = function(pkeys, on_success, on_error) { + + var batch = that.table_facet_create_get_records_command(pkeys, on_success, on_error); + + for (var i=0; i<batch.commands.length; i++) { + var command = batch.commands[i]; + command.set_option('type', that.group_type); + } + + return batch; + }; + + that.create_refresh_command = function() { + + var command = that.search_facet_create_refresh_command(); + + command.set_option('type', that.group_type); + + return command; + }; + + that.create_content = function(container) { + + var header = $('<div/>', { + 'class': 'automember-header' + }).appendTo(container); + + var content = $('<div/>', { + 'class': 'automember-content' + }).appendTo(container); + + that.default_group_widget.create(header); + that.table.create(content); + + }; + + init(); + + return that; +}; + +IPA.automember.rule_details_facet = function(spec) { + + spec = spec || {}; + + spec.fields = [ + { + name: 'cn', + widget: 'general.cn' + }, + { + type: 'textarea', + name: 'description', + widget: 'general.description' + }, + { + type: 'automember_condition', + name: 'automemberinclusiveregex', + widget: 'inclusive.automemberinclusiveregex' + }, + { + type: 'automember_condition', + name: 'automemberexclusiveregex', + widget: 'exclusive.automemberexclusiveregex' + } + ]; + + spec.widgets = [ + { + type: 'details_table_section', + name: 'general', + label: IPA.messages.details.general, + widgets: [ + { + name: 'cn' + }, + { + type: 'textarea', + name: 'description' + } + ] + }, + { + factory: IPA.collapsible_section, + name: 'inclusive', + label: IPA.messages.objects.automember.inclusive, + widgets: [ + { + type: 'automember_condition', + name: 'automemberinclusiveregex', + group_type: spec.group_type, + add_command: 'add_condition', + remove_command: 'remove_condition', + adder_dialog: { + title: IPA.messages.objects.automember.add_condition, + fields: [ + { + name: 'key', + type: 'select', + options: IPA.automember.get_condition_attributes(spec.group_type), + label: IPA.messages.objects.automember.attribute + }, + { + name: 'automemberinclusiveregex', + label: IPA.messages.objects.automember.expression + } + ] + } + } + ] + }, + { + factory: IPA.collapsible_section, + name: 'exclusive', + label: IPA.messages.objects.automember.exclusive, + widgets: [ + { + type: 'automember_condition', + name: 'automemberexclusiveregex', + group_type: spec.group_type, + add_command: 'add_condition', + remove_command: 'remove_condition', + adder_dialog: { + title: IPA.messages.objects.automember.add_condition, + fields: [ + { + name: 'key', + type: 'select', + options: IPA.automember.get_condition_attributes(spec.group_type), + label: IPA.messages.objects.automember.attribute + }, + { + name: 'automemberexclusiveregex', + label: IPA.messages.objects.automember.expression + } + ] + } + } + ] + } + ]; + + var that = IPA.details_facet(spec); + + that.group_type = spec.group_type; + + that.get_refresh_command_name = function() { + return that.entity.name+that.group_type+'_show'; + }; + + that.create_refresh_command = function() { + + var command = that.details_facet_create_refresh_command(); + command.set_option('type', that.group_type); + + return command; + }; + + that.create_update_command = function() { + + var command = that.details_facet_create_update_command(); + command.set_option('type', that.group_type); + + return command; + }; + + return that; +}; + +IPA.automember.rule_adder_dialog = function(spec) { + + spec = spec || {}; + + var that = IPA.entity_adder_dialog(spec); + + that.show_edit_page = function (entity,result) { + var pkey_name = entity.metadata.primary_key; + var pkey = result[pkey_name]; + if (pkey instanceof Array) { + pkey = pkey[0]; + } + var facet = IPA.current_entity.get_facet(); + var facetname = facet.group_type === 'group' ? 'usergrouprule' : + 'hostgrouprule'; + + IPA.nav.show_entity_page(that.entity, facetname, pkey); + }; + + that.reset = function() { + + var field = that.fields.get_field('cn'); + var facet = IPA.current_entity.get_facet(); + + field.widget.other_entity = IPA.get_entity(facet.group_type); + + that.dialog_reset(); + }; + + that.create_add_command = function(record) { + + var facet = IPA.current_entity.get_facet(); + var command = that.entity_adder_dialog_create_add_command(record); + command.name = that.entity.name+facet.group_type+'_show'; + command.set_option('type', facet.group_type); + + return command; + }; + + + return that; +}; + +IPA.automember.rule_deleter_dialog = function(spec) { + + spec = spec || {}; + + var that = IPA.search_deleter_dialog(spec); + + that.create_command = function() { + + var facet = IPA.current_entity.get_facet(); + + var batch = that.search_deleter_dialog_create_command(); + + for (var i=0; i<batch.commands.length; i++) { + var command = batch.commands[i]; + command.set_option('type', facet.group_type); + } + + return batch; + }; + + return that; +}; + +IPA.automember.get_condition_attributes = function(type) { + var options = []; + + if (type === 'group') { + options = IPA.metadata.objects.user.aciattrs; + } else if (type === 'hostgroup') { + options = IPA.metadata.objects.host.aciattrs; + } + + var list_options = IPA.create_options(options); + return list_options; +}; + +IPA.automember.parse_condition_regex = function(regex) { + + var delimeter_index = regex.indexOf('='); + var condition = { + condition: regex, + attribute: regex.substring(0, delimeter_index), + expression: regex.substring(delimeter_index+1) + }; + + return condition; +}; + +IPA.automember.condition_field = function(spec) { + + spec = spec || {}; + 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.field_factories['automember_condition'] = IPA.automember.condition_field; + +IPA.automember.condition_widget = function(spec) { + + spec = spec || {}; + + spec.columns = $.merge(spec.columns || [], [ + { + name: 'attribute', + label: IPA.messages.objects.automember.attribute + }, + { + name: 'expression', + label: IPA.messages.objects.automember.expression + } + ]); + + spec.value_attribute = 'condition'; + + var that = IPA.attribute_table_widget(spec); + + that.group_type = spec.group_type; + + that.get_additional_options = function() { + return [ + { + name: 'type', + value: that.group_type + } + ]; + }; + + that.on_add = function(data) { + + if (data.result.completed === 0) { + that.refresh_facet(); + } else { + that.reload_facet(data); + } + }; + + that.on_remove = function(data) { + + var results = data.result.results; + + var i = results.length - 1; + while (i >= 0) { + if (results[i].completed === 1){ + that.reload_facet({ result: results[i] }); + return; + } + i--; + } + + that.refresh_facet(); + }; + + that.create_remove_command = function(values, on_success, on_error) { + + var batch = IPA.batch_command({ + name: 'automember_remove_condition', + on_success: on_success, + on_error: on_error + }); + + var pkeys = that.get_pkeys(); + + for (var i=0; i<values.length; i++) { + var condition = IPA.automember.parse_condition_regex(values[i]); + + var command = that.attribute_table_create_remove_command([]); + command.set_option('key', condition.attribute); + command.set_option(that.attribute_name, condition.expression); + + batch.add_command(command); + } + + return batch; + }; + + return that; +}; + +IPA.widget_factories['automember_condition'] = IPA.automember.condition_widget; + +IPA.automember.default_group_widget = function(spec) { + + spec = spec || {}; + + var that = IPA.widget(spec); + that.group_type = spec.group_type; + that.group = ''; + + var init = function() { + + that.group_select = IPA.entity_select_widget({ + name: 'automemberdefaultgroup', + other_entity: that.group_type, + other_field: 'cn', + show_undo: false + }); + + that.group_select.value_changed.attach(that.group_changed); + }; + + that.get_group = function() { + + var group = that.group_select.save(); + group = group.length === 0 ? '' : group[0]; + return group; + }; + + that.set_group = function(group) { + + if (group === that.group) return; + + that.group = group; + that.group_select.update([group]); + }; + + that.group_changed = function() { + + var group = that.get_group(); + + if (group === that.group) return; + + if (group === '') { + that.remove_default_group(); + } else { + that.set_default_group(group); + } + }; + + that.load = function(data) { + + var group = data.result.result.automemberdefaultgroup; + + if (group) group = group[0]; + + if (!group || group.indexOf('cn=') === -1) { + group = ''; + } else { + //extract from dn + var i1 = group.indexOf('='); + var i2 = group.indexOf(','); + if (i1 > -1 && i2 > -1) { + group = group.substring(i1 + 1,i2); + } + } + + that.update(group); + }; + + that.update = function(group) { + + group = group || ''; + + that.set_group(group); + }; + + that.create_command = function(method) { + + method = 'default_group_' + method; + var command_name = that.entity.name + that.group_type + '_' + method; + + var command = IPA.command({ + name: command_name, + entity: that.entity.name, + method: method, + options: { + type: that.group_type + } + }); + + return command; + }; + + that.refresh = function() { + + var command = that.create_command('show'); + command.on_success = that.load; + + command.execute(); + }; + + that.remove_default_group = function() { + + var command = that.create_command('remove'); + + command.on_success = function() { + that.update(''); + }; + command.on_error = that.refresh; + + command.execute(); + }; + + that.set_default_group = function(group) { + + var command = that.create_command('set'); + command.on_success = that.load; + command.on_error = that.refresh; + command.set_option('automemberdefaultgroup', group); + + command.execute(); + }; + + + that.create = function(container) { + + var title = that.get_title(); + + var default_group = $('<div />', { + 'class': 'default_group' + }).appendTo(container); + + that.header = $('<h2/>', { + name: 'header', + text: title, + title: title + }).appendTo(default_group); + + that.group_select.create(default_group); + }; + + that.get_title = function() { + if (that.group_type === 'group') { + return IPA.messages.objects.automember.default_user_group; + } else { + return IPA.messages.objects.automember.default_host_group; + } + }; + + init(); + + return that; +}; + + +IPA.register('automember', IPA.automember.entity);
\ No newline at end of file diff --git a/install/ui/src/freeipa/automount.js b/install/ui/src/freeipa/automount.js new file mode 100644 index 000000000..3a4491d8b --- /dev/null +++ b/install/ui/src/freeipa/automount.js @@ -0,0 +1,354 @@ +/*jsl:import ipa.js */ +/*jsl:import search.js */ + +/* Authors: + * Adam Young <ayoung@redhat.com> + * + * Copyright (C) 2010 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/>. + */ + +/* REQUIRES: ipa.js, details.js, search.js, add.js, facet.js, entity.js */ + +IPA.automount = {}; + +IPA.automount.location_entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + that.entity_init(); + + that.builder.facet_groups([ 'automountmap', 'settings' ]). + search_facet({ + title: IPA.metadata.objects.automountlocation.label, + columns:['cn'] + }). + nested_search_facet({ + facet_group: 'automountmap', + nested_entity: 'automountmap', + label: IPA.metadata.objects.automountmap.label, + tab_label: IPA.metadata.objects.automountmap.label, + name: 'maps', + columns: [ 'automountmapname' ] + }). + details_facet({ + sections:[ + { + name: 'identity', + label: IPA.messages.details.identity, + fields: [ 'cn' ] + } + ] + }). + adder_dialog({ + fields: [ 'cn' ] + }); + }; + + return that; +}; + +IPA.automount.map_entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + that.entity_init(); + + that.builder.containing_entity('automountlocation'). + facet_groups([ 'automountkey', 'settings' ]). + nested_search_facet({ + factory: IPA.automount.key_search_facet, + facet_group: 'automountkey', + nested_entity: 'automountkey', + search_all_entries: true, + label: IPA.metadata.objects.automountkey.label, + tab_label: IPA.metadata.objects.automountkey.label, + name: 'keys', + columns: [ + { + factory: IPA.automount_key_column, + name: 'automountkey', + label: IPA.get_entity_param('automountkey', 'automountkey').label + }, + 'automountinformation' + ] + }). + details_facet({ + sections: [ + { + name: 'identity', + label: IPA.messages.details.identity, + fields: [ + 'automountmapname', + { + type: 'textarea', + name: 'description' + } + ] + } + ] + }). + adder_dialog({ + factory: IPA.automountmap_adder_dialog, + sections: [ + { + name: 'general', + fields: [ + { + type: 'radio', + name: 'method', + enabled: false, //don't use value in add command + label: IPA.messages.objects.automountmap.map_type, + options: [ + { + value: 'add', + label: IPA.messages.objects.automountmap.direct + }, + { + value: 'add_indirect', + label: IPA.messages.objects.automountmap.indirect + } + ] + }, + 'automountmapname', + { + type: 'textarea', + name: 'description' + } + ] + }, + { + name: 'indirect', + fields: [ + { + name: 'key', + label: IPA.get_command_option( + 'automountmap_add_indirect', 'key').label + }, + { + name: 'parentmap', + label: IPA.get_command_option( + 'automountmap_add_indirect', 'parentmap').label + } + ] + } + ] + }); + }; + + return that; +}; + +IPA.automount.key_entity = function(spec) { + + spec = spec || {}; + + spec.policies = spec.policies || [ + IPA.facet_update_policy({ + source_facet: 'details', + dest_entity: 'automountmap', + dest_facet: 'keys' + }) + ]; + + var that = IPA.entity(spec); + + that.init = function() { + that.entity_init(); + + that.builder.containing_entity('automountmap'). + details_facet({ + factory: IPA.automount.key_details_facet, + sections: [ + { + name:'identity', + label: IPA.messages.details.identity, + fields: [ + { + name: 'automountkey', + read_only: true + }, + 'automountinformation' + ] + } + ], + disable_breadcrumb: false + }). + adder_dialog({ + show_edit_page : function(entity, result){ + var key = result.automountkey[0]; + var info = result.automountinformation[0]; + var state = IPA.nav.get_path_state(entity.name); + state[entity.name + '-facet'] = 'default'; + state[entity.name + '-info'] = info; + state[entity.name + '-pkey'] = key; + IPA.nav.push_state(state); + return false; + }, + fields:['automountkey','automountinformation'] + }); + }; + + return that; +}; + +IPA.automount.key_details_facet = function(spec) { + + var that = IPA.details_facet(spec); + + that.create_update_command = function() { + + var command = that.details_facet_create_update_command(); + + command.args.pop(); + + var key = IPA.nav.get_state(that.entity.name + '-pkey'); + var info = IPA.nav.get_state(that.entity.name + '-info'); + + command.options.newautomountinformation = command.options.automountinformation; + command.options.automountkey = key; + command.options.automountinformation = info; + + return command; + }; + + that.create_refresh_command = function() { + + var command = that.details_facet_create_refresh_command(); + + command.args.pop(); + + var key = IPA.nav.get_state(that.entity.name + '-pkey'); + var info = IPA.nav.get_state(that.entity.name + '-info'); + + command.options.automountkey = key; + command.options.automountinformation = info; + + return command; + }; + + return that; +}; + +IPA.automount_key_column = function(spec) { + + var that = IPA.column(spec); + + that.setup = function(container, record) { + + container.empty(); + + var key = record.automountkey; + if (key instanceof Array) key = key[0]; + var info = record.automountinformation; + if (info instanceof Array) info = info[0]; + + $('<a/>', { + href: '#'+key, + text: key, + click: function() { + var state = IPA.nav.get_path_state(that.entity.name); + state[that.entity.name + '-facet'] = 'default'; + state[that.entity.name + '-info'] = info; + state[that.entity.name + '-pkey'] = key; + IPA.nav.push_state(state); + return false; + } + }).appendTo(container); + + }; + + return that; +}; + +IPA.automountmap_adder_dialog = function(spec) { + + var that = IPA.entity_adder_dialog(spec); + + that.create = function() { + that.entity_adder_dialog_create(); + + var method_widget = that.widgets.get_widget('general.method'); + var indirect_section = that.widgets.get_widget('indirect'); + var key_field = that.fields.get_field('key'); + var parentmap_field = that.fields.get_field('parentmap'); + + var direct_input = $('input[value="add"]', method_widget.container); + direct_input.change(function() { + that.method = 'add'; + + key_field.set_enabled(false); + parentmap_field.set_enabled(false); + + key_field.set_required(false); + indirect_section.set_visible(false); + }); + + var indirect_input = $('input[value="add_indirect"]', method_widget.container); + indirect_input.change(function() { + that.method = 'add_indirect'; + + key_field.set_enabled(true); + parentmap_field.set_enabled(true); + + key_field.set_required(true); + indirect_section.set_visible(true); + }); + + direct_input.click(); + }; + + that.reset = function() { + that.dialog_reset(); + + var method_widget = that.widgets.get_widget('general.method'); + + var direct_input = $('input[value="add"]', method_widget.container); + direct_input.click(); + }; + + return that; +}; + +IPA.automount.key_search_facet = function(spec) { + + var that = IPA.nested_search_facet(spec); + + that.get_selected_values = function() { + + var values = []; + + $('input[name="description"]:checked', that.table.tbody).each(function() { + var value = {}; + $('div', $(this).parent().parent()).each(function() { + var div = $(this); + var name = div.attr('name'); + value[name] = div.text(); + }); + values.push(value); + }); + + return values; + }; + + return that; +}; + +IPA.register('automountlocation', IPA.automount.location_entity); +IPA.register('automountmap', IPA.automount.map_entity); +IPA.register('automountkey', IPA.automount.key_entity); diff --git a/install/ui/src/freeipa/certificate.js b/install/ui/src/freeipa/certificate.js new file mode 100755 index 000000000..782033db1 --- /dev/null +++ b/install/ui/src/freeipa/certificate.js @@ -0,0 +1,924 @@ +/*jsl:import ipa.js */ + +/* Authors: + * Endi Sukma Dewata <edewata@redhat.com> + * Petr Vobornik <pvoborni@redhat.com> + * + * Copyright (C) 2010 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/>. + */ + +IPA.cert = {}; + +IPA.cert.BEGIN_CERTIFICATE = '-----BEGIN CERTIFICATE-----'; +IPA.cert.END_CERTIFICATE = '-----END CERTIFICATE-----'; + +IPA.cert.BEGIN_CERTIFICATE_REQUEST = '-----BEGIN CERTIFICATE REQUEST-----'; +IPA.cert.END_CERTIFICATE_REQUEST = '-----END CERTIFICATE REQUEST-----'; + +/* + * Pre-compiled regular expression to match a PEM cert. + * + * regexp group 1: entire canonical cert (delimiters plus base64) + * regexp group 2: base64 data inside PEM delimiters + */ +IPA.cert.PEM_CERT_REGEXP = RegExp('(-----BEGIN CERTIFICATE-----([^-]*)-----END CERTIFICATE-----)'); + +/* + * Pre-compiled regular expression to match a CSR (Certificate Signing Request). + * The delimiter "CERTIFICATE REQUEST" is the cononical standard, however some legacy + * software will produce a delimiter with "NEW" in it, i.e. "NEW CERTIFICATE REQUEST" + * This regexp will work with either form. + * + * regexp group 1: entire canonical CSR (delimiters plus base64) + * regexp group 2: base64 data inside canonical CSR delimiters + * regexp group 3: entire legacy CSR (delimiters plus base64) + * regexp group 4: base64 data inside legacy CSR delimiters + */ +IPA.cert.PEM_CSR_REGEXP = RegExp('(-----BEGIN CERTIFICATE REQUEST-----([^-]*)-----END CERTIFICATE REQUEST-----)|(-----BEGIN NEW CERTIFICATE REQUEST-----([^-]*)-----END NEW CERTIFICATE REQUEST-----)'); + +IPA.cert.CERTIFICATE_STATUS_MISSING = 0; +IPA.cert.CERTIFICATE_STATUS_VALID = 1; +IPA.cert.CERTIFICATE_STATUS_REVOKED = 2; + +IPA.cert.CRL_REASON = [ + 'unspecified', + 'key_compromise', + 'ca_compromise', + 'affiliation_changed', + 'superseded', + 'cessation_of_operation', + 'certificate_hold', + null, + 'remove_from_crl', + 'privilege_withdrawn', + 'aa_compromise' +]; + +IPA.cert.parse_dn = function(dn) { + + var result = {}; + if (!dn) return result; + + // TODO: Use proper LDAP DN parser + var rdns = dn.split(','); + for (var i=0; i<rdns.length; i++) { + var rdn = rdns[i]; + if (!rdn) continue; + + var parts = rdn.split('='); + var name = $.trim(parts[0].toLowerCase()); + var value = $.trim(parts[1]); + + var old_value = result[name]; + if (!old_value) { + result[name] = value; + } else if (typeof old_value == "string") { + result[name] = [old_value, value]; + } else { + result[name].push(value); + } + } + + return result; +}; + +IPA.cert.pem_format_base64 = function(text) { + /* + * Input is assumed to be base64 possibly with embedded whitespace. + * Format the base64 text such that it conforms to PEM, which is a + * sequence of 64 character lines, except for the last line which + * may be less than 64 characters. The last line does NOT have a + * new line appended to it. + */ + var formatted = ""; + + /* Strip out any whitespace including line endings */ + text = text.replace(/\s*/g,""); + + /* + * Break up into lines with 64 chars each. + * Do not add a newline to final line. + */ + for (var i = 0; i < text.length; i+=64) { + formatted += text.substring(i, i+64); + if (i+64 < text.length) { + formatted += "\n"; + } + } + return (formatted); +}; + +IPA.cert.pem_cert_format = function(text) { + /* + * Input is assumed to be either PEM formated data or the + * base64 encoding of DER binary certificate data. Return data + * in PEM format. The function checks if the input text is PEM + * formatted, if so it just returns the input text. Otherwise + * the input is treated as base64 which is formatted to be PEM> + */ + + /* + * Does the text already have the PEM delimiters? + * If so just return the text unmodified. + */ + if (text.match(IPA.cert.PEM_CERT_REGEXP)) { + return text; + } + /* No PEM delimiters so format the base64 & add the delimiters. */ + return IPA.cert.BEGIN_CERTIFICATE + "\n" + + IPA.cert.pem_format_base64(text) + "\n" + + IPA.cert.END_CERTIFICATE; +}; + +IPA.cert.pem_csr_format = function(text) { + /* + * Input is assumed to be either PEM formated data or the base64 + * encoding of DER binary certificate request (csr) data. Return + * data in PEM format. The function checks if the input text is + * PEM formatted, if so it just returns the input text. Otherwise + * the input is treated as base64 which is formatted to be PEM> + */ + + /* + * Does the text already have the PEM delimiters? + * If so just return the text unmodified. + */ + if (text.match(IPA.cert.PEM_CSR_REGEXP)) { + return text; + } + + /* No PEM delimiters so format the base64 & add the delimiters. */ + return IPA.cert.BEGIN_CERTIFICATE_REQUEST + "\n" + + IPA.cert.pem_format_base64(text) + "\n" + + IPA.cert.END_CERTIFICATE_REQUEST; +}; + +IPA.cert.download_dialog = function(spec) { + + spec = spec || {}; + + var that = IPA.dialog(spec); + + that.width = spec.width || 500; + that.height = spec.height || 380; + that.add_pem_delimiters = typeof spec.add_pem_delimiters == 'undefined' ? true : spec.add_pem_delimiters; + + that.certificate = spec.certificate || ''; + + that.create_button({ + name: 'close', + label: IPA.messages.buttons.close, + click: function() { + that.close(); + } + }); + + that.create = function() { + var textarea = $('<textarea/>', { + 'class': 'certificate', + readonly: 'yes' + }).appendTo(that.container); + + var certificate = that.certificate; + + if (that.add_pem_delimiters) { + certificate = IPA.cert.pem_cert_format(that.certificate); + } + + textarea.val(certificate); + }; + + return that; +}; + +IPA.cert.revoke_dialog = function(spec) { + + spec = spec || {}; + spec.width = spec.width || 500; + spec.ok_label = spec.ok_label || IPA.messages.buttons.revoke; + + var that = IPA.confirm_dialog(spec); + + that.get_reason = function() { + return that.select.val(); + }; + + that.create = function() { + + var table = $('<table/>').appendTo(that.container); + + var tr = $('<tr/>').appendTo(table); + + var td = $('<td/>').appendTo(tr); + td.append(IPA.messages.objects.cert.note+':'); + + td = $('<td/>').appendTo(tr); + td.append(IPA.messages.objects.cert.revoke_confirmation); + + tr = $('<tr/>').appendTo(table); + + td = $('<td/>').appendTo(tr); + td.append(IPA.messages.objects.cert.reason+':'); + + td = $('<td/>').appendTo(tr); + + that.select = $('<select/>').appendTo(td); + for (var i=0; i<IPA.cert.CRL_REASON.length; i++) { + var reason = IPA.cert.CRL_REASON[i]; + if (!reason) continue; + $('<option/>', { + 'value': i, + 'html': IPA.messages.objects.cert[reason] + }).appendTo(that.select); + } + }; + + return that; +}; + +IPA.cert.view_dialog = function(spec) { + + spec = spec || {}; + + var that = IPA.dialog(spec); + + that.width = spec.width || 600; + that.height = spec.height || 500; + + that.subject = IPA.cert.parse_dn(spec.certificate.subject); + that.serial_number = spec.certificate.serial_number || ''; + that.serial_number_hex = spec.certificate.serial_number_hex || ''; + that.issuer = IPA.cert.parse_dn(spec.certificate.issuer); + that.issued_on = spec.certificate.valid_not_before || ''; + that.expires_on = spec.certificate.valid_not_after || ''; + that.md5_fingerprint = spec.certificate.md5_fingerprint || ''; + that.sha1_fingerprint = spec.certificate.sha1_fingerprint || ''; + + that.create_button({ + name: 'close', + label: IPA.messages.buttons.close, + click: function() { + that.close(); + } + }); + + that.create = function() { + + var table = $('<table/>').appendTo(that.container); + + var tr = $('<tr/>').appendTo(table); + $('<td/>', { + 'colspan': 2, + 'html': '<h3>'+IPA.messages.objects.cert.issued_to+'</h3>' + }).appendTo(tr); + + tr = $('<tr/>').appendTo(table); + $('<td>'+IPA.messages.objects.cert.common_name+':</td>').appendTo(tr); + $('<td/>', { + text: that.subject.cn + }).appendTo(tr); + + tr = $('<tr/>').appendTo(table); + $('<td>'+IPA.messages.objects.cert.organization+':</td>').appendTo(tr); + $('<td/>', { + text: that.subject.o + }).appendTo(tr); + + tr = $('<tr/>').appendTo(table); + $('<td>'+IPA.messages.objects.cert.organizational_unit+':</td>').appendTo(tr); + $('<td/>', { + text: that.subject.ou + }).appendTo(tr); + + tr = $('<tr/>').appendTo(table); + $('<td>'+IPA.messages.objects.cert.serial_number+':</td>').appendTo(tr); + $('<td/>', { + text: that.serial_number + }).appendTo(tr); + + tr = $('<tr/>').appendTo(table); + $('<td>'+IPA.messages.objects.cert.serial_number_hex+':</td>').appendTo(tr); + $('<td/>', { + text: that.serial_number_hex + }).appendTo(tr); + + tr = $('<tr/>').appendTo(table); + $('<td/>', { + 'colspan': 2, + 'html': '<h3>'+IPA.messages.objects.cert.issued_by+'</h3>' + }).appendTo(tr); + + tr = $('<tr/>').appendTo(table); + $('<td>'+IPA.messages.objects.cert.common_name+':</td>').appendTo(tr); + $('<td/>', { + text: that.issuer.cn + }).appendTo(tr); + + tr = $('<tr/>').appendTo(table); + $('<td>'+IPA.messages.objects.cert.organization+':</td>').appendTo(tr); + $('<td/>', { + text: that.issuer.o + }).appendTo(tr); + + tr = $('<tr/>').appendTo(table); + $('<td>'+IPA.messages.objects.cert.organizational_unit+':</td>').appendTo(tr); + $('<td/>', { + text: that.issuer.ou + }).appendTo(tr); + + tr = $('<tr/>').appendTo(table); + $('<td/>', { + 'colspan': 2, + 'html': '<h3>'+IPA.messages.objects.cert.validity+'</h3>' + }).appendTo(tr); + + tr = $('<tr/>').appendTo(table); + $('<td>'+IPA.messages.objects.cert.issued_on+':</td>').appendTo(tr); + $('<td/>', { + text: that.issued_on + }).appendTo(tr); + + tr = $('<tr/>').appendTo(table); + $('<td>'+IPA.messages.objects.cert.expires_on+':</td>').appendTo(tr); + $('<td/>', { + text: that.expires_on + }).appendTo(tr); + + tr = $('<tr/>').appendTo(table); + $('<td/>', { + 'colspan': 2, + 'html': '<h3>'+IPA.messages.objects.cert.fingerprints+'</h3>' + }).appendTo(tr); + + tr = $('<tr/>').appendTo(table); + $('<td>'+IPA.messages.objects.cert.sha1_fingerprint+':</td>').appendTo(tr); + $('<td/>', { + text: that.sha1_fingerprint + }).appendTo(tr); + + tr = $('<tr/>').appendTo(table); + $('<td>'+IPA.messages.objects.cert.md5_fingerprint+':</td>').appendTo(tr); + $('<td/>', { + text: that.md5_fingerprint + }).appendTo(tr); + }; + + return that; +}; + +IPA.cert.request_dialog = function(spec) { + + spec = spec || {}; + + var that = IPA.dialog(spec); + + that.width = spec.width || 600; + that.height = spec.height || 480; + that.message = spec.message; + + that.request = spec.request; + + that.create_button({ + name: 'issue', + label: IPA.messages.buttons.issue, + click: function() { + var values = {}; + var request = $.trim(that.textarea.val()); + values.request = IPA.cert.pem_csr_format(request); + if (that.request) { + that.request(values); + } + that.close(); + } + }); + + that.create_button({ + name: 'cancel', + label: IPA.messages.buttons.cancel, + click: function() { + that.close(); + } + }); + + that.create = function() { + that.container.append(that.message); + + that.textarea = $('<textarea/>', { + 'class': 'certificate' + }).appendTo(that.container); + }; + + return that; +}; + +IPA.cert.loader = function(spec) { + + spec = spec || {}; + + var that = {}; + that.get_pkey = spec.get_pkey; + that.get_name = spec.get_name; + that.get_principal = spec.get_principal; + that.get_hostname = spec.get_hostname; + + that.load = function (result) { + + var certificate = { + issuer: result.issuer, + certificate: result.certificate, + md5_fingerprint: result.md5_fingerprint, + revocation_reason: result.revocation_reason, + serial_number: result.serial_number, + serial_number_hex: result.serial_number_hex, + sha1_fingerprint: result.sha1_fingerprint, + subject: result.subject, + valid_not_after: result.valid_not_after, + valid_not_before: result.valid_not_before + }; + + if (that.get_entity_certificate) { + certificate.certificate = that.get_entity_certificate(result); + } else if (!certificate.certificate && result.usercertificate) { + // default method of storing certificate for object commands + // which include certificate + certificate.certificate = result.usercertificate[0].__base64__; + } + + var info = {}; + + if (that.get_pkey) info.pkey = that.get_pkey(result); + if (that.get_name) info.name = that.get_name(result); + if (that.get_principal) info.principal = that.get_principal(result); + if (that.get_hostname) info.hostname = that.get_hostname(result); + + certificate.entity_info = info; + + return certificate; + }; + + return that; +}; + +IPA.cert.load_policy = function(spec) { + + spec = spec || {}; + spec.loader = spec.loader || { + factory: IPA.cert.loader, + get_pkey: spec.get_pkey, + get_name: spec.get_name, + get_principal: spec.get_principal, + get_hostname: spec.get_hostname + }; + + var that = IPA.facet_policy(); + that.loader = IPA.build(spec.loader); + + that.post_load = function(data) { + + // update cert info in facet (show at least something) + var certificate = that.loader.load(data.result.result); + + //store cert directly to facet. FIXME: introduce concept of models + that.container.certificate = certificate; + that.notify_loaded(); + + // initialize another load of certificate because current entity + // show commands don't contain revocation_reason so previous data + // might be slightly incorrect + if (certificate && certificate.certificate && !IPA.cert.is_selfsign()) { + that.load_revocation_reason(certificate.serial_number); + } + }; + + that.load_revocation_reason = function(serial_number) { + IPA.command({ + entity: 'cert', + method: 'show', + args: [serial_number], + on_success: function(data, text_status, xhr) { + var cert = that.container.certificate; + cert.revocation_reason = data.result.result.revocation_reason; + that.notify_loaded(); + } + }).execute(); + }; + + that.notify_loaded = function() { + if (that.container.certificate_loaded) { + that.container.certificate_loaded.notify( + [that.container.certificate], that.container); + } + }; + + return that; +}; + +IPA.cert.is_selfsign = function() { + return IPA.env.ra_plugin == 'selfsign'; +}; + +IPA.cert.view_action = function(spec) { + + spec = spec || {}; + spec.name = spec.name || 'view_cert'; + spec.label = spec.label || IPA.messages.buttons.view; + spec.enable_cond = spec.enable_cond || ['has_certificate']; + + var that = IPA.action(spec); + that.entity_label = spec.entity_label; + + that.execute_action = function(facet) { + + var certificate = facet.certificate; + if (!certificate) that.facet.refresh(); + + var entity_label = that.entity_label || facet.entity.metadata.label_singular; + var entity_name = certificate.entity_info.name; + + var title = IPA.messages.objects.cert.view_certificate; + title = title.replace('${entity}', entity_label); + title = title.replace('${primary_key}', entity_name); + + var dialog = IPA.cert.view_dialog({ + title: title, + certificate: certificate + }); + + dialog.open(); + }; + + return that; +}; + +IPA.cert.get_action = function(spec) { + + spec = spec || {}; + spec.name = spec.name || 'get_cert'; + spec.label = spec.label || IPA.messages.buttons.get; + spec.enable_cond = spec.enable_cond || ['has_certificate']; + + var that = IPA.action(spec); + that.entity_label = spec.entity_label; + + that.execute_action = function(facet) { + + var certificate = facet.certificate; + if (!certificate) that.facet.refresh(); + + var entity_label = that.entity_label || facet.entity.metadata.label_singular; + var entity_name = certificate.entity_info.name; + + var title = IPA.messages.objects.cert.view_certificate; + title = title.replace('${entity}', entity_label); + title = title.replace('${primary_key}', entity_name); + + var dialog = IPA.cert.download_dialog({ + title: title, + certificate: certificate.certificate + }); + + dialog.open(); + }; + + return that; +}; + +IPA.cert.request_action = function(spec) { + + spec = spec || {}; + spec.name = spec.name || 'request_cert'; + spec.label = spec.label || IPA.messages.objects.cert.new_certificate; + + var that = IPA.action(spec); + that.entity_label = spec.entity_label; + + that.execute_action = function(facet) { + + var certificate = facet.certificate; + if (!certificate) facet.refresh(); + + var entity_principal = certificate.entity_info.principal; + var entity_label = that.entity_label || facet.entity.metadata.label_singular; + var entity_name = certificate.entity_info.name; + var hostname = certificate.entity_info.hostname; + + var title = IPA.messages.objects.cert.issue_certificate; + title = title.replace('${entity}', entity_label); + title = title.replace('${primary_key}', entity_name); + + var request_message = IPA.messages.objects.cert.request_message; + request_message = request_message.replace(/\$\{hostname\}/g, hostname); + request_message = request_message.replace(/\$\{realm\}/g, IPA.env.realm); + + var dialog = IPA.cert.request_dialog({ + title: title, + message: request_message, + request: function(values) { + + IPA.command({ + entity: 'cert', + method: 'request', + args: [values.request], + options: { + 'principal': entity_principal + }, + on_success: function(data, text_status, xhr) { + facet.refresh(); + IPA.notify_success(IPA.messages.objects.cert.requested); + } + }).execute(); + } + }); + + dialog.open(); + }; + + return that; +}; + +IPA.cert.revoke_action = function(spec) { + + spec = spec || {}; + spec.name = spec.name || 'revoke_cert'; + spec.label = spec.label || IPA.messages.buttons.revoke; + spec.enable_cond = spec.enable_cond || ['has_certificate']; + spec.disable_cond = spec.disable_cond || ['certificate_revoked']; + spec.hide_cond = spec.hide_cond || ['selfsign']; + spec.confirm_dialog = spec.confirm_dialog || IPA.cert.revoke_dialog; + spec.needs_confirm = spec.needs_confirm !== undefined ? spec.needs_confirm : true; + + var that = IPA.action(spec); + that.entity_label = spec.entity_label; + that.confirm_msg = spec.request_message; + + that.update_confirm_dialog = function(facet) { + + var certificate = facet.certificate; + + var entity_label = that.entity_label || facet.entity.metadata.label_singular; + var entity_name = certificate.entity_info.name; + + var title = IPA.messages.objects.cert.revoke_certificate; + title = title.replace('${entity}', entity_label); + title = title.replace('${primary_key}', entity_name); + + that.dialog.title = title; + that.dialog.message = that.get_confirm_message(facet); + }; + + that.execute_action = function(facet) { + + var certificate = facet.certificate; + + IPA.command({ + entity: 'cert', + method: 'revoke', + args: [certificate.serial_number], + options: { + 'revocation_reason': that.dialog.get_reason() + }, + on_success: function(data, text_status, xhr) { + facet.refresh(); + IPA.notify_success(IPA.messages.objects.cert.revoked); + } + }).execute(); + }; + + return that; +}; + +IPA.cert.restore_action = function(spec) { + + spec = spec || {}; + spec.name = spec.name || 'restore_cert'; + spec.label = spec.label || IPA.messages.buttons.restore; + spec.enable_cond = spec.enable_cond || ['has_certificate', 'certificate_hold']; + spec.hide_cond = spec.hide_cond || ['selfsign']; + spec.confirm_msg = spec.confirm_msg || IPA.messages.objects.cert.restore_confirmation; + spec.confirm_dialog = spec.confirm_dialog || { + factory: IPA.confirm_dialog, + ok_label: IPA.messages.buttons.restore + }; + spec.needs_confirm = spec.needs_confirm !== undefined ? spec.needs_confirm : true; + + var that = IPA.action(spec); + that.entity_label = spec.entity_label; + + that.update_confirm_dialog = function(facet) { + + var certificate = facet.certificate; + + var entity_label = that.entity_label || facet.entity.metadata.label_singular; + var entity_name = certificate.entity_info.name; + + var title = IPA.messages.objects.cert.restore_certificate; + title = title.replace('${entity}', entity_label); + title = title.replace('${primary_key}', entity_name); + + that.dialog.title = title; + that.dialog.message = that.get_confirm_message(facet); + }; + + that.execute_action = function(facet) { + + var certificate = facet.certificate; + + IPA.command({ + entity: 'cert', + method: 'remove_hold', + args: [certificate.serial_number], + on_success: function(data, text_status, xhr) { + facet.refresh(); + IPA.notify_success(IPA.messages.objects.cert.restored); + } + }).execute(); + }; + + return that; +}; + +IPA.cert.certificate_evaluator = function(spec) { + + spec.name = spec.name || 'has_certificate_evaluator'; + spec.event = spec.event || 'certificate_loaded'; + + var that = IPA.state_evaluator(spec); + + that.on_event = function(certificate) { + + var old_state, record, state, value, loaded_value; + + old_state = that.state; + that.state = []; + + if (certificate && certificate.certificate) { + that.state.push('has_certificate'); + + if (certificate.revocation_reason !== undefined) { + that.state.push('certificate_revoked'); + + if (certificate.revocation_reason === 6) { + that.state.push('certificate_hold'); + } + } + } + + if (IPA.cert.is_selfsign()) { + that.state.push('selfsign'); + } + + that.notify_on_change(old_state); + }; + + return that; +}; + + +IPA.cert.status_widget = function(spec) { + + spec = spec || {}; + + var that = IPA.input_widget(spec); + + that.create = function(container) { + + that.widget_create(container); + + that.status_valid = $('<div/>', { + name: 'certificate-valid', + style: 'display: none;' + }).appendTo(container); + + $('<img/>', { + src: 'images/check-icon.png', + style: 'float: left;', + 'class': 'status-icon' + }).appendTo(that.status_valid); + + var content_div = $('<div/>', { + style: 'float: left;' + }).appendTo(that.status_valid); + + content_div.append('<b>'+IPA.messages.objects.cert.valid+'</b>'); + + that.status_revoked = $('<div/>', { + name: 'certificate-revoked', + style: 'display: none;' + }).appendTo(container); + + $('<img/>', { + src: 'images/caution-icon.png', + style: 'float: left;', + 'class': 'status-icon' + }).appendTo(that.status_revoked); + + content_div = $('<div/>', { + style: 'float: left;' + }).appendTo(that.status_revoked); + + content_div.append('<b>'+IPA.messages.objects.cert.revoked+'</b>'); + content_div.append(' '); + that.revocation_reason = $('<span/>', { + 'name': 'revocation_reason' + }).appendTo(content_div); + + that.status_missing = $('<div/>', { + name: 'certificate-missing', + style: 'display: none;' + }).appendTo(container); + + $('<img/>', { + src: 'images/caution-icon.png', + style: 'float: left;', + 'class': 'status-icon' + }).appendTo(that.status_missing); + + content_div = $('<div/>', { + style: 'float: left;' + }).appendTo(that.status_missing); + + content_div.append('<b>'+IPA.messages.objects.cert.missing+'</b>'); + }; + + that.update = function(certificate) { + + certificate = certificate || {}; + + var selfsign = IPA.cert.is_selfsign(); + var has_certificate = certificate.certificate; + var revoked = certificate.revocation_reason !== undefined; + var status = IPA.cert.CERTIFICATE_STATUS_MISSING; + + if (has_certificate && (selfsign || !revoked)) { + status = IPA.cert.CERTIFICATE_STATUS_VALID; + } else if (has_certificate) { + status = IPA.cert.CERTIFICATE_STATUS_REVOKED; + } + that.set_status(status, certificate.revocation_reason); + }; + + that.clear = function() { + that.status_valid.css('display', 'none'); + that.status_missing.css('display', 'none'); + that.status_revoked.css('display', 'none'); + that.revocation_reason.text(''); + }; + + that.set_status = function(status, revocation_reason) { + that.status_valid.css('display', status === IPA.cert.CERTIFICATE_STATUS_VALID ? '' : 'none'); + that.status_missing.css('display', status === IPA.cert.CERTIFICATE_STATUS_MISSING ? '' : 'none'); + + if (!IPA.cert.is_selfsign()) { + that.status_revoked.css('display', status === IPA.cert.CERTIFICATE_STATUS_REVOKED ? '' : 'none'); + + var reason = IPA.cert.CRL_REASON[revocation_reason]; + that.revocation_reason.html(revocation_reason === undefined || reason === null ? '' : IPA.messages.objects.cert[reason]); + } + }; + + return that; +}; + +IPA.cert.status_field = function(spec) { + + spec = spec || {}; + + var that = IPA.field(spec); + that.registered = false; + + that.load = function(result) { + that.register_listener(); + that.reset(); + }; + + that.set_certificate = function(certificate) { + that.values = certificate; + that.reset(); + }; + + that.register_listener = function() { + if (!that.registered) { + that.registered = true; + that.container.certificate_loaded.attach(that.set_certificate); + } + }; + + return that; +}; + +IPA.widget_factories['certificate_status'] = IPA.cert.status_widget; +IPA.field_factories['certificate_status'] = IPA.cert.status_field;
\ No newline at end of file diff --git a/install/ui/src/freeipa/details.js b/install/ui/src/freeipa/details.js new file mode 100644 index 000000000..c3e84e434 --- /dev/null +++ b/install/ui/src/freeipa/details.js @@ -0,0 +1,1239 @@ +/*jsl:import ipa.js */ + +/* Authors: + * Pavel Zuna <pzuna@redhat.com> + * Adam Young <ayoung@redhat.com> + * Endi S. Dewata <edewata@redhat.com> + * Petr Vobornik <pvoborni@redhat.com> + * + * Copyright (C) 2010 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/>. + */ + +/* IPA Object Details - populating definiton lists from entry data */ + +/* REQUIRES: ipa.js */ + +IPA.expanded_icon = 'expanded-icon'; +IPA.collapsed_icon = 'collapsed-icon'; + +IPA.details_builder = function(spec) { + + var that = {}; + + that.widgets = spec.container.widgets; + that.fields = spec.container.fields; + + that.widget_builder = spec.widget_builder || IPA.widget_builder(); + that.field_builder = spec.field_builder || IPA.field_builder(); + that.section_builder = spec.section_builder || IPA.section_builder(); + + that.build_widget = function(spec) { + + if (!spec) return; + + that.widget_builder.build_widget(spec, that.widgets); + }; + + that.build_widgets = function(specs) { + + if (!specs) return; + + that.widget_builder.build_widgets(specs, that.widgets); + }; + + that.build_field = function(spec) { + + if (!spec) return; + + that.field_builder.build_field(spec, that.fields); + }; + + that.build_fields = function(specs) { + + if (!specs) return; + + that.field_builder.build_fields(specs, that.fields); + }; + + that.build_sections = function(specs) { + + if (!specs) return; + + that.section_builder.build_sections(specs); + }; + + that.build = function(spec) { + + if (spec.sections) { + that.build_sections(spec.sections); + + } else if (spec.fields && !spec.widgets) { + + var sections = [ + { + fields: spec.fields + } + ]; + + that.build_sections(sections); + + } else { + that.build_fields(spec.fields); + that.build_widgets(spec.widgets); + } + }; + + return that; +}; + +IPA.section_builder = function(spec) { + + spec = spec || {}; + + var that = {}; + + that.container = spec.container; + that.section_factory = spec.section_factory || IPA.details_table_section; + + that.field_builder = spec.field_builder; + that.widget_builder = spec.widget_builder; + + that.build_sections = function(sections) { + + if(!sections) return; + + for (var i=0; i < sections.length; i++) { + that.build_section(sections[i], i); + } + }; + + that.build_section = function(section_spec, index) { + section_spec.entity = that.container.entity; + section_spec.facet = that.container; + + if (!section_spec.label && section_spec.name && that.container.entity) { + var obj_messages = IPA.messages.objects[that.container.entity.name]; + section_spec.label = obj_messages[section_spec.name]; + } + + if(!section_spec.name) section_spec.name = 'section'+index; + + section_spec.factory = section_spec.factory || that.section_factory; + var section = section_spec.factory(section_spec); + + that.container.widgets.add_widget(section); + + that.create_fields(section, section_spec.fields); + }; + + that.create_fields = function(section, fields_spec) { + + for (var i=0; i < fields_spec.length; i++) { + that.create_field(section, fields_spec[i]); + } + }; + + that.create_field = function(section, field_spec) { + + var widget = that.widget_builder.build_widget(field_spec, section.widgets); + + //spec.factory refers to widget factory + if(field_spec.factory) delete field_spec.factory; + + var field = that.field_builder.build_field(field_spec, that.container.fields); + + if(widget && field) { + field.widget_name = section.name+'.'+widget.name; + } + }; + + return that; +}; + +IPA.facet_policy = function() { + + var that = {}; + + that.init = function() { + }; + + that.post_create = function() { + }; + + that.post_load = function(data) { + }; + + return that; +}; + +IPA.facet_policies = function(spec) { + + var that = {}; + + that.container = spec.container; + that.policies = []; + + that.add_policy = function(policy) { + + policy.container = that.container; + that.policies.push(policy); + }; + + that.add_policies = function(policies) { + + if (!policies) return; + + for (var i=0; i<policies.length; i++) { + that.add_policy(policies[i]); + } + }; + + that.init = function() { + + for (var i=0; i<that.policies.length; i++) { + that.policies[i].init(); + } + }; + + that.post_create = function() { + + for (var i=0; i<that.policies.length; i++) { + that.policies[i].post_create(); + } + }; + + that.post_load = function(data) { + + for (var i=0; i<that.policies.length; i++) { + that.policies[i].post_load(data); + } + }; + + that.add_policies(spec.policies); + + return that; +}; + +IPA.details_facet = function(spec, no_init) { + + spec = spec || {}; + spec.name = spec.name || 'details'; + + spec.actions = spec.actions || []; + spec.actions.unshift( + IPA.refresh_action, + IPA.reset_action, + IPA.update_action); + + spec.control_buttons = spec.control_buttons || []; + spec.control_buttons.unshift( + { + name: 'refresh', + label: IPA.messages.buttons.refresh, + icon: 'reset-icon' + }, + { + name: 'reset', + label: IPA.messages.buttons.reset, + icon: 'reset-icon' + }, + { + name: 'update', + label: IPA.messages.buttons.update, + icon: 'update-icon' + }); + + spec.state = spec.state || {}; + spec.state.evaluators = spec.state.evaluators || []; + spec.state.evaluators.push(IPA.dirty_state_evaluator); + + var that = IPA.facet(spec, true); + + that.entity = IPA.get_entity(spec.entity); + that.update_command_name = spec.update_command_name || 'mod'; + that.command_mode = spec.command_mode || 'save'; // [save, info] + that.check_rights = spec.check_rights !== undefined ? spec.check_rights : true; + + that.label = spec.label || IPA.messages && IPA.messages.facets && IPA.messages.facets.details; + that.facet_group = spec.facet_group || 'settings'; + + that.widgets = IPA.widget_container(); + that.fields = IPA.field_container({ container: that }); + that.policies = IPA.facet_policies({ + container: that, + policies: spec.policies + }); + + that.fields.add_field = function(field) { + + if (field.dirty_changed) { + field.dirty_changed.attach(that.field_dirty_changed); + } + that.fields.container_add_field(field); + }; + + that.dirty = false; + that.dirty_changed = IPA.observer(); + + /* the primary key used for show and update is built as an array. + for most entities, this will be a single element long, but for some + it requires the containing entities primary keys as well.*/ + that.get_primary_key = function(from_url) { + + var pkey = that.entity.get_primary_key_prefix(); + + if (from_url) { + pkey.push(that.pkey); + } else { + var pkey_name = that.entity.metadata.primary_key; + if (!pkey_name){ + return pkey; + } + var pkey_val = that.data.result.result[pkey_name]; + if (pkey_val instanceof Array) { + pkey.push(pkey_val[0]); + } else { + pkey.push(pkey_val); + } + } + + return pkey; + }; + + that.create = function(container) { + if (that.entity.facets.length == 1) { + if (that.disable_breadcrumb === undefined) { + that.disable_breadcrumb = true; + } + if (that.disable_facet_tabs === undefined) { + that.disable_facet_tabs = true; + } + } + + that.facet_create(container); + that.policies.post_create(); + }; + + that.create_controls = function() { + + that.create_control_buttons(that.controls); + }; + + that.create_header = function(container) { + + that.facet_create_header(container); + + that.create_controls(); + + that.expand_button = IPA.action_button({ + name: 'expand_all', + href: 'expand_all', + label: IPA.messages.details.expand_all, + 'class': 'right-aligned-facet-controls', + style: 'display: none;', + click: function() { + that.expand_button.css('display', 'none'); + that.collapse_button.css('display', 'inline-block'); + + var widgets = that.widgets.get_widgets(); + for (var i=0; i<widgets.length; i++) { + var widget = widgets[i]; + if(widget.toggle) { + widget.toggle(true); + } + } + return false; + } + }).appendTo(that.controls); + + that.collapse_button = IPA.action_button({ + name: 'collapse_all', + href: 'collapse_all', + label: IPA.messages.details.collapse_all, + 'class': 'right-aligned-facet-controls', + click: function() { + that.expand_button.css('display', 'inline-block'); + that.collapse_button.css('display', 'none'); + + var widgets = that.widgets.get_widgets(); + for (var i=0; i<widgets.length; i++) { + var widget = widgets[i]; + if(widget.toggle) { + widget.toggle(false); + } + } + return false; + } + }).appendTo(that.controls); + }; + + that.widgets.create_widget_delimiter = function(container) { + container.append('<hr/>'); + }; + + that.create_content = function(container) { + + that.content = $('<div/>', { + 'class': 'details-content' + }).appendTo(container); + + that.widgets.create(that.content); + + $('<span/>', { + name: 'summary', + 'class': 'details-summary' + }).appendTo(container); + }; + + that.show = function() { + that.facet_show(); + + that.pkey = IPA.nav.get_state(that.entity.name+'-pkey'); + that.old_key_prefix = that.entity.get_primary_key_prefix(); + that.header.set_pkey(that.pkey); + }; + + that.needs_update = function() { + if (that._needs_update !== undefined) return that._needs_update; + + var needs_update = that.facet_needs_update(); + + var pkey = IPA.nav.get_state(that.entity.name+'-pkey'); + var key_prefix = that.entity.get_primary_key_prefix(); + + needs_update = needs_update || pkey !== that.pkey; + needs_update = needs_update || IPA.array_diff(key_prefix, that.old_key_prefix); + + return needs_update; + }; + + that.field_dirty_changed = function(dirty) { + + var old_dirty = that.dirty; + + if (dirty) { + that.dirty = true; + } else { + that.dirty = that.is_dirty(); + } + + if (old_dirty !== that.dirty) { + that.dirty_changed.notify([that.dirty]); + } + }; + + that.is_dirty = function() { + var fields = that.fields.get_fields(); + for (var i=0; i<fields.length; i++) { + if (fields[i].is_dirty()) { + return true; + } + } + return false; + }; + + that.load = function(data) { + that.facet_load(data); + + var fields = that.fields.get_fields(); + for (var i=0; i<fields.length; i++) { + var field = fields[i]; + field.load(data.result.result); + } + that.policies.post_load(data); + that.post_load.notify([data], that); + that.clear_expired_flag(); + }; + + that.save = function(record) { + var fields = that.fields.get_fields(); + for (var i=0; i<fields.length; i++) { + var field = fields[i]; + field.save(record); + } + }; + + that.save_as_update_info = function(only_dirty, require_value) { + + var record = {}; + var update_info = IPA.update_info_builder.new_update_info(); + var fields = that.fields.get_fields(); + + that.save(record); + + for (var i=0; i<fields.length; i++) { + var field = fields[i]; + + if (only_dirty && !field.is_dirty()) continue; + + var values = record[field.param]; + if (require_value && !values) continue; + + update_info.append_field(field, values); + } + + return update_info; + }; + + that.reset = function() { + var fields = that.fields.get_fields(); + for (var i=0; i<fields.length; i++) { + var field = fields[i]; + field.reset(); + } + }; + + + that.validate = function() { + var valid = true; + var fields = that.fields.get_fields(); + for (var i=0; i<fields.length; i++) { + var field = fields[i]; + valid = field.validate() && field.validate_required() && valid; + } + return valid; + }; + + that.nofify_update_success = function() { + var msg = IPA.messages.details.updated; + var key = that.get_primary_key(); + key = key[key.length -1] || ''; + msg = msg.replace('${entity}', that.entity.metadata.label_singular); + msg = msg.replace('${primary_key}', key); + IPA.notify_success(msg); + }; + + + that.update_on_success = function(data, text_status, xhr) { + that.load(data); + that.on_update.notify(); + that.nofify_update_success(); + }; + + that.update_on_error = function(xhr, text_status, error_thrown) { + }; + + that.add_fields_to_command = function(update_info, command) { + + for (var i=0; i < update_info.fields.length; i++) { + var field_info = update_info.fields[i]; + if (field_info.field.flags.indexOf('no_command') > -1) continue; + var values = field_info.field.save(); + IPA.command_builder.add_field_option( + command, + field_info.field, + values); + } + }; + + that.create_fields_update_command = function(update_info) { + + var args = that.get_primary_key(); + + var options = { all: true }; + if (that.check_rights) options.rights = true; + + var command = IPA.command({ + entity: that.entity.name, + method: that.update_command_name, + args: args, + options: options + }); + + //set command options + that.add_fields_to_command(update_info, command); + + return command; + }; + + that.create_batch_update_command = function(update_info) { + + var batch = IPA.batch_command({ + name: that.entity.name + '_details_update' + }); + + var new_update_info = IPA.update_info_builder.copy(update_info); + + if (update_info.fields.length > 0) { + var command = that.create_fields_update_command(update_info); + new_update_info.append_command(command, IPA.config.default_priority); + } + + new_update_info.commands.sort(function(a, b) { + return a.priority - b.priority; + }); + + for (var i=0; i < new_update_info.commands.length; i++) { + batch.add_command(new_update_info.commands[i].command); + } + + return batch; + }; + + that.show_validation_error = function() { + var dialog = IPA.message_dialog({ + name: 'validation_error', + title: IPA.messages.dialogs.validation_title, + message: IPA.messages.dialogs.validation_message + }); + dialog.open(); + }; + + that.create_update_command = function() { + + var command, update_info; + + if (that.command_mode === 'info') { + update_info = that.get_update_info(); + } else { + update_info = that.save_as_update_info(true, true); + } + + if (update_info.commands.length <= 0) { + //normal command + command = that.create_fields_update_command(update_info); + } else { + //batch command + command = that.create_batch_update_command(update_info); + } + + return command; + }; + + that.update = function(on_success, on_error) { + + var command = that.create_update_command(); + + command.on_success = function(data, text_status, xhr) { + that.update_on_success(data, text_status, xhr); + if (on_success) on_success.call(this, data, text_status, xhr); + }; + + command.on_error = function(xhr, text_status, error_thrown) { + that.update_on_error(xhr, text_status, error_thrown); + if (on_error) on_error.call(this, xhr, text_status, error_thrown); + }; + + command.execute(); + }; + + that.get_refresh_command_name = function() { + return that.entity.name+'_show'; + }; + + that.create_refresh_command = function() { + + var options = { all: true }; + if (that.check_rights) options.rights = true; + + var command = IPA.command({ + name: that.get_refresh_command_name(), + entity: that.entity.name, + method: 'show', + options: options + }); + + if (that.pkey) { + command.args = that.get_primary_key(true); + } + + return command; + }; + + that.refresh_on_success = function(data, text_status, xhr) { + that.load(data); + that.show_content(); + }; + + that.refresh_on_error = function(xhr, text_status, error_thrown) { + that.redirect_error(error_thrown); + that.report_error(error_thrown); + }; + + that.refresh = function(on_success, on_error) { + + that.pkey = IPA.nav.get_state(that.entity.name+'-pkey'); + + if (!that.pkey && that.entity.redirect_facet) { + that.redirect(); + return; + } + + var command = that.create_refresh_command(); + + command.on_success = function(data, text_status, xhr) { + that.refresh_on_success(data, text_status, xhr); + if (on_success) on_success.call(this, data, text_status, xhr); + }; + + command.on_error = function(xhr, text_status, error_thrown) { + that.refresh_on_error(xhr, text_status, error_thrown); + if (on_error) on_error.call(this, xhr, text_status, error_thrown); + }; + + command.execute(); + }; + + that.clear = function() { + that.header.clear(); + + that.widgets.clear(); + }; + + that.get_update_info = function() { + + var update_info = IPA.update_info_builder.new_update_info(); + + var fields = that.fields.get_fields(); + for (var i = 0; i < fields.length; i++) { + var field = fields[i]; + if (field.get_update_info) { + var ui = field.get_update_info(); + update_info = IPA.update_info_builder.merge(update_info, ui); + } + } + + return update_info; + }; + + that.create_builder = function() { + + var widget_builder = IPA.widget_builder({ + widget_options: { + entity: that.entity, + facet: that + } + }); + var field_builder = IPA.field_builder({ + field_options: { + entity: that.entity + } + }); + var section_builder = IPA.section_builder({ + container: that, + widget_builder: widget_builder, + field_builder: field_builder + }); + + that.builder = IPA.details_builder({ + container: that, + widget_builder: widget_builder, + field_builder: field_builder, + section_builder: section_builder + }); + }; + + that.init_details_facet = function() { + + that.init_facet(); + that.create_builder(); + that.builder.build(spec); + that.fields.widgets_created(); + that.policies.init(); + }; + + if (!no_init) that.init_details_facet(); + + // methods that should be invoked by subclasses + that.details_facet_create_update_command = that.create_update_command; + that.details_facet_create_refresh_command = that.create_refresh_command; + that.details_facet_refresh_on_success = that.refresh_on_success; + that.details_facet_load = that.load; + + return that; +}; + +IPA.update_info = function(spec) { + + var that = {}; + + that.fields = spec.fields || []; + that.commands = spec.commands || []; + + that.append_field = function(field, value) { + var field_info = IPA.update_info_builder.new_field_info(field, value); + that.fields.push(field_info); + }; + + that.append_command = function (command, priority) { + var command_info = IPA.update_info_builder.new_command_info(command, priority); + that.commands.push(command_info); + }; + + return that; +}; + +IPA.command_info = function(spec) { + + var that = {}; + + that.command = spec.command; + that.priority = spec.priority || IPA.config.default_priority; + + return that; +}; + +IPA.field_info = function(spec) { + + var that = {}; + + that.field = spec.field; + that.value = spec.value; + + return that; +}; + +IPA.update_info_builder = function() { + + var that = {}; + + that.new_update_info = function (fields, commands) { + return IPA.update_info({ + fields: fields, + commands: commands + }); + }; + + that.new_field_info = function(field, value) { + return IPA.field_info({ + field: field, + value: value + }); + }; + + that.new_command_info = function(command, priority) { + return IPA.command_info({ + command: command, + priority: priority + }); + }; + + that.merge = function(a, b) { + return that.new_update_info( + a.fields.concat(b.fields), + a.commands.concat(b.commands)); + }; + + that.copy = function(original) { + return that.new_update_info( + original.fields.concat([]), + original.commands.concat([])); + }; + + return that; +}(); + +IPA.command_builder = function() { + + var that = {}; + + that.add_field_option = function(command, field, values) { + if (!field || !values) return; + + var name = field.param; + + if (field.metadata) { + if (field.metadata.primary_key) return; + if (values.length === 1) { + command.set_option(name, values[0]); + } else { + command.set_option(name, values); + } + } else { + if (values.length) { + command.add_option('setattr', name+'='+values[0]); + } else { + command.add_option('setattr', name+'='); + } + for (var k=1; k<values.length; k++) { + command.add_option('addattr', name+'='+values[k]); + } + } + }; + + return that; +}(); + +IPA.select_action = function(spec) { + + spec = spec || {}; + spec.name = spec.name || 'select_action'; + spec.label = spec.label || '-- select action --'; + + var that = IPA.action(spec); + + that.execute_action = function(facet) { + }; + + return that; +}; + +IPA.refresh_action = function(spec) { + + spec = spec || {}; + spec.name = spec.name || 'refresh'; + spec.label = spec.label || IPA.messages.buttons.refresh; + + var that = IPA.action(spec); + + that.execute_action = function(facet) { + facet.refresh(); + }; + + return that; +}; + +IPA.reset_action = function(spec) { + + spec = spec || {}; + spec.name = spec.name || 'reset'; + spec.label = spec.label || IPA.messages.buttons.reset; + spec.enable_cond = spec.enable_cond || ['dirty']; + + var that = IPA.action(spec); + + that.execute_action = function(facet) { + facet.reset(); + }; + + return that; +}; + +IPA.update_action = function(spec) { + + spec = spec || {}; + spec.name = spec.name || 'update'; + spec.label = spec.label || IPA.messages.buttons.update; + spec.needs_confirm = spec.needs_confirm !== undefined ? spec.needs_confirm : false; + spec.enable_cond = spec.enable_cond || ['dirty']; + + var that = IPA.action(spec); + + that.execute_action = function(facet) { + + if (!facet.validate()) { + facet.show_validation_error(); + return; + } + + facet.update(); + }; + + return that; +}; + +IPA.boolean_state_evaluator = function(spec) { + + spec = spec || {}; + + spec.event = spec.event || 'post_load'; + + var that = IPA.state_evaluator(spec); + + that.name = spec.name || 'boolean_state_evaluator'; + that.field = spec.field; + that.true_state = spec.true_state || that.field_name + '-true'; + that.false_state = spec.false_state || that.field_name + '-false'; + that.invert_value = spec.invert_value; + that.parser = IPA.build({ + factory: spec.parser || IPA.boolean_formatter, + invert_value: that.invert_value + }); + + 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]); + + if (value === true) { + that.state.push(that.true_state); + } else { + that.state.push(that.false_state); + } + + that.notify_on_change(old_state); + }; + + return that; +}; + +IPA.enable_state_evaluator = function(spec) { + + spec = spec || {}; + spec.name = spec.name || 'enable_state_evaluator'; + spec.true_state = spec.true_state || 'enabled'; + spec.false_state = spec.false_state || 'disabled'; + + var that = IPA.boolean_state_evaluator(spec); + + return that; +}; + +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); + that.attribute = spec.attribute; + + that.on_event = function(data) { + + var old_state, record, rights, i, state; + + old_state = that.state; + record = data.result.result; + + that.state = []; + + if (record.attributelevelrights) { + rights = record.attributelevelrights[that.attribute]; + } + + // Full rights if we don't know the rights. Better to allow action and + // then to show error dialog than not be able to do something. + rights = rights || 'rscwo'; + + for (i=0; i<rights.length; i++) { + state = that.attribute + '_' + rights.charAt(i); + that.state.push(state); + } + + that.notify_on_change(old_state); + }; + + return that; +}; + +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); + that.attribute = spec.attribute; + that.value = spec.value; + that.representation = spec.representation; + + that.on_event = function(data) { + + var old_state, record, state, value, loaded_value; + + old_state = that.state; + record = data.result.result; + value = that.normalize_value(that.value); + loaded_value = record[that.attribute]; + loaded_value = that.normalize_value(loaded_value); + + that.state = []; + + if (!IPA.array_diff(value, loaded_value)) { + that.state.push(that.get_state_text()); + } + + that.notify_on_change(old_state); + }; + + that.normalize_value = function(original) { + + var value = original; + + if (!(value instanceof Array)) { + value = [value]; + } + return value; + }; + + that.get_state_text = function() { + + var representation, value; + + representation = that.representation; + + if (!representation) { + value = that.normalize_value(that.value); + representation = that.attribute + '_' + value[0]; + } + + return representation; + }; + + return that; +}; + +IPA.object_class_evaluator = function(spec) { + + spec.name = spec.name || 'object_class_evaluator'; + spec.event = spec.event || 'post_load'; + + var that = IPA.state_evaluator(spec); + + + that.on_event = function(data) { + + var old_state, classes, i; + + old_state = that.state; + classes = data.result.result.objectclass; + + that.state = []; + + for (i=0; i<classes.length; i++) { + that.state.push('oc_'+classes[i]); + } + + that.notify_on_change(old_state); + }; + + return that; +}; + +IPA.object_action = function(spec) { + + spec = spec || {}; + + var that = IPA.action(spec); + + that.method = spec.method; + that.confirm_msg = spec.confirm_msg || IPA.messages.actions.confirm; + that.options = spec.options || {}; + + that.execute_action = function(facet, on_success, on_error) { + + var entity_name = facet.entity.name; + var pkey = IPA.nav.get_state(entity_name+'-pkey'); + + IPA.command({ + entity: entity_name, + method: that.method, + args: [pkey], + options: that.options, + on_success: that.get_on_success(facet, on_success), + on_error: that.get_on_error(facet, on_error) + }).execute(); + }; + + that.on_success = function(facet, data, text_status, xhr) { + + IPA.notify_success(data.result.summary); + facet.on_update.notify(); + }; + + that.on_error = function(facet, xhr, text_status, error_thrown) { + }; + + that.get_on_success = function(facet, on_success) { + return function(data, text_status, xhr) { + that.on_success(facet, data, text_status, xhr); + if (on_success) on_success.call(this, data, text_status, xhr); + }; + }; + + that.get_on_error = function(facet, on_error) { + return function(xhr, text_status, error_thrown) { + that.on_error(facet, xhr, text_status, error_thrown); + if (on_error) on_error.call(this, xhr, text_status, error_thrown); + }; + }; + + that.get_confirm_message = function(facet) { + var pkey = IPA.nav.get_state(facet.entity.name+'-pkey'); + var msg = that.confirm_msg.replace('${object}', pkey); + return msg; + }; + + that.object_execute_action = that.execute_action; + + return that; +}; + +IPA.enable_action = function(spec) { + + spec = spec || {}; + spec.name = spec.name || 'enable'; + spec.method = spec.method || 'enable'; + spec.confirm_msg = spec.confirm_msg || IPA.messages.actions.enable_confirm; + spec.label = spec.label || IPA.messages.buttons.enable; + spec.disable_cond = spec.disable_cond || ['enabled']; + + var that = IPA.object_action(spec); + + return that; +}; + +IPA.disable_action = function(spec) { + + spec = spec || {}; + spec.name = spec.name || 'disable'; + spec.method = spec.method || 'disable'; + spec.confirm_msg = spec.confirm_msg || IPA.messages.actions.disable_confirm; + spec.label = spec.label || IPA.messages.buttons.disable; + spec.enable_cond = spec.enable_cond || ['enabled']; + + var that = IPA.object_action(spec); + + return that; +}; + +IPA.delete_action = function(spec) { + + spec = spec || {}; + spec.name = spec.name || 'delete'; + spec.method = spec.method || 'del'; + spec.confirm_msg = spec.confirm_msg || IPA.messages.actions.delete_confirm; + spec.label = spec.label || IPA.messages.buttons.remove; + + var that = IPA.object_action(spec); + + that.execute_action = function(facet, on_success, on_error) { + + if (facet.is_dirty()) facet.reset(); + + that.object_execute_action(facet, on_success, on_error); + }; + + that.on_success = function(facet, data, text_status, xhr) { + + IPA.notify_success(data.result.summary); + facet.on_update.notify(); + facet.redirect(); + }; + + return that; +}; + + +IPA.enabled_summary_cond = function() { + return { + pos: ['enabled'], + neg: [], + description: IPA.messages.status.enabled, + state: ['enabled'] + }; +}; + +IPA.disabled_summary_cond = function() { + return { + pos: [], + neg: ['enabled'], + description: IPA.messages.status.disabled, + state: ['disabled'] + }; +}; diff --git a/install/ui/src/freeipa/develop.js b/install/ui/src/freeipa/develop.js new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/install/ui/src/freeipa/develop.js diff --git a/install/ui/src/freeipa/dialog.js b/install/ui/src/freeipa/dialog.js new file mode 100644 index 000000000..4e603155a --- /dev/null +++ b/install/ui/src/freeipa/dialog.js @@ -0,0 +1,849 @@ +/*jsl:import ipa.js */ +/* Authors: + * Endi Sukma Dewata <edewata@redhat.com> + * Petr Vobornik <pvoborni@redhat.com> + * + * Copyright (C) 2010 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/>. + */ + +/* REQUIRES: widget.js, details.js */ + +IPA.opened_dialogs = { + + dialogs: [], + + top_dialog: function() { + var top = null; + if (this.dialogs.length) top = this.dialogs[this.dialogs.length - 1]; + return top; + }, + + focus_top: function() { + var top = this.top_dialog(); + if (top) { + top.container.dialog('moveToTop'); //make sure the last dialog is top dialog + top.focus_first_element(); + } + }, + + add_dialog: function(dialog) { + this.dialogs.push(dialog); + }, + + remove_dialog: function(dialog) { + var index = this.dialogs.indexOf(dialog); + if (index > -1) this.dialogs.splice(index, 1); + } +}; + +IPA.dialog_button = function(spec) { + + spec = spec || {}; + + var that = {}; + + that.name = spec.name; + that.label = spec.label || spec.name; + that.click = spec.click || click; + that.visible = spec.visible !== undefined ? spec.visible : true; + + function click() { + } + + that.set_enabled = function(enabled) { + if (enabled) { + that.element.removeClass('ui-state-disabled'); + } else { + that.element.addClass('ui-state-disabled'); + } + }; + + that.is_enabled = function() { + return !that.element.hasClass('ui-state-disabled'); + }; + + return that; +}; + +/** + * This is a base class for dialog boxes. + */ +IPA.dialog = function(spec) { + + spec = spec || {}; + + var that = {}; + + that.entity = IPA.get_entity(spec.entity); + that.name = spec.name || 'dialog'; + that.id = spec.id; + that.title = spec.title; + that.width = spec.width || 500; + that.height = spec.height; + that.close_on_escape = spec.close_on_escape !== undefined ? + spec.close_on_escape : true; + + that.widgets = IPA.widget_container(); + that.fields = IPA.field_container({ container: that }); + that.buttons = $.ordered_map(); + that.policies = IPA.facet_policies({ + container: that, + policies: spec.policies + }); + + that.create_button = function(spec) { + var factory = spec.factory || IPA.dialog_button; + var button = factory(spec); + that.add_button(button); + return button; + }; + + that.add_button = function(button) { + that.buttons.put(button.name, button); + }; + + that.get_button = function(name) { + return that.buttons.get(name); + }; + + that.field = function(field) { + that.fields.add_field(field); + return that; + }; + + that.validate = function() { + var valid = true; + var fields = that.fields.get_fields(); + for (var i=0; i<fields.length; i++) { + var field = fields[i]; + valid = field.validate() && field.validate_required() && valid; + } + return valid; + }; + + that.get_id = function() { + if (that.id) return that.id; + if (that.name) return that.name; + return null; + }; + + + /** + * Create content layout + */ + that.create = function() { + + that.message_container = $('<div/>', { + style: 'display: none', + 'class': 'dialog-message ui-state-highlight ui-corner-all' + }).appendTo(that.container); + + var widgets = that.widgets.get_widgets(); + for (var i=0; i<widgets.length; i++) { + var widget = widgets[i]; + + var div = $('<div/>', { + name: widget.name, + 'class': 'dialog-section' + }).appendTo(that.container); + + widget.create(div); + } + + that.policies.post_create(); + }; + + that.show_message = function(message) { + that.message_container.text(message); + that.message_container.css('display', ''); + }; + + that.hide_message = function() { + that.message_container.css('display', 'none'); + }; + + /** + * Open dialog + */ + that.open = function(container) { + + that.container = $('<div/>', { + id : that.get_id(), + 'data-name': that.name + }); + + if (container) { + container.append(that.container); + } + + that.create(); + that.reset(); + + that.container.dialog({ + title: that.title, + modal: true, + closeOnEscape: that.close_on_escape, + width: that.width, + minWidth: that.width, + height: that.height, + minHeight: that.height, + close: function(event, ui) { + that.close(); + } + }); + + that.set_buttons(); + that.register_listeners(); + IPA.opened_dialogs.add_dialog(that); + that.focus_first_element(); + }; + + 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 + + // code taken from jquery dialog source code + $(element.find(':tabbable').get().concat( + ui_dialog.find('.ui-dialog-buttonpane :tabbable').get().concat( + ui_dialog.get()))).eq(0).focus(); + }; + + that.option = function(name, value) { + that.container.dialog('option', name, value); + }; + + that.set_buttons = function() { + + // create a map of button labels and handlers + var dialog_buttons = {}; + for (var i=0; i<that.buttons.values.length; i++) { + var button = that.buttons.values[i]; + if (!button.visible) continue; + dialog_buttons[button.label] = button.click; + } + + //set buttons to dialog + that.option('buttons', dialog_buttons); + + // find button elements + var parent = that.container.parent(); + var buttons = $('.ui-dialog-buttonpane .ui-dialog-buttonset button', parent); + + buttons.each(function(index) { + var button = that.buttons.values[index]; + button.element = $(this); + }); + }; + + that.display_buttons = function(names) { + + for (var i=0; i<that.buttons.values.length; i++) { + var button = that.buttons.values[i]; + + button.visible = names.indexOf(button.name) > -1; + } + that.set_buttons(); + }; + + that.save = function(record) { + var fields = that.fields.get_fields(); + for (var i=0; i<fields.length; i++) { + var field = fields[i]; + field.save(record); + } + }; + + that.close = function() { + that.container.dialog('destroy'); + that.container.remove(); + that.remove_listeners(); + IPA.opened_dialogs.remove_dialog(that); + IPA.opened_dialogs.focus_top(); + }; + + that.reset = function() { + var fields = that.fields.get_fields(); + for (var i=0; i<fields.length; i++) { + fields[i].reset(); + } + }; + + that.register_listeners = function() {}; + that.remove_listeners = function() {}; + + that.create_builder = function() { + + var widget_builder = IPA.widget_builder({ + widget_options: { + entity: that.entity, + facet: that + } + }); + var field_builder = IPA.field_builder({ + field_options: { + undo: false, + entity: that.entity, + facet: that + } + }); + var section_builder = IPA.section_builder({ + container: that, + section_factory: IPA.details_table_section_nc, + widget_builder: widget_builder, + field_builder: field_builder + }); + + that.builder = IPA.details_builder({ + container: that, + widget_builder: widget_builder, + field_builder: field_builder, + section_builder: section_builder + }); + }; + + that.init = function() { + + that.create_builder(); + that.builder.build(spec); + that.fields.widgets_created(); + that.policies.init(); + }; + + that.init(); + + that.dialog_create = that.create; + that.dialog_open = that.open; + that.dialog_close = that.close; + that.dialog_save = that.save; + that.dialog_reset = that.reset; + that.dialog_validate = that.validate; + + return that; +}; + +/** + * This dialog provides an interface for searching and selecting + * values from the available results. + */ +IPA.adder_dialog = function(spec) { + + spec = spec || {}; + + spec.name = spec.name || 'adder_dialog'; + + var that = IPA.dialog(spec); + + IPA.confirm_mixin().apply(that); + + that.external = spec.external; + that.width = spec.width || 600; + that.height = spec.height || 360; + + if (!that.entity) { + var except = { + expected: false, + message:'Adder dialog created without entity.' + }; + throw except; + } + + var init = function() { + that.available_table = IPA.table_widget({ + entity: that.entity, + name: 'available', + scrollable: true + }); + + that.selected_table = IPA.table_widget({ + entity: that.entity, + name: 'selected', + scrollable: true + }); + + if (spec.columns) { + for (var i=0; i<spec.columns.length; i++) { + that.create_column(spec.columns[i]); + } + } + }; + + that.get_column = function(name) { + return that.available_table.get_column(name); + }; + + that.get_columns = function() { + return that.available_table.get_columns(); + }; + + that.add_column = function(column) { + that.available_table.add_column(column); + that.selected_table.add_column(column); + }; + + that.set_columns = function(columns) { + that.clear_columns(); + for (var i=0; i<columns.length; i++) { + that.add_column(columns[i]); + } + }; + + that.clear_columns = function() { + that.available_table.clear_columns(); + that.selected_table.clear_columns(); + }; + + that.create_column = function(spec) { + spec.entity = that.entity; + var column = IPA.column(spec); + that.add_column(column); + return column; + }; + + that.create = function() { + + // do not call that.dialog_create(); + + var container = $('<div/>', { + 'class': 'adder-dialog' + }).appendTo(that.container); + + var top_panel = $('<div/>', { + 'class': 'adder-dialog-top' + }).appendTo(container); + + $('<input/>', { + type: 'text', + name: 'filter', + keyup: function(event) { + if (event.keyCode === $.ui.keyCode.ENTER) { + that.search(); + return false; + } + } + }).appendTo(top_panel); + + top_panel.append(' '); + + that.find_button = IPA.button({ + name: 'find', + label: IPA.messages.buttons.find, + click: function() { + that.search(); + return false; + } + }).appendTo(top_panel); + + top_panel.append(IPA.create_network_spinner()); + + var left_panel = $('<div/>', { + 'class': 'adder-dialog-left' + }).appendTo(container); + + var available_panel = $('<div/>', { + name: 'available', + 'class': 'adder-dialog-available' + }).appendTo(left_panel); + + $('<div/>', { + html: IPA.messages.dialogs.available, + 'class': 'adder-dialog-header ui-widget-header' + }).appendTo(available_panel); + + var available_content = $('<div/>', { + 'class': 'adder-dialog-content' + }).appendTo(available_panel); + + that.available_table.create(available_content); + + + var right_panel = $('<div/>', { + 'class': 'adder-dialog-right' + }).appendTo(container); + + var selected_panel = $('<div/>', { + name: 'selected', + 'class': 'adder-dialog-selected' + }).appendTo(right_panel); + + $('<div/>', { + html: IPA.messages.dialogs.prospective, + 'class': 'adder-dialog-header ui-widget-header' + }).appendTo(selected_panel); + + var selected_content = $('<div/>', { + 'class': 'adder-dialog-content' + }).appendTo(selected_panel); + + that.selected_table.create(selected_content); + + + var buttons_panel = $('<div/>', { + name: 'buttons', + 'class': 'adder-dialog-buttons' + }).appendTo(container); + + var div = $('<div/>').appendTo(buttons_panel); + IPA.button({ + name: 'add', + label: '>>', + click: function() { + that.add(); + that.update_buttons(); + return false; + } + }).appendTo(div); + + div = $('<div/>').appendTo(buttons_panel); + IPA.button({ + name: 'remove', + label: '<<', + click: function() { + that.remove(); + that.update_buttons(); + return false; + } + }).appendTo(div); + + that.filter_field = $('input[name=filter]', that.container); + + if (that.external) { + container.addClass('adder-dialog-with-external'); + + var external_panel = $('<div/>', { + name: 'external', + 'class': 'adder-dialog-external' + }).appendTo(left_panel); + + $('<div/>', { + html: IPA.messages.objects.sudorule.external, + 'class': 'adder-dialog-header ui-widget-header' + }).appendTo(external_panel); + + var external_content = $('<div/>', { + 'class': 'adder-dialog-content' + }).appendTo(external_panel); + + that.external_field = $('<input/>', { + type: 'text', + name: 'external' + }).appendTo(external_content); + } + + that.search(); + }; + + that.open = function(container) { + + var add_button = that.create_button({ + name: 'add', + label: IPA.messages.buttons.add, + click: function() { + if (!add_button.is_enabled()) return; + that.execute(); + } + }); + + that.create_button({ + name: 'cancel', + label: IPA.messages.buttons.cancel, + click: function() { + that.close(); + } + }); + + that.dialog_open(container); + + that.update_buttons(); + }; + + that.add = function() { + var rows = that.available_table.remove_selected_rows(); + that.selected_table.add_rows(rows); + }; + + that.remove = function() { + var rows = that.selected_table.remove_selected_rows(); + that.available_table.add_rows(rows); + }; + + that.update_buttons = function() { + + var values = that.selected_table.save(); + + var button = that.get_button('add'); + button.set_enabled(values && values.length); + }; + + that.get_filter = function() { + return that.filter_field.val(); + }; + + that.clear_available_values = function() { + that.available_table.empty(); + }; + + that.clear_selected_values = function() { + that.selected_table.empty(); + }; + + that.add_available_value = function(record) { + that.available_table.add_record(record); + }; + + that.get_selected_values = function() { + return that.selected_table.save(); + }; + + that.execute = function() { + }; + + that.on_confirm = function() { + + var add_button = that.get_button('add'); + if (add_button.is_enabled()) { + that.execute(); + } + }; + + init(); + + that.adder_dialog_create = that.create; + + return that; +}; + +/** + * This dialog displays the values to be deleted. + */ +IPA.deleter_dialog = function (spec) { + + spec = spec || {}; + + spec.title = spec.title || IPA.messages.buttons.remove; + spec.name = spec.name || 'deleter_dialog'; + spec.message = spec.message || IPA.messages.search.delete_confirm; + spec.ok_label = spec.ok_label || IPA.messages.buttons.remove; + + var that = IPA.confirm_dialog(spec); + that.values = spec.values || []; + that.on_ok = spec.on_ok || function() { + that.execute(); + }; + + that.add_value = function(value) { + that.values.push(value); + }; + + that.set_values = function(values) { + that.values = values; + }; + + that.create = function() { + + $('<p/>', { + 'text': that.message + }).appendTo(that.container); + + var div = $('<div/>', { + style: 'overflow:auto; max-height: 100px' + }).appendTo(that.container); + + var ul = $('<ul/>'); + ul.appendTo(div); + + for (var i=0; i<that.values.length; i++) { + var value = that.values[i]; + if (value instanceof Object){ + var first = true; + var str_value = ""; + for (var key in value){ + if (value.hasOwnProperty(key)){ + if (!first){ + str_value += ','; + } + str_value += (key + ':' +value[key]); + first = false; + } + } + value = str_value; + } + + $('<li/>',{ + 'text': value + }).appendTo(ul); + } + }; + + that.deleter_dialog_create = that.create; + + return that; +}; + +IPA.message_dialog = function(spec) { + + spec = spec || {}; + + spec.name = spec.name || 'message_dialog'; + + var that = IPA.confirm_dialog(spec); + + that.open = function(container) { + + that.confirm_dialog_open(container); + that.confirmed = true; // there are no options to confirm + }; + + that.buttons.remove('cancel'); + + that.message_dialog_create = that.create; + + return that; +}; + +IPA.confirm_dialog = function(spec) { + + spec = spec || {}; + spec.name = spec.name || 'confirm_dialog'; + spec.title = spec.title || IPA.messages.dialogs.confirmation; + + var that = IPA.dialog(spec); + IPA.confirm_mixin().apply(that); + + that.message = spec.message || ''; + that.on_ok = spec.on_ok; + that.on_cancel = spec.on_cancel; + that.ok_label = spec.ok_label || IPA.messages.buttons.ok; + that.cancel_label = spec.cancel_label || IPA.messages.buttons.cancel; + that.confirmed = false; + that.confirm_on_enter = spec.confirm_on_enter !== undefined ? spec.confirm_on_enter : true; + + that.create = function() { + $('<p/>', { + 'text': that.message + }).appendTo(that.container); + }; + + that.close = function() { + + that.dialog_close(); + + if (that.confirmed) { + if (that.on_ok) { + that.on_ok(); + } + } else { + if (that.on_cancel) { + that.on_cancel(); + } + } + }; + + that.open = function(container) { + + that.confirmed = false; + that.dialog_open(container); + }; + + that.on_confirm = function() { + that.confirmed = true; + that.close(); + }; + + that.create_buttons = function() { + + that.create_button({ + name: 'ok', + label: that.ok_label, + click: function() { + that.confirmed = true; + that.close(); + } + }); + + that.create_button({ + name: 'cancel', + label: that.cancel_label, + click: function() { + that.confirmed = false; + that.close(); + } + }); + }; + + that.create_buttons(); + + that.confirm_dialog_close = that.close; + that.confirm_dialog_open = that.open; + + return that; +}; + +IPA.confirm_mixin = function() { + + return { + mixin: { + + ignore_enter_rules: { + src_elements: ['a', 'button'], + src_types: ['textarea', 'select-one'] + }, + + test_ignore: function(event) { + + var ir = this.ignore_enter_rules, + t = event.target, + + ignore = ir.src_elements.indexOf(t.tagName.toLowerCase()) > -1 || + ir.src_types.indexOf(t.type) > -1; + + return ignore; + }, + + register_listeners: function() { + var self = this; + this._on_key_up_listener = function(e) { self.on_key_up(e); }; + var dialog_container = this.container.parent('.ui-dialog'); + dialog_container.bind('keyup', this._on_key_up_listener); + }, + + remove_listeners: function() { + var dialog_container = this.container.parent('.ui-dialog'); + dialog_container.unbind('keyup', this._on_key_up_listener); + }, + + on_key_up: function(event) { + if (event.keyCode === $.ui.keyCode.ENTER && + !this.test_ignore(event) && + !!this.on_confirm) { + event.preventDefault(); + this.on_confirm(); + } else if (event.keyCode === $.ui.keyCode.ESCAPE && + !!this.on_cancel) { + event.preventDefault(); + this.on_cancel(); + } + } + }, + + apply: function(obj) { + $.extend(obj, this.mixin); + } + }; +}; diff --git a/install/ui/src/freeipa/dns.js b/install/ui/src/freeipa/dns.js new file mode 100644 index 000000000..d08f4140b --- /dev/null +++ b/install/ui/src/freeipa/dns.js @@ -0,0 +1,2552 @@ +/*jsl:import ipa.js */ +/*jsl:import search.js */ +/*jsl:import net.js */ + +/* Authors: + * Adam Young <ayoung@redhat.com> + * Petr Vobornik <pvoborni@redhat.com> + * + * Copyright (C) 2010 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/>. + */ + +/* REQUIRES: ipa.js, details.js, search.js, add.js, facet.js, entity.js, + * net.js, widget.js */ + +IPA.dns = { + zone_permission_name: 'Manage DNS zone ${dnszone}' +}; + +IPA.dns.config_entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + + if (!IPA.dns_enabled) { + throw { + expected: true + }; + } + + that.entity_init(); + + that.builder.details_facet({ + title: IPA.metadata.objects.config.label, + sections: [ + { + name: 'options', + label: IPA.messages.objects.dnsconfig.options, + fields: [ + { + type: 'checkbox', + name: 'idnsallowsyncptr' + }, + { + type: 'multivalued', + name: 'idnsforwarders', + validators: [IPA.dnsforwarder_validator()] + }, + { + type: 'radio', + name: 'idnsforwardpolicy', + default_value: 'first', + options: [ + { + value: 'first', + label: IPA.messages.objects.dnsconfig.forward_first + }, + { + value: 'only', + label: IPA.messages.objects.dnsconfig.forward_only + }, + { + value: 'none', + label: IPA.messages.objects.dnsconfig.forward_none + } + ] + }, + 'idnszonerefresh' + ] + } + ], + needs_update: true + }); + }; + + return that; +}; + +IPA.dns.zone_entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + + if (!IPA.dns_enabled) { + throw { + expected: true + }; + } + + that.entity_init(); + + that.builder.facet_groups([ 'dnsrecord', 'settings' ]). + search_facet({ + row_enabled_attribute: 'idnszoneactive', + title: IPA.metadata.objects.dnszone.label, + columns: [ + 'idnsname', + { + name: 'idnszoneactive', + label: IPA.messages.status.label, + formatter: IPA.boolean_status_formatter() + } + ], + actions: [ + IPA.batch_disable_action, + IPA.batch_enable_action + ], + control_buttons: [ + { + name: 'disable', + label: IPA.messages.buttons.disable, + icon: 'disabled-icon' + }, + { + name: 'enable', + label: IPA.messages.buttons.enable, + icon: 'enabled-icon' + } + ] + }). + details_facet({ + factory: IPA.dnszone_details_facet, + command_mode: 'info', + sections: [ + { + name: 'identity', + fields: [ + 'idnsname', + 'idnssoamname', + 'idnssoarname', + 'idnssoaserial', + 'idnssoarefresh', + 'idnssoaretry', + 'idnssoaexpire', + 'idnssoaminimum', + 'dnsttl', + { + type: 'combobox', + name: 'dnsclass', + options: [ + 'IN', 'CS', 'CH', 'HS' + ] + }, + { + type: 'radio', + name: 'idnsallowdynupdate', + options: [ + { value: 'TRUE', label: IPA.get_message('true') }, + { value: 'FALSE', label: IPA.get_message('false') } + ] + }, + { + type: 'textarea', + name: 'idnsupdatepolicy' + }, + { + type: 'netaddr', + name: 'idnsallowquery', + validators: [ + IPA.network_validator({ + specials: ['any', 'none', 'localhost', 'localnets'], + allow_negation: true, + allow_host_address: true + })] + }, + { + type: 'netaddr', + name: 'idnsallowtransfer', + validators: [ + IPA.network_validator({ + specials: ['any', 'none', 'localhost', 'localnets'], + allow_negation: true, + allow_host_address: true + })] + }, + { + type: 'multivalued', + name: 'idnsforwarders', + validators: [IPA.dnsforwarder_validator()] + }, + { + type: 'radio', + name: 'idnsforwardpolicy', + default_value: 'first', + options: [ + { + value: 'first', + label: IPA.messages.objects.dnsconfig.forward_first + }, + { + value: 'only', + label: IPA.messages.objects.dnsconfig.forward_only + }, + { + value: 'none', + label: IPA.messages.objects.dnsconfig.forward_none + } + ] + }, + { + type: 'checkbox', + name: 'idnsallowsyncptr' + } + ] + }], + actions: [ + IPA.select_action, + IPA.enable_action, + IPA.disable_action, + IPA.delete_action, + IPA.dns.add_permission_action, + IPA.dns.remove_permission_action + ], + header_actions: ['select_action', 'enable', 'disable', 'delete', + 'add_permission', 'remove_permission'], + state: { + evaluators: [ + { + factory: IPA.enable_state_evaluator, + field: 'idnszoneactive' + }, + { + factory: IPA.acl_state_evaluator, + attribute: 'managedby' + }, + IPA.dns.zone_has_permission_evaluator + ], + summary_conditions: [ + IPA.enabled_summary_cond(), + IPA.disabled_summary_cond() + ] + } + }). + nested_search_facet({ + factory: IPA.dns.record_search_facet, + facet_group: 'dnsrecord', + nested_entity : 'dnsrecord', + name: 'records', + deleter_dialog: IPA.dns.record_search_deleter_dialog, + title: IPA.metadata.objects.dnszone.label_singular, + label: IPA.metadata.objects.dnsrecord.label, + tab_label: IPA.metadata.objects.dnsrecord.label, + columns: [ + { + name: 'idnsname', + label: IPA.get_entity_param('dnsrecord', 'idnsname').label, + primary_key: true + }, + { + name: 'type', + label: IPA.messages.objects.dnsrecord.type + }, + { + name: 'data', + label: IPA.messages.objects.dnsrecord.data + } + ] + }). + standard_association_facets(). + adder_dialog({ + factory: IPA.dnszone_adder_dialog, + height: 300, + sections: [ + { + factory: IPA.dnszone_name_section, + name: 'name', + fields: [ + { + type: 'dnszone_name', + name: 'idnsname', + required: false, + radio_name: 'dnszone_name_type' + }, + { + type: 'dnszone_name', + name: 'name_from_ip', + radio_name: 'dnszone_name_type', + validators: [IPA.network_validator()] + } + ] + }, + { + name: 'other', + fields: [ + 'idnssoamname', + { + name: 'idnssoarname', + required: false + }, + { + type: 'force_dnszone_add_checkbox', + name: 'force', + metadata: IPA.get_command_option('dnszone_add', 'force') + } + ] + } + ], + policies: [ + IPA.add_dns_zone_name_policy() + ] + }); + }; + + return that; +}; + +IPA.dnszone_details_facet = function(spec, no_init) { + + spec = spec || {}; + + var that = IPA.details_facet(spec, true); + that.permission_load = IPA.observer(); + that.permission_status = 'unknown'; // [unknown, set, none] + + that.refresh_on_success = function(data, text_status, xhr) { + // do not load data from batch + + that.show_content(); + }; + + that.create_refresh_command = function() { + + var pkey = IPA.nav.get_state(that.entity.name+'-pkey'); + + var batch = IPA.batch_command({ + name: 'dnszone_details_refresh' + }); + + var dnszone_command = that.details_facet_create_refresh_command(); + + dnszone_command.on_success = function(data, text_status, xhr) { + // create data that mimics dnszone-show output + var dnszone_data = {}; + dnszone_data.result = data; + that.load(dnszone_data); + }; + + batch.add_command(dnszone_command); + + var permission_name = IPA.dns.zone_permission_name.replace('${dnszone}', pkey); + + var permission_command = IPA.command({ + entity: 'permission', + method: 'show', + args: [permission_name], + options: {}, + retry: false + }); + + permission_command.on_success = function(data, text_status, xhr) { + that.permission_status = 'set'; + that.permission_load.notify([that.permission_status], that); + }; + + permission_command.on_error = function(xhr, text_status, error_thrown) { + if (error_thrown && error_thrown.code === 4001) { + //NotFound error + that.permission_status = 'none'; + } else { + that.permission_status = 'unknown'; + } + + that.permission_load.notify([that.permission_status], that); + }; + + batch.add_command(permission_command); + + return batch; + }; + + that.update_on_success = function(data, text_status, xhr) { + that.refresh(); + that.on_update.notify(); + that.nofify_update_success(); + }; + + that.update_on_error = function(xhr, text_status, error_thrown) { + that.refresh(); + }; + + if (!no_init) that.init_details_facet(); + + return that; +}; + +IPA.dnszone_name_section = function(spec) { + + spec = spec || {}; + + var that = IPA.details_table_section(spec); + + that.create = function(container) { + that.container = container; + + that.message_container = $('<div/>', { + style: 'display: none', + 'class': 'dialog-message ui-state-highlight ui-corner-all' + }).appendTo(that.container); + + var table = $('<table/>', { + 'class': 'section-table' + }).appendTo(that.container); + + var idnsname = that.widgets.get_widget('idnsname'); + + var tr = $('<tr/>').appendTo(table); + + var td = $('<td/>', { + 'class': 'section-cell-label', + title: idnsname.label + }).appendTo(tr); + + var label = $('<label/>', { + name: 'idnsname', + 'class': 'field-label', + 'for': idnsname.radio_id + }).appendTo(td); + + idnsname.create_radio(label); + + label.append(idnsname.label+':'); + + idnsname.create_required(td); + + td = $('<td/>', { + 'class': 'section-cell-field', + title: idnsname.label + }).appendTo(tr); + + var span = $('<span/>', { + name: 'idnsname', + 'class': 'field' + }).appendTo(td); + + idnsname.create(span); + + var idnsname_input = $('input', span); + + var name_from_ip = that.widgets.get_widget('name_from_ip'); + + tr = $('<tr/>').appendTo(table); + + td = $('<td/>', { + 'class': 'section-cell-label', + title: name_from_ip.label + }).appendTo(tr); + + label = $('<label/>', { + name: 'name_from_ip', + 'class': 'field-label', + 'for': name_from_ip.radio_id + }).appendTo(td); + + name_from_ip.create_radio(label); + + label.append(name_from_ip.label+':'); + + name_from_ip.create_required(td); + + td = $('<td/>', { + 'class': 'section-cell-field', + title: name_from_ip.label + }).appendTo(tr); + + span = $('<span/>', { + name: 'name_from_ip', + 'class': 'field' + }).appendTo(td); + + name_from_ip.create(span); + + idnsname.radio.click(); + }; + + + return that; +}; + +IPA.add_dns_zone_name_policy = function() { + + var that = IPA.facet_policy(); + + that.init = function() { + var idnsname_w = this.container.widgets.get_widget('name.idnsname'); + var name_from_ip_w = this.container.widgets.get_widget('name.name_from_ip'); + + var idnsname_f = this.container.fields.get_field('idnsname'); + var name_from_ip_f = this.container.fields.get_field('name_from_ip'); + + idnsname_w.radio_clicked.attach(function() { + idnsname_w.input.prop('disabled', false); + name_from_ip_w.input.prop('disabled', true); + + idnsname_f.set_required(true); + name_from_ip_f.set_required(false); + + name_from_ip_f.reset(); + }); + + name_from_ip_w.radio_clicked.attach(function() { + idnsname_w.input.prop('disabled', true); + name_from_ip_w.input.prop('disabled', false); + + idnsname_f.set_required(false); + name_from_ip_f.set_required(true); + + idnsname_f.reset(); + }); + }; + + return that; +}; + +IPA.dnszone_name_widget = function(spec) { + + spec = spec || {}; + + var that = IPA.text_widget(spec); + + that.radio_name = spec.radio_name; + that.radio_clicked = IPA.observer(); + that.text_save = that.save; + that.radio_id = IPA.html_util.get_next_id(that.radio_name); + + that.save = function() { + + var values = []; + + if (that.radio.is(':checked')) { + values = that.text_save(); + } + return values; + }; + + that.create_radio = function(container) { + + that.radio = $('<input/>', { + type: 'radio', + id: that.radio_id, + name: that.radio_name, + value: that.name, + click: function() { + that.radio_clicked.notify([], that); + } + }).appendTo(container); + }; + + return that; +}; + +IPA.widget_factories['dnszone_name'] = IPA.dnszone_name_widget; + +IPA.force_dnszone_add_checkbox_widget = function(spec) { + var metadata = IPA.get_command_option('dnszone_add', spec.name); + spec.label = metadata.label; + spec.tooltip = metadata.doc; + return IPA.checkbox_widget(spec); +}; + +IPA.widget_factories['force_dnszone_add_checkbox'] = IPA.force_dnszone_add_checkbox_widget; +IPA.field_factories['force_dnszone_add_checkbox'] = IPA.checkbox_field; + +IPA.dnszone_adder_dialog = function(spec) { + + spec = spec || {}; + + var that = IPA.entity_adder_dialog(spec); + + that.create = function() { + that.entity_adder_dialog_create(); + that.container.addClass('dnszone-adder-dialog'); + }; + + return that; +}; + +IPA.dns.add_permission_action = function(spec) { + + spec = spec || {}; + spec.name = spec.name || 'add_permission'; + spec.label = spec.label || IPA.messages.objects.dnszone.add_permission; + spec.enable_cond = spec.enable_cond || ['permission-none', 'managedby_w']; + + var that = IPA.action(spec); + + that.execute_action = function(facet) { + + var pkey = IPA.nav.get_state('dnszone-pkey'); + + var command = IPA.command({ + entity: 'dnszone', + method: 'add_permission', + args: [pkey], + options: {}, + on_success: function(data, text_status, xhr) { + facet.refresh(); + IPA.notify_success(data.result.summary); + } + }); + + command.execute(); + }; + + return that; +}; + +IPA.dns.remove_permission_action = function(spec) { + + spec = spec || {}; + spec.name = spec.name || 'remove_permission'; + spec.label = spec.label || IPA.messages.objects.dnszone.remove_permission; + spec.enable_cond = spec.enable_cond || ['permission-set', 'managedby_w']; + + var that = IPA.action(spec); + + that.execute_action = function(facet) { + + var pkey = IPA.nav.get_state('dnszone-pkey'); + + var command = IPA.command({ + entity: 'dnszone', + method: 'remove_permission', + args: [pkey], + options: {}, + on_success: function(data, text_status, xhr) { + facet.refresh(); + IPA.notify_success(data.result.summary); + } + }); + + command.execute(); + }; + + return that; +}; + +IPA.dns.zone_has_permission_evaluator = function(spec) { + spec = spec || {}; + + spec.event = spec.event || 'permission_load'; + + var that = IPA.state_evaluator(spec); + + that.on_event = function(permission_status) { + + var old_state = that.state; + that.state = [ + 'permission-'+permission_status + ]; + + that.notify_on_change(old_state); + }; + + return that; +}; + +IPA.dns.record_search_facet = function(spec) { + + var that = IPA.nested_search_facet(spec); + + that.get_records = function(pkeys, on_success, on_error) { + + var batch = IPA.batch_command({ + name: that.get_records_command_name(), + on_success: on_success, + on_error: on_error + }); + + var zone = IPA.nav.get_state('dnszone-pkey'); + + for (var i=0; i<pkeys.length; i++) { + var pkey = pkeys[i]; + + var command = IPA.command({ + entity: that.table.entity.name, + method: 'show', + args: [zone, pkey], + options: { all: true } + }); + + batch.add_command(command); + } + + batch.execute(); + }; + + + that.load_records = function(records) { + that.table.empty(); + + var types = IPA.dns_record_types(); + + for (var i=0; i<records.length; i++) { + + var original = records[i]; + var record = { + idnsname: original.idnsname, + values: [] + }; + + for (var j=0; j<types.length; j++) { + var type = types[j]; + if (!original[type.value]) continue; + + var values = original[type.value]; + for (var k=0; k<values.length; k++) { + record.values.push({ + type: type.label, + data: values[k] + }); + } + } + + that.add_record(record); + } + that.table.set_values(that.selected_values); + }; + + that.add_record = function(record) { + + for (var i=0; i<record.values.length; i++) { + + var value = record.values[i]; + + if (i === 0) { + value.idnsname = record.idnsname; + } + + var tr = that.table.add_record(value); + + if (i > 0) { + $('input[name="'+that.table.name+'"]', tr).remove(); + } + } + }; + + return that; +}; + +IPA.dns.record_search_deleter_dialog = function(spec) { + + spec = spec || {}; + + var that = IPA.search_deleter_dialog(spec); + + that.create_command = function() { + + var batch = that.search_deleter_dialog_create_command(); + + for (var i=0; i<batch.commands.length; i++) { + var command = batch.commands[i]; + command.set_option('del_all', true); + } + + return batch; + }; + + return that; +}; + +IPA.dns.record_metadata = null; +IPA.dns.get_record_metadata = function() { + + if (IPA.dns.record_metadata === null) { + + IPA.dns.record_metadata = [ + { + name: 'arecord', + attributes: [ + { + name: 'a_part_ip_address', + validators: [IPA.ip_v4_address_validator()] + }, + { + type: 'checkbox', + name: 'a_extra_create_reverse' + } + ], + columns: [ + { + factory: IPA.dns.ptr_redirection_column, + name: 'a_part_ip_address' + } + ] + }, + { + name: 'aaaarecord', + attributes: [ + { + name:'aaaa_part_ip_address', + validators: [IPA.ip_v6_address_validator()] + }, + { + type: 'checkbox', + name: 'aaaa_extra_create_reverse' + } + ], + columns: [ + { + factory: IPA.dns.ptr_redirection_column, + name: 'aaaa_part_ip_address' + } + ] + }, + { + name: 'a6record', + attributes: [ + 'a6_part_data' + ], + columns: ['a6_part_data'] + }, + { + name: 'afsdbrecord', + attributes: [ + 'afsdb_part_subtype', + 'afsdb_part_hostname' + ], + columns: ['afsdb_part_subtype', 'afsdb_part_hostname'] + }, + { + name: 'certrecord', + attributes: [ + 'cert_part_type', + 'cert_part_key_tag', + 'cert_part_algorithm', + { + name: 'cert_part_certificate_or_crl', + type: 'textarea' + } + ], + columns: ['cert_part_type','cert_part_key_tag','cert_part_algorithm'] + }, + { + name: 'cnamerecord', + attributes: [ + 'cname_part_hostname' + ], + columns: ['cname_part_hostname'] + }, + { + name: 'dnamerecord', + attributes: [ + 'dname_part_target' + ], + columns: ['dname_part_target'] + }, + { + name: 'dsrecord', + attributes: [ + 'ds_part_key_tag', + 'ds_part_algorithm', + 'ds_part_digest_type', + { + name: 'ds_part_digest', + type: 'textarea' + } + ], + columns: ['ds_part_key_tag', 'ds_part_algorithm', + 'ds_part_digest_type'] + }, + { + name: 'keyrecord', + attributes: [ + 'key_part_flags', + 'key_part_protocol', + 'key_part_algorithm', + { + name: 'key_part_public_key', + type: 'textarea' + } + ], + columns: ['key_part_flags', 'key_part_protocol', + 'key_part_algorithm'] + }, + { + name: 'kxrecord', + attributes: [ + 'kx_part_preference', + 'kx_part_exchanger' + ], + columns: ['kx_part_preference', 'kx_part_exchanger'] + }, + { + name: 'locrecord', + attributes: [ + 'loc_part_lat_deg', + 'loc_part_lat_min', + 'loc_part_lat_sec', + { + name: 'loc_part_lat_dir', + options: IPA.create_options(['N','S']), + type: 'radio', + widget_opt: { + default_value: 'N' + } + }, + 'loc_part_lon_deg', + 'loc_part_lon_min', + 'loc_part_lon_sec', + { + name: 'loc_part_lon_dir', + options: IPA.create_options(['E','W']), + type: 'radio', + widget_opt: { + default_value: 'E' + } + }, + 'loc_part_altitude', + 'loc_part_size', + 'loc_part_h_precision', + 'loc_part_v_precision' + ], + columns: ['dnsdata'] + }, + { + name: 'mxrecord', + attributes: [ + 'mx_part_preference', + 'mx_part_exchanger' + ], + columns: ['mx_part_preference', 'mx_part_exchanger'] + }, + { + name: 'naptrrecord', + attributes: [ + 'naptr_part_order', + 'naptr_part_preference', + { + name: 'naptr_part_flags', + type: 'select', + options: IPA.create_options(['S', 'A', 'U', 'P']) + }, + 'naptr_part_service', + 'naptr_part_regexp', + 'naptr_part_replacement' + ], + adder_attributes: [], + columns: ['dnsdata'] + }, + { + name: 'nsrecord', + attributes: [ + 'ns_part_hostname' + ], + adder_attributes: [], + columns: ['ns_part_hostname'] + }, + { + name: 'nsecrecord', + attributes: [ + 'nsec_part_next', + 'nsec_part_types' +// TODO: nsec_part_types is multivalued attribute. New selector +// widget or at least new validator should be created. +// { +// name: 'nsec_part_types', +// options: IPA.create_options(['SOA', 'A', 'AAAA', 'A6', 'AFSDB', +// 'APL', 'CERT', 'CNAME', 'DHCID', 'DLV', 'DNAME', 'DNSKEY', +// 'DS', 'HIP', 'IPSECKEY', 'KEY', 'KX', 'LOC', 'MX', 'NAPTR', +// 'NS', 'NSEC','NSEC3', 'NSEC3PARAM', 'PTR', 'RRSIG', 'RP', +// 'SIG', 'SPF', 'SRV', 'SSHFP', 'TA', 'TKEY', 'TSIG', 'TXT']), +// type: 'select' +// } + ], + adder_attributes: [], + columns: [ 'nsec_part_next', 'nsec_part_types'] + }, + { + name: 'ptrrecord', + attributes: [ + 'ptr_part_hostname' + ], + adder_attributes: [], + columns: [ 'ptr_part_hostname'] + }, + { + name: 'rrsigrecord', + attributes: [ + { + name: 'rrsig_part_type_covered', + type: 'select', + options: IPA.create_options(['SOA', 'A', 'AAAA', 'A6', 'AFSDB', + 'APL', 'CERT', 'CNAME', 'DHCID', 'DLV', 'DNAME', + 'DNSKEY', 'DS', 'HIP', 'IPSECKEY', 'KEY', 'KX', + 'LOC', 'MX', 'NAPTR', 'NS', 'NSEC', 'NSEC3', + 'NSEC3PARAM', 'PTR', 'RRSIG', 'RP', 'SPF', 'SRV', + 'SSHFP', 'TA', 'TKEY', 'TSIG', 'TXT']) + }, + 'rrsig_part_algorithm', + 'rrsig_part_labels', + 'rrsig_part_original_ttl', + 'rrsig_part_signature_expiration', + 'rrsig_part_signature_inception', + 'rrsig_part_key_tag', + 'rrsig_part_signers_name', + { + name: 'rrsig_part_signature', + type: 'textarea' + } + ], + adder_attributes: [], + columns: ['dnsdata'] + }, + { + name: 'sigrecord', + attributes: [ + { + name: 'sig_part_type_covered', + type: 'select', + options: IPA.create_options(['SOA', 'A', 'AAAA', 'A6', 'AFSDB', + 'APL', 'CERT', 'CNAME', 'DHCID', 'DLV', 'DNAME', + 'DNSKEY', 'DS', 'HIP', 'IPSECKEY', 'KEY', 'KX', + 'LOC', 'MX', 'NAPTR', 'NS', 'NSEC', 'NSEC3', + 'NSEC3PARAM', 'PTR', 'RRSIG', 'RP', 'SPF', 'SRV', + 'SSHFP', 'TA', 'TKEY', 'TSIG', 'TXT']) + }, + 'sig_part_algorithm', + 'sig_part_labels', + 'sig_part_original_ttl', + 'sig_part_signature_expiration', + 'sig_part_signature_inception', + 'sig_part_key_tag', + 'sig_part_signers_name', + { + name: 'sig_part_signature', + type: 'textarea' + } + ], + adder_attributes: [], + columns: ['dnsdata'] + }, + { + name: 'srvrecord', + attributes: [ + 'srv_part_priority', + 'srv_part_weight', + 'srv_part_port', + 'srv_part_target' + ], + adder_attributes: [], + columns: ['srv_part_priority', 'srv_part_weight', 'srv_part_port', + 'srv_part_target'] + }, + { + name: 'sshfprecord', + attributes: [ + 'sshfp_part_algorithm', + 'sshfp_part_fp_type', + { + name: 'sshfp_part_fingerprint', + type: 'textarea' + } + ], + adder_attributes: [], + columns: ['sshfp_part_algorithm', 'sshfp_part_fp_type'] + }, + { + name: 'txtrecord', + attributes: [ + 'txt_part_data' + ], + adder_attributes: [], + columns: ['txt_part_data'] + } + ]; + + //set required flags for attributes based on 'dnsrecord_optional' flag + //in param metadata + + for (var i=0; i<IPA.dns.record_metadata.length; i++) { + var type = IPA.dns.record_metadata[i]; + + for (var j=0; j<type.attributes.length; j++) { + var attr = type.attributes[j]; + if (typeof attr === 'string') { + attr = { + name: attr + }; + type.attributes[j] = attr; + } + var attr_meta = IPA.get_entity_param('dnsrecord', attr.name); + + if (attr_meta && attr_meta.flags.indexOf('dnsrecord_optional') === -1) { + attr.required = true; + } + } + } + + } + + return IPA.dns.record_metadata; +}; + + +IPA.dns.get_record_type = function(type_name) { + + var metadata = IPA.dns.get_record_metadata(); + + for (var i=0; i<metadata.length; i++) { + var type = metadata[i]; + if (type.name === type_name) return type; + } + + return null; +}; + +IPA.dns.record_entity = function(spec) { + + spec = spec || {}; + + spec.policies = spec.policies || [ + IPA.facet_update_policy({ + source_facet: 'details', + dest_entity: 'dnszone', + dest_facet: 'records' + }), + IPA.adder_facet_update_policy() + ]; + + var that = IPA.entity(spec); + + that.init = function() { + + if (!IPA.dns_enabled) { + throw { + expected: true + }; + } + + that.entity_init(); + + that.builder.containing_entity('dnszone'). + details_facet({ + factory: IPA.dns.record_details_facet, + disable_breadcrumb: false, + fields: [ + { + type: 'dnsrecord_host_link', + name: 'idnsname', + other_entity: 'host', + widget: 'identity.idnsname' + } + ], + widgets:[ + { + name: 'identity', + label: IPA.messages.details.identity, + type: 'details_table_section', + widgets: [ + { + type: 'dnsrecord_host_link', + name: 'idnsname', + other_entity: 'host', + label: IPA.get_entity_param( + 'dnsrecord', 'idnsname').label + } + ] + } + ] + }). + adder_dialog({ + factory: IPA.dns.record_adder_dialog, + fields: [ + { + name: 'idnsname', + widget: 'general.idnsname' + }, + { + name: 'record_type', + type: 'dnsrecord_type', + flags: ['no_command'], + widget: 'general.record_type' + } + ], + widgets: [ + { + name: 'general', + type: 'details_table_section_nc', + widgets: [ + 'idnsname', + { + type: 'dnsrecord_type', + name: 'record_type', + label: IPA.messages.objects.dnsrecord.type + } + ] + } + ], + policies: [ + IPA.dnsrecord_adder_dialog_type_policy({ + type_field: 'record_type' + }) + ] + }); + }; + + return that; +}; + +IPA.dns.record_adder_dialog = function(spec) { + + spec = spec || {}; + spec.retry = spec.retry !== undefined ? spec.retry : false; + + IPA.dns.record_prepare_spec(spec, IPA.dns.record_prepare_editor_for_type); + + var that = IPA.entity_adder_dialog(spec); + + that.on_error = IPA.create_4304_error_handler(that); + + return that; +}; + +IPA.dns.record_details_facet = function(spec) { + + IPA.dns.record_prepare_details_spec(spec); + + var that = IPA.details_facet(spec); + + that.load = function(data) { + + if (!data.result.result.idnsname) { + that.reset(); + var dialog = IPA.dnsrecord_redirection_dialog(); + dialog.open(that.container); + return; + } + + that.details_facet_load(data); + }; + + that.create_refresh_command = function() { + + var command = that.details_facet_create_refresh_command(); + command.set_option('structured', true); + return command; + }; + + return that; +}; + +IPA.dnsrecord_redirection_dialog = function(spec) { + spec = spec || {}; + spec.title = spec.title || IPA.messages.dialogs.redirection; + + var that = IPA.message_dialog(spec); + + that.create = function() { + $('<p/>', { + 'text': IPA.messages.objects.dnsrecord.deleted_no_data + }).appendTo(that.container); + $('<p/>', { + 'text': IPA.messages.objects.dnsrecord.redirection_dnszone + }).appendTo(that.container); + }; + + that.on_ok = function() { + IPA.nav.show_page('dnszone','default'); + }; + + return that; +}; + +/* + * Spec preparation methods + */ + +IPA.dns.record_prepare_spec = function(spec, type_prepare_method) { + + var metadata = IPA.dns.get_record_metadata(); + + var fields = []; + var widgets = []; + + for (var i=0; i<metadata.length; i++) { + + var type = metadata[i]; + + type_prepare_method(type, fields, widgets); + } + + IPA.dns.extend_spec(spec, fields, widgets); +}; + +IPA.dns.extend_spec = function(spec, fields, widgets) { + + if (spec.sections) delete spec.sections; + + if (spec.fields instanceof Array) { + spec.fields.push.apply(spec.fields, fields); + } else { + spec.fields = fields; + } + + if (spec.widgets instanceof Array) { + spec.widgets.push.apply(spec.widgets, widgets); + } else { + spec.widgets = widgets; + } +}; + +IPA.dns.record_prepare_editor_for_type = function(type, fields, widgets, update) { + + var set_defined = function(property, object, name) { + if (property !== undefined) { + object[name] = property; + } + }; + + var copy_obj = function(source, dest) { + if (source !== null || source !== undefined) { + $.extend(source,dest); + } + }; + + var section = { + name: type.name, + type: 'details_table_section_nc', + widgets: [] + }; + widgets.push(section); + + for (var i=0; i<type.attributes.length;i++) { + var attribute = type.attributes[i]; + + if (typeof attribute === 'string') { + attribute = { + name: attribute + }; + } + + var metadata = IPA.get_entity_param('dnsrecord', attribute.name); + if (metadata && update && metadata.flags && + metadata.flags.indexOf('no_update') > -1) continue; + + //create field + var field = {}; + + field.name = attribute.name; + field.label = attribute.label || + IPA.dns.record_get_attr_label(attribute.name); + set_defined(attribute.type, field, 'type'); + set_defined(attribute.validators, field, 'validators'); + set_defined(attribute.required, field, 'required'); + copy_obj(widget, attribute.field_opt); + + field.widget = type.name+'.'+field.name; + fields.push(field); + + //create editor widget + var widget = {}; + if (typeof attribute === 'string') { + widget.name = attribute; + } else { + widget.name = attribute.name; + set_defined(attribute.type, widget, 'type'); + set_defined(attribute.options, widget, 'options'); + copy_obj(widget, attribute.widget_opt); + } + section.widgets.push(widget); + } +}; + +IPA.dns.record_prepare_details_spec = function(spec, type_prepare_method) { + + var metadata = IPA.dns.get_record_metadata(); + + var fields = []; + var widgets = []; + + var standard_record_section = { + name: 'standard_types', + type: 'details_table_section', + label: IPA.messages.objects.dnsrecord.standard, + widgets: [] + }; + + var other_record_section = { + name: 'other_types', + type: 'details_table_section', + label: IPA.messages.objects.dnsrecord.other, + widgets: [] + }; + + widgets.push(standard_record_section); + widgets.push(other_record_section); + + var standard_types = ['arecord', 'aaaarecord', 'ptrrecord', 'srvrecord', + 'txtrecord', 'cnamerecord', 'mxrecord', 'nsrecord']; + + for (var i=0; i<metadata.length; i++) { + + var type = metadata[i]; + + if (standard_types.indexOf(type.name) > -1) { + IPA.dns.record_prepare_details_for_type(type, fields, standard_record_section); + } else { + IPA.dns.record_prepare_details_for_type(type, fields, other_record_section); + } + } + + IPA.dns.extend_spec(spec, fields, widgets); +}; + +IPA.dns.record_prepare_details_for_type = function(type, fields, container) { + + var index = type.name.search('record$'); + var dnstype = type.name.substring(0, index).toUpperCase(); + + var type_widget = { + name: type.name, + type: 'dnsrecord_type_table', + record_type: type.name, + value_attribute: 'dnsdata', + dnstype: dnstype, + columns: type.columns + }; + + container.widgets.push(type_widget); + + var field = { + name: type.name, + type: 'dnsrecord_type_table', + dnstype: dnstype, + label: dnstype, + widget: container.name+'.'+type.name + }; + + fields.push(field); +}; + +/* + * Widgets and policies + */ + + +IPA.dnsrecord_host_link_field = function(spec) { + var that = IPA.link_field(spec); + that.other_pkeys = function() { + var pkey = that.entity.get_primary_key(); + return [pkey[0]+'.'+pkey[1]]; + }; + return that; +}; + +IPA.field_factories['dnsrecord_host_link'] = IPA.dnsrecord_host_link_field; +IPA.widget_factories['dnsrecord_host_link'] = IPA.link_widget; + +IPA.dns_record_types = function() { + + //only supported + var attrs = ['A', 'AAAA', 'A6', 'AFSDB', 'CERT', 'CNAME', 'DNAME', + 'DS','KEY', 'KX', 'LOC', 'MX', 'NAPTR', 'NS', 'NSEC', + 'PTR', 'RRSIG', 'SRV', 'SIG', 'SSHFP', 'TXT']; + var record_types = []; + for (var i=0; i<attrs.length; i++) { + var attr = attrs[i]; + + var rec_type = { + label: attr, + value: attr.toLowerCase()+'record' + }; + record_types.push(rec_type); + } + return record_types; +}; + +IPA.dns.record_get_attr_label = function(part_name) { + + var metadata = IPA.get_entity_param('dnsrecord', part_name); + + if (!metadata) return null; + + var label = metadata.label; + + if (part_name.indexOf('_part_') > -1) { + + label = label.substring(label.indexOf(' ')); + } else if (part_name.indexOf('_extra_') > -1) { + + label = label.substring(label.indexOf(' ')); + } + + return label; +}; + + +IPA.dnsrecord_type_field = function(spec) { + + spec = spec || {}; + var that = IPA.field(spec); + + that.type_changed = IPA.observer(); + + that.get_type = function() { + + return that.widget.save()[0]; + }; + + that.on_type_change = function() { + + that.type_changed.notify([], that); + }; + + that.widgets_created = function() { + + that.field_widgets_created(); + that.widget.value_changed.attach(that.on_type_change); + }; + + that.reset = function() { + that.field_reset(); + that.on_type_change(); + }; + + return that; +}; + +IPA.field_factories['dnsrecord_type'] = IPA.dnsrecord_type_field; + + +IPA.dnsrecord_type_widget = function(spec) { + + spec.options = IPA.dns_record_types(); + var that = IPA.select_widget(spec); + return that; +}; + +IPA.widget_factories['dnsrecord_type'] = IPA.dnsrecord_type_widget; + + +IPA.dnsrecord_adder_dialog_type_policy = function(spec) { + + spec = spec || {}; + + var that = IPA.facet_policy(spec); + + that.type_field_name = spec.type_field; + + that.post_create = function() { + that.type_field = that.container.fields.get_field(that.type_field_name); + that.type_field.type_changed.attach(that.on_type_change); + that.on_type_change(); + }; + + that.on_type_change = function() { + + var type = that.type_field.get_type(); + + that.allow_fields_for_type(type); + that.show_widgets_for_type(type); + }; + + that.allow_fields_for_type = function(type) { + + type = type.substring(0, type.indexOf('record')); + + var fields = that.container.fields.get_fields(); + + for (var i=0; i<fields.length; i++) { + + var field = fields[i]; + var fieldtype; + var attr_types = ['_part_', '_extra_', 'record']; + + for (var j=0; j<attr_types.length; j++) { + var index = field.name.indexOf(attr_types[j]); + if (index > -1) { + fieldtype = field.name.substring(0, index); + break; + } + } + + field.enabled = (field.name === 'idnsname' || + field.name === that.type_field_name || + fieldtype === type); + } + }; + + that.show_widgets_for_type = function(type) { + + var widgets = that.container.widgets.get_widgets(); + + for (var i=0; i<widgets.length; i++) { + var widget = widgets[i]; + var visible = widget.name.indexOf(type) === 0 || + widget.name === 'general'; + widget.set_visible(visible); + } + }; + + return that; +}; + + +IPA.dns.record_type_table_field = function(spec) { + + spec = spec || {}; + + var that = IPA.field(spec); + + that.dnstype = spec.dnstype; + + that.load = function(record) { + + var data = {}; + + data.idnsname = record.idnsname; + data.dnsrecords = []; + + for (var i=0, j=0; i<record.dnsrecords.length; i++) { + + var dnsrecord = record.dnsrecords[i]; + if(dnsrecord.dnstype === that.dnstype) { + + dnsrecord.position = j; + j++; + data.dnsrecords.push(dnsrecord); + } + } + + that.values = data; + + that.load_writable(record); + that.reset(); + }; + + return that; +}; + +IPA.field_factories['dnsrecord_type_table'] = IPA.dns.record_type_table_field; + + +IPA.dns.record_type_table_widget = function(spec) { + + spec = spec || {}; + spec.columns = spec.columns || []; + + spec.columns.push({ + name: 'position', + label: '', + factory: IPA.dns.record_modify_column, + width: '106px' + }); + + var that = IPA.table_widget(spec); + + that.dnstype = spec.dnstype; + + that.create_column = function(spec) { + + if (typeof spec === 'string') { + spec = { + name: spec + }; + } + + spec.entity = that.entity; + spec.label = spec.label || IPA.dns.record_get_attr_label(spec.name); + + var factory = spec.factory || IPA.column; + + var column = factory(spec); + that.add_column(column); + return column; + }; + + that.create_columns = function() { + that.clear_columns(); + if (spec.columns) { + for (var i=0; i<spec.columns.length; i++) { + that.create_column(spec.columns[i]); + } + } + + var modify_column = that.columns.get('position'); + modify_column.link_handler = that.on_modify; + }; + + that.create = function(container) { + + that.create_columns(); + that.table_create(container); + + container.addClass('dnstype-table'); + + that.remove_button = IPA.action_button({ + name: 'remove', + label: IPA.messages.buttons.remove, + icon: 'remove-icon', + 'class': 'action-button-disabled', + click: function() { + if (!that.remove_button.hasClass('action-button-disabled')) { + that.remove_handler(); + } + return false; + } + }).appendTo(that.buttons); + + that.add_button = IPA.action_button({ + name: 'add', + label: IPA.messages.buttons.add, + icon: 'add-icon', + click: function() { + if (!that.add_button.hasClass('action-button-disabled')) { + that.add_handler(); + } + return false; + } + }).appendTo(that.buttons); + }; + + that.set_enabled = function(enabled) { + that.table_set_enabled(enabled); + if (enabled) { + if(that.add_button) { + that.add_button.removeClass('action-button-disabled'); + } + } else { + $('.action-button', that.table).addClass('action-button-disabled'); + that.unselect_all(); + } + that.enabled = enabled; + }; + + that.select_changed = function() { + + var values = that.get_selected_values(); + + if (that.remove_button) { + if (values.length === 0) { + that.remove_button.addClass('action-button-disabled'); + } else { + that.remove_button.removeClass('action-button-disabled'); + } + } + }; + + that.add_handler = function() { + var facet = that.entity.get_facet(); + + if (facet.is_dirty()) { + var dialog = IPA.dirty_dialog({ + entity:that.entity, + facet: facet + }); + + dialog.callback = function() { + that.show_add_dialog(); + }; + + dialog.open(that.container); + + } else { + that.show_add_dialog(); + } + }; + + that.remove_handler = function() { + var facet = that.entity.get_facet(); + + if (facet.is_dirty()) { + var dialog = IPA.dirty_dialog({ + entity:that.entity, + facet: facet + }); + + dialog.callback = function() { + that.show_remove_dialog(); + }; + + dialog.open(that.container); + + } else { + that.show_remove_dialog(); + } + }; + + that.show_remove_dialog = function() { + + var selected_values = that.get_selected_values(); + + if (!selected_values.length) { + var message = IPA.messages.dialogs.remove_empty; + alert(message); + return; + } + + var dialog = IPA.deleter_dialog({ + entity: that.entity, + values: selected_values + }); + + dialog.execute = function() { + that.remove( + selected_values, + that.idnsname[0], + function(data) { + that.reload_facet(data); + that.notify_facet_update(); + that.facet.nofify_update_success(); + }, + function() { + that.refresh_facet(); + } + ); + }; + + + dialog.open(that.container); + }; + + that.remove = function(values, pkey, on_success, on_error) { + + var dnszone = IPA.nav.get_state('dnszone-pkey'); + + var command = IPA.command({ + entity: that.entity.name, + method: 'del', + args: [dnszone, pkey], + on_success: on_success, + on_error: on_error + }); + + var record_name = that.dnstype.toLowerCase()+'record'; + command.set_option(record_name, values); + command.set_option('structured', true); + + command.execute(); + }; + + that.create_add_dialog = function() { + + var title = IPA.messages.dialogs.add_title; + var label = that.entity.metadata.label_singular; + + var dialog_spec = { + entity: that.entity, + fields: [], + widgets: [], + retry: false, + title: title.replace('${entity}', label) + }; + + var dnstype = that.dnstype.toLowerCase(); + var type = IPA.dns.get_record_type(dnstype+'record'); + + IPA.dns.record_prepare_editor_for_type(type, dialog_spec.fields, + dialog_spec.widgets); + + var dialog = IPA.entity_adder_dialog(dialog_spec); + + var cancel_button = dialog.buttons.get('cancel'); + dialog.buttons.empty(); + + dialog.on_error = IPA.create_4304_error_handler(dialog); + + dialog.get_add_message = function() { + var label = that.entity.metadata.label_singular; + var message = IPA.messages.dialogs.add_confirmation; + message = message.replace('${entity}', label); + return message; + }; + + dialog.create_button({ + name: 'add', + label: IPA.messages.buttons.add, + click: function() { + dialog.hide_message(); + dialog.add( + function(data, text_status, xhr) { + + if (data.result.result.dnsrecords) { + that.reload_facet(data); + } else { + that.refresh_facet(); + } + dialog.close(); + that.notify_facet_update(); + IPA.notify_success(dialog.get_add_message()); + }, + dialog.on_error); + } + }); + + dialog.create_button({ + name: 'add_and_add_another', + label: IPA.messages.buttons.add_and_add_another, + click: function() { + dialog.hide_message(); + dialog.add( + function(data, text_status, xhr) { + + dialog.show_message(dialog.get_add_message()); + + if (data.result.result.dnsrecords) { + that.reload_facet(data); + } else { + that.refresh_facet(); + } + dialog.reset(); + that.notify_facet_update(); + }, + dialog.on_error); + } + }); + + dialog.buttons.put('cancel', cancel_button); + + dialog.create_add_command = function(record) { + + var dnszone = IPA.nav.get_state('dnszone-pkey'); + + var command = dialog.entity_adder_dialog_create_add_command(record); + command.args = [dnszone, that.idnsname[0]]; + command.set_option('structured', true); + + return command; + }; + + return dialog; + }; + + that.show_add_dialog = function() { + + var dialog = that.create_add_dialog(); + dialog.open(that.container); + }; + + that.create_mod_dialog = function() { + + var title = IPA.messages.dialogs.edit_title; + var label = that.entity.metadata.label_singular; + + var dialog_spec = { + entity: that.entity, + fields: [], + widgets: [], + title: title.replace('${entity}', label) + }; + + var dnstype = that.dnstype.toLowerCase(); + + var type = IPA.dns.get_record_type(dnstype+'record'); + + IPA.dns.record_prepare_editor_for_type(type, dialog_spec.fields, + dialog_spec.widgets, true); + + var dialog = IPA.entity_adder_dialog(dialog_spec); + + dialog.buttons.empty(); + + dialog.create_button({ + name: 'modify', + label: IPA.messages.buttons.update, + click: function() { + dialog.modify(); + } + }); + + dialog.create_button({ + name: 'cancel', + label: IPA.messages.buttons.cancel, + click: function() { + dialog.reset(); + dialog.close(); + } + }); + + dialog.load = function(record, full_value) { + + dialog.full_value = full_value; + + var fields = dialog.fields.get_fields(); + + for (var i=0; i<fields.length; i++) { + var field = fields[i]; + field.load(record); + } + }; + + dialog.modify = function() { + + if (!dialog.validate()) return; + + var record = {}; + dialog.save(record); + + var command = dialog.create_add_command(record); + + command.on_success = function(data) { + that.reload_facet(data); + dialog.close(); + that.notify_facet_update(); + that.facet.nofify_update_success(); + }; + command.on_error = function() { + that.refresh_facet(); + dialog.close(); + }; + command.execute(); + }; + + dialog.create_add_command = function(record) { + + var dnszone = IPA.nav.get_state('dnszone-pkey'); + + var command = dialog.entity_adder_dialog_create_add_command(record); + + command.method = 'mod'; + command.args = [dnszone, that.idnsname[0]]; + + var record_name = that.dnstype.toLowerCase()+'record'; + command.set_option(record_name, dialog.full_value); + command.set_option('structured', true); + + return command; + }; + + return dialog; + }; + + that.reload_facet = function(data) { + + //FIXME: seems as bad approach + var facet = IPA.current_entity.get_facet(); + facet.load(data); + }; + + that.refresh_facet = function() { + + //FIXME: seems as bad approach + var facet = IPA.current_entity.get_facet(); + facet.refresh(); + }; + + that.notify_facet_update = function() { + var facet = IPA.current_entity.get_facet(); + facet.on_update.notify(); + }; + + that.update = function(values) { + + that.idnsname = values.idnsname; + that.dnsrecords = values.dnsrecords; + that.table_update(that.dnsrecords); + that.unselect_all(); + }; + + that.on_modify = function(position) { + + var values = that.values[position]; + + var dialog = that.create_mod_dialog(); + dialog.open(); + dialog.load(that.records[position], values); + + return false; + }; + + + return that; +}; + +IPA.widget_factories['dnsrecord_type_table'] = IPA.dns.record_type_table_widget; + +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); + }; + + return that; +}; + +IPA.field_factories['netaddr'] = IPA.dns.netaddr_field; +IPA.widget_factories['netaddr'] = IPA.multivalued_widget; + + + +IPA.dns.record_modify_column = function(spec) { + + spec = spec || {}; + + var that = IPA.column(spec); + + that.text = spec.text || IPA.messages.buttons.edit; + + that.setup = function(container, record, suppress_link) { + + container.empty(); + + var value = record[that.name]; + + $('<a/>', { + href: '#'+that.text, + text: that.text, + style: 'float: right;', + click: function() { + return that.link_handler(value); + } + }).appendTo(container); + }; + + return that; +}; + +IPA.dns.ptr_redirection_column = function(spec) { + + spec = spec || {}; + + var that = IPA.column(spec); + + that.link = true; + + that.link_handler = function(value) { + + var address = NET.ip_address(value); + + var dialog = IPA.dns.ptr_redirection_dialog({ + address: address + }); + dialog.open(); + + return false; + }; + + return that; +}; + +IPA.dns.ptr_redirection_dialog = function(spec) { + + spec = spec || {}; + + spec.title = IPA.messages.objects.dnsrecord.ptr_redir_title; + + var that = IPA.dialog(spec); + + that.address = spec.address; + + that.create = function() { + + that.status_div = $('<div />', { + 'class': 'redirection-status' + }).appendTo(that.container); + }; + + that.create_buttons = function() { + + that.create_button({ + name: 'close', + label: IPA.messages.buttons.close, + click: function() { + that.close(); + } + }); + }; + + that.create_add_record_button = function() { + + $('<a />', { + text: IPA.messages.objects.dnsrecord.ptr_redir_create, + href: '#create_record', + click: function() { + that.create_record(); + return false; + } + }).appendTo(that.container); + }; + + that.append_status = function(message) { + + $('<div />', { + text: message + }).appendTo(that.status_div); + }; + + that.open = function() { + + that.dialog_open(); + that.start_redirect(); + }; + + //step 0 - preparation + that.start_redirect = function() { + + if (!that.address.valid) { + that.append_status(IPA.messages.objects.dnsrecord.ptr_redir_address_err); + } else { + that.reverse_address = that.address.get_reverse().toLowerCase()+'.'; + + var record = IPA.nav.get_state('dnsrecord-pkey'); + var zone = IPA.nav.get_state('dnszone-pkey'); + + if (record && zone && record !== '' && zone !== '') { + that.dns_record = { + name: record, + zone: zone + }; + } + + that.get_zones(); + } + }; + + //1st step: get all zones + that.get_zones = function() { + + that.append_status(IPA.messages.objects.dnsrecord.ptr_redir_zones); + + var command = IPA.command({ + entity: 'dnszone', + method: 'find', + options: { + pkey_only: true + }, + on_success: that.find_zone, + on_error: function() { + that.append_status(IPA.messages.objects.dnsrecord.ptr_redir_zones_err); + } + }); + + command.execute(); + }; + + //2nd step: find target zone + that.find_zone = function(data) { + var zones = data.result.result; + var target_zone = null; + + for (var i=0; i<zones.length; i++) { + + var zone_name = zones[i].idnsname[0]; + if (that.reverse_address.indexOf(zone_name) > -1) { + var msg = IPA.messages.objects.dnsrecord.ptr_redir_zone; + msg = msg.replace('${zone}', zone_name); + that.append_status(msg); + + if (!target_zone || + (target_zone && zone_name.length > target_zone.length)) { + + target_zone = zone_name; + } + + break; + } + } + + if (target_zone) { + that.zone = target_zone; + that.check_record(); + } else { + that.append_status(IPA.messages.objects.dnsrecord.ptr_redir_zone_err); + } + }; + + //3rd step: check record existance + that.check_record = function(zone) { + + that.append_status(IPA.messages.objects.dnsrecord.ptr_redir_record); + + var i1 = that.reverse_address.indexOf(that.zone); + var record_name = that.reverse_address.substring(0,i1 - 1); + that.record_keys = [that.zone, record_name]; + + var command = IPA.command({ + entity: 'dnsrecord', + method: 'show', + args: that.record_keys, + on_success: function() { + that.redirect(); + }, + on_error: function() { + that.append_status(IPA.messages.objects.dnsrecord.ptr_redir_record_err); + if (that.dns_record) { + that.create_add_record_button(); + } + }, + retry: false + }); + + command.execute(); + }; + + //4th-a step: actual redirect + that.redirect = function() { + + var entity = IPA.get_entity('dnsrecord'); + + IPA.nav.show_entity_page( + entity, + 'default', + that.record_keys); + + that.close(); + }; + + //4th-b optional step: create PTR record + that.create_record = function() { + + that.append_status(IPA.messages.objects.dnsrecord.ptr_redir_creating); + + var ptr = that.dns_record.name +'.' + that.dns_record.zone; + + var command = IPA.command({ + entity: 'dnsrecord', + method: 'add', + args: that.record_keys, + options: { + ptrrecord: [ptr] + }, + on_success: function() { + that.redirect(); + }, + on_error: function() { + that.append_status(IPA.messages.objects.dnsrecord.ptr_redir_creating_err); + } + }); + + command.execute(); + }; + + + that.create_buttons(); + + return that; +}; + +IPA.ip_address_validator = function(spec) { + + spec = spec || {}; + var that = IPA.validator(spec); + + that.address_type = spec.address_type; + that.message = spec.message || IPA.messages.widget.validation.ip_address; + + that.validate = function(value) { + + if (IPA.is_empty(value)) return that.true_result(); + + var address = NET.ip_address(value); + + if (!address.valid || !that.is_type_match(address.type)) { + return that.false_result(); + } + + return that.true_result(); + }; + + that.is_type_match = function(net_type) { + + return (!that.address_type || + + (that.address_type === 'IPv4' && + (net_type === 'v4-quads' || net_type === 'v4-int')) || + + (that.address_type === 'IPv6' && net_type === 'v6')); + }; + + that.ip_address_validate = that.validate; + + return that; +}; + +IPA.ip_v4_address_validator = function(spec) { + + spec = spec || {}; + spec.address_type = 'IPv4'; + spec.message = IPA.messages.widget.validation.ip_v4_address; + return IPA.ip_address_validator(spec); +}; + +IPA.ip_v6_address_validator = function(spec) { + + spec = spec || {}; + spec.address_type = 'IPv6'; + spec.message = IPA.messages.widget.validation.ip_v6_address; + return IPA.ip_address_validator(spec); +}; + +IPA.dnsforwarder_validator = function(spec) { + + spec = spec || {}; + var that = IPA.ip_address_validator(spec); + + that.validate = function(value) { + + var address_part = value; + + if (value.indexOf(' ') > - 1) { + var parts = value.split(' '); + + if (parts.length !== 3 || parts[1] !== 'port') return that.false_result(); + + address_part = parts[0]; + var port = parts[2]; + + if (!port.match(/^[1-9]\d*$|^0$/) || port < 0 || port > 65535) { + var message = IPA.messages.widget.validation.port; + message = message.replace('${port}', port); + return that.false_result(message); + } + } + + return that.ip_address_validate(address_part); + }; + + return that; +}; + +IPA.network_validator = function(spec) { + + spec = spec || {}; + + var that = IPA.validator(spec); + + that.allow_negation = spec.allow_negation; + that.allow_host_address = spec.allow_host_address; + that.specials = spec.specials || []; + that.message = spec.message || IPA.messages.widget.validation.net_address; + + that.validate = function(value) { + + if (IPA.is_empty(value)) return that.true_result(); + + if (typeof value !== 'string') return that.false_result(); + + if (that.specials.indexOf(value) > -1) { + return that.true_result(); + } + + var address_part, mask; + + if (value.indexOf('/') > -1) { + + var parts = value.split('/'); + + if (parts.length === 2) { + address_part = parts[0]; + mask = parts[1]; + + if (mask === '') return that.false_result(); + + } else { + return that.false_result(); + } + } else if (that.allow_host_address) { + address_part = value; + } else { + return that.false_result(); + } + + + if (that.allow_negation && address_part.indexOf('!') === 0) { + address_part = address_part.substring(1); + } + + var address = NET.ip_address(address_part); + if (!address.valid) return that.false_result(); + + if (mask) { + + var mask_length = 32; + if (address.type === 'v6') mask_length = 128; + + if (!mask.match(/^[1-9]\d*$/) || mask < 8 || mask > mask_length) { + return that.false_result(); + } + } + + return that.true_result(); + }; + + return that; +}; + +IPA.register('dnsconfig', IPA.dns.config_entity); +IPA.register('dnszone', IPA.dns.zone_entity); +IPA.register('dnsrecord', IPA.dns.record_entity); diff --git a/install/ui/src/freeipa/entitle.js b/install/ui/src/freeipa/entitle.js new file mode 100644 index 000000000..ccf82173b --- /dev/null +++ b/install/ui/src/freeipa/entitle.js @@ -0,0 +1,745 @@ +/*jsl:import ipa.js */ + +/* Authors: + * Endi S. Dewata <edewata@redhat.com> + * + * Copyright (C) 2010 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/>. + */ + +/* REQUIRES: ipa.js, details.js, search.js, add.js, facet.js, entity.js */ + + +IPA.entitle = {}; + +IPA.entitle.unregistered = 'unregistered'; +IPA.entitle.online = 'online'; +IPA.entitle.offline = 'offline'; + +IPA.entitle.entity = function(spec) { + + spec = spec || {}; + + var that = IPA.entity(spec); + + that.status = IPA.entitle.unregistered; + + that.init = function() { + that.entity_init(); + + that.builder.facet_groups([ 'account', 'certificates' ]). + details_facet({ + factory: IPA.entitle.details_facet, + label: IPA.messages.objects.entitle.account, + facet_group: 'account', + sections: [ + { + name: 'general', + label: IPA.messages.details.general, + fields: [ + { + name: 'uuid', + label: IPA.get_command_option('entitle_register', 'ipaentitlementid').label, + read_only: true + }, + { + factory: IPA.entitle.download_widget, + name: 'certificate', + label: IPA.messages.objects.entitle.certificate + } + ] + }, + { + name: 'status', + label: IPA.messages.objects.entitle.status, + fields: [ + { + name: 'product', + label: IPA.messages.objects.entitle.product, + read_only: true + }, + { + name: 'quantity', + label: IPA.get_command_arg('entitle_consume', 'quantity').label, + read_only: true + }, + { + name: 'consumed', + label: IPA.messages.objects.entitle.consumed, + read_only: true + } + ] + } + ] + }). + facet({ + factory: IPA.entitle.certificates_facet, + name: 'certificates', + label: IPA.messages.objects.entitle.certificates, + facet_group: 'certificates', + columns: [ + { + name: 'product', + label: IPA.messages.objects.entitle.product + }, + { + name: 'quantity', + label: IPA.get_command_arg('entitle_consume', 'quantity').label + }, + { + name: 'start', + label: IPA.messages.objects.entitle.start + }, + { + name: 'end', + label: IPA.messages.objects.entitle.end + }, + { + factory: IPA.entitle.certificate_column, + name: 'certificate', + label: IPA.messages.objects.entitle.certificate + } + ] + }). + standard_association_facets(). + dialog({ + factory: IPA.entitle.register_online_dialog, + name: 'online_registration', + title: IPA.messages.objects.entitle.registration, + fields: [ + { + name: 'username', + label: IPA.get_command_arg('entitle_register', 'username').label + }, + { + name: 'password', + label: IPA.get_command_option('entitle_register', 'password').label, + type: 'password' + } +/* currently not supported + , { + name: 'ipaentitlementid', + label: IPA.get_command_option('entitle_register', 'ipaentitlementid').label + } +*/ + ] + }). + dialog({ + factory: IPA.entitle.register_offline_dialog, + name: 'offline_registration', + title: IPA.messages.objects.entitle.import_certificate, + message: IPA.messages.objects.entitle.import_message, + fields: [ + { + name: 'certificate', + label: IPA.messages.objects.entitle.certificate + } + ] + }). + dialog({ + factory: IPA.entitle.consume_dialog, + name: 'consume', + title: IPA.messages.objects.entitle.consume_entitlement, + fields: [ + { + name: 'quantity', + label: IPA.get_command_arg('entitle_consume', 'quantity').label, + metadata: IPA.get_command_arg('entitle_consume', 'quantity') + } + ] + }). + dialog({ + factory: IPA.entitle.import_dialog, + name: 'import', + title: IPA.messages.objects.entitle.import_certificate, + message: IPA.messages.objects.entitle.import_message, + fields: [ + { + name: 'certificate', + label: IPA.messages.objects.entitle.certificate + } + ] + }); + }; + + that.get_accounts = function(on_success, on_error) { + + var command = IPA.command({ + name: 'entitle_find_'+that.status, + entity: 'entitle', + method: 'find', + options: { all: true }, + on_success: on_success, + on_error: on_error + }); + + command.execute(); + }; + + that.get_status = function(on_success, on_error) { + + var command = IPA.command({ + name: 'entitle_status_'+that.status, + entity: 'entitle', + method: 'status', + on_success: function(data, text_status, xhr) { + if (data.result.result.uuid == 'IMPORTED') { + that.status = IPA.entitle.offline; + } else { + that.status = IPA.entitle.online; + } + + if (on_success) { + on_success.call(this, data, text_status, xhr); + } + }, + on_error: function(xhr, text_status, error_thrown) { + that.status = IPA.entitle.unregistered; + + if (on_error) { + on_error.call(this, xhr, text_status, error_thrown); + } + }, + retry: false + }); + + command.execute(); + }; + + that.get_certificates = function(on_success, on_error) { + + var command = IPA.command({ + entity: 'entitle', + method: 'get', + on_success: on_success, + on_error: on_error, + retry: false + }); + + command.execute(); + }; + + that.register_online = function(username, password, ipaentitlementid, on_success, on_error) { + + var command = IPA.command({ + entity: 'entitle', + method: 'register', + args: [ username ], + options: { + password: password + }, + on_success: function(data, text_status, xhr) { + that.status = IPA.entitle.online; + if (on_success) { + on_success.call(this, data, text_status, xhr); + } + }, + on_error: on_error + }); + + if (ipaentitlementid) { + command.set_option('ipaentitlementid', ipaentitlementid); + } + + command.execute(); + }; + + that.register_offline = function(certificate, on_success, on_error) { + + var command = IPA.command({ + entity: 'entitle', + method: 'import', + args: [ certificate ], + on_success: function(data, text_status, xhr) { + that.status = IPA.entitle.offline; + if (on_success) { + on_success.call(this, data, text_status, xhr); + } + }, + on_error: on_error + }); + + command.execute(); + }; + + that.consume = function(quantity, on_success, on_error) { + + var command = IPA.command({ + entity: 'entitle', + method: 'consume', + args: [ quantity ], + on_success: on_success, + on_error: on_error + }); + + command.execute(); + }; + + that.import_certificate = function(certificate, on_success, on_error) { + + var command = IPA.command({ + entity: 'entitle', + method: 'import', + args: [ certificate ], + on_success: function(data, text_status, xhr) { + that.status = IPA.entitle.offline; + if (on_success) { + on_success.call(this, data, text_status, xhr); + } + }, + on_error: on_error + }); + + command.execute(); + }; + + return that; +}; + +IPA.entitle.details_facet = function(spec) { + + spec = spec || {}; + spec.disable_breadcrumb = true; + + var that = IPA.details_facet(spec); + + that.create_controls = function() { + + that.register_buttons = $('<span/>', { + name: 'register_buttons' + }).appendTo(that.controls); + + that.register_online_button = IPA.action_button({ + name: 'register', + label: IPA.messages.objects.entitle.register, + icon: 'register-icon', + click: function() { + var dialog = that.entity.get_dialog('online_registration'); + dialog.open(that.container); + return false; + } + }).appendTo(that.register_buttons); + + that.register_online_button.css('display', 'none'); +/* + that.register_offline_button = IPA.action_button({ + name: 'import', + label: IPA.messages.objects.entitle.import, + icon: 'import-icon', + click: function() { + var dialog = that.entity.get_dialog('offline_registration'); + dialog.open(that.container); + return false; + } + }).appendTo(that.register_buttons); + + that.register_offline_button.css('display', 'none'); +*/ + }; + + that.refresh = function() { + + var summary = $('span[name=summary]', that.container).empty(); + summary.append(IPA.messages.objects.entitle.loading); + + function on_success(data, text_status, xhr) { + if (that.entity.status == IPA.entitle.unregistered) { + that.register_online_button.css('display', 'inline'); + // that.register_offline_button.css('display', 'inline'); + + } else { + that.register_online_button.css('display', 'none'); + // that.register_offline_button.css('display', 'none'); + } + + that.load(data); + + summary.empty(); + } + + function on_error(xhr, text_status, error_thrown) { + + that.register_online_button.css('display', 'inline'); + // that.register_offline_button.css('display', 'inline'); + + var data = {}; + data.result = {}; + data.result.result = { + uuid: '', + product: '', + quantity: 0, + consumed: 0 + }; + that.load(data); + + summary.empty(); + summary.append(error_thrown.name+': '+error_thrown.message); + } + + that.entity.get_status( + on_success, + on_error); + }; + + return that; +}; + +IPA.entitle.certificates_facet = function(spec) { + + spec = spec || {}; + spec.disable_facet_tabs = false; + spec.selectable = false; + + var that = IPA.table_facet(spec); + + var init = function() { + that.init_table(that.entity); + }; + + that.create_header = function(container) { + + that.facet_create_header(container); + + that.consume_buttons = $('<span/>', { + name: 'consume_buttons' + }).appendTo(that.controls); + + that.consume_button = IPA.action_button({ + name: 'consume', + label: IPA.messages.objects.entitle.consume, + icon: 'consume-icon', + click: function() { + var dialog = that.entity.get_dialog('consume'); + dialog.open(that.container); + return false; + } + }).appendTo(that.consume_buttons); + + that.consume_button.css('display', 'none'); + + that.import_button = IPA.action_button({ + name: 'import', + label: IPA.messages.objects.entitle.import_button, + icon: 'import-icon', + click: function() { + var dialog = that.entity.get_dialog('import'); + dialog.open(that.container); + return false; + } + }).appendTo(that.consume_buttons); + + that.import_button.css('display', 'none'); + }; + + that.refresh = function() { + + function on_success(data, text_status, xhr) { + + if (that.entity.status == IPA.entitle.online) { + that.consume_button.css('display', 'inline'); + that.import_button.css('display', 'none'); + + } else if (that.entity.status == IPA.entitle.offline) { + that.consume_button.css('display', 'none'); + that.import_button.css('display', 'inline'); + + } else { + that.consume_button.css('display', 'none'); + that.import_button.css('display', 'inline'); + } + + that.load(data); + } + + function on_error(xhr, text_status, error_thrown) { + + that.consume_button.css('display', 'none'); + that.import_button.css('display', 'inline'); + + that.table.summary.text(error_thrown.name+': '+error_thrown.message); + } + + that.entity.get_status( + function(data, text_status, xhr) { + that.entity.get_certificates( + on_success, + on_error); + }, + on_error); + }; + + init(); + + return that; +}; + +IPA.entitle.certificate_column = function(spec) { + + spec = spec || {}; + + var that = IPA.column(spec); + + that.setup = function(container, record) { + + container.empty(); + + var certificate = record[that.name]; + + $('<a/>', { + href: '#download', + html: IPA.messages.objects.entitle.download, + click: function() { + var dialog = IPA.cert.download_dialog({ + title: IPA.messages.objects.entitle.download_certificate, + certificate: certificate + }); + dialog.open(); + return false; + } + }).appendTo(container); + }; + + return that; +}; + +IPA.entitle.certificate_dialog = function(spec) { + + spec = spec || {}; + + var that = IPA.dialog(spec); + + that.width = spec.width || 500; + that.height = spec.height || 400; + that.message = spec.message; + that.label = spec.label; + + that.get_certificate = function() { + var certificate = that.textarea.val(); + return IPA.cert.BEGIN_CERTIFICATE+'\n'+ + $.trim(certificate)+'\n'+ + IPA.cert.END_CERTIFICATE+'\n'; + }; + + that.create = function() { + that.container.append(that.message); + that.container.append('<br/>'); + that.container.append('<br/>'); + + that.container.append(IPA.cert.BEGIN_CERTIFICATE); + that.container.append('<br/>'); + + that.textarea = $('<textarea/>', { + style: 'width: 100%; height: 225px;' + }).appendTo(that.container); + + that.container.append('<br/>'); + that.container.append(IPA.cert.END_CERTIFICATE); + }; + + return that; +}; + +IPA.entitle.register_online_dialog = function(spec) { + + spec = spec || {}; + + var that = IPA.dialog(spec); + + that.create_button({ + name: 'register', + label: IPA.messages.objects.entitle.register, + click: function() { + var record = {}; + that.save(record); + + that.entity.register_online( + record.username[0], + record.password[0], + record.ipaentitlementid[0], + function() { + var facet = that.entity.get_facet(); + facet.refresh(); + that.close(); + } + ); + } + }); + + that.create_button({ + name: 'cancel', + label: IPA.messages.buttons.cancel, + click: function() { + that.close(); + } + }); + + return that; +}; + +IPA.entitle.register_offline_dialog = function(spec) { + + spec = spec || {}; + + var that = IPA.entitle.certificate_dialog(spec); + + that.create_button({ + name: 'register', + label: that.label, + click: function() { + that.entity.register_offline( + that.get_certificate(), + function() { + var facet = that.entity.get_facet(); + facet.refresh(); + that.close(); + } + ); + } + }); + + that.create_button({ + name: 'cancel', + label: IPA.messages.buttons.cancel, + click: function() { + that.close(); + } + }); + + return that; +}; + +IPA.entitle.consume_dialog = function(spec) { + + spec = spec || {}; + + var that = IPA.dialog(spec); + + that.create_button({ + name: 'consume', + label: IPA.messages.objects.entitle.consume, + click: function() { + + if (!that.validate()) { + return; + } + + var record = {}; + that.save(record); + + that.entity.consume( + record.quantity[0], + function() { + var facet = that.entity.get_facet(); + facet.refresh(); + that.close(); + } + ); + } + }); + + that.create_button({ + name: 'cancel', + label: IPA.messages.buttons.cancel, + click: function() { + that.close(); + } + }); + + return that; +}; + +IPA.entitle.import_dialog = function(spec) { + + spec = spec || {}; + + var that = IPA.entitle.certificate_dialog(spec); + + that.create_button({ + name: 'import', + label: IPA.messages.objects.entitle.import_button, + click: function() { + that.entity.import_certificate( + that.get_certificate(), + function() { + var facet = that.entity.get_facet(); + facet.refresh(); + that.close(); + } + ); + } + }); + + that.create_button({ + name: 'cancel', + label: IPA.messages.buttons.cancel, + click: function() { + that.close(); + } + }); + + return that; +}; + +IPA.entitle.download_widget = function(spec) { + + spec = spec || {}; + + var that = IPA.input_widget(spec); + + that.create = function(container) { + that.link = $('<a/>', { + 'href': '#download', + 'html': IPA.messages.objects.entitle.download, + 'click': function() { + that.entity.get_accounts( + function(data, text_status, xhr) { + var userpkcs12 = data.result.result[0].userpkcs12; + if (!userpkcs12) { + alert(IPA.messages.objects.entitle.no_certificate); + return; + } + + /* + * WARNING - despite using cert.download_dialog() and passing + * a certificate, it's NOT a certificate, it's a binary + * PKCS12 file that's been base64 encoded! + * Hence the reason add_pem_delimiters is false. + */ + var dialog = IPA.cert.download_dialog({ + title: IPA.messages.objects.entitle.download_certificate, + certificate: userpkcs12[0].__base64__, + add_pem_delimiters: false + }); + dialog.open(); + } + ); + return false; + } + }).appendTo(container); + }; + + that.update = function() { + if (that.entity.status == IPA.entitle.online) { + that.link.css('display', 'inline'); + } else { + that.link.css('display', 'none'); + } + }; + + return that; +}; + +IPA.register('entitle', IPA.entitle.entity); diff --git a/install/ui/src/freeipa/entity.js b/install/ui/src/freeipa/entity.js new file mode 100644 index 000000000..d474ad95b --- /dev/null +++ b/install/ui/src/freeipa/entity.js @@ -0,0 +1,731 @@ +/*jsl:import ipa.js */ +/*jsl:import facet.js */ +/*jsl:import navigation.js */ + +/* Authors: + * Pavel Zuna <pzuna@redhat.com> + * Endi Sukma Dewata <edewata@redhat.com> + * Adam Young <ayoung@redhat.com> + * Petr Vobornik <pvoborni@redhat.com> + * + * Copyright (C) 2010-2011 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/>. + */ + +/* REQUIRES: ipa.js, facet.js, details.js, search.js, add.js */ + +IPA.entity = function(spec) { + + spec = spec || {}; + + spec.policies = spec.policies || [ + IPA.search_facet_update_policy(), + IPA.details_facet_update_policy() + ]; + + var that = {}; + + that.name = spec.name; + that.label = spec.label; + + that.metadata = spec.metadata; + that.builder = spec.builder; + + that.dialogs = $.ordered_map(); + that.dialog_specs = spec.dialogs || []; + that.dialogs_created = false; + + that.policies = IPA.entity_policies({ + entity: that, + policies: spec.policies + }); + + that.facets = $.ordered_map(); + that.facet_groups = $.ordered_map(); + that.facet_specs = spec.facets || []; + that.facets_created = false; + + // current facet + that.facet = null; + + that.redirect_facet = spec.redirect_facet; + that.containing_entity = null; + + that.init = function() { + if (!that.metadata) { + that.metadata = that.get_default_metadata(); + if (!that.metadata) { + throw { + expected: true, + message: "Entity " + that.name + " not supported by server." + }; + } + } + that.label = that.label || that.metadata.label || that.name; + }; + + that.get_default_metadata = function() { + return IPA.metadata.objects[that.name]; + }; + + that.get_containing_entity = function() { + return that.containing_entity; + }; + + that.get_dialog = function(name) { + + //build all dialogs on the first time + if(!that.dialogs_created) { + var builder = IPA.dialog_builder(that); + builder.build_dialogs(); + that.dialogs_created = true; + } + + return that.dialogs.get(name); + }; + + that.add_dialog = function(dialog) { + return that.dialog(dialog); + }; + + that.dialog = function(dialog) { + dialog.entity = that; + that.dialogs.put(dialog.name, dialog); + return that; + }; + + that.add_facet_group = function(facet_group) { + that.facet_groups.put(facet_group.name, facet_group); + }; + + that.get_facet_group = function(name) { + return that.facet_groups.get(name); + }; + + that.remove_facet_groups = function() { + that.facet_groups.empty(); + }; + + that.get_facet = function(name) { + + //build all facets on the first time + if(!that.facets_created) { + var builder = IPA.facet_builder(that); + builder.build_facets(); + that.facets_created = true; + that.policies.facets_created(); + } + + if (name === undefined) { + // return the current facet + if (that.facet) return that.facet; + + // return the main facet + return that.facets.values[0]; + + } else if (name === 'default') { + // return the first facet in the first facet group + var facet_groups = that.facet_groups.values; + for (var i=0; i<facet_groups.length; i++) { + var facet_group = facet_groups[i]; + var facets = facet_group.facets.values; + if (!facets.length) continue; + return facets[0]; + } + + return that.facets.values[0]; + } + + return that.facets.get(name); + }; + + that.add_facet = function(facet) { + facet.entity = that; + + that.facets.put(facet.name, facet); + + if (facet.facet_group) { + var facet_group = that.get_facet_group(facet.facet_group); + if (facet_group) { + facet_group.add_facet(facet); + } + } + + return that; + }; + + that.create = function(container) { + that.container = container; + }; + + that.display = function(container) { + + var prev_entity = IPA.current_entity; + var prev_facet = prev_entity ? prev_entity.facet : null; + + IPA.current_entity = that; + + var facet_name = IPA.nav.get_state(that.name+'-facet'); + that.facet = that.get_facet(facet_name); + + var needs_update = that.facet.needs_update(); + + // same entity, same facet, and doesn't need updating => return + if (that == prev_entity && that.facet == prev_facet && !needs_update) { + return; + } + + if (prev_facet) { + prev_facet.hide(); + } + + var facet_container = $('.facet[name="'+that.facet.name+'"]', that.container); + if (!facet_container.length) { + facet_container = $('<div/>', { + name: that.facet.name, + 'class': 'facet' + }).appendTo(that.container); + + that.facet.create(facet_container); + } + + if (needs_update) { + that.facet.clear(); + that.facet.show(); + that.facet.header.select_tab(); + that.facet.refresh(); + } else { + that.facet.show(); + that.facet.header.select_tab(); + } + }; + + that.get_primary_key_prefix = function() { + var pkey = []; + var current_entity = that; + current_entity = current_entity.get_containing_entity(); + while(current_entity !== null){ + + var key = IPA.nav.get_state(current_entity.name+'-pkey'); + if (key){ + pkey.unshift(key); + } + current_entity = current_entity.get_containing_entity(); + } + return pkey; + }; + + /*gets the primary key for the current entity out of the URL parameters */ + that.get_primary_key = function() { + var pkey = that.get_primary_key_prefix(); + var current_entity = that; + pkey.unshift(IPA.nav.get_state(current_entity.name+'-pkey')); + return pkey; + }; + + /* most entites only require -pkey for their primary keys, but some + are more specific. This call allows those entites a place + to override the other parameters. */ + that.get_key_names = function() { + return [that.name + '-pkey']; + }; + + that.entity_init = that.init; + + return that; +}; + +IPA.nested_tab_labels = {}; + +IPA.get_nested_tab_label = function(entity_name){ + + if (!IPA.nested_tab_labels[entity_name]){ + IPA.nested_tab_labels[entity_name] = "LABEL"; + + } + return IPA.nested_tab_labels[entity_name]; + +}; + +/*Returns the entity requested, as well as: + any nested tabs underneath it or + its parent tab and the others nested at the same level*/ + +IPA.nested_tabs = function(entity_name) { + + var siblings = []; + var i; + var i2; + var nested_entities; + var sub_i; + var sub_tab; + + var key = entity_name; + function push_sibling(sibling){ + siblings.push (sibling); + IPA.nested_tab_labels[key] = sub_tab; + } + + + if (!IPA.nav.tabs) { + siblings.push(entity_name); + return siblings; + } + + for (var top_i = 0; top_i < IPA.nav.tabs.length; top_i++) { + var top_tab = IPA.nav.tabs[top_i]; + for (sub_i = 0; sub_i < top_tab.children.length; sub_i++) { + sub_tab = top_tab.children[sub_i]; + nested_entities = sub_tab.children; + if (sub_tab.name === entity_name){ + push_sibling(entity_name); + } + if (sub_tab.children){ + for (i = 0; i < nested_entities.length; i += 1){ + if (sub_tab.name === entity_name){ + push_sibling(nested_entities[i].name); + }else{ + if (nested_entities[i].name === entity_name){ + push_sibling(sub_tab.name); + for (i2 = 0; i2 < nested_entities.length; i2 += 1){ + key = nested_entities[i].name; + push_sibling(nested_entities[i2].name); + } + } + } + } + } + } + } + + return siblings; +}; + +IPA.entity_builder = function() { + + var that = {}; + + var entity = null; + var facet_group = null; + var facet = null; + var section = null; + + that.entity = function(spec) { + var factory = IPA.entity; + if (spec instanceof Object) { + factory = spec.factory || IPA.entity; + } else { + spec = { name: spec }; + } + spec.builder = that; + + entity = factory(spec); + + that.facet_groups([ + 'member', + 'settings', + 'memberof', + 'managedby' + ]); + + return that; + }; + + that.facet_group = function(spec) { + spec.entity = entity; + if (spec instanceof Object) { + var factory = spec.factory || IPA.facet_group; + facet_group = factory(spec); + } else { + facet_group = IPA.facet_group({ name: spec }); + } + + if (facet_group.label == undefined) { + facet_group.label = IPA.messages.facet_groups[facet_group.name]; + } + + entity.add_facet_group(facet_group); + + return that; + }; + + that.facet_groups = function(specs) { + + entity.remove_facet_groups(); + + for (var i=0; i<specs.length; i++) { + specs[i].entity = entity; + that.facet_group(specs[i]); + } + + return that; + }; + + that.facet = function(spec) { + + spec.entity = entity; + entity.facet_specs.push(spec); + + return that; + }; + + that.search_facet = function(spec) { + + spec.type = spec.type || 'search'; + + that.facet(spec); + + add_redirect_info(spec.name); + + return that; + }; + + that.nested_search_facet = function(spec) { + + spec.type = spec.type || 'nested_search'; + + that.facet(spec); + + return that; + }; + + that.details_facet = function(spec) { + + spec.type = spec.type || 'details'; + + that.facet(spec); + + return that; + }; + + that.association_facet = function(spec) { + + spec.type = spec.type || 'association'; + + that.facet(spec); + + return that; + }; + + that.attribute_facet = function(spec) { + + spec.type = spec.type || 'attribute'; + + that.facet(spec); + + return that; + }; + + that.standard_association_facets = function(spec) { + + spec = spec || {}; + spec.entity = entity; + + var direct_associations = []; + var indirect_associations = []; + + for (var association in entity.metadata.attribute_members) { + if (association == 'memberindirect' || + association == 'memberofindirect') { + indirect_associations.push(association); + } else { + direct_associations.push(association); + } + } + + // make sure direct facets are created first + var attribute_members = direct_associations.concat(indirect_associations); + + for (var i=0; i<attribute_members.length; i++) { + var attribute_member = attribute_members[i]; + var other_entities = entity.metadata.attribute_members[attribute_member]; + + for (var j=0; j<other_entities.length; j++) { + + var other_entity = other_entities[j]; + var association_name = attribute_member+'_'+other_entity; + + //already prepared facet + var facet = get_spec_by_name(entity.facet_specs, association_name); + //already prepared direct facet for indirect facet + var direct_facet = get_direct_facet(entity.facet_specs, + attribute_member, + other_entity); + if (facet || direct_facet) { + continue; //in both cases don't prepare new facet + } + + var tmp_spec = $.extend({}, spec); + tmp_spec.name = association_name; + + that.association_facet(tmp_spec); + } + } + + return that; + }; + + function get_spec_by_name(specs, name) { + if(!specs || !specs.length) return null; + + for(var i=0; i<specs.length; i++) { + if(specs[i].name === name) { + return specs[i]; + } + } + + return null; + } + + /* + * If it's an indirect attribute member, return its direct facets spec + * if it exists. + */ + function get_direct_facet(facets, attribute_member, other_entity) { + + var index = attribute_member.indexOf('indirect'); + if(index > -1) { + var direct_attribute_member = attribute_member.substring(0, index); + return get_spec_by_name(facets, + direct_attribute_member+'_'+other_entity); + } + + return null; + } + + function add_redirect_info(facet_name){ + facet_name = facet_name || 'search'; + if (!entity.redirect_facet){ + entity.redirect_facet = facet_name; + } + } + + that.containing_entity = function(entity_name) { + add_redirect_info(); + entity.containing_entity = IPA.get_entity(entity_name); + return that; + }; + + that.dialog = function(spec) { + + if (spec instanceof Object) { + spec.factory = spec.factory || IPA.dialog; + spec.entity = entity; + + } else { + spec = { + factory: IPA.dialog, + name: spec, + entity: entity + }; + } + + entity.dialog_specs.push(spec); + return that; + }; + + that.adder_dialog = function(spec) { + spec.factory = spec.factory || IPA.entity_adder_dialog; + spec.name = spec.name || 'add'; + + if (!spec.title) { + var title = IPA.messages.dialogs.add_title; + var label = entity.metadata.label_singular; + spec.title = title.replace('${entity}', label); + } + + return that.dialog(spec); + }; + + that.deleter_dialog = function(spec) { + spec.factory = spec.factory || IPA.search_deleter_dialog; + spec.name = spec.name || 'remove'; + + return that.dialog(spec); + }; + + that.build = function(){ + return entity; + }; + + return that; +}; + +IPA.dialog_builder = function(entity) { + + var that = {}; + + that.build_dialogs = function() { + + if(entity.dialog_specs && entity.dialog_specs.length) { + var dialogs = entity.dialog_specs; + for(var i=0; i<dialogs.length; i++) { + that.build_dialog(dialogs[i]); + } + } + }; + + that.build_dialog = function(spec) { + //do common logic + spec.entity = entity; + + //add dialog + var dialog = spec.factory(spec); + entity.dialog(dialog); + }; + + return that; +}; + +IPA.entity_policy = function(spec) { + + spec = spec || {}; + + var that = {}; + + that.entity = spec.entity; + + that.facets_created = function() { + }; + + return that; +}; + +IPA.entity_policies = function(spec) { + + var that = {}; + + that.entity = spec.entity; + that.policies = []; + + that.add_policy = function(policy) { + + policy.entity = that.entity; + that.policies.push(policy); + }; + + that.add_policies = function(policies) { + + if (!policies) return; + + for (var i=0; i<policies.length; i++) { + that.add_policy(policies[i]); + } + }; + + that.facets_created = function() { + + for (var i=0; i<that.policies.length; i++) { + that.policies[i].facets_created(); + } + }; + + that.add_policies(spec.policies); + + return that; +}; + +IPA.facet_update_policy = function(spec) { + + spec = spec || {}; + + var that = IPA.entity_policy(); + + that.event = spec.event || 'on_update'; + that.source_facet_name = spec.source_facet; + that.dest_facet_name = spec.dest_facet; + that.dest_entity_name = spec.dest_entity; + + that.facets_created = function() { + + that.source_facet = that.entity.get_facet(that.source_facet_name); + var dest_entity = that.entity; + if (that.dest_entity_name) { + dest_entity = IPA.get_entity(that.dest_entity_name); + if (!dest_entity) return; + } + that.dest_facet = dest_entity.get_facet(that.dest_facet_name); + + if (!that.source_facet || !that.dest_facet) return; + + var event = that.source_facet[that.event]; + if (!event && !event.attach) return; + + event.attach(that.set_expired_flag); + }; + + that.set_expired_flag = function() { + + that.dest_facet.set_expired_flag(); + }; + + return that; +}; + +IPA.adder_facet_update_policy = function(spec) { + + spec = spec || {}; + + var that = IPA.entity_policy(); + + that.event = spec.event || 'added'; + that.dialog_name = spec.dialog_name || 'add'; + that.dest_facet_name = spec.dest_facet || 'details'; + that.dest_entity_name = spec.dest_entity; + + that.facets_created = function() { + + that.dialog = that.entity.get_dialog(that.dialog_name); + var dest_entity = that.entity; + if (that.dest_entity_name) { + dest_entity = IPA.get_entity(that.dest_entity_name); + if (!dest_entity) return; + } + that.dest_facet = dest_entity.get_facet(that.dest_facet_name); + + if (!that.dialog || !that.dest_facet) return; + + var event = that.dialog[that.event]; + if (!event && !event.attach) return; + + event.attach(that.set_expired_flag); + }; + + that.set_expired_flag = function() { + + that.dest_facet.set_expired_flag(); + }; + + return that; +}; + +IPA.search_facet_update_policy = function(spec) { + + spec = spec || {}; + spec.source_facet = 'search'; + spec.dest_facet = 'details'; + + return IPA.facet_update_policy(spec); +}; + +IPA.details_facet_update_policy = function(spec) { + + spec = spec || {}; + spec.source_facet = 'details'; + spec.dest_facet = 'search'; + + return IPA.facet_update_policy(spec); +};
\ No newline at end of file diff --git a/install/ui/src/freeipa/facet.js b/install/ui/src/freeipa/facet.js new file mode 100644 index 000000000..25d48bf66 --- /dev/null +++ b/install/ui/src/freeipa/facet.js @@ -0,0 +1,2044 @@ +/*jsl:import ipa.js */ + +/* Authors: + * Pavel Zuna <pzuna@redhat.com> + * Endi Sukma Dewata <edewata@redhat.com> + * Adam Young <ayoung@redhat.com> + * Petr Vobornik <pvoborni@redhat.com> + * + * Copyright (C) 2010-2011 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/>. + */ + +/* REQUIRES: ipa.js, details.js, search.js, add.js */ + +IPA.facet = function(spec, no_init) { + + spec = spec || {}; + spec.state = spec.state || {}; + $.extend(spec.state, { factory: IPA.state }); + + var that = {}; + + that.entity = IPA.get_entity(spec.entity); + + that.name = spec.name; + that.label = spec.label; + that.title = spec.title || that.label; + that.tab_label = spec.tab_label || that.label; + that.display_class = spec.display_class; + + that.disable_breadcrumb = spec.disable_breadcrumb; + that.disable_facet_tabs = spec.disable_facet_tabs; + + that.action_state = IPA.build(spec.state); + that.actions = IPA.build({ actions: spec.actions }, IPA.action_holder_builder); + + that.header_actions = spec.header_actions; + that.header = spec.header || IPA.facet_header({ facet: that }); + + that._needs_update = spec.needs_update; + that.expired_flag = true; + that.last_updated = null; + that.expire_timeout = spec.expire_timeout || 600; //[seconds] + that.on_update = IPA.observer(); + that.post_load = IPA.observer(); + + that.dialogs = $.ordered_map(); + + // facet group name + that.facet_group = spec.facet_group; + + that.redirect_info = spec.redirect_info; + + that.state = {}; + + that.get_dialog = function(name) { + return that.dialogs.get(name); + }; + + that.dialog = function(dialog) { + that.dialogs.put(dialog.name, dialog); + return that; + }; + + that.create = function(container) { + + that.container = container; + + if (that.disable_facet_tabs) that.container.addClass('no-facet-tabs'); + that.container.addClass(that.display_class); + + that.header_container = $('<div/>', { + 'class': 'facet-header' + }).appendTo(container); + that.create_header(that.header_container); + + that.content = $('<div/>', { + 'class': 'facet-content' + }).appendTo(container); + + that.error_container = $('<div/>', { + 'class': 'facet-content facet-error' + }).appendTo(container); + + that.create_content(that.content); + }; + + that.create_header = function(container) { + + that.header.create(container); + + that.controls = $('<div/>', { + 'class': 'facet-controls' + }).appendTo(container); + }; + + that.create_content = function(container) { + }; + + that.create_control_buttons = function(container) { + + if (that.control_buttons) { + that.control_buttons.create(container); + } + }; + + that.set_title = function(container, title) { + var element = $('h1', that.title_container); + element.html(title); + }; + + that.show = function() { + that.container.css('display', 'block'); + that.show_content(); + }; + + that.show_content = function() { + that.content.css('display', 'block'); + that.error_container.css('display', 'none'); + }; + + that.show_error = function() { + that.content.css('display', 'none'); + that.error_container.css('display', 'block'); + }; + + that.error_displayed = function() { + return that.error_container && + that.error_container.css('display') === 'block'; + }; + + that.hide = function() { + that.container.css('display', 'none'); + }; + + that.load = function(data) { + that.data = data; + that.header.load(data); + }; + + that.refresh = function() { + }; + + that.clear = function() { + }; + + that.needs_update = function() { + + if (that._needs_update !== undefined) return that._needs_update; + + var needs_update = false; + + if (that.expire_timeout && that.expire_timeout > 0) { + + if (!that.last_updated) { + needs_update = true; + } else { + var now = Date.now(); + needs_update = (now - that.last_updated) > that.expire_timeout * 1000; + } + } + + needs_update = needs_update || that.expired_flag; + needs_update = needs_update || that.error_displayed(); + + return needs_update; + }; + + that.set_expired_flag = function() { + that.expired_flag = true; + }; + + that.clear_expired_flag = function() { + that.expired_flag = false; + that.last_updated = Date.now(); + }; + + that.is_dirty = function() { + return false; + }; + + that.report_error = function(error_thrown) { + + var add_option = function(ul, text, handler) { + + var li = $('<li/>').appendTo(ul); + $('<a />', { + href: '#', + text: text, + click: function() { + handler(); + return false; + } + }).appendTo(li); + }; + + var title = IPA.messages.error_report.title; + title = title.replace('${error}', error_thrown.name); + + that.error_container.empty(); + that.error_container.append('<h1>'+title+'</h1>'); + + var details = $('<div/>', { + 'class': 'error-details' + }).appendTo(that.error_container); + details.append('<p>'+error_thrown.message+'</p>'); + + $('<div/>', { + text: IPA.messages.error_report.options + }).appendTo(that.error_container); + + var options_list = $('<ul/>').appendTo(that.error_container); + + add_option( + options_list, + IPA.messages.error_report.refresh, + function() { + that.refresh(); + } + ); + + add_option( + options_list, + IPA.messages.error_report.main_page, + function() { + IPA.nav.show_top_level_page(); + } + ); + + add_option( + options_list, + IPA.messages.error_report.reload, + function() { + window.location.reload(false); + } + ); + + that.error_container.append('<p>'+IPA.messages.error_report.problem_persists+'</p>'); + + that.show_error(); + }; + + that.get_redirect_facet = function() { + + var entity = that.entity; + while (entity.containing_entity) { + entity = entity.get_containing_entity(); + } + var facet_name = that.entity.redirect_facet; + var entity_name = entity.name; + var tab_name, facet; + + if (that.redirect_info) { + entity_name = that.redirect_info.entity || entity_name; + facet_name = that.redirect_info.facet || facet_name; + tab_name = that.redirect_info.tab; + } + + if (tab_name) { + facet = IPA.nav.get_tab_facet(tab_name); + } + + if (!facet) { + entity = IPA.get_entity(entity_name); + facet = entity.get_facet(facet_name); + } + + return facet; + }; + + that.redirect = function() { + + var facet = that.get_redirect_facet(); + if (!facet) return; + IPA.nav.show_page(facet.entity.name, facet.name); + }; + + var redirect_error_codes = [4001]; + + that.redirect_error = function(error_thrown) { + + /*If the error is in talking to the server, don't attempt to redirect, + as there is nothing any other facet can do either. */ + for (var i=0; i<redirect_error_codes.length; i++) { + if (error_thrown.code === redirect_error_codes[i]) { + that.redirect(); + return; + } + } + }; + + that.init_facet = function() { + + that.action_state.init(that); + that.actions.init(that); + that.header.init(); + + var buttons_spec = { + factory: IPA.control_buttons_widget, + name: 'control-buttons', + 'class': 'control-buttons', + buttons: spec.control_buttons + }; + + that.control_buttons = IPA.build(buttons_spec); + that.control_buttons.init(that); + }; + + if (!no_init) that.init_facet(); + + // methods that should be invoked by subclasses + that.facet_create = that.create; + that.facet_create_header = that.create_header; + that.facet_create_content = that.create_content; + that.facet_needs_update = that.needs_update; + that.facet_show = that.show; + that.facet_hide = that.hide; + that.facet_load = that.load; + + return that; +}; + +IPA.facet_header = function(spec) { + + spec = spec || {}; + + var that = {}; + + that.facet = spec.facet; + + that.init = function() { + + if (that.facet.header_actions) { + + var widget_builder = IPA.widget_builder({ + widget_options: { + entity: that.facet.entity, + facet: that.facet + } + }); + + var widget = { + factory: IPA.action_list_widget, + actions: that.facet.header_actions + }; + + that.action_list = widget_builder.build_widget(widget); + that.action_list.init(that.facet); + } + + that.facet.action_state.changed.attach(that.update_summary); + + that.title_widget = IPA.facet_title(); + }; + + that.select_tab = function() { + if (that.facet.disable_facet_tabs) return; + + $(that.facet_tabs).find('a').removeClass('selected'); + var facet_name = IPA.nav.get_state(that.facet.entity.name+'-facet'); + + if (!facet_name || facet_name === 'default') { + that.facet_tabs.find('a:first').addClass('selected'); + } else { + that.facet_tabs.find('a#' + facet_name ).addClass('selected'); + } + }; + + that.set_pkey = function(value) { + + if (!value) return; + + var key, i; + var pkey_max = that.get_max_pkey_length(); + var limited_value = IPA.limit_text(value, pkey_max); + + if (!that.facet.disable_breadcrumb) { + var breadcrumb = []; + var keys = []; + + var entity = that.facet.entity.get_containing_entity(); + + while (entity) { + key = IPA.nav.get_state(entity.name+'-pkey'); + breadcrumb.unshift($('<a/>', { + 'class': 'breadcrumb-element', + text: key, + title: entity.metadata.label_singular, + click: function(entity) { + return function() { + IPA.nav.show_page(entity.name, 'default'); + return false; + }; + }(entity) + })); + + entity = entity.get_containing_entity(); + keys.unshift(key); + } + + //calculation of breadcrumb keys length + keys.push(value); + var max_bc_l = 140; //max chars which can fit on one line + var max_key_l = (max_bc_l / keys.length) - 4; //4 chars as divider + var bc_l = 0; + var to_limit = keys.length; + + //count how many won't be limited and how much space they take + for (i=0; i<keys.length; i++) { + var key_l = keys[i].length; + if (key_l <= max_key_l) { + to_limit--; + bc_l += key_l + 4; + } + } + + max_key_l = ((max_bc_l - bc_l) / to_limit) - 4; + + + that.path.empty(); + + for (i=0; i<breadcrumb.length; i++) { + var item = breadcrumb[i]; + key = IPA.limit_text(keys[i], max_key_l); + item.text(key); + + that.path.append(' » '); + that.path.append(item); + } + + that.path.append(' » '); + + key = IPA.limit_text(keys[i], max_key_l); + + $('<span>', { + 'class': 'breadcrumb-element', + title: value, + text: key + }).appendTo(that.path); + } + + var title_info = { + title: that.facet.label, + pkey: limited_value, + pkey_tooltip: value + }; + that.title_widget.update(title_info); + + that.adjust_elements(); + }; + + that.create_facet_link = function(container, other_facet) { + + var li = $('<li/>', { + name: other_facet.name, + title: other_facet.name, + click: function() { + if (li.hasClass('entity-facet-disabled')) { + return false; + } + + var pkey = IPA.nav.get_state(that.facet.entity.name+'-pkey'); + IPA.nav.show_page(that.facet.entity.name, other_facet.name, pkey); + + return false; + } + }).appendTo(container); + + $('<a/>', { + text: other_facet.tab_label, + id: other_facet.name + }).appendTo(li); + }; + + that.create_facet_group = function(container, facet_group) { + + var section = $('<div/>', { + name: facet_group.name, + 'class': 'facet-group' + }).appendTo(container); + + $('<div/>', { + 'class': 'facet-group-label' + }).appendTo(section); + + var ul = $('<ul/>', { + 'class': 'facet-tab' + }).appendTo(section); + + var facets = facet_group.facets.values; + for (var i=0; i<facets.length; i++) { + var facet = facets[i]; + that.create_facet_link(ul, facet); + } + }; + + that.create = function(container) { + + that.container = container; + + if (!that.facet.disable_breadcrumb) { + that.breadcrumb = $('<div/>', { + 'class': 'breadcrumb' + }).appendTo(container); + + that.back_link = $('<span/>', { + 'class': 'back-link' + }).appendTo(that.breadcrumb); + + var redirect_facet = that.facet.get_redirect_facet(); + + $('<a/>', { + text: redirect_facet.label, + click: function() { + that.facet.redirect(); + return false; + } + }).appendTo(that.back_link); + + + that.path = $('<span/>', { + 'class': 'path' + }).appendTo(that.breadcrumb); + } + + that.title_widget.create(container); + that.title_widget.update({ title: that.facet.label }); + + if (that.action_list) { + that.action_list_container = $('<div/>', { + 'class': 'facet-action-list' + }).appendTo(container); + + that.action_list.create(that.action_list_container); + } + + if (!that.facet.disable_facet_tabs) { + that.facet_tabs = $('<div/>', { + 'class': 'facet-tabs' + }).appendTo(container); + + var facet_groups = that.facet.entity.facet_groups.values; + for (var i=0; i<facet_groups.length; i++) { + var facet_group = facet_groups[i]; + if (facet_group.facets.length) { + that.create_facet_group(that.facet_tabs, facet_group); + } + } + } + }; + + that.load = function(data) { + if (!data) return; + var result = data.result.result; + if (!that.facet.disable_facet_tabs) { + var pkey = that.facet.pkey; + + var facet_groups = that.facet.entity.facet_groups.values; + for (var i=0; i<facet_groups.length; i++) { + var facet_group = facet_groups[i]; + + var span = $('.facet-group[name='+facet_group.name+']', that.facet_tabs); + if (!span.length) continue; + + var label = facet_group.label; + if (pkey && label) { + var limited_pkey = IPA.limit_text(pkey, 20); + label = label.replace('${primary_key}', limited_pkey); + } else { + label = ''; + } + + var label_container = $('.facet-group-label', span); + label_container.text(label); + if (pkey) label_container.attr('title', pkey); + + var facets = facet_group.facets.values; + for (var j=0; j<facets.length; j++) { + var facet = facets[j]; + var link = $('li[name='+facet.name+'] a', span); + + var values = result ? result[facet.name] : null; + if (values) { + link.text(facet.tab_label+' ('+values.length+')'); + } else { + link.text(facet.tab_label); + } + } + } + } + }; + + that.update_summary = function() { + var summary = that.facet.action_state.summary(); + + if (summary.state.length > 0) { + var css_class = summary.state.join(' '); + that.title_widget.set_class(css_class); + that.title_widget.set_icon_tooltip(summary.description); + } + + that.adjust_elements(); + }; + + that.get_max_pkey_length = function() { + + var label_w, max_pkey_w, max_pkey_l, al, al_w, icon_w, char_w, container_w; + + container_w = that.container.width(); + icon_w = that.title_widget.icon.width(); + label_w = that.title_widget.title.width(); + char_w = label_w / that.title_widget.title.text().length; + max_pkey_w = container_w - icon_w - label_w; + max_pkey_w -= 10; //some space correction to be safe + + if (that.action_list) { + al = that.action_list.container; + al_w = al.width(); + + max_pkey_w -= al_w; + } + + max_pkey_l = Math.ceil(max_pkey_w / char_w); + + return max_pkey_l; + }; + + that.adjust_elements = function() { + + if (that.action_list) { + + var action_list = that.action_list.container; + var max_width = that.container.width(); + var al_width = action_list.width(); + var title_width = that.title_widget.title_container.width(); + var title_max = max_width - al_width; + + that.title_widget.set_max_width(title_max); + action_list.css('left', title_width + 'px'); + } + }; + + that.clear = function() { + that.load(); + if (that.action_list) that.action_list.clear(); + }; + + return that; +}; + +IPA.facet_title = function(spec) { + + spec = spec || {}; + + var that = {}; + + that.update = function(data) { + + var tooltip = data.tooltip || data.title; + var pkey_tooltip = data.pkey_tooltip || data.pkey; + var icon_tooltip = data.icon_tooltip || ''; + + that.title.text(data.title); + that.title.attr('title', tooltip); + + if (data.pkey) { + that.title.text(data.title + ': '); + that.pkey.text(data.pkey); + that.pkey.attr('title', pkey_tooltip); + } + + if (data.css_class) that.set_class(data.css_class); + + that.set_icon_tooltip(icon_tooltip); + }; + + that.create = function(container) { + + that.title_container = $('<div/>', { + 'class': 'facet-title' + }).appendTo(container); + + that.icon = $('<div />', { + 'class': 'header-icon' + }).appendTo(that.title_container); + + var h3 = $('<h3/>').appendTo(that.title_container); + + that.title = $('<span/>').appendTo(h3); + + that.pkey = $('<span/>', { + 'class': 'facet-pkey' + }).appendTo(h3); + }; + + that.set_max_width = function(width) { + that.title_container.css('max-width', width+'px'); + }; + + that.set_class = function(css_class) { + + if (that.css_class) { + that.title_container.removeClass(that.css_class); + } + + if (css_class) { + that.title_container.addClass(css_class); + } + + that.css_class = css_class; + }; + + that.set_icon_tooltip = function(tooltip) { + that.icon.attr('title', tooltip); + }; + + return that; +}; + +IPA.table_facet = function(spec, no_init) { + + spec = spec || {}; + + var that = IPA.facet(spec, no_init); + + that.managed_entity = spec.managed_entity ? IPA.get_entity(spec.managed_entity) : that.entity; + + that.pagination = spec.pagination === undefined ? true : spec.pagination; + that.search_all_entries = spec.search_all_entries; + that.search_all_attributes = spec.search_all_attributes; + that.sort_enabled = spec.sort_enabled === undefined ? true : spec.sort_enabled; + that.selectable = spec.selectable === undefined ? true : spec.selectable; + that.select_changed = IPA.observer(); + + that.row_enabled_attribute = spec.row_enabled_attribute; + that.row_disabled_attribute = spec.row_disabled_attribute; + that.details_facet_name = spec.details_facet || 'default'; + + that.table_name = spec.table_name; + + that.columns = $.ordered_map(); + + that.get_columns = function() { + return that.columns.values; + }; + + that.get_column = function(name) { + return that.columns.get(name); + }; + + that.add_column = function(column) { + column.entity = that.managed_entity; + that.columns.put(column.name, column); + }; + + that.create_column = function(spec) { + var column; + if (spec instanceof Object) { + var factory = spec.factory || IPA.column; + } else { + factory = IPA.column; + spec = { name: spec }; + } + + spec.entity = that.managed_entity; + column = factory(spec); + + that.add_column(column); + return column; + }; + + that.column = function(spec){ + that.create_column(spec); + return that; + }; + + that.create_content = function(container) { + that.table.create(container); + }; + + that.load = function(data) { + that.facet_load(data); + + if (!data) { + that.table.empty(); + that.table.summary.text(''); + that.table.pagination_control.css('visibility', 'hidden'); + return; + } + + that.table.current_page = 1; + that.table.total_pages = 1; + + if (that.pagination) { + that.load_page(data); + } else { + that.load_all(data); + } + + that.table.current_page_input.val(that.table.current_page); + that.table.total_pages_span.text(that.table.total_pages); + + that.table.pagination_control.css('visibility', 'visible'); + + that.post_load.notify([data], that); + that.clear_expired_flag(); + }; + + + that.load_all = function(data) { + + var result = data.result.result; + var records = []; + for (var i=0; i<result.length; i++) { + var record = that.table.get_record(result[i], 0); + records.push(record); + } + that.load_records(records); + + if (data.result.truncated) { + var message = IPA.messages.search.truncated; + message = message.replace('${counter}', data.result.count); + that.table.summary.text(message); + } else { + that.table.summary.text(data.result.summary || ''); + } + }; + + that.get_records_map = function(data) { + + var records_map = $.ordered_map(); + + var result = data.result.result; + var pkey_name = that.managed_entity.metadata.primary_key; + + for (var i=0; i<result.length; i++) { + var record = result[i]; + var pkey = record[pkey_name]; + if (pkey instanceof Array) pkey = pkey[0]; + records_map.put(pkey, record); + } + + return records_map; + }; + + that.load_page = function(data) { + + // get primary keys (and the complete records if search_all_entries is true) + var records_map = that.get_records_map(data); + + var total = records_map.length; + that.table.total_pages = total ? Math.ceil(total / that.table.page_length) : 1; + + delete that.table.current_page; + + var state = {}; + var page = parseInt(IPA.nav.get_state(that.entity.name+'-page'), 10) || 1; + if (page < 1) { + state[that.entity.name+'-page'] = 1; + IPA.nav.push_state(state); + return; + } else if (page > that.table.total_pages) { + state[that.entity.name+'-page'] = that.table.total_pages; + IPA.nav.push_state(state); + return; + } + that.table.current_page = page; + + if (!total) { + that.table.summary.text(IPA.messages.association.no_entries); + that.load_records([]); + return; + } + + // calculate the start and end of the current page + var start = (that.table.current_page - 1) * that.table.page_length + 1; + var end = that.table.current_page * that.table.page_length; + end = end > total ? total : end; + + var summary = IPA.messages.association.paging; + summary = summary.replace('${start}', start); + summary = summary.replace('${end}', end); + summary = summary.replace('${total}', total); + that.table.summary.text(summary); + + // sort map based on primary keys + if (that.sort_enabled) { + records_map = records_map.sort(); + } + + // trim map leaving the entries visible in the current page only + records_map = records_map.slice(start-1, end); + + var columns = that.table.columns.values; + if (columns.length == 1) { // show primary keys only + that.load_records(records_map.values); + return; + } + + if (that.search_all_entries) { + // map contains the primary keys and the complete records + that.load_records(records_map.values); + return; + } + + // get the complete records + that.get_records( + records_map.keys, + function(data, text_status, xhr) { + var results = data.result.results; + for (var i=0; i<records_map.length; i++) { + var pkey = records_map.keys[i]; + var record = records_map.get(pkey); + // merge the record obtained from the refresh() + // with the record obtained from get_records() + $.extend(record, results[i].result); + } + that.load_records(records_map.values); + }, + function(xhr, text_status, error_thrown) { + that.load_records([]); + var summary = that.table.summary.empty(); + summary.append(error_thrown.name+': '+error_thrown.message); + } + ); + }; + + that.load_records = function(records) { + that.table.empty(); + for (var i=0; i<records.length; i++) { + that.add_record(records[i]); + } + that.table.set_values(that.selected_values); + }; + + that.add_record = function(record) { + + var tr = that.table.add_record(record); + + var attribute; + if (that.row_enabled_attribute) { + attribute = that.row_enabled_attribute; + } else if (that.row_disabled_attribute) { + attribute = that.row_disabled_attribute; + } else { + return; + } + + var value = record[attribute]; + var column = that.table.get_column(attribute); + if (column.formatter) value = column.formatter.parse(value); + + that.table.set_row_enabled(tr, value); + }; + + that.get_records_command_name = function() { + return that.managed_entity.name+'_get_records'; + }; + + that.create_get_records_command = function(pkeys, on_success, on_error) { + + var batch = IPA.batch_command({ + name: that.get_records_command_name(), + on_success: on_success, + on_error: on_error + }); + + for (var i=0; i<pkeys.length; i++) { + var pkey = pkeys[i]; + + var command = IPA.command({ + entity: that.table.entity.name, + method: 'show', + args: [ pkey ], + options: { all: true } + }); + + batch.add_command(command); + } + + return batch; + }; + + that.get_records = function(pkeys, on_success, on_error) { + + var batch = that.create_get_records_command(pkeys, on_success, on_error); + + batch.execute(); + }; + + that.get_selected_values = function() { + return that.table.get_selected_values(); + }; + + that.init_table = function(entity) { + + that.table = IPA.table_widget({ + 'class': 'content-table', + name: that.table_name || entity.metadata.primary_key, + label: entity.metadata.label, + entity: entity, + pagination: true, + search_all_attributes: that.search_all_attributes, + scrollable: true, + selectable: that.selectable && !that.read_only + }); + + var columns = that.columns.values; + for (var i=0; i<columns.length; i++) { + var column = columns[i]; + + var metadata = IPA.get_entity_param(entity.name, column.name); + column.primary_key = metadata && metadata.primary_key; + column.link = (column.link === undefined ? true : column.link) && column.primary_key; + + if (column.link && column.primary_key) { + column.link_handler = function(value) { + IPA.nav.show_page(entity.name, that.details_facet_name, value); + return false; + }; + } + + that.table.add_column(column); + } + + that.table.select_changed = function() { + that.selected_values = that.get_selected_values(); + that.select_changed.notify([that.selected_values]); + }; + + that.table.prev_page = function() { + if (that.table.current_page > 1) { + var state = {}; + state[that.entity.name+'-page'] = that.table.current_page - 1; + that.set_expired_flag(); + IPA.nav.push_state(state); + } + }; + + that.table.next_page = function() { + if (that.table.current_page < that.table.total_pages) { + var state = {}; + state[that.entity.name+'-page'] = that.table.current_page + 1; + that.set_expired_flag(); + IPA.nav.push_state(state); + } + }; + + that.table.set_page = function(page) { + if (page < 1) { + page = 1; + } else if (page > that.total_pages) { + page = that.total_pages; + } + var state = {}; + state[that.entity.name+'-page'] = page; + that.set_expired_flag(); + IPA.nav.push_state(state); + }; + }; + + that.init_table_columns = function() { + var columns = spec.columns || []; + for (var i=0; i<columns.length; i++) { + that.create_column(columns[i]); + } + }; + + if (!no_init) that.init_table_columns(); + + that.table_facet_create_get_records_command = that.create_get_records_command; + + return that; +}; + +IPA.facet_group = function(spec) { + + spec = spec || {}; + + var that = {}; + + that.name = spec.name; + that.label = spec.label; + + that.facets = $.ordered_map(); + + that.add_facet = function(facet) { + that.facets.put(facet.name, facet); + }; + + that.get_facet = function(name) { + return that.facets.get(name); + }; + + that.get_facet_index = function(name) { + return that.facets.get_key_index(name); + }; + + that.get_facet_by_index = function(index) { + return that.facets.get_value_by_index(index); + }; + + that.get_facet_count = function(index) { + return that.facets.length; + }; + + return that; +}; + +IPA.facet_builder = function(entity) { + + var that = {}; + + that.prepare_methods = {}; + + function init() { + that.prepare_methods.search = that.prepare_search_spec; + that.prepare_methods.nested_search = that.prepare_nested_search_spec; + that.prepare_methods.details = that.prepare_details_spec; + that.prepare_methods.association = that.prepare_association_spec; + that.prepare_methods.attribute = that.prepare_attribute_spec; + } + + that.build_facets = function() { + + if(entity.facet_specs && entity.facet_specs.length) { + var facets = entity.facet_specs; + for(var i=0; i<facets.length; i++) { + var facet_spec = facets[i]; + that.build_facet(facet_spec); + } + } + }; + + that.build_facet = function(spec) { + + //do common logic + spec.entity = entity; + + //prepare spec based on type + var type = spec.type; + if (type) { + var prepare_method = that.prepare_methods[type]; + if (prepare_method) { + prepare_method.call(that, spec); + } + } + + //add facet + var facet = spec.factory(spec); + entity.add_facet(facet); + }; + + function add_redirect_info(facet_name) { + + facet_name = facet_name || 'search'; + if (!entity.redirect_facet){ + entity.redirect_facet = facet_name; + } + } + + that.prepare_search_spec = function(spec) { + + spec.title = spec.title || entity.metadata.label; + spec.label = spec.label || entity.metadata.label; + spec.tab_label = spec.tab_label || IPA.messages.facets.search; + spec.factory = spec.factory || IPA.search_facet; + + add_redirect_info(); + return spec; + }; + + that.prepare_nested_search_spec = function(spec) { + + spec.title = spec.title || entity.metadata.label_singular; + spec.label = spec.label || entity.metadata.label; + spec.tab_label = spec.tab_label || IPA.messages.facets.search; + spec.factory = spec.factory || IPA.nested_search_facet; + + return spec; + }; + + that.prepare_details_spec = function(spec) { + spec.title = spec.title || entity.metadata.label_singular; + spec.label = spec.label || entity.metadata.label_singular; + spec.tab_label = spec.tab_label || IPA.messages.facets.details; + spec.factory = spec.factory || IPA.details_facet; + + return spec; + }; + + that.prepare_attribute_spec = function(spec) { + spec.title = spec.title || entity.metadata.label_singular; + spec.label = spec.label || entity.metadata.label_singular; + + var attr_metadata = IPA.get_entity_param(entity.name, spec.attribute); + spec.tab_label = spec.tab_label || attr_metadata.label; + spec.factory = spec.factory || IPA.attribute_facet; + + entity.policies.add_policy(IPA.build({ + factory: IPA.facet_update_policy, + source_facet: 'search', + dest_facet: spec.name + })); + + return spec; + }; + + that.prepare_association_spec = function(spec) { + + spec.entity = entity; + + var index = spec.name.indexOf('_'); + spec.attribute_member = spec.attribute_member || + spec.name.substring(0, index); + spec.other_entity = spec.other_entity || + spec.name.substring(index+1); + + spec.add_title = IPA.messages.association.add[spec.attribute_member]; + spec.remove_title = IPA.messages.association.remove[spec.attribute_member]; + + spec.facet_group = spec.facet_group || spec.attribute_member; + + spec.factory = spec.factory || IPA.association_facet; + + spec.label = spec.label || entity.metadata.label_singular; + spec.tab_label = spec.tab_label || + (IPA.metadata.objects[spec.other_entity] ? + IPA.metadata.objects[spec.other_entity].label : spec.other_entity); + + if (that.has_indirect_attribute_member(spec)) { + + spec.indirect_attribute_member = spec.attribute_member + 'indirect'; + } + + if (spec.facet_group === 'memberindirect' || + spec.facet_group === 'memberofindirect') { + + spec.read_only = true; + } + + entity.policies.add_policy(IPA.build({ + factory: IPA.facet_update_policy, + source_facet: 'search', + dest_facet: spec.name + })); + + return spec; + }; + + that.has_indirect_attribute_member = function(spec) { + + var indirect_members = entity.metadata.attribute_members[spec.attribute_member + 'indirect']; + if (indirect_members) { + if (indirect_members.indexOf(spec.other_entity) > -1) { + return true; + } + } + return false; + }; + + init(); + + return that; +}; + +IPA.action = function(spec) { + + spec = spec || {}; + + var that = {}; + + that.name = spec.name; + that.label = spec.label; + + that.enabled = spec.enabled !== undefined ? spec.enabled : true; + that.enable_cond = spec.enable_cond || []; + that.disable_cond = spec.disable_cond || []; + that.enabled_changed = IPA.observer(); + + that.visible = spec.visible !== undefined ? spec.visible : true; + that.show_cond = spec.show_cond || []; + that.hide_cond = spec.hide_cond || []; + that.visible_changed = IPA.observer(); + + that.handler = spec.handler; + + that.needs_confirm = spec.needs_confirm !== undefined ? spec.needs_confirm : false; + that.confirm_msg = spec.confirm_msg || IPA.messages.actions.confirm; + + that.confirm_dialog = spec.confirm_dialog !== undefined ? spec.confirm_dialog : + IPA.confirm_dialog; + + + + that.execute_action = function(facet, on_success, on_error) { + + if (that.handler) { + that.handler(facet, on_success, on_error); + } + }; + + that.execute = function(facet, on_success, on_error) { + + if (!that.enabled || !that.visible) return; + + if (that.needs_confirm) { + + var confirmed = false; + + if (that.confirm_dialog) { + + that.dialog = IPA.build(that.confirm_dialog); + that.update_confirm_dialog(facet); + that.dialog.on_ok = function () { + that.execute_action(facet, on_success, on_error); + }; + that.dialog.open(); + } else { + var msg = that.get_confirm_message(facet); + confirmed = IPA.confirm(msg); + } + + if (!confirmed) return; + } + + that.execute_action(facet, on_success, on_error); + }; + + that.update_confirm_dialog = function(facet) { + that.dialog.message = that.get_confirm_message(facet); + }; + + that.get_confirm_message = function(facet) { + return that.confirm_msg; + }; + + that.set_enabled = function(enabled) { + + var old = that.enabled; + + that.enabled = enabled; + + if (old !== that.enabled) { + that.enabled_changed.notify([that.enabled], that); + } + }; + + that.set_visible = function(visible) { + + var old = that.visible; + + that.visible = visible; + + if (old !== that.visible) { + that.visible_changed.notify([that.visible], that); + } + }; + + return that; +}; + +IPA.action_builder = function(spec) { + + spec = spec || {}; + spec.factory = spec.factory || IPA.action; + var that = IPA.builder(spec); + return that; +}; + + +IPA.action_holder = function(spec) { + + spec = spec || {}; + + var that = {}; + + that.actions = $.ordered_map(); + + that.init = function(facet) { + + var i, action, actions; + + that.facet = facet; + actions = IPA.build(spec.actions, IPA.action_builder) || []; + + for (i=0; i<actions.length; i++) { + action = actions[i]; + that.actions.put(action.name, action); + } + + that.facet.action_state.changed.attach(that.state_changed); + that.facet.post_load.attach(that.on_load); + }; + + that.state_changed = function(state) { + + var actions, action, i, enabled, visible; + + actions = that.actions.values; + + for (i=0; i<actions.length; i++) { + + action = actions[i]; + + enabled = IPA.eval_cond(action.enable_cond, action.disable_cond, state); + visible = IPA.eval_cond(action.show_cond, action.hide_cond, state); + action.set_enabled(enabled); + action.set_visible(visible); + } + }; + + that.get = function(name) { + return that.actions.get(name); + }; + + that.add = function(action) { + that.actions.put(action.name, action); + }; + + that.on_load = function() { + var state = that.facet.action_state.get(); + that.state_changed(state); + }; + + return that; +}; + +IPA.action_holder_builder = function(spec) { + + spec = spec || {}; + spec.factory = spec.factory || IPA.action_holder; + var that = IPA.builder(spec); + return that; +}; + + +IPA.state = function(spec) { + + spec = spec || {}; + spec.summary_evaluator = spec.summary_evaluator || IPA.summary_evaluator; + + var that = {}; + + that.state = $.ordered_map(); + + //when state changes. Params: state, Context: this + that.changed = IPA.observer(); + + that.evaluators = IPA.build(spec.evaluators, IPA.state_evaluator_builder) || []; + that.summary_evaluator = IPA.build(spec.summary_evaluator); + + that.summary_conditions = spec.summary_conditions || []; + + that.init = function(facet) { + + var i, evaluator; + + that.facet = facet; + + for (i=0; i<that.evaluators.length; i++) { + evaluator = that.evaluators[i]; + evaluator.init(facet); + evaluator.changed.attach(that.on_eval_changed); + } + }; + + that.on_eval_changed = function() { + + var evaluator = this; + + that.state.put(evaluator.name, evaluator.state); + + that.notify(); + }; + + that.get = function() { + + var state, i; + + state = []; + + var values = that.state.values; + + for (i=0; i<values.length; i++) { + $.merge(state, values[i]); + } + + return state; + }; + + that.summary = function() { + + var summary = that.summary_evaluator.evaluate(that); + return summary; + }; + + that.notify = function(state) { + + state = state || that.get(); + + that.changed.notify([state], that); + }; + + return that; +}; + +IPA.summary_evaluator = function(spec) { + + spec = spec || {}; + + var that = {}; + + that.evaluate = function(state) { + + var conds, cond, i, summary, state_a; + + conds = state.summary_conditions; + state_a = state.get(); + + for (i=0; i<conds.length; i++) { + cond = conds[i]; + if (IPA.eval_cond(cond.pos, cond.neg, state_a)) { + summary = { + state: cond.state, + description: cond.description + }; + break; + } + } + + summary = summary || { + state: state_a, + description: '' + }; + + return summary; + }; + + return that; +}; + +IPA.state_evaluator = function(spec) { + + spec = spec || {}; + + var that = {}; + + that.name = spec.name || 'state_evaluator'; + that.event_name = spec.event; + + //when state changes. Params: state, Context: this + that.changed = IPA.observer(); + that.state = []; + that.first_pass = true; + + that.init = function(facet) { + + if (that.event_name && facet[that.event_name]) { + facet[that.event_name].attach(that.on_event); + } + }; + + that.on_event = function() { + }; + + that.notify_on_change = function(old_state) { + + if (that.first_pass || IPA.array_diff(that.state, old_state)) { + that.changed.notify([that.state], that); + that.first_pass = false; + } + }; + + return that; +}; + +IPA.state_evaluator_builder = function(spec) { + + spec = spec || {}; + spec.factory = spec.factory || IPA.state_evaluator; + var that = IPA.builder(spec); + return that; +}; + +IPA.dirty_state_evaluator = function(spec) { + + spec = spec || {}; + + spec.event = spec.event || 'dirty_changed'; + + var that = IPA.state_evaluator(spec); + that.name = spec.name || 'dirty_state_evaluator'; + + that.on_event = function(dirty) { + + var old_state = that.state; + that.state = []; + + if (dirty) { + that.state.push('dirty'); + } + + that.notify_on_change(old_state); + }; + + return that; +}; + +IPA.selected_state_evaluator = function(spec) { + + spec = spec || {}; + + spec.event = spec.event || 'select_changed'; + + var that = IPA.state_evaluator(spec); + that.name = spec.name || 'selected_state_evaluator'; + + that.on_event = function(selected) { + + var old_state = that.state; + that.state = []; + + if (selected && selected.length > 0) { + that.state.push('item-selected'); + } + + that.notify_on_change(old_state); + }; + + return that; +}; + +IPA.self_service_state_evaluator = function(spec) { + + spec = spec || {}; + + spec.event = spec.event || 'post_load'; + + var that = IPA.state_evaluator(spec); + that.name = spec.name || 'self_service_state_evaluator'; + + that.on_event = function() { + + var old_state = that.state; + that.state = []; + + var self_service = IPA.nav.name === 'self-service'; + + if (self_service) { + that.state.push('self-service'); + } + + that.notify_on_change(old_state); + }; + + return that; +}; + +IPA.facet_attr_state_evaluator = function(spec) { + + spec = spec || {}; + + spec.event = spec.event || 'post_load'; + + var that = IPA.state_evaluator(spec); + that.name = spec.name || 'facet_attr_se'; + that.attribute = spec.attribute; + that.value = spec.value; + that.state_value = spec.state_value; + + that.on_event = function() { + + var old_state = that.state; + that.state = []; + + var facet = this; + + if (facet[that.attribute] === that.value) { + that.state.push(that.state_value); + } + + that.notify_on_change(old_state); + }; + + return that; +}; + +IPA.read_only_state_evaluator = function(spec) { + + spec = spec || {}; + + spec.name = spec.name || 'read_only_se'; + spec.attribute = spec.attribute || 'read_only'; + spec.state_value = spec.state_value || 'read-only'; + spec.value = spec.value !== undefined ? spec.value : true; + + var that = IPA.facet_attr_state_evaluator(spec); + return that; +}; + +IPA.association_type_state_evaluator = function(spec) { + + + spec = spec || {}; + + spec.name = spec.name || 'association_type_se'; + spec.attribute = spec.attribute || 'association_type'; + spec.state_value = spec.state_value || 'direct'; + spec.value = spec.value !== undefined ? spec.value : 'direct'; + + var that = IPA.facet_attr_state_evaluator(spec); + return that; +}; + +IPA.action_button_widget = function(spec) { + + spec = spec || {}; + + var that = IPA.widget(spec); + + that.name = spec.name; + that.label = spec.label; + that.tooltip = spec.tooltip; + that.href = spec.href || that.name; + that.icon = spec.icon; + + that.action_name = spec.action || that.name; + + that.enabled = spec.enabled !== undefined ? spec.enabled : true; + that.visible = spec.visible !== undefined ? spec.visible : true; + + that.show_cond = spec.show_cond || []; + that.hide_cond = spec.hide_cond || []; + + that.init = function(facet) { + + that.facet = facet; + that.action = that.facet.actions.get(that.action_name); + that.action.enabled_changed.attach(that.set_enabled); + that.action.visible_changed.attach(that.set_visible); + }; + + that.create = function(container) { + + that.widget_create(container); + + that.button_element = IPA.action_button({ + name: that.name, + href: that.href, + label: that.label, + icon: that.icon, + click: function() { + that.on_click(); + return false; + } + }).appendTo(container); + + that.set_enabled(that.action.enabled); + that.set_visible(that.action.visible); + }; + + that.on_click = function() { + + if (!that.enabled) return; + + that.action.execute(that.facet); + }; + + that.set_enabled = function(enabled) { + that.enabled = enabled; + + if (that.button_element) { + if (enabled) { + that.button_element.removeClass('action-button-disabled'); + } else { + that.button_element.addClass('action-button-disabled'); + } + } + }; + + that.set_visible = function(visible) { + + that.visible = visible; + + if (that.button_element) { + if (visible) { + that.button_element.show(); + } else { + that.button_element.hide(); + } + } + }; + + return that; +}; + +IPA.action_button_widget_builder = function(spec) { + + spec = spec || {}; + spec.factory = spec.factory || IPA.action_button_widget; + var that = IPA.builder(spec); + return that; +}; + +IPA.control_buttons_widget = function(spec) { + + + spec = spec || {}; + + var that = IPA.widget(spec); + + that.buttons = IPA.build(spec.buttons, IPA.action_button_widget_builder) || []; + + that.init = function(facet) { + + var i; + + for (i=0; i<that.buttons.length; i++) { + + var button = that.buttons[i]; + button.init(facet); + } + }; + + that.create = function(container) { + + that.container = $('<div/>', { + 'class': that['class'] + }).appendTo(container); + + for (var i=0; i<that.buttons.length; i++) { + + var button = that.buttons[i]; + button.create(that.container); + } + }; + + return that; +}; + +IPA.eval_cond = function(enable_cond, disable_cond, state) { + + var i, cond; + + if (disable_cond) { + for (i=0; i<disable_cond.length; i++) { + cond = disable_cond[i]; + if (state.indexOf(cond) > -1) { + return false; + } + } + } + + if (enable_cond) { + for (i=0; i<enable_cond.length; i++) { + cond = enable_cond[i]; + if (state.indexOf(cond) < 0) { + return false; + } + } + } + + return true; +}; + + +IPA.action_list_widget = function(spec) { + + spec = spec || {}; + + spec.widgets = spec.widgets || [ + { + type: 'html', + css_class: 'separator' + }, + { + type: 'select', + name: 'action', + undo: false + }, + { + type: 'button', + name: 'apply', + label: IPA.messages.actions.apply + } + ]; + + var that = IPA.composite_widget(spec); + + that.action_names = spec.actions || []; + that.actions = $.ordered_map(); + + that.init = function(facet) { + + var options, actions, action, name, i; + + that.facet = facet; + + that.action_select = that.widgets.get_widget('action'); + that.apply_button = that.widgets.get_widget('apply'); + + that.action_select.value_changed.attach(that.on_action_change); + that.apply_button.click = that.on_apply; + + for (i=0; i<that.action_names.length; i++) { + name = that.action_names[i]; + action = that.facet.actions.get(name); + that.add_action(action, true); + } + + that.init_options(); + }; + + that.add_action = function(action, batch) { + that.actions.put(action.name, action); + action.enabled_changed.attach(that.action_enabled_changed); + action.visible_changed.attach(that.action_visible_changed); + + if(!batch) { + that.init_options(); + that.recreate_options(); + that.select_first_enabled(); + } + }; + + that.init_options = function() { + + var options, actions, action, i; + + options = []; + actions = that.actions.values; + + for (i=0; i< actions.length; i++) { + action = actions[i]; + options.push({ + label: action.label, + value: action.name + }); + } + + that.action_select.options = options; + }; + + that.recreate_options = function() { + + that.action_select.create_options(); + }; + + that.on_action_change = function() { + + var action = that.get_selected(); + that.apply_button.set_enabled(action.enabled); + }; + + that.on_apply = function() { + + var action = that.get_selected(); + + if (action.enabled) { + action.execute(that.facet, + that.on_action_success, + that.on_action_error); + } + }; + + that.on_action_success = function() { + + that.facet.refresh(); + }; + + that.on_action_error = function() { + + that.facet.refresh(); + }; + + that.action_enabled_changed = function(enabled) { + var action = this; + var selected_action = that.get_selected(); + that.action_select.set_options_enabled(action.enabled, [action.name]); + + if (!action.enabled && action === selected_action) { + that.select_first_enabled(); + } + }; + + that.get_selected = function() { + var selected = that.action_select.save()[0]; + var action = that.actions.get(selected); + return action; + }; + + that.get_disabled = function() { + + var disabled = []; + var actions = that.action.values; + + for (var i=0; i< actions.length; i++) { + var action = actions[i]; + if (!that.action.enabled) { + disabled.push(action.name); + } + } + + return disabled; + }; + + that.select_first_enabled = function() { + + var actions = that.actions.values; + + var first = actions[0].name; + + for (var i=0; i< actions.length; i++) { + var action = actions[i]; + if (action.enabled) { + first = action.name; + break; + } + } + + that.action_select.update([first]); + }; + + that.clear = function() { + + that.select_first_enabled(); + }; + + return that; +};
\ No newline at end of file diff --git a/install/ui/src/freeipa/field.js b/install/ui/src/freeipa/field.js new file mode 100644 index 000000000..ff807dcfc --- /dev/null +++ b/install/ui/src/freeipa/field.js @@ -0,0 +1,951 @@ +/*jsl:import ipa.js */ +/* Authors: + * Endi Sukma Dewata <edewata@redhat.com> + * Adam Young <ayoung@redhat.com> + * Pavel Zuna <pzuna@redhat.com> + * Petr Vobornik <pvoborni@redhat.com> + * + * Copyright (C) 2011 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/>. + */ + +/* REQUIRES: ipa.js, widget.js */ + +IPA.field = function(spec) { + spec = spec || {}; + + var that = {}; + + that.entity = IPA.get_entity(spec.entity); + that.container = null; + that.name = spec.name; + that.param = spec.param || spec.name; + that.label = spec.label; + that.tooltip = spec.tooltip; + that.measurement_unit = spec.measurement_unit; + that.formatter = spec.formatter; + + that.widget = null; + that.widget_name = spec.widget; + + // override the required flag in metadata + that.required = spec.required; + + // read_only is set when widget is created + that.read_only = spec.read_only; + + // writable is set during load + that.writable = true; + + that.enabled = spec.enabled === undefined ? true : spec.enabled; + that.flags = spec.flags || []; + + that.undo = spec.undo === undefined ? true : spec.undo; + + that.metadata = spec.metadata; + that.validators = spec.validators || []; + + that.priority = spec.priority; + + that.values = []; + that.dirty = false; + that.valid = true; + + that.dirty_changed = IPA.observer(); + + var init = function() { + if (!that.metadata && that.entity) { + that.metadata = IPA.get_entity_param(that.entity.name, that.param); + } + if (that.metadata) { + if (that.label === undefined) { + that.label = that.metadata.label; + } + if (that.tooltip === undefined) { + that.tooltip = that.metadata.doc; + } + } + + that.validators.push(IPA.metadata_validator()); + }; + + that.is_required = function() { + if (that.read_only) return false; + if (!that.writable) return false; + + if (that.required !== undefined) return that.required; + return that.metadata && that.metadata.required; + }; + + that.set_required = function(required) { + that.required = required; + + that.update_required(); + }; + + that.update_required = function() { + if(that.widget && that.widget.set_required) { + that.widget.set_required(that.is_required()); + } + }; + + that.validate_required = function() { + var values = that.save(); + if (IPA.is_empty(values) && that.is_required() && that.enabled) { + that.valid = false; + var message = IPA.get_message('widget.validation.required', + "Required field"); + that.show_error(message); + return false; + } + return true; + }; + + /** + * 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. + */ + that.validate = function() { + that.hide_error(); + that.valid = true; + + if (!that.enabled) return that.valid; + + var values = that.save(); + + if (IPA.is_empty(values)) { + return that.valid; + } + + var value = values[0]; + + for (var i=0; i<that.validators.length; i++) { + var validation_result = that.validators[i].validate(value, that); + that.valid = validation_result.valid; + if (!that.valid) { + that.show_error(validation_result.message); + break; + } + } + + return that.valid; + }; + + /** + * This function stores the entire record and the values + * of the field, then invoke reset() to update the UI. + */ + that.load = function(record) { + that.record = record; + + that.values = that.get_value(record, that.param); + + that.load_writable(record); + + that.reset(); + }; + + that.get_value = function(record, name) { + + var value = record[name]; + + if (!(value instanceof Array)) { + value = value !== undefined ? [value] : []; + } + + if (!value.length) { + value = ['']; + } + + return value; + }; + + that.load_writable = function(record) { + + that.writable = true; + + if (that.metadata) { + if (that.metadata.primary_key) { + that.writable = false; + } + + if (that.metadata.flags && 'no_update' in that.metadata.flags) { + that.writable = false; + } + } + + if (record.attributelevelrights) { + var rights = record.attributelevelrights[that.param]; + var oc_rights= record.attributelevelrights['objectclass']; + var write_oc = oc_rights && oc_rights.indexOf('w') > -1; + + // Some objects in LDAP may not have set proper object class and + // therefore server doesn't send proper attribute rights. Flag + // 'w_if_no_aci' should be used when we want to ensure that UI + // shows edit interface in such cases. Usable only when user can + // modify object classes. + // For all others, lack of rights means no write. + if ((!rights && !(that.flags.indexOf('w_if_no_aci') > -1 && write_oc)) || + (rights && rights.indexOf('w') < 0)) { + that.writable = false; + } + } + }; + + that.reset = function() { + that.set_widget_flags(); + that.update_required(); + that.update(); + that.validate(); + that.set_dirty(false); + }; + + that.update = function() { + + if (!that.widget || !that.widget.update) return; + + var formatted_values; + + // The formatter is currently only used on read-only fields only + // because it cannot parse formatted values back to internal values. + if (that.formatter && that.read_only) { + formatted_values = []; + for (var i=0; that.values && i<that.values.length; i++) { + var value = that.values[i]; + var formatted_value = that.formatter.format(value); + formatted_values.push(formatted_value); + } + } else { + formatted_values = that.values; + } + + that.widget.update(formatted_values); + }; + + that.get_update_info = function() { + + var update_info = IPA.update_info_builder.new_update_info(); + if (that.is_dirty()) { + var values = that.save(); + var field_info = IPA.update_info_builder.new_field_info(that, values); + update_info.fields.push(field_info); + } + return update_info; + }; + + /** + * 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. + */ + that.save = function(record) { + + var values = that.values; + + if(!that.enabled) return ['']; + + if(that.widget) { + values = that.widget.save(); + } + + if(record) { + record[that.param] = values; + } + + return values; + }; + + /** + * This function compares the original values and the + * values entered in the UI. If the values have changed + * it will return true. + */ + that.test_dirty = function() { + + if (that.read_only) return false; + + var values = that.save(); + + //check for empty value: null, [''], '', [] + var orig_empty = IPA.is_empty(that.values); + var new_empty= IPA.is_empty(values); + if (orig_empty && new_empty) return false; + if (orig_empty != new_empty) return true; + + //strict equality - checks object's ref equality, numbers, strings + if (values === that.values) return false; + + //compare values in array + if (values.length !== that.values.length) return true; + + return !that.dirty_are_equal(that.values, values); + }; + + that.dirty_are_equal = function(orig_vals, new_vals) { + + orig_vals.sort(); + new_vals.sort(); + + for (var i=0; i<orig_vals.length; i++) { + if (orig_vals[i] !== new_vals[i]) { + return false; + } + } + + return true; + }; + + /** + * This function compares the original values and the + * values entered in the UI. If the values have changed + * it will return true. + */ + that.is_dirty = function() { + return that.dirty; + }; + + that.set_dirty = function(dirty) { + var old = that.dirty; + that.dirty = dirty; + if (that.undo) { + that.show_undo(dirty); + } + + if (old !== dirty) { + that.dirty_changed.notify([], that); + } + }; + + + that.show_error = function(message) { + if (that.widget && that.widget.show_error) that.widget.show_error(message); + }; + + that.hide_error = function() { + if (that.widget && that.widget.hide_error) that.widget.hide_error(); + }; + + that.show_undo = function(value) { + if (that.widget && that.widget.show_undo) { + if(value) { that.widget.show_undo(); } + else { that.widget.hide_undo(); } + } + }; + + that.set_enabled = function(value) { + that.enabled = value; + if (that.widget && that.widget.set_enabled) { + that.widget.set_enabled(value); + } + }; + + that.refresh = function() { + }; + + that.set_widget_flags = function() { + + if (that.widget) { + if (that.label) that.widget.label = that.label; + if (that.tooltip) that.widget.tooltip = that.tooltip; + if (that.measurement_unit) that.widget.measurement_unit = that.measurement_unit; + that.widget.undo = that.undo; + that.widget.writable = that.writable; + that.widget.read_only = that.read_only; + } + }; + + that.widgets_created = function() { + + that.widget = that.container.widgets.get_widget(that.widget_name); + + if(that.widget) { + that.set_widget_flags(); + + that.widget.value_changed.attach(that.widget_value_changed); + that.widget.undo_clicked.attach(that.widget_undo_clicked); + } + }; + + that.widget_value_changed = function() { + that.set_dirty(that.test_dirty()); + that.validate(); + }; + + that.widget_undo_clicked = function() { + that.reset(); + }; + + init(); + + // methods that should be invoked by subclasses + that.field_dirty_are_equal = that.dirty_are_equal; + that.field_load = that.load; + that.field_reset = that.reset; + that.field_save = that.save; + that.field_set_dirty = that.set_dirty; + that.field_show_error = that.show_error; + that.field_test_dirty = that.test_dirty; + that.field_widgets_created = that.widgets_created; + + return that; +}; + +IPA.validator = function(spec) { + + spec = spec || {}; + + var that = {}; + + that.message = spec.message || IPA.get_message('widget.validation.error'); + + that.false_result = function(message) { + return { + valid: false, + message: message || that.message + }; + }; + + that.true_result = function() { + return { + valid: true + }; + }; + + that.validate = function() { + return that.true_result(); + }; + + return that; +}; + +IPA.metadata_validator = function(spec) { + + var that = IPA.validator(spec); + + that.validate = function(value, context) { + + var message; + var metadata = context.metadata; + var number = false; + + if (!metadata || IPA.is_empty(value)) return that.true_result(); + + if (metadata.type === 'int') { + number = true; + if (!value.match(/^-?\d+$/)) { + return that.false_result(IPA.messages.widget.validation.integer); + } + } else if (metadata.type === 'Decimal') { + number = true; + if (!value.match(/^-?\d+(\.\d+)?$/)) { + return that.false_result(IPA.messages.widget.validation.decimal); + } + } + + if (number) { + + if (IPA.defined(metadata.minvalue, true) && Number(value) < Number(metadata.minvalue)) { + message = IPA.messages.widget.validation.min_value; + message = message.replace('${value}', metadata.minvalue); + return that.false_result(message); + } + + if (IPA.defined(metadata.maxvalue, true) && Number(value) > Number(metadata.maxvalue)) { + message = IPA.messages.widget.validation.max_value; + message = message.replace('${value}', metadata.maxvalue); + return that.false_result(message); + } + } + + if (metadata.pattern) { + var regex = new RegExp(metadata.pattern); + if (!value.match(regex)) { + return that.false_result(metadata.pattern_errmsg); + } + } + + return that.true_result(); + }; + + return that; +}; + +IPA.unsupported_validator = function(spec) { + + var that = IPA.validator(spec); + + that.unsupported = spec.unsupported || []; + that.message = spec.message || IPA.messages.widget.validation.unsupported; + + that.validate = function(value, context) { + + if (IPA.is_empty(value)) return that.true_result(); + + if (that.unsupported.indexOf(value) > -1) return that.false_result(); + + return that.true_result(); + }; + + return that; +}; + +IPA.same_password_validator = function(spec) { + + spec = spec || {}; + + var that = IPA.validator(spec); + that.other_field = spec.other_field; + + that.message = spec.message || IPA.get_message('password.password_must_match', + "Passwords must match"); + that.validate = function(value, context) { + + var other_field = context.container.fields.get_field(that.other_field); + var other_value = other_field.save(); + var this_value = context.save(); + + if (IPA.array_diff(this_value, other_value)) return that.false_result(); + + return that.true_result(); + }; + + return that; +}; + +IPA.checkbox_field = function(spec) { + + spec = spec || {}; + + var that = IPA.field(spec); + + that.checked = spec.checked || false; + that.boolean_formatter = IPA.boolean_formatter(); + + that.load = function(record) { + + that.record = record; + + that.values = that.get_value(record, that.param); + + var value = that.boolean_formatter.parse(that.values); + if (value === '') value = that.widget.checked; //default value + + that.values = [value]; + + that.load_writable(record); + + that.reset(); + }; + + 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 + that.is_required = function() { + return false; + }; + + that.checkbox_load = that.load; + + return that; +}; + +IPA.checkboxes_field = function(spec) { + + spec = spec || {}; + + var that = IPA.field(spec); + + return that; +}; + +IPA.radio_field = function(spec) { + + spec = spec || {}; + + var that = IPA.field(spec); + + // a radio will always have a value, so it's never required + that.is_required = function() { + return false; + }; + + that.widgets_created = function() { + + that.field_widgets_created(); + }; + + return that; +}; + +IPA.multivalued_field = function(spec) { + + spec = spec || {}; + + var that = IPA.field(spec); + + that.load = function(record) { + + that.field_load(record); + }; + + that.test_dirty = function() { + var dirty = that.field_test_dirty(); + dirty = dirty || that.widget.test_dirty(); //also checks order + return dirty; + }; + + that.validate = function() { + + var values = that.save(); + + return that.validate_core(values); + }; + + that.validate_core = function(values) { + + that.hide_error(); + that.valid = true; + + if (IPA.is_empty(values)) { + return that.valid; + } + + for (var i=0; i<values.length; i++) { + + for (var j=0; j<that.validators.length; j++) { + + var validation_result = that.validators[j].validate(values[i], that); + if (!validation_result.valid) { + that.valid = false; + var row_index = that.widget.get_saved_value_row_index(i); + that.widget.show_child_error(row_index, validation_result.message); + break; + } + } + } + + return that.valid; + }; + + return that; +}; + +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. + that.flags = spec.flags || ['w_if_no_aci']; + + that.sshfp_attr = spec.sshfp_attr || 'sshpubkeyfp'; + + that.load = function(record) { + + var keys = that.get_value(record, that.param); + var fingerprints = that.get_value(record, that.sshfp_attr); + + var values = []; + + if (keys.length === fingerprints.length) { + for (var i=0; i<keys.length; i++) { + + if (keys[i] === '') continue; + + var value = { + key: keys[i], + fingerprint: fingerprints[i] + }; + values.push(value); + } + } + + that.values = values; + + that.load_writable(record); + + that.reset(); + }; + + that.dirty_are_equal = function(orig_vals, new_vals) { + + var i; + var orig_keys = []; + + for (i=0; i<orig_vals.length; i++) { + orig_keys.push(orig_vals[i].key); + } + + return that.field_dirty_are_equal(orig_keys, new_vals); + }; + + return that; +}; + +IPA.select_field = function(spec) { + + spec = spec || {}; + + var that = IPA.field(spec); + + that.widgets_created = function() { + + that.field_widgets_created(); + }; + + return that; +}; + + +IPA.combobox_field = function(spec) { + + spec = spec || {}; + + var that = IPA.field(spec); + + that.widgets_created = function() { + + that.field_widgets_created(); + that.widget.input_field_changed.attach(that.on_input_field_changed); + }; + + that.on_input_field_changed = function() { + that.validate(); + }; + + return that; +}; + +IPA.link_field = function(spec) { + + spec = spec || {}; + + var that = IPA.field(spec); + + that.other_entity = IPA.get_entity(spec.other_entity); + + function other_pkeys () { + return that.entity.get_primary_key(); + } + that.other_pkeys = spec.other_pkeys || other_pkeys; + + that.on_link_clicked = function() { + + IPA.nav.show_entity_page( + that.other_entity, + 'default', + that.other_pkeys()); + }; + + that.load = function(record) { + + that.field_load(record); + that.check_entity_link(); + }; + + that.check_entity_link = function() { + + //In some cases other entity may not be present. + //For example when DNS is not configured. + if (!that.other_entity) { + that.widget.is_link = false; + that.widget.update(that.values); + return; + } + + IPA.command({ + entity: that.other_entity.name, + method: 'show', + args: that.other_pkeys(), + options: {}, + retry: false, + on_success: function(data) { + that.widget.is_link = data.result && data.result.result; + that.widget.update(that.values); + }, + on_error: function() { + that.widget.is_link = false; + that.widget.update(that.values); + } + }).execute(); + }; + + that.widgets_created = function() { + that.field_widgets_created(); + that.widget.link_clicked.attach(that.on_link_clicked); + }; + + + return that; +}; + +IPA.enable_field = function(spec) { + + spec = spec || {}; + + var that = IPA.radio_field(spec); + + that.enable_method = spec.enable_method || 'enable'; + that.disable_method = spec.enable_method || 'disable'; + that.enable_option = spec.enable_option || 'TRUE'; + + that.get_update_info = function() { + + var info = IPA.update_info_builder.new_update_info(); + if(that.test_dirty()) { + var values = that.save(); + var method = that.disable_method; + + if(values[0] === that.enable_option) { + method = that.enable_method; + } + + var command = IPA.command({ + entity: that.entity.name, + method: method, + args: that.entity.get_primary_key(), + options: {all: true, rights: true} + }); + + + info.append_command(command, that.priority); + } + + return info; + }; + + return that; +}; + +// TODO: Add support for nested fields +IPA.field_container = function(spec) { + + spec = spec || {}; + + var that = {}; + + that.container = spec.container; //usually facet or dialog + + that.fields = $.ordered_map(); + + that.get_field = function(name) { + return that.fields.get(name); + }; + + that.get_fields = function(name) { + return that.fields.values; + }; + + that.add_field = function(field) { + field.container = that.container; + that.fields.put(field.name, field); + }; + + that.widgets_created = function() { + var fields = that.fields.values; + + for (var i=0; i<fields.length; i++) { + fields[i].widgets_created(); + } + }; + + that.container_add_field = that.add_field; + + return that; +}; + +IPA.field_builder = function(spec) { + + spec = spec || {}; + + var that = {}; + + that.default_factory = spec.default_factory || IPA.field; + that.container = spec.container; + that.field_options = spec.field_options || {}; + + that.get_field_factory = function(spec) { + + var factory; + if (spec.factory) { + factory = spec.factory; + } else if(spec.type) { + factory = IPA.field_factories[spec.type]; + } + + if (!factory) { + factory = that.default_factory; + } + + return factory; + }; + + that.build_field = function(spec, container) { + + container = container || that.container; + + if(!(spec instanceof Object)) { + spec = { name: spec }; + } + + if(that.field_options) { + $.extend(spec, that.field_options); + } + + var factory = that.get_field_factory(spec); + + var field = factory(spec); + + if(container) { + container.add_field(field); + } + + return field; + }; + + that.build_fields = function(specs, container) { + + container = container || that.container; + + for(var i=0; i<specs.length; i++) { + that.build_field(specs[i], container); + } + }; + + return that; +}; + +IPA.field_factories['checkbox'] = IPA.checkbox_field; +IPA.field_factories['checkboxes'] = IPA.checkboxes_field; +IPA.field_factories['combobox'] = IPA.combobox_field; +IPA.field_factories['enable'] = IPA.enable_field; +IPA.field_factories['entity_select'] = IPA.combobox_field; +IPA.field_factories['field'] = IPA.field; +IPA.field_factories['link'] = IPA.link_field; +IPA.field_factories['multivalued'] = IPA.multivalued_field; +IPA.field_factories['password'] = IPA.field; +IPA.field_factories['radio'] = IPA.radio_field; +IPA.field_factories['select'] = IPA.select_field; +IPA.field_factories['sshkeys'] = IPA.sshkeys_field; +IPA.field_factories['textarea'] = IPA.field; +IPA.field_factories['text'] = IPA.field; +IPA.field_factories['value_map'] = IPA.field;
\ No newline at end of file diff --git a/install/ui/src/freeipa/group.js b/install/ui/src/freeipa/group.js new file mode 100644 index 000000000..8317d679f --- /dev/null +++ b/install/ui/src/freeipa/group.js @@ -0,0 +1,259 @@ +/*jsl:import ipa.js */ + +/* Authors: + * Pavel Zuna <pzuna@redhat.com> + * Endi Dewata <edewata@redhat.com> + * Adam Young <ayoung@redhat.com> + * + * Copyright (C) 2010 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/>. + */ + +/* REQUIRES: ipa.js, details.js, search.js, add.js, facet.js, entity.js */ + +IPA.group = {}; + +IPA.group.entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + that.entity_init(); + + that.builder.search_facet({ + columns: [ + 'cn', + 'gidnumber', + 'description' + ] + }). + details_facet({ + sections: [ + { + name: 'details', + fields: [ + 'cn', + { + type: 'textarea', + name: 'description' + }, + { + type: 'value_map', + name: 'external', + param: 'objectclass', + label: IPA.messages.objects.group.type, + default_label: IPA.messages.objects.group.normal, + value_map: { + ipaexternalgroup: IPA.messages.objects.group.external, + posixgroup: IPA.messages.objects.group.posix + } + }, + 'gidnumber' + ] + } + ], + actions: [ + IPA.select_action, + IPA.group.make_posix_action, + IPA.group.make_external_action, + IPA.delete_action + ], + header_actions: ['select_action', 'make_posix', 'make_external', 'delete'], + state: { + evaluators: [ + IPA.object_class_evaluator + ] + } + }). + association_facet({ + name: 'member_user', + columns:[ + 'uid', + 'uidnumber', + 'mail', + 'telephonenumber', + 'title' + ], + adder_columns:[ + { + name: 'cn', + width: '100px' + }, + { + name: 'uid', + primary_key: true, + width: '100px' + } + ] + }). + association_facet({ + name: 'member_group' + }). + attribute_facet({ + name: 'member_external', + attribute: 'ipaexternalmember', + tab_label: 'External', + facet_group: 'member', + columns: [ + { + name: 'ipaexternalmember', + label: IPA.get_command_option('group_add_member', 'ipaexternalmember').label + } + ] + + }). + association_facet({ + name: 'memberof_group', + associator: IPA.serial_associator + }). + association_facet({ + name: 'memberof_netgroup', + associator: IPA.serial_associator + }). + association_facet({ + name: 'memberof_role', + associator: IPA.serial_associator + }). + association_facet({ + name: 'memberof_hbacrule', + associator: IPA.serial_associator, + add_method: 'add_user', + remove_method: 'remove_user' + }). + association_facet({ + name: 'memberof_sudorule', + associator: IPA.serial_associator, + add_method: 'add_user', + remove_method: 'remove_user' + }). + standard_association_facets(). + adder_dialog({ + factory: IPA.group_adder_dialog, + fields: [ + 'cn', + { + type: 'textarea', + name: 'description' + }, + { + type: 'radio', + name: 'type', + label: IPA.messages.objects.group.type, + flags: ['no_command'], + default_value: 'posix', + options: [ + { + value: 'normal', + label: IPA.messages.objects.group.normal + }, + { + value: 'external', + label: IPA.messages.objects.group.external + }, + { + value: 'posix', + label: IPA.messages.objects.group.posix + } + ] + }, + 'gidnumber' + ] + }); + }; + + return that; +}; + +IPA.group_adder_dialog = function(spec) { + + spec = spec || {}; + + var that = IPA.entity_adder_dialog(spec); + + var init = function() { + + var type_field = that.fields.get_field('type'); + type_field.widget.value_changed.attach(that.on_type_change); + }; + + that.on_type_change = function(value) { + + var gid_field = that.fields.get_field('gidnumber'); + var external_field = that.fields.get_field('external'); + + var posix = value[0] === 'posix'; + + if (!posix) { + gid_field.reset(); + } + + gid_field.set_enabled(posix); + }; + + that.create_add_command = function(record) { + + var command = that.entity_adder_dialog_create_add_command(record); + + var type_field = that.fields.get_field('type'); + var type = type_field.save()[0]; + + if (type === 'normal') { + command.set_option('nonposix', true); + } else if (type === 'external') { + command.set_option('external', true); + } + + return command; + }; + + init(); + + return that; +}; + +IPA.group.make_posix_action = function(spec) { + + spec = spec || {}; + spec.name = spec.name || 'make_posix'; + spec.method = spec.method || 'mod'; + spec.label = spec.label || IPA.messages.objects.group.make_posix; + spec.disable_cond = spec.disable_cond || ['oc_posixgroup', 'oc_ipaexternalgroup']; + spec.options = spec.options || { + posix: true + }; + + var that = IPA.object_action(spec); + + return that; +}; + +IPA.group.make_external_action = function(spec) { + + spec = spec || {}; + spec.name = spec.name || 'make_external'; + spec.method = spec.method || 'mod'; + spec.label = spec.label || IPA.messages.objects.group.make_external; + spec.disable_cond = spec.disable_cond || ['oc_posixgroup','oc_ipaexternalgroup']; + spec.options = spec.options || { + external: true + }; + + var that = IPA.object_action(spec); + + return that; +}; + +IPA.register('group', IPA.group.entity); diff --git a/install/ui/src/freeipa/hbac.js b/install/ui/src/freeipa/hbac.js new file mode 100644 index 000000000..63c928aab --- /dev/null +++ b/install/ui/src/freeipa/hbac.js @@ -0,0 +1,553 @@ +/*jsl:import ipa.js */ + +/* Authors: + * Endi Sukma Dewata <edewata@redhat.com> + * Adam Young <ayoung@redhat.com> + * + * Copyright (C) 2010 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/>. + */ + +/* REQUIRES: ipa.js, details.js, search.js, add.js, facet.js, entity.js */ + +IPA.hbac = { + //priority of commands in details facet + remove_method_priority: IPA.config.default_priority - 1 +}; + +IPA.hbac.rule_entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + that.entity_init(); + + that.builder.search_facet({ + row_enabled_attribute: 'ipaenabledflag', + search_all_attributes: true, + columns: [ + 'cn', + { + name: 'ipaenabledflag', + label: IPA.messages.status.label, + formatter: IPA.boolean_status_formatter() + }, + 'description' + ], + actions: [ + IPA.batch_disable_action, + IPA.batch_enable_action + ], + control_buttons: [ + { + name: 'disable', + label: IPA.messages.buttons.disable, + icon: 'disabled-icon' + }, + { + name: 'enable', + label: IPA.messages.buttons.enable, + icon: 'enabled-icon' + } + ] + }). + details_facet({ + factory: IPA.hbacrule_details_facet, + entity: that, + command_mode: 'info', + actions: [ + IPA.select_action, + IPA.enable_action, + IPA.disable_action, + IPA.delete_action + ], + header_actions: ['select_action', 'enable', 'disable', 'delete'], + state: { + evaluators: [ + { + factory: IPA.enable_state_evaluator, + field: 'ipaenabledflag' + } + ], + summary_conditions: [ + IPA.enabled_summary_cond(), + IPA.disabled_summary_cond() + ] + } + }). + adder_dialog({ + fields: [ 'cn' ] + }); + }; + + return that; +}; + +IPA.hbac.service_entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + that.entity_init(); + + that.builder.search_facet({ + columns: [ + 'cn', + 'description' + ] + }). + details_facet({ + sections: [ + { + name: 'general', + label: IPA.messages.details.general, + fields: [ + 'cn', + { + type: 'textarea', + name: 'description' + } + ] + } + ] + }). + association_facet({ + name: 'memberof_hbacsvcgroup', + associator: IPA.serial_associator, + columns:[ + 'cn', + 'description' + ], + adder_columns: [ + { + name: 'cn', + primary_key: true, + width: '100px' + }, + { + name: 'description', + width: '100px' + } + ] + }). + standard_association_facets(). + adder_dialog({ + fields: [ + 'cn', + { + type: 'textarea', + name: 'description' + } + ] + }); + }; + + return that; +}; + +IPA.hbac.service_group_entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + that.entity_init(); + + that.builder.search_facet({ + columns: [ + 'cn', + 'description' + ] + }). + details_facet({ + sections: [ + { + name: 'general', + label: IPA.messages.details.general, + fields: [ + 'cn', + { + type: 'textarea', + name: 'description' + } + ] + } + ] + }). + association_facet({ + name: 'member_hbacsvc', + columns:[ + 'cn', + 'description' + ], + adder_columns: [ + { + name: 'cn', + primary_key: true, + width: '100px' + }, + { + name: 'description', + width: '100px' + } + ] + }). + standard_association_facets(). + adder_dialog({ + fields: [ + 'cn', + { + type: 'textarea', + name: 'description' + } + ] + }); + }; + + return that; +}; + +IPA.hbacrule_details_facet = function(spec) { + + var entity_name = spec.entity.name; + + // + // General + // + + spec.fields = [ + { + name: 'cn', + read_only: true, + widget: 'general.cn' + }, + { + type: 'textarea', + name: 'description', + widget: 'general.description' + } + ]; + + spec.widgets = [ + { + type: 'details_table_section', + name: 'general', + label: IPA.messages.details.general, + widgets: [ + { + name: 'cn' + }, + { + type: 'textarea', + name: 'description' + } + ] + } + ]; + + // + // Users + // + + spec.fields.push( + { + type: 'radio', + name: 'usercategory', + widget: 'user.rule.usercategory' + }, + { + type: 'rule_association_table', + name: 'memberuser_user', + widget: 'user.rule.memberuser_user', + priority: IPA.hbac.remove_method_priority + }, + { + type: 'rule_association_table', + name: 'memberuser_group', + widget: 'user.rule.memberuser_group', + priority: IPA.hbac.remove_method_priority + } + ); + + spec.widgets.push( + { + factory: IPA.collapsible_section, + name: 'user', + label: IPA.messages.objects.hbacrule.user, + widgets: [ + { + factory: IPA.rule_details_widget, + name: 'rule', + radio_name: 'usercategory', + options: [ + { value: 'all', + label: IPA.messages.objects.hbacrule.anyone }, + { value: '', + label: IPA.messages.objects.hbacrule.specified_users } + ], + tables: [ + { name: 'memberuser_user' }, + { name: 'memberuser_group' } + ], + widgets: [ + { + type: 'rule_association_table', + id: entity_name+'-memberuser_user', + name: 'memberuser_user', + add_method: 'add_user', + remove_method: 'remove_user', + add_title: IPA.messages.association.add.member, + remove_title: IPA.messages.association.remove.member + }, + { + type: 'rule_association_table', + id: entity_name+'-memberuser_group', + name: 'memberuser_group', + add_method: 'add_user', + remove_method: 'remove_user', + add_title: IPA.messages.association.add.member, + remove_title: IPA.messages.association.remove.member + } + ] + } + ] + } + ); + + // + // Hosts + // + + spec.fields.push( + { + type: 'radio', + name: 'hostcategory', + widget: 'host.rule.hostcategory' + }, + { + type: 'rule_association_table', + name: 'memberhost_host', + widget: 'host.rule.memberhost_host', + priority: IPA.hbac.remove_method_priority + }, + { + type: 'rule_association_table', + name: 'memberhost_hostgroup', + widget: 'host.rule.memberhost_hostgroup', + priority: IPA.hbac.remove_method_priority + } + ); + + spec.widgets.push( + { + factory: IPA.collapsible_section, + name: 'host', + label: IPA.messages.objects.hbacrule.host, + widgets: [ + { + factory: IPA.rule_details_widget, + name: 'rule', + radio_name: 'hostcategory', + options: [ + { + 'value': 'all', + 'label': IPA.messages.objects.hbacrule.any_host + }, + { + 'value': '', + 'label': IPA.messages.objects.hbacrule.specified_hosts + } + ], + tables: [ + { 'name': 'memberhost_host' }, + { 'name': 'memberhost_hostgroup' } + ], + widgets: [ + { + type: 'rule_association_table', + id: entity_name+'-memberuser_user', + name: 'memberhost_host', + add_method: 'add_host', + remove_method: 'remove_host', + add_title: IPA.messages.association.add.member, + remove_title: IPA.messages.association.remove.member + }, + { + type: 'rule_association_table', + id: entity_name+'-memberuser_group', + name: 'memberhost_hostgroup', + add_method: 'add_host', + remove_method: 'remove_host', + add_title: IPA.messages.association.add.member, + remove_title: IPA.messages.association.remove.member + } + ] + } + ] + } + ); + + // + // Service + // + + spec.fields.push( + { + type: 'radio', + name: 'servicecategory', + widget: 'service.rule.servicecategory' + }, + { + type: 'rule_association_table', + name: 'memberservice_hbacsvc', + widget: 'service.rule.memberservice_hbacsvc', + priority: IPA.hbac.remove_method_priority + }, + { + type: 'rule_association_table', + name: 'memberservice_hbacsvcgroup', + widget: 'service.rule.memberservice_hbacsvcgroup', + priority: IPA.hbac.remove_method_priority + } + ); + + spec.widgets.push( + { + factory: IPA.collapsible_section, + name: 'service', + label: IPA.messages.objects.hbacrule.service, + widgets: [ + { + factory: IPA.rule_details_widget, + name: 'rule', + radio_name: 'servicecategory', + options: [ + { 'value': 'all', 'label': IPA.messages.objects.hbacrule.any_service }, + { 'value': '', 'label': IPA.messages.objects.hbacrule.specified_services } + ], + tables: [ + { 'name': 'memberservice_hbacsvc' }, + { 'name': 'memberservice_hbacsvcgroup' } + ], + widgets: [ + { + type: 'rule_association_table', + id: entity_name+'-memberuser_user', + name: 'memberservice_hbacsvc', + add_method: 'add_service', + remove_method: 'remove_service', + add_title: IPA.messages.association.add.member, + remove_title: IPA.messages.association.remove.member + }, + { + type: 'rule_association_table', + id: entity_name+'-memberuser_group', + name: 'memberservice_hbacsvcgroup', + add_method: 'add_service', + remove_method: 'remove_service', + add_title: IPA.messages.association.add.member, + remove_title: IPA.messages.association.remove.member + } + ] + } + ] + } + ); + + // + // Source host + // + + spec.fields.push( + { + type: 'radio', + name: 'sourcehostcategory', + widget: 'sourcehost.rule.sourcehostcategory' + }, + { + type: 'rule_association_table', + name: 'sourcehost_host', + widget: 'sourcehost.rule.sourcehost_host', + priority: IPA.hbac.remove_method_priority + }, + { + type: 'rule_association_table', + name: 'sourcehost_hostgroup', + widget: 'sourcehost.rule.sourcehost_hostgroup', + priority: IPA.hbac.remove_method_priority + } + ); + + spec.widgets.push( + { + factory: IPA.collapsible_section, + name: 'sourcehost', + label: IPA.messages.objects.hbacrule.sourcehost, + widgets: [ + { + factory: IPA.rule_details_widget, + name: 'rule', + radio_name: 'sourcehostcategory', + options: [ + { 'value': 'all', 'label': IPA.messages.objects.hbacrule.any_host }, + { 'value': '', 'label': IPA.messages.objects.hbacrule.specified_hosts } + ], + tables: [ + { 'name': 'sourcehost_host' }, + { 'name': 'sourcehost_hostgroup' } + ], + widgets: [ + { + type: 'rule_association_table', + id: entity_name+'-memberuser_user', + name: 'sourcehost_host', + add_method: 'add_sourcehost', + remove_method: 'remove_sourcehost', + add_title: IPA.messages.association.add.sourcehost, + remove_title: IPA.messages.association.remove.sourcehost + }, + { + type: 'rule_association_table', + id: entity_name+'-memberuser_group', + name: 'sourcehost_hostgroup', + add_method: 'add_sourcehost', + remove_method: 'remove_sourcehost', + add_title: IPA.messages.association.add.sourcehost, + remove_title: IPA.messages.association.remove.sourcehost + } + ] + } + ] + } + ); + + var that = IPA.details_facet(spec); + + that.update_on_success = function(data, text_status, xhr) { + that.refresh(); + that.on_update.notify(); + that.nofify_update_success(); + }; + + that.update_on_error = function(xhr, text_status, error_thrown) { + that.refresh(); + }; + + return that; +}; + +IPA.register('hbacrule', IPA.hbac.rule_entity); +IPA.register('hbacsvc', IPA.hbac.service_entity); +IPA.register('hbacsvcgroup', IPA.hbac.service_group_entity); diff --git a/install/ui/src/freeipa/hbactest.js b/install/ui/src/freeipa/hbactest.js new file mode 100644 index 000000000..93d17c719 --- /dev/null +++ b/install/ui/src/freeipa/hbactest.js @@ -0,0 +1,842 @@ +/*jsl:import ipa.js */ + +/* Authors: + * Endi Sukma Dewata <edewata@redhat.com> + * + * Copyright (C) 2010 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/>. + */ + +/* REQUIRES: ipa.js, details.js, search.js, add.js, facet.js, entity.js,hbac.js */ + +IPA.hbac.test_entity = function(spec) { + + var that = IPA.entity(spec); + + that.get_default_metadata = function() { + return IPA.metadata.commands[that.name]; + }; + + that.init = function() { + that.entity_init(); + + that.label = IPA.messages.objects.hbactest.label; + + that.builder.facet_groups([ 'default' ]). + facet({ + factory: IPA.hbac.test_select_facet, + name: 'user', + label: IPA.messages.objects.hbacrule.user, + managed_entity: 'user', + disable_breadcrumb: true, + facet_group: 'default', + row_disabled_attribute: 'nsaccountlock', + columns: [ + 'uid', + 'givenname', + 'sn', + { + name: 'nsaccountlock', + label: IPA.messages.status.label, + formatter: IPA.boolean_status_formatter({ + invert_value: true + }) + } + ] + }). + facet({ + factory: IPA.hbac.test_select_facet, + name: 'targethost', + label: IPA.messages.objects.hbacrule.host, + managed_entity: 'host', + disable_breadcrumb: true, + facet_group: 'default', + columns: [ + 'fqdn', + 'description', + { + name: 'has_keytab', + label: IPA.messages.objects.host.enrolled, + formatter: IPA.boolean_formatter() + } + ] + }). + facet({ + factory: IPA.hbac.test_select_facet, + name: 'service', + label: IPA.messages.objects.hbacrule.service, + managed_entity: 'hbacsvc', + disable_breadcrumb: true, + facet_group: 'default', + columns: [ + 'cn', + 'description' + ] + }). + facet({ + factory: IPA.hbac.test_select_facet, + name: 'sourcehost', + label: IPA.messages.objects.hbacrule.sourcehost, + managed_entity: 'host', + disable_breadcrumb: true, + facet_group: 'default', + columns: [ + 'fqdn', + 'description', + { + name: 'has_keytab', + label: IPA.messages.objects.host.enrolled, + formatter: IPA.boolean_formatter() + } + ] + }). + facet({ + factory: IPA.hbac.test_rules_facet, + name: 'rules', + label: IPA.messages.objects.hbactest.rules, + managed_entity: 'hbacrule', + disable_breadcrumb: true, + facet_group: 'default', + row_enabled_attribute: 'ipaenabledflag', + columns: [ + 'cn', + { + name: 'ipaenabledflag', + label: IPA.messages.status.label, + formatter: IPA.boolean_status_formatter() + }, + 'description' + ] + }). + facet({ + factory: IPA.hbac.test_run_facet, + name: 'run_test', + label: IPA.messages.objects.hbactest.run_test, + managed_entity: 'hbacrule', + disable_breadcrumb: true, + facet_group: 'default', + row_enabled_attribute: 'ipaenabledflag', + columns: [ + 'cn', + { + name: 'matched', + label: IPA.messages.objects.hbactest.matched, + formatter: IPA.boolean_formatter() + }, + { + name: 'ipaenabledflag', + label: IPA.messages.status.label, + formatter: IPA.boolean_status_formatter() + }, + 'description' + ] + }); + }; + + return that; +}; + +IPA.hbac.test_facet = function(spec) { + + spec = spec || {}; + + var that = IPA.table_facet(spec); + + var init = function() { + + that.managed_entity = IPA.get_entity(that.managed_entity); + + var columns = that.columns.values; + for (var i=0; i<columns.length; i++) { + var column = columns[i]; + + var metadata = IPA.get_entity_param(that.managed_entity.name, column.name); + column.primary_key = metadata && metadata.primary_key; + column.link = column.primary_key; + } + + that.init_table(that.managed_entity); + }; + + that.create_buttons = function(container) { + + var buttons = $('<div/>', { + 'class': 'hbac-test-navigation-buttons' + }).appendTo(container); + + var facet_group = that.entity.get_facet_group('default'); + var index = facet_group.get_facet_index(that.name); + + if (index > 0) { + that.prev_button = IPA.button({ + name: 'prev', + label: IPA.messages.widget.prev, + icon: 'ui-icon ui-icon-triangle-1-w', + click: function() { + if (!that.prev_button.hasClass('action-button-disabled')) { + that.prev(); + } + return false; + } + }).appendTo(buttons); + + buttons.append(' '); + } + + that.next_button = IPA.button({ + name: 'next', + label: IPA.messages.widget.next, + icon: 'ui-icon ui-icon-triangle-1-e', + click: function() { + if (!that.next_button.hasClass('action-button-disabled')) { + that.next(); + } + return false; + } + }).appendTo(buttons); + }; + + that.prev = function() { + var facet_group = that.entity.get_facet_group('default'); + var index = facet_group.get_facet_index(that.name); + if (index <= 0) return; + + var facet = facet_group.get_facet_by_index(index - 1); + + var state = {}; + state[that.entity.name+'-facet'] = facet.name; + IPA.nav.push_state(state); + }; + + that.next = function() { + var facet_group = that.entity.get_facet_group('default'); + var index = facet_group.get_facet_index(that.name); + if (index >= facet_group.get_facet_count() - 1) return; + + var facet = facet_group.get_facet_by_index(index + 1); + + var state = {}; + state[that.entity.name+'-facet'] = facet.name; + IPA.nav.push_state(state); + }; + + that.get_search_command_name = function() { + return that.managed_entity.name + '_find' + (that.pagination ? '_pkeys' : ''); + }; + + that.refresh = function() { + + var filter = IPA.nav.get_state(that.entity.name+'-'+that.name+'-filter'); + + var command = IPA.command({ + name: that.get_search_command_name(), + entity: that.managed_entity.name, + method: 'find', + args: [filter] + }); + + if (that.pagination) { + command.set_option('pkey_only', true); + command.set_option('sizelimit', 0); + } + + command.on_success = function(data, text_status, xhr) { + that.load(data); + that.show_content(); + }; + + command.on_error = function(xhr, text_status, error_thrown) { + that.report_error(error_thrown); + }; + + command.execute(); + }; + + init(); + + return that; +}; + +IPA.hbac.test_select_facet = function(spec) { + + var that = IPA.hbac.test_facet(spec); + + var init = function() { + that.table.multivalued = false; + + that.table.set_values = function(values) { + if (values && values.length && values[0] === '__external__') { + if (that.external_radio) that.external_radio.prop('checked', true); + } else { + that.table.table_set_values(values); + } + }; + }; + + that.create_content = function(container) { + + var header = $('<div/>', { + 'class': 'hbac-test-header' + }).appendTo(container); + + var title = $('<span/>', { + text: that.label, + 'class': 'hbac-test-title' + }).appendTo(header); + + var filter_container = $('<div/>', { + 'class': 'search-filter' + }).appendTo(header); + + that.filter = $('<input/>', { + type: 'text', + name: 'filter' + }).appendTo(filter_container); + + that.filter.keypress(function(e) { + /* if the key pressed is the enter key */ + if (e.which == 13) { + that.find(); + } + }); + + that.find_button = IPA.action_button({ + name: 'find', + icon: 'search-icon', + click: function() { + that.find(); + return false; + } + }).appendTo(filter_container); + + header.append(IPA.create_network_spinner()); + + var content = $('<div/>', { + 'class': 'hbac-test-content' + }).appendTo(container); + + that.table.create(content); + + var id = that.entity.name+'-'+that.name+'-external'; + var pkey_name = that.managed_entity.metadata.primary_key; + + var tr = $('<tr/>').appendTo(that.table.tfoot); + + var td = $('<td/>', { + name: 'external' + }).appendTo(tr); + + that.external_radio = $('<input/>', { + id: id, + type: 'radio', + name: pkey_name, + value: '__external__', + click: function() { + that.selected_values = [ that.external_radio.val() ]; + } + }).appendTo(td); + + var message = IPA.messages.objects.hbactest.specify_external; + message = message.replace('${entity}', that.managed_entity.metadata.label_singular); + + $('<label/>', { + text: message+':', + 'for': id + }).appendTo(td); + + td.append(' '); + + that.external_text = $('<input/>', { + name: id, + focus: function() { + that.external_radio.click(); + } + }).appendTo(td); + + var footer = $('<div/>', { + 'class': 'hbac-test-footer' + }).appendTo(container); + + that.create_buttons(footer); + }; + + that.find = function() { + + var old_filter = IPA.nav.get_state(that.entity.name+'-'+that.name+'-filter'); + var filter = that.filter.val(); + + that.set_expired_flag(); + + if (old_filter === filter) { + that.refresh(); + } else { + var state = {}; + state[that.entity.name+'-'+that.name+'-filter'] = filter; + IPA.nav.push_state(state); + } + }; + + that.get_selected_values = function() { + var values = that.table.get_selected_values(); + if (values && values.length) return values; + + if (that.external_radio && that.external_radio.is(':checked')) { + return [ that.external_radio.val() ]; + } + + return []; + }; + + that.reset = function() { + delete that.selected_values; + if (that.external_radio) that.external_radio.prop('checked', false); + if (that.external_text) that.external_text.val(''); + }; + + that.save = function(record) { + if (that.selected_values && that.selected_values.length) { + var value = that.selected_values[0]; + if (that.external_radio && value === that.external_radio.val()) { + record[that.name] = that.external_text.val(); + } else { + record[that.name] = value; + } + } + }; + + that.validate = function(record) { + if (record[that.name]) return true; + + return false; + }; + + init(); + + return that; +}; + +IPA.hbac.test_rules_facet = function(spec) { + + spec = spec || {}; + + var that = IPA.hbac.test_facet(spec); + + var init = function() { + }; + + that.create_content = function(container) { + + var header = $('<div/>', { + 'class': 'hbac-test-header' + }).appendTo(container); + + var title = $('<span/>', { + text: that.label, + 'class': 'hbac-test-title' + }).appendTo(header); + + header.append(' '); + + that.enabled = $('<input/>', { + id: 'hbactest-rules-include-enabled', + type: 'checkbox', + name: 'enabled' + }).appendTo(header); + + $('<label/>', { + 'for': 'hbactest-rules-include-enabled', + text: IPA.messages.objects.hbactest.include_enabled + }).appendTo(header); + + that.disabled = $('<input/>', { + id: 'hbactest-rules-include-disabled', + type: 'checkbox', + name: 'disabled' + }).appendTo(header); + + $('<label/>', { + 'for': 'hbactest-rules-include-disabled', + text: IPA.messages.objects.hbactest.include_disabled + }).appendTo(header); + + var content = $('<div/>', { + 'class': 'hbac-test-content' + }).appendTo(container); + + that.table.create(content); + + var footer = $('<div/>', { + 'class': 'hbac-test-footer' + }).appendTo(container); + + that.create_buttons(footer); + }; + + that.get_selected_values = function() { + return that.table.get_selected_values(); + }; + + that.reset = function() { + delete that.selected_values; + if (that.enabled) that.enabled.prop('checked', false); + if (that.disabled) that.disabled.prop('checked', false); + }; + + that.save = function(record) { + if (that.selected_values && that.selected_values.length) { + record[that.name] = that.selected_values; + } + if (that.enabled && that.enabled.is(':checked')) { + record['enabled'] = true; + } + if (that.disabled && that.disabled.is(':checked')) { + record['disabled'] = true; + } + }; + + init(); + + return that; +}; + +IPA.hbac.test_run_facet = function(spec) { + + spec = spec || {}; + + var that = IPA.hbac.test_facet(spec); + + var init = function() { + that.table.selectable = false; + that.show_matched = true; + that.show_unmatched = true; + }; + + that.create_content = function(container) { + + var header = $('<div/>', { + 'class': 'hbac-test-header' + }).appendTo(container); + + var top_panel = $('<div/>', { + 'class': 'hbac-test-top-panel' + }).appendTo(header); + + var button_panel = $('<div/>', { + 'class': 'hbac-test-button-panel' + }).appendTo(top_panel); + + that.run_button = IPA.button({ + name: 'run_test', + label: IPA.messages.objects.hbactest.run_test, + click: function() { + if (!that.run_button.hasClass('action-button-disabled')) { + that.run(); + } + return false; + } + }).appendTo(button_panel); + + var result_panel = $('<div/>', { + 'class': 'hbac-test-result-panel' + }).appendTo(top_panel); + + that.test_result = $('<p/>', { + 'class': 'hbac-test-title' + }).appendTo(result_panel); + + var title = $('<span/>', { + text: IPA.messages.objects.hbactest.rules, + 'class': 'hbac-test-title' + }).appendTo(header); + + header.append(' '); + + that.matched_checkbox = $('<input/>', { + id: 'hbactest-rules-matched', + type: 'checkbox', + name: 'matched', + checked: true, + change: function() { + that.show_matched = that.matched_checkbox.is(':checked'); + that.refresh(); + } + }).appendTo(header); + + $('<label/>', { + 'for': 'hbactest-rules-matched', + text: IPA.messages.objects.hbactest.matched + }).appendTo(header); + + that.unmatched_checkbox = $('<input/>', { + id: 'hbactest-rules-unmatched', + type: 'checkbox', + name: 'unmatched', + checked: true, + change: function() { + that.show_unmatched = that.unmatched_checkbox.is(':checked'); + that.refresh(); + } + }).appendTo(header); + + $('<label/>', { + 'for': 'hbactest-rules-unmatched', + text: IPA.messages.objects.hbactest.unmatched + }).appendTo(header); + + var content = $('<div/>', { + 'class': 'hbac-test-content' + }).appendTo(container); + + that.table.create(content); + + var footer = $('<div/>', { + 'class': 'hbac-test-footer' + }).appendTo(container); + + var buttons = $('<div/>', { + 'class': 'hbac-test-navigation-buttons' + }).appendTo(footer); + + that.prev_button = IPA.button({ + name: 'prev', + label: IPA.messages.widget.prev, + icon: 'ui-icon ui-icon-triangle-1-w', + click: function() { + if (!that.prev_button.hasClass('action-button-disabled')) { + that.prev(); + } + return false; + } + }).appendTo(buttons); + + buttons.append(' '); + + that.new_test_button = IPA.button({ + name: 'new_test', + label: IPA.messages.objects.hbactest.new_test, + click: function() { + if (!that.new_test_button.hasClass('action-button-disabled')) { + that.new_test(); + } + return false; + } + }).appendTo(buttons); + }; + + that.new_test = function() { + var facet = that.entity.get_facet('user'); + facet.reset(); + + facet = that.entity.get_facet('targethost'); + facet.reset(); + + facet = that.entity.get_facet('service'); + facet.reset(); + + facet = that.entity.get_facet('sourcehost'); + facet.reset(); + + facet = that.entity.get_facet('rules'); + facet.reset(); + + facet = that.entity.get_facet('run_test'); + facet.reset(); + + var state = {}; + state[that.entity.name+'-facet'] = 'user'; + IPA.nav.push_state(state); + }; + + that.reset = function() { + delete that.data; + that.show_matched = true; + that.show_unmatched = true; + if (that.matched_checkbox) that.matched_checkbox.prop('checked', true); + if (that.unmatched_checkbox) that.unmatched_checkbox.prop('checked', true); + that.refresh(); + }; + + that.refresh = function() { + if (that.data) { + var message = that.data.result.value ? + IPA.messages.objects.hbactest.access_granted : + IPA.messages.objects.hbactest.access_denied; + that.test_result.text(message); + + } else { + that.test_result.text(''); + } + + that.load(that.data); + }; + + that.run = function() { + + var command = IPA.command({ method: 'hbactest' }); + + var options = {}; + var validation_results = { + valid: true, + invalid_facets: [] + }; + + var facet = that.entity.get_facet('user'); + facet.save(options); + that.validate_facet(facet, options, validation_results); + + facet = that.entity.get_facet('targethost'); + facet.save(options); + that.validate_facet(facet, options, validation_results); + + facet = that.entity.get_facet('service'); + facet.save(options); + that.validate_facet(facet, options, validation_results); + + facet = that.entity.get_facet('sourcehost'); + facet.save(options); + that.validate_facet(facet, options, validation_results); + + if (!validation_results.valid) { + var dialog = IPA.hbac.validation_dialog({ + validation_results: validation_results + }); + dialog.open(); + return; + } + + facet = that.entity.get_facet('rules'); + facet.save(options); + + command.set_options(options); + + command.on_success = function(data, text_status, xhr) { + that.data = data; + that.refresh(); + }; + + command.execute(); + }; + + that.validate_facet = function(facet, options, validation_results) { + + var facet_valid = facet.validate(options); + + validation_results.valid = facet_valid && validation_results.valid; + + if (!facet_valid) { + validation_results.invalid_facets.push(facet); + } + }; + + that.get_records_map = function(data) { + + var records_map = $.ordered_map(); + + var matched = data.result.matched; + if (that.show_matched && matched) { + for (var i=0; i<matched.length; i++) { + var pkey = matched[i]; + records_map.put(pkey, { matched: true }); + } + } + + var notmatched = data.result.notmatched; + if (that.show_unmatched && notmatched) { + for (i=0; i<notmatched.length; i++) { + pkey = notmatched[i]; + records_map.put(pkey, { matched: false }); + } + } + + return records_map; + }; + + that.get_records_command_name = function() { + if (that.show_matched && !that.show_unmatched) { + return 'hbactest_matched'; + } + if (!that.show_matched && that.show_unmatched) { + return 'hbactest_unmatched'; + } + return that.managed_entity.name+'_get_records'; + }; + + init(); + + return that; +}; + +IPA.hbac.validation_dialog = function(spec) { + + spec = spec || {}; + spec.title = spec.title || IPA.messages.dialogs.validation_title; + spec.message = spec.message || IPA.messages.dialogs.validation_message; + + var that = IPA.message_dialog(spec); + + that.validation_results = spec.validation_results; + + that.create = function() { + + if (that.message) { + that.message_dialog_create(); + } + + if (that.validation_results && that.validation_results.invalid_facets) { + var invalid_facets = that.validation_results.invalid_facets; + + var ul; + + if (invalid_facets.length > 0) { + var div = $('<div/>',{ + text: IPA.messages.objects.hbactest.missing_values + }).appendTo(that.container); + ul = $('<ul/>').appendTo(that.container); + } + + for (var i=0; i<invalid_facets.length; i++) { + + var facet = invalid_facets[i]; + + var li = $('<li />').appendTo(ul); + + var metadata = IPA.get_command_option('hbactest', facet.name); + + $('<a />', { + href: '#'+facet.name, + text: metadata.label, + click: function(facet) { + return function() { + that.redirect_to_facet(facet); + return false; + }; + }(facet) + }).appendTo(li); + } + } + }; + + that.redirect_to_facet = function(facet) { + that.close(); + var state = {}; + state[facet.entity.name+'-facet'] = facet.name; + IPA.nav.push_state(state); + }; + + return that; +}; + +IPA.register('hbactest', IPA.hbac.test_entity); diff --git a/install/ui/src/freeipa/host.js b/install/ui/src/freeipa/host.js new file mode 100644 index 000000000..30086b414 --- /dev/null +++ b/install/ui/src/freeipa/host.js @@ -0,0 +1,947 @@ +/*jsl:import ipa.js */ +/*jsl:import certificate.js */ + +/* Authors: + * Pavel Zuna <pzuna@redhat.com> + * Endi S. Dewata <edewata@redhat.com> + * + * Copyright (C) 2010 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/>. + */ + +/* REQUIRES: ipa.js, details.js, search.js, add.js, facet.js, entity.js */ + +IPA.host = {}; + +IPA.host.entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + that.entity_init(); + + that.builder.search_facet({ + columns: [ + 'fqdn', + 'description', + { + name: 'has_keytab', + label: IPA.messages.objects.host.enrolled, + formatter: IPA.boolean_formatter() + } + ] + }). + details_facet({ + factory: IPA.host.details_facet, + sections: [ + { + name: 'details', + fields: [ + { + type: 'host_dnsrecord_entity_link', + name: 'fqdn', + other_entity: 'dnsrecord' + }, + 'krbprincipalname', + { + type: 'textarea', + name: 'description' + }, + 'l', + 'nshostlocation', + 'nshardwareplatform', + 'nsosversion', + { + type: 'sshkeys', + name: 'ipasshpubkey', + label: IPA.messages.objects.sshkeystore.keys + }, + { + type: 'multivalued', + name: 'macaddress', + flags: ['w_if_no_aci'] + } + ] + }, + { + name: 'enrollment', + action_panel: { + factory: IPA.action_panel, + name: 'enrollment_actions', + actions: ['unprovision', 'set_otp', 'reset_otp'] + }, + fields: [ + { + factory: IPA.host_keytab_widget, + name: 'has_keytab', + label: IPA.messages.objects.host.keytab + }, + { + type: 'host_password', + name: 'has_password', + label: IPA.messages.objects.host.password + } + ] + }, + { + name: 'certificate', + action_panel: { + factory: IPA.action_panel, + name: 'cert_actions', + actions: [ + 'request_cert', 'view_cert', 'get_cert', + 'revoke_cert', 'restore_cert' + ] + }, + fields: [ + { + type: 'certificate_status', + name: 'certificate_status', + label: IPA.messages.objects.host.status + } + ] + } + ], + actions: [ + IPA.host.unprovision_action, + { + factory: IPA.host.set_otp_action, + name: 'set_otp', + label: IPA.messages.objects.host.password_set_title, + status: 'missing', + hide_cond: ['has_password'] + }, + { + factory: IPA.host.set_otp_action, + name: 'reset_otp', + label: IPA.messages.objects.host.password_reset_title, + status: 'present', + show_cond: ['has_password'] + }, + IPA.cert.view_action, + IPA.cert.get_action, + IPA.cert.request_action, + IPA.cert.revoke_action, + IPA.cert.restore_action + ], + state: { + evaluators: [ + IPA.host.has_password_evaluator, + IPA.host.has_keytab_evaluator, + IPA.host.userpassword_acl_evaluator, + IPA.host.krbprincipalkey_acl_evaluator, + IPA.cert.certificate_evaluator + ] + }, + policies: [ + IPA.host.enrollment_policy(), + IPA.host.certificate_policy() + ] + }). + association_facet({ + name: 'managedby_host', + add_method: 'add_managedby', + remove_method: 'remove_managedby' + }). + association_facet({ + name: 'memberof_hostgroup', + associator: IPA.serial_associator + }). + association_facet({ + name: 'memberof_netgroup', + associator: IPA.serial_associator + }). + association_facet({ + name: 'memberof_role', + associator: IPA.serial_associator + }). + association_facet({ + name: 'memberof_hbacrule', + associator: IPA.serial_associator, + add_method: 'add_host', + remove_method: 'remove_host' + }). + association_facet({ + name: 'memberof_sudorule', + associator: IPA.serial_associator, + add_method: 'add_host', + remove_method: 'remove_host' + }). + standard_association_facets(). + adder_dialog({ + factory: IPA.host_adder_dialog, + height: 300, + sections: [ + { + factory: IPA.composite_widget, + name: 'fqdn', + fields: [ + { + type: 'host_fqdn', + name: 'fqdn', + required: true + } + ] + }, + { + name: 'other', + fields: [ + { + name: 'ip_address', + validators: [ IPA.ip_address_validator() ], + metadata: IPA.get_command_option('host_add', 'ip_address') + }, + { + type: 'force_host_add_checkbox', + name: 'force', + metadata: IPA.get_command_option('host_add', 'force') + } + ] + } + ] + }). + deleter_dialog({ + factory: IPA.host_deleter_dialog + }); + }; + + return that; +}; + +IPA.host.details_facet = function(spec, no_init) { + + var that = IPA.details_facet(spec, true); + that.certificate_loaded = IPA.observer(); + + that.get_refresh_command_name = function() { + return that.entity.name+'_show_'+that.pkey; + }; + + if (!no_init) that.init_details_facet(); + + return that; +}; + +IPA.host_fqdn_widget = function(spec) { + + spec = spec || {}; + + spec.widgets = [ + { + type: 'text', + name: 'hostname', + label: IPA.messages.objects.service.host, + required: true + }, + { + type: 'dnszone_select', + name: 'dnszone', + label: IPA.metadata.objects.dnszone.label_singular, + editable: true, + empty_option: false, + required: true, + searchable: true + } + ]; + + var that = IPA.composite_widget(spec); + + that.create = function(container) { + that.container = container; + + var hostname = that.widgets.get_widget('hostname'); + var dnszone = that.widgets.get_widget('dnszone'); + + var table = $('<table/>', { + 'class': 'fqdn' + }).appendTo(that.container); + + var tr = $('<tr/>').appendTo(table); + + var th = $('<th/>', { + 'class': 'hostname', + title: hostname.label, + text: hostname.label + }).appendTo(tr); + + $('<span/>', { + 'class': 'required-indicator', + text: IPA.required_indicator + }).appendTo(th); + + th = $('<th/>', { + 'class': 'dnszone', + title: dnszone.label, + text: dnszone.label + }).appendTo(tr); + + $('<span/>', { + 'class': 'required-indicator', + text: IPA.required_indicator + }).appendTo(th); + + tr = $('<tr/>').appendTo(table); + + var td = $('<td/>', { + 'class': 'hostname' + }).appendTo(tr); + + var span = $('<span/>', { + name: hostname.name + }).appendTo(td); + hostname.create(span); + + td = $('<td/>', { + 'class': 'dnszone' + }).appendTo(tr); + + span = $('<span/>', { + name: dnszone.name + }).appendTo(td); + dnszone.create(span); + + var hostname_input = $('input', hostname.container); + var dnszone_input = $('input', dnszone.container); + + hostname_input.keyup(function(e) { + var value = hostname_input.val(); + var i = value.indexOf('.'); + if (i >= 0) { + var hostname = value.substr(0, i); + var dnszone = value.substr(i+1); + hostname_input.val(hostname); + if (dnszone) { + dnszone_input.val(dnszone); + dnszone_input.focus(); + } + IPA.select_range(dnszone_input, 0, dnszone_input.val().length); + } + }); + }; + + return that; +}; + +IPA.host_fqdn_field = function(spec) { + + spec = spec || {}; + + var that = IPA.field(spec); + + that.validate_required = function() { + + var hostname = that.hostname_widget.save(); + var dnszone = that.dns_zone_widget.save(); + + var valid = true; + + if(!hostname.length || hostname[0] === '') { + that.hostname_widget.show_error(IPA.messages.widget.validation.required); + that.valid = valid = false; + } + + if(!dnszone.length || dnszone[0] === '') { + that.dns_zone_widget.show_error(IPA.messages.widget.validation.required); + that.valid = valid = false; + } + + return valid; + }; + + that.hide_error = function() { + that.hostname_widget.hide_error(); + that.dns_zone_widget.hide_error(); + }; + + that.save = function(record) { + + if(!record) record = {}; + + var hostname = that.hostname_widget.save()[0]; + var dnszone = that.dns_zone_widget.save()[0]; + + record.fqdn = hostname && dnszone ? [ hostname+'.'+dnszone ] : []; + + return record.fqdn; + }; + + that.reset = function() { + + that.hostname_widget.update([]); + that.dns_zone_widget.update([]); + }; + + that.widgets_created = function() { + + that.widget = that.container.widgets.get_widget(that.widget_name); + that.hostname_widget = that.widget.widgets.get_widget('hostname'); + that.dns_zone_widget = that.widget.widgets.get_widget('dnszone'); + }; + + return that; +}; + +IPA.field_factories['host_fqdn'] = IPA.host_fqdn_field; +IPA.widget_factories['host_fqdn'] = IPA.host_fqdn_widget; + +IPA.host_adder_dialog = function(spec) { + + spec = spec || {}; + spec.retry = spec.retry !== undefined ? spec.retry : false; + + if (!IPA.dns_enabled) { + + //When server is installed without DNS support, a use of host_fqdn_widget + //is bad because there are no DNS zones. IP address field is useless as + //well. Special section and IP address field should be removed and normal + //fqdn textbox has to be added. + spec.sections.shift(); + spec.sections[0].fields.shift(); + spec.sections[0].fields.unshift('fqdn'); + delete spec.height; + } + + var that = IPA.entity_adder_dialog(spec); + + that.create = function() { + that.entity_adder_dialog_create(); + that.container.addClass('host-adder-dialog'); + }; + + that.on_error = IPA.create_4304_error_handler(that); + + return that; +}; + +IPA.host_deleter_dialog = function(spec) { + + spec = spec || {}; + + var that = IPA.search_deleter_dialog(spec); + + that.create = function() { + + that.deleter_dialog_create(); + + var metadata = IPA.get_command_option('host_del', 'updatedns'); + + that.updatedns = $('<input/>', { + type: 'checkbox', + name: 'updatedns', + title: metadata.doc + }).appendTo(that.container); + + that.container.append(' '); + + that.container.append(metadata.doc); + }; + + that.create_command = function() { + var batch = that.search_deleter_dialog_create_command(); + var updatedns = that.updatedns.is(':checked'); + + for (var i=0; i<batch.commands.length; i++) { + var command = batch.commands[i]; + command.set_option('updatedns', updatedns); + } + + return batch; + }; + + 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 IPA.command({ + entity: that.other_entity.name, + method: 'find', + args: [filter], + options: { + forward_only: true + } + }); + }; + + return that; +}; + +IPA.field_factories['dnszone_select'] = IPA.field; +IPA.widget_factories['dnszone_select'] = IPA.dnszone_select_widget; + +IPA.host_dnsrecord_entity_link_field = function(spec){ + var that = IPA.link_field(spec); + + that.other_pkeys = function(){ + var pkey = that.entity.get_primary_key()[0]; + var first_dot = pkey.search(/\./); + var pkeys = []; + pkeys[1] = pkey.substring(0,first_dot); + pkeys[0] = pkey.substring(first_dot+1); + return pkeys; + }; + + return that; +}; + +IPA.field_factories['host_dnsrecord_entity_link'] = IPA.host_dnsrecord_entity_link_field; +IPA.widget_factories['host_dnsrecord_entity_link'] = IPA.link_widget; + +IPA.force_host_add_checkbox_widget = function(spec) { + var metadata = IPA.get_command_option('host_add', spec.name); + spec.label = metadata.label; + spec.tooltip = metadata.doc; + return IPA.checkbox_widget(spec); +}; + +IPA.widget_factories['force_host_add_checkbox'] = IPA.force_host_add_checkbox_widget; +IPA.field_factories['force_host_add_checkbox'] = IPA.checkbox_field; + +IPA.host.enrollment_policy = function(spec) { + + var that = IPA.facet_policy(); + + that.init = function() { + + var keytab_field = that.container.fields.get_field('has_keytab'); + var password_field = that.container.fields.get_field('has_password'); + + var super_set_password = password_field.set_password; + password_field.set_password = function(password, on_success, on_error) { + super_set_password.call( + this, + password, + function(data, text_status, xhr) { + keytab_field.load(data.result.result); + if (on_success) on_success.call(this, data, text_status, xhr); + }, + on_error); + }; + }; + + return that; +}; + +IPA.host_keytab_widget = function(spec) { + + spec = spec || {}; + + var that = IPA.input_widget(spec); + + that.create = function(container) { + + that.widget_create(container); + + that.missing_span = $('<span/>', { + name: 'missing', + style: 'display: none;' + }).appendTo(container); + + $('<img/>', { + src: 'images/caution-icon.png', + 'class': 'status-icon' + }).appendTo(that.missing_span); + + that.missing_span.append(' '); + + that.missing_span.append(IPA.messages.objects.host.keytab_missing); + + that.present_span = $('<span/>', { + name: 'present', + style: 'display: none;' + }).appendTo(container); + + $('<img/>', { + src: 'images/check-icon.png', + 'class': 'status-icon' + }).appendTo(that.present_span); + + that.present_span.append(' '); + + that.present_span.append(IPA.messages.objects.host.keytab_present); + }; + + that.update = function(values) { + set_status(values[0] ? 'present' : 'missing'); + }; + + that.clear = function() { + that.present_span.css('display', 'none'); + that.missing_span.css('display', 'none'); + }; + + function set_status(status) { + that.present_span.css('display', status == 'present' ? 'inline' : 'none'); + that.missing_span.css('display', status == 'missing' ? 'inline' : 'none'); + } + + return that; +}; + +IPA.host_unprovision_dialog = function(spec) { + + spec.title = spec.title || IPA.messages.objects.host.unprovision_title; + + spec = spec || {}; + + var that = IPA.dialog(spec); + that.facet = spec.facet; + + that.title = that.title.replace('${entity}', that.entity.metadata.label_singular); + + that.create = function() { + that.container.append(IPA.messages.objects.host.unprovision_confirmation); + }; + + that.create_buttons = function() { + + that.create_button({ + name: 'unprovision', + label: IPA.messages.objects.host.unprovision, + click: function() { + that.unprovision( + function(data, text_status, xhr) { + that.facet.refresh(); + that.close(); + IPA.notify_success(IPA.messages.objects.host.unprovisioned); + }, + function(xhr, text_status, error_thrown) { + that.close(); + } + ); + } + }); + + that.create_button({ + name: 'cancel', + label: IPA.messages.buttons.cancel, + click: function() { + that.close(); + } + }); + }; + + that.unprovision = function(on_success, on_error) { + + var pkey = that.entity.get_primary_key(); + + var command = IPA.command({ + name: that.entity.name+'_disable_'+pkey, + entity: that.entity.name, + method: 'disable', + args: pkey, + on_success: on_success, + on_error: on_error + }); + + command.execute(); + }; + + that.create_buttons(); + + return that; +}; + +IPA.host.unprovision_action = function(spec) { + + spec = spec || {}; + spec.name = spec.name || 'unprovision'; + spec.label = spec.label || IPA.messages.objects.host.unprovision; + spec.enable_cond = spec.enable_cond || ['has_keytab', 'krbprincipalkey_w']; + + var that = IPA.action(spec); + + that.execute_action = function(facet) { + + var dialog = IPA.host_unprovision_dialog({ + entity: facet.entity, + facet: facet + }); + + dialog.open(); + }; + + return that; +}; + +IPA.host.krbprincipalkey_acl_evaluator = function(spec) { + + spec.name = spec.name || 'unprovision_acl_evaluator'; + spec.attribute = spec.attribute || 'krbprincipalkey'; + + var that = IPA.acl_state_evaluator(spec); + return that; +}; + +IPA.host.has_keytab_evaluator = function(spec) { + + spec.name = spec.name || 'has_keytab_evaluator'; + spec.attribute = spec.attribute || 'has_keytab'; + spec.value = spec.value || [true]; + spec.representation = spec.representation || 'has_keytab'; + + var that = IPA.value_state_evaluator(spec); + return that; +}; + +IPA.host_password_widget = function(spec) { + + spec = spec || {}; + + var that = IPA.input_widget(spec); + + that.create = function(container) { + + that.widget_create(container); + + that.missing_span = $('<span/>', { + name: 'missing' + }).appendTo(container); + + $('<img/>', { + src: 'images/caution-icon.png', + 'class': 'status-icon' + }).appendTo(that.missing_span); + + that.missing_span.append(' '); + + that.missing_span.append(IPA.messages.objects.host.password_missing); + + that.present_span = $('<span/>', { + name: 'present', + style: 'display: none;' + }).appendTo(container); + + $('<img/>', { + src: 'images/check-icon.png', + 'class': 'status-icon' + }).appendTo(that.present_span); + + that.present_span.append(' '); + + that.present_span.append(IPA.messages.objects.host.password_present); + }; + + that.update = function(values) { + set_status(values[0] ? 'present' : 'missing'); + }; + + that.clear = function() { + that.missing_span.css('display', 'none'); + that.present_span.css('display', 'none'); + }; + + function set_status(status) { + + that.status = status; + + if (status == 'missing') { + that.missing_span.css('display', 'inline'); + that.present_span.css('display', 'none'); + } else { + that.missing_span.css('display', 'none'); + that.present_span.css('display', 'inline'); + } + } + + return that; +}; + +IPA.widget_factories['host_password'] = IPA.host_password_widget; +IPA.field_factories['host_password'] = IPA.field; + +IPA.host.set_otp_dialog = function(spec) { + + spec = spec || {}; + spec.width = spec.width || 400; + spec.sections = spec.sections || [ + { + fields: [ + { + name: 'password1', + label: IPA.messages.password.new_password, + type: 'password', + required: true + }, + { + name: 'password2', + label: IPA.messages.password.verify_password, + type: 'password', + required: true, + validators: [IPA.same_password_validator({ + other_field: 'password1' + })] + } + ] + } + ]; + + var that = IPA.dialog(spec); + + IPA.confirm_mixin().apply(that); + + that.facet = spec.facet; + + that.set_status = function(status) { + + var button = that.get_button('set_password'); + + if (status == 'missing') { + that.title = IPA.messages.objects.host.password_set_title; + button.label = IPA.messages.objects.host.password_set_button; + } else { + that.title = IPA.messages.objects.host.password_reset_title; + button.label = IPA.messages.objects.host.password_reset_button; + } + }; + + that.on_confirm = function() { + + if (!that.validate()) return; + + var new_password = that.fields.get_field('password1').save()[0]; + that.set_otp(new_password); + + that.close(); + }; + + that.create_buttons = function() { + + that.create_button({ + name: 'set_password', + label: IPA.messages.objects.host.password_set_button, + click: function() { + that.on_confirm(); + } + }); + + that.create_button({ + name: 'cancel', + label: IPA.messages.buttons.cancel, + click: function() { + that.close(); + } + }); + }; + + that.set_otp = function(password) { + var pkey = that.entity.get_primary_key(); + + var command = IPA.command({ + entity: that.entity.name, + method: 'mod', + args: pkey, + options: { + all: true, + rights: true, + userpassword: password + }, + on_success: function(data) { + that.facet.load(data); + that.close(); + IPA.notify_success(IPA.messages.objects.host.password_set_success); + }, + on_error: function() { + that.close(); + } + }); + + command.execute(); + }; + + that.create_buttons(); + + return that; +}; + +IPA.host.set_otp_action = function(spec) { + + spec = spec || {}; + spec.name = spec.name || 'set_otp'; + spec.label = spec.label || IPA.messages.objects.host.password_set_title; + spec.enable_cond = spec.enable_cond || ['userpassword_w']; + + var that = IPA.action(spec); + that.status = spec.status || 'missing'; + + that.execute_action = function(facet) { + + var dialog = IPA.host.set_otp_dialog({ + entity: facet.entity, + facet: facet + }); + + dialog.set_status(that.status); + + dialog.open(); + }; + + return that; +}; + +IPA.host.userpassword_acl_evaluator = function(spec) { + + spec.name = spec.name || 'userpassword_acl_evaluator'; + spec.attribute = spec.attribute || 'userpassword'; + + var that = IPA.acl_state_evaluator(spec); + return that; +}; + +IPA.host.has_password_evaluator = function(spec) { + + spec.name = spec.name || 'has_password_evaluator'; + spec.attribute = spec.attribute || 'has_password'; + spec.value = spec.value || [true]; + spec.representation = spec.representation || 'has_password'; + + var that = IPA.value_state_evaluator(spec); + return that; +}; + +IPA.host.certificate_policy = function(spec) { + + spec = spec || {}; + + spec.get_pkey = spec.get_pkey || function(result) { + var values = result.fqdn; + return values ? values[0] : null; + }; + + spec.get_name = spec.get_name || function(result) { + var values = result.fqdn; + return values ? values[0] : null; + }; + + spec.get_principal = spec.get_principal || function(result) { + var values = result.krbprincipalname; + return values ? values[0] : null; + }; + + spec.get_hostname = spec.get_hostname || spec.get_name; + + var that = IPA.cert.load_policy(spec); + return that; +}; + +IPA.register('host', IPA.host.entity); diff --git a/install/ui/src/freeipa/hostgroup.js b/install/ui/src/freeipa/hostgroup.js new file mode 100644 index 000000000..20e7179fb --- /dev/null +++ b/install/ui/src/freeipa/hostgroup.js @@ -0,0 +1,90 @@ +/*jsl:import ipa.js */ + +/* Authors: + * Pavel Zuna <pzuna@redhat.com> + * + * Copyright (C) 2010 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/>. + */ + +/* REQUIRES: ipa.js, details.js, search.js, add.js, facet.js, entity.js */ + +IPA.hostgroup = {}; + +IPA.hostgroup.entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + that.entity_init(); + + that.builder.search_facet({ + columns: [ + 'cn', + 'description' + ] + }). + details_facet({ + sections: [ + { + name: 'identity', + label: IPA.messages.objects.hostgroup.identity, + fields: [ + 'cn', + { + type: 'textarea', + name: 'description' + } + ] + } + ] + }). + association_facet({ + name: 'memberof_hostgroup', + associator: IPA.serial_associator + }). + association_facet({ + name: 'memberof_netgroup', + associator: IPA.serial_associator + }). + association_facet({ + name: 'memberof_hbacrule', + associator: IPA.serial_associator, + add_method: 'add_host', + remove_method: 'remove_host' + }). + association_facet({ + name: 'memberof_sudorule', + associator: IPA.serial_associator, + add_method: 'add_host', + remove_method: 'remove_host' + }). + standard_association_facets(). + adder_dialog({ + fields: [ + 'cn', + { + type: 'textarea', + name: 'description' + } + ] + }); + }; + + return that; +}; + +IPA.register('hostgroup', IPA.hostgroup.entity); diff --git a/install/ui/src/freeipa/idrange.js b/install/ui/src/freeipa/idrange.js new file mode 100644 index 000000000..b2ce3169f --- /dev/null +++ b/install/ui/src/freeipa/idrange.js @@ -0,0 +1,162 @@ +/*jsl:import ipa.js */ + +/* Authors: + * Petr Vobornik <pvoborni@redhat.com> + * + * Copyright (C) 2010 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/>. + */ + +/* REQUIRES: ipa.js, details.js, search.js, add.js, facet.js, entity.js */ + +IPA.idrange = {}; + +IPA.idrange.entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + that.entity_init(); + + that.builder.search_facet({ + columns: [ + 'cn', + 'ipabaseid', + 'ipaidrangesize', + 'iparangetype' + ] + }). + details_facet({ + sections: [ + { + name: 'details', + fields: [ + 'cn', + 'iparangetype', + { + name: 'ipabaseid', + label: IPA.messages.objects.idrange.ipabaseid, + tooltip: IPA.get_entity_param('idrange', 'ipabaseid').label + }, + { + name: 'ipaidrangesize', + label: IPA.messages.objects.idrange.ipaidrangesize, + tooltip: IPA.get_entity_param('idrange', 'ipaidrangesize').label + }, + { + name: 'ipabaserid', + label: IPA.messages.objects.idrange.ipabaserid, + tooltip: IPA.get_entity_param('idrange', 'ipabaserid').label + }, + { + name: 'ipasecondarybaserid', + label: IPA.messages.objects.idrange.ipasecondarybaserid, + tooltip: IPA.get_entity_param('idrange', 'ipasecondarybaserid').label + }, + { + name: 'ipanttrusteddomainsid', + label: IPA.messages.objects.idrange.ipanttrusteddomainsid, + tooltip: IPA.get_entity_param('idrange', 'ipanttrusteddomainsid').label + } + ] + } + ] + }). + adder_dialog({ + fields: [ + { + name: 'cn', + widget: 'idrange.cn' + }, + { + name: 'ipabaseid', + label: IPA.messages.objects.idrange.ipabaseid, + tooltip: IPA.get_entity_param('idrange', 'ipabaseid').label, + widget: 'idrange.ipabaseid' + }, + { + name: 'ipaidrangesize', + label: IPA.messages.objects.idrange.ipaidrangesize, + tooltip: IPA.get_entity_param('idrange', 'ipaidrangesize').label, + widget: 'idrange.ipaidrangesize' + }, + { + name: 'ipabaserid', + label: IPA.messages.objects.idrange.ipabaserid, + tooltip: IPA.get_entity_param('idrange', 'ipabaserid').label, + widget: 'idrange.ipabaserid' + }, + { + name: 'ipasecondarybaserid', + label: IPA.messages.objects.idrange.ipasecondarybaserid, + tooltip: IPA.get_entity_param('idrange', 'ipasecondarybaserid').label, + widget: 'type.ipasecondarybaserid' + }, + { + name: 'ipanttrusteddomainsid', + label: IPA.messages.objects.idrange.ipanttrusteddomainsid, + tooltip: IPA.get_entity_param('idrange', 'ipanttrusteddomainsid').label, + widget: 'type.ipanttrusteddomainsid' + } + ], + widgets: [ + { + type: 'details_table_section_nc', + name: 'idrange', + widgets: [ + 'cn', + 'ipabaseid', + 'ipaidrangesize', + 'ipabaserid' + ] + }, + { + type: 'multiple_choice_section', + name: 'type', + label: IPA.messages.objects.idrange.type, + choices: [ + { + name: 'local', + label: IPA.messages.objects.idrange.type_local, + fields: ['ipasecondarybaserid'], + required: ['ipasecondarybaserid'], + enabled: true + }, + { + name: 'ad', + label: IPA.messages.objects.idrange.type_ad, + fields: ['ipanttrusteddomainsid'], + required: ['ipanttrusteddomainsid'] + } + ], + widgets: [ + 'ipasecondarybaserid', + 'ipanttrusteddomainsid' + ] + } + ], + policies: [ + IPA.multiple_choice_section_policy({ + widget: 'type' + }) + ] + }); + }; + + return that; +}; + +IPA.register('idrange', IPA.idrange.entity); diff --git a/install/ui/src/freeipa/ipa.js b/install/ui/src/freeipa/ipa.js new file mode 100644 index 000000000..cc62f0897 --- /dev/null +++ b/install/ui/src/freeipa/ipa.js @@ -0,0 +1,2122 @@ +/*jsl:import jquery.ordered-map.js */ +/* Authors: + * Pavel Zuna <pzuna@redhat.com> + * Adam Young <ayoung@redhat.com> + * Endi Dewata <edewata@redhat.com> + * John Dennis <jdennis@redhat.com> + * Petr Vobornik <pvoborni@redhat.com> + * + * Copyright (C) 2010 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/>. +*/ + + +/* REQUIRES: jquery.ordered-map.js */ +/*global $:true, location:true */ + +var IPA = function() { + + var that = { + jsonrpc_id: 0 + }; + + // live server path + that.url = '/ipa/ui/'; + + that.ajax_options = { + type: 'POST', + contentType: 'application/json', + dataType: 'json', + async: true, + processData: false + }; + + that.metadata = {}; + that.messages = {}; + that.whoami = {}; + + that.entities = $.ordered_map(); + that.entity_factories = {}; + that.field_factories = {}; + that.widget_factories = {}; + + that.network_call_count = 0; + + that.ui = {}; + + /* initialize the IPA JSON-RPC helper */ + that.init = function(params) { + + // if current path matches live server path, use live data + if (that.url && window.location.pathname.substring(0, that.url.length) === that.url) { + that.json_url = params.url || '/ipa/session/json'; + that.login_url = params.url || '/ipa/session/login_kerberos'; + + } else { // otherwise use fixtures + that.json_path = params.url || "test/data"; + // that.login_url is not needed for fixtures + } + + $.ajaxSetup(that.ajax_options); + + var batch = IPA.batch_command({ + name: 'ipa_init', + retry: false, + on_success: function() { + that.init_metadata({ + on_success: params.on_success, + on_error: params.on_error + }); + }, + on_error: function(xhr, text_status, error_thrown) { + + // On IE the request is missing after authentication, + // so the request needs to be resent. + if (error_thrown.code === 909) { + batch.execute(); + + } else { + var ajax = this; + + var dialog = IPA.error_dialog({ + xhr: xhr, + text_status: text_status, + error_thrown: error_thrown, + command: batch + }); + + dialog.on_cancel = function() { + dialog.close(); + if (params.on_error) { + params.on_error.call(ajax, xhr, text_status, error_thrown); + } + }; + + dialog.open(); + } + } + }); + + batch.add_command(IPA.command({ + method: 'i18n_messages', + on_success: function(data, text_status, xhr) { + that.messages = data.messages; + } + })); + + batch.add_command(IPA.command({ + entity: 'config', + method: 'show', + on_success: function(data, text_status, xhr) { + that.server_config = data.result; + } + })); + + batch.add_command(that.get_whoami_command(true)); + + batch.add_command(IPA.command({ + method: 'env', + on_success: function(data, text_status, xhr) { + that.env = data.result; + that.version = that.env.version; + } + })); + + batch.add_command(IPA.command({ + entity: 'dns', + method: 'is_enabled', + on_success: function(data, text_status, xhr) { + that.dns_enabled = data.result; + } + })); + + + + batch.execute(); + }; + + that.get_whoami_command = function(batch) { + return IPA.command({ + entity: 'user', + method: 'find', + options: { + whoami: true, + all: true + }, + on_success: function(data, text_status, xhr) { + that.whoami = batch ? data.result[0] : data.result.result[0]; + that.principal = that.whoami.krbprincipalname[0]; + } + }); + }; + + that.init_metadata = function(params) { + + var objects = IPA.command({ + name: 'ipa_init_objects', + method: 'json_metadata', + options: { + object: 'all' + }, + on_success: function(data, text_status, xhr) { + that.metadata.objects = data.result.objects; + } + }); + + var commands = IPA.command({ + name: 'ipa_init_commands', + method: 'json_metadata', + options: { + command: 'all' + }, + on_success: function(data, text_status, xhr) { + that.metadata.commands = data.result.commands; + } + }); + + var metadata_command = IPA.concurrent_command({ + commands: [ + objects, + commands + ], + on_success: function(data, text_status, xhr) { + IPA.ui.initialized = true; + if (params.on_success) { + params.on_success.call(this, data, text_status, xhr); + } + }, + on_error: params.on_error + }); + + metadata_command.execute(); + }; + + that.register = function(name, factory) { + that.remove_entity(name); + that.entity_factories[name] = factory; + }; + + that.create_entity = function(name) { + var factory = that.entity_factories[name]; + if (!factory) return null; + + try { + var builder = IPA.entity_builder(); + + builder.entity({ + factory: factory, + name: name + }); + + var entity = builder.build(); + entity.init(); + + return entity; + + } catch (e) { + if (e.expected) { + /*expected exceptions thrown by builder just mean that + entities are not to be registered. */ + return null; + } + + if (e.message) { + alert(e.message); + } else { + alert(e); + } + + return null; + } + }; + + that.get_entities = function() { + return that.entities.values; + }; + + that.get_entity = function(name) { + if (typeof name === 'object') return name; + var entity = that.entities.get(name); + if (!entity) { + entity = that.create_entity(name); + if (entity) that.add_entity(entity); + } + return entity; + }; + + that.add_entity = function(entity) { + that.entities.put(entity.name, entity); + }; + + that.remove_entity = function(name) { + that.entities.remove(name); + }; + + that.display_activity_icon = function() { + that.network_call_count++; + $('.network-activity-indicator').css('visibility', 'visible'); + }; + + that.hide_activity_icon = function() { + that.network_call_count--; + + if (0 === that.network_call_count) { + $('.network-activity-indicator').css('visibility', 'hidden'); + } + }; + + that.get_message = function(id, default_message) { + var messages = IPA.messages; + var keys = id.split(/\./); + + for (var i=0; messages && i<keys.length; i++) { + var key = keys[i]; + var value = messages[key]; + + // undefined key => not found + if (!value) return default_message; + + // if value is string + if (typeof value === 'string') { + + // and it's the last key => found + if (i === keys.length-1) return value; + + // otherwise value should have been a container => not found + return default_message; + } + + // value is container => check next key + messages = value; + } + + // no more keys/messages => not found + return default_message; + }; + + return that; +}(); + +IPA.get_credentials = function() { + var status; + + function error_handler(xhr, text_status, error_thrown) { + status = xhr.status; + IPA.ui.logged_kerberos = false; + } + + function success_handler(data, text_status, xhr) { + status = xhr.status; + IPA.ui.logged_kerberos = true; + } + + var request = { + url: IPA.login_url, + cache: false, + async: false, + type: "GET", + success: success_handler, + error: error_handler + }; + + $.ajax(request); + + return status; +}; + +IPA.logout = function() { + + function show_error(message) { + var dialog = IPA.message_dialog({ + name: 'logout_error', + message: message, + title: IPA.messages.login.logout_error + }); + dialog.open(); + } + + function redirect () { + window.location = 'logout.html'; + } + + function success_handler(data, text_status, xhr) { + if (data && data.error) { + show_error(data.error.message); + } else { + redirect(); + } + } + + function error_handler(xhr, text_status, error_thrown) { + if (xhr.status === 401) { + redirect(); + } else { + show_error(text_status); + } + } + + var command = { + method: 'session_logout', + params: [[], {}] + }; + + var request = { + url: IPA.json_url || IPA.json_path + '/session_logout.json', + data: JSON.stringify(command), + success: success_handler, + error: error_handler + }; + + $.ajax(request); +}; + +IPA.login_password = function(username, password) { + + var result = 'invalid'; + + function success_handler(data, text_status, xhr) { + result = 'success'; + IPA.ui.logged_password = true; + } + + function error_handler(xhr, text_status, error_thrown) { + + if (xhr.status === 401) { + var reason = xhr.getResponseHeader("X-IPA-Rejection-Reason"); + + //change result from invalid only if we have a header which we + //understand + if (reason === 'password-expired' || reason === 'denied') { + result = reason; + } + } + + IPA.ui.logged_password = false; + } + + var data = { + user: username, + password: password + }; + + var request = { + url: '/ipa/session/login_password', + data: data, + contentType: 'application/x-www-form-urlencoded', + processData: true, + dataType: 'html', + async: false, + type: 'POST', + success: success_handler, + error: error_handler + }; + + IPA.display_activity_icon(); + $.ajax(request); + IPA.hide_activity_icon(); + + return result; +}; + +IPA.reset_password = function(username, old_password, new_password) { + + //possible results: 'ok', 'invalid-password', 'policy-error' + + var status, result, reason, invalid, failure, data, request; + + status = 'invalid'; + result = { + status: status, + message: IPA.get_message('password.reset_failure', + "Password reset was not successful.") + }; + + function success_handler(data, text_status, xhr) { + + result.status = xhr.getResponseHeader("X-IPA-Pwchange-Result") || status; + + if (result.status === 'policy-error') { + result.message = xhr.getResponseHeader("X-IPA-Pwchange-Policy-Error"); + } else if (result.status === 'invalid-password') { + result.message = IPA.get_message('password.invalid_password', + "The password or username you entered is incorrect."); + } + + return result; + } + + function error_handler(xhr, text_status, error_thrown) { + return result; + } + + data = { + user: username, + old_password: old_password, + new_password: new_password + }; + + request = { + url: '/ipa/session/change_password', + data: data, + contentType: 'application/x-www-form-urlencoded', + processData: true, + dataType: 'html', + async: false, + type: 'POST', + success: success_handler, + error: error_handler + }; + + IPA.display_activity_icon(); + $.ajax(request); + IPA.hide_activity_icon(); + + return result; +}; + +IPA.update_password_expiration = function() { + + var now, expires, notify_days, diff, message, container; + + expires = IPA.whoami.krbpasswordexpiration; + expires = expires ? IPA.parse_utc_date(expires[0]) : null; + + notify_days = IPA.server_config.ipapwdexpadvnotify; + notify_days = notify_days ? notify_days[0] : 0; + + now = new Date(); + + container = $('.header-passwordexpires'); + container.empty(); + + if (expires) { + + diff = expires.getTime() - now.getTime(); + diff = Math.floor(diff / 86400000); + + if (diff <= notify_days) { + message = IPA.messages.password.expires_in; + message = message.replace('${days}', diff); + container.append(message + ' '); + $('<a/>', { + href: '#reset-password', + click: function() { + IPA.password_selfservice(); + return false; + }, + text: IPA.messages.password.reset_password_sentence, + title: IPA.messages.password.reset_password + }).appendTo(container); + } + } +}; + +IPA.password_selfservice = function() { + var reset_dialog = IPA.user_password_dialog({ + self_service: true, + on_success: function() { + var command = IPA.get_whoami_command(); + var orig_on_success = command.on_success; + command.on_success = function(data, text_status, xhr) { + orig_on_success.call(this, data, text_status, xhr); + IPA.update_password_expiration(); + }; + command.execute(); + + alert(IPA.messages.password.password_change_complete); + reset_dialog.close(); + } + }); + reset_dialog.open(); +}; + +IPA.parse_utc_date = function(value) { + + if (!value) return null; + + // verify length + if (value.length != 'YYYYmmddHHMMSSZ'.length) { + return null; + } + + // We only handle GMT + if (value.charAt(value.length -1) !== 'Z') { + return null; + } + + var date = new Date(); + + date.setUTCFullYear( + value.substring(0, 4), // YYYY + value.substring(4, 6)-1, // mm (0-11) + value.substring(6, 8)); // dd (1-31) + + date.setUTCHours( + value.substring(8, 10), // HH (0-23) + value.substring(10, 12), // MM (0-59) + value.substring(12, 14)); // SS (0-59) + + return date; +}; + +/** + * 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 + */ +IPA.command = function(spec) { + + spec = spec || {}; + + var that = {}; + + that.name = spec.name; + + that.entity = spec.entity; + that.method = spec.method; + + that.args = $.merge([], spec.args || []); + that.options = $.extend({}, spec.options || {}); + + that.on_success = spec.on_success; + that.on_error = spec.on_error; + + that.retry = typeof spec.retry == 'undefined' ? true : spec.retry; + + that.error_message = spec.error_message || IPA.get_message('dialogs.batch_error_message', 'Some operations failed.'); + that.error_messages = $.ordered_map({ + 911: 'Missing HTTP referer. <br/> You have to configure your browser to send HTTP referer header.' + }); + + that.get_command = function() { + return (that.entity ? that.entity+'_' : '') + that.method; + }; + + that.add_arg = function(arg) { + that.args.push(arg); + }; + + that.add_args = function(args) { + $.merge(that.args, args); + }; + + that.set_option = function(name, value) { + that.options[name] = value; + }; + + that.set_options = function(options) { + $.extend(that.options, options); + }; + + that.add_option = function(name, value) { + var values = that.options[name]; + if (!values) { + values = []; + that.options[name] = values; + } + values.push(value); + }; + + that.get_option = function(name) { + return that.options[name]; + }; + + that.remove_option = function(name) { + delete that.options[name]; + }; + + that.execute = function() { + + function dialog_open(xhr, text_status, error_thrown) { + + var ajax = this; + + var dialog = IPA.error_dialog({ + xhr: xhr, + text_status: text_status, + error_thrown: error_thrown, + command: that + }); + + dialog.on_cancel = function() { + dialog.close(); + if (that.on_error) { + that.on_error.call(ajax, xhr, text_status, error_thrown); + } + }; + + dialog.open(); + } + + function auth_dialog_open(xhr, text_status, error_thrown) { + + var ajax = this; + + var dialog = IPA.unauthorized_dialog({ + xhr: xhr, + text_status: text_status, + error_thrown: error_thrown, + close_on_escape: false, + command: that + }); + + dialog.open(); + } + + /* + * Special error handler used the first time this command is + * submitted. It checks to see if the session credentials need + * to be acquired and if so sends a request to a special url + * to establish the sesion credentials. If acquiring the + * session credentials is successful it simply resubmits the + * exact same command after setting the error handler back to + * the normal error handler. If aquiring the session + * credentials fails the normal error handler is invoked to + * process the error returned from the attempt to aquire the + * session credentials. + */ + function error_handler_login(xhr, text_status, error_thrown) { + if (xhr.status === 401) { + var login_status = IPA.get_credentials(); + + if (login_status === 200) { + that.request.error = error_handler; + $.ajax(that.request); + return; + } + } + // error_handler() calls IPA.hide_activity_icon() + error_handler.call(this, xhr, text_status, error_thrown); + } + + /* + * Normal error handler, handles all errors. + * error_handler_login() is initially used to trap the + * special case need to aquire session credentials, this is + * not a true error, rather it's an indication an extra step + * needs to be taken before normal processing can continue. + */ + function error_handler(xhr, text_status, error_thrown) { + + IPA.hide_activity_icon(); + + if (xhr.status === 401) { + auth_dialog_open(xhr, text_status, error_thrown); + return; + } else if (!error_thrown) { + error_thrown = { + name: xhr.responseText || IPA.get_message('errors.unknown_error', 'Unknown Error'), + message: xhr.statusText || IPA.get_message('errors.unknown_error', 'Unknown Error') + }; + + } else if (typeof error_thrown == 'string') { + error_thrown = { + name: error_thrown, + message: error_thrown + }; + } + + // custom messages for set of codes + var error_msg = that.error_messages.get(error_thrown.code); + if (error_msg) { + error_msg = error_msg.replace('${message}', error_thrown.message); + error_thrown.message = error_msg; + } + + // global specical cases error handlers section + + // With trusts, user from trusted domain can use his ticket but he + // doesn't have rights for LDAP modify. It will throw internal errror. + // We should offer form base login. + if (xhr.status === 500 && IPA.ui.logged_kerberos && !IPA.ui.initialized) { + auth_dialog_open(xhr, text_status, error_thrown); + return; + } + + if (that.retry) { + dialog_open.call(this, xhr, text_status, error_thrown); + + } else if (that.on_error) { + //custom error handling, maintaining AJAX call's context + that.on_error.call(this, xhr, text_status, error_thrown); + } + } + + function success_handler(data, text_status, xhr) { + + if (!data) { + // error_handler() calls IPA.hide_activity_icon() + error_handler.call(this, xhr, text_status, /* error_thrown */ { + name: IPA.get_message('errors.http_error', 'HTTP Error')+' '+xhr.status, + url: this.url, + message: data ? xhr.statusText : IPA.get_message('errors.no_response', 'No response') + }); + + } else if (IPA.version && data.version && IPA.version !== data.version) { + window.location.reload(); + + } else if (IPA.principal && data.principal && IPA.principal !== data.principal) { + window.location.reload(); + + } else if (data.error) { + // error_handler() calls IPA.hide_activity_icon() + error_handler.call(this, xhr, text_status, /* error_thrown */ { + name: IPA.get_message('errors.ipa_error', 'IPA Error')+' '+data.error.code, + code: data.error.code, + message: data.error.message, + data: data + }); + + } else { + IPA.hide_activity_icon(); + + var ajax = this; + var failed = that.get_failed(that, data.result, text_status, xhr); + if (!failed.is_empty()) { + var dialog = IPA.error_dialog({ + xhr: xhr, + text_status: text_status, + error_thrown: { + name: IPA.get_message('dialogs.batch_error_title', 'Operations Error'), + message: that.error_message + }, + command: that, + errors: failed.errors, + visible_buttons: ['ok'] + }); + + dialog.on_ok = function() { + dialog.close(); + if (that.on_success) that.on_success.call(ajax, data, text_status, xhr); + }; + + dialog.open(); + + } else { + //custom success handling, maintaining AJAX call's context + if (that.on_success) that.on_success.call(this, data, text_status, xhr); + } + } + } + + that.data = { + method: that.get_command(), + params: [that.args, that.options] + }; + + that.request = { + url: IPA.json_url || IPA.json_path + '/' + (that.name || that.data.method) + '.json', + data: JSON.stringify(that.data), + success: success_handler, + error: error_handler_login + }; + + IPA.display_activity_icon(); + $.ajax(that.request); + }; + + that.get_failed = function(command, result, text_status, xhr) { + var errors = IPA.error_list(); + if(result && result.failed) { + for(var association in result.failed) { + for(var member_name in result.failed[association]) { + var member = result.failed[association][member_name]; + for(var i = 0; i < member.length; i++) { + if(member[i].length > 1) { + var name = IPA.get_message('errors.ipa_error', 'IPA Error'); + var message = member[i][1]; + if(member[i][0]) + message = member[i][0] + ': ' + message; + errors.add(command, name, message, text_status); + } + } + } + } + } + return errors; + }; + + that.check_option = function(option_name) { + + var metadata = IPA.get_command_option(that.get_command(), option_name); + return metadata !== null; + }; + + that.to_json = function() { + var json = {}; + + json.method = that.get_command(); + + json.params = []; + json.params[0] = that.args || []; + json.params[1] = that.options || {}; + + return json; + }; + + that.to_string = function() { + var string = that.get_command().replace(/_/g, '-'); + + for (var i=0; i<that.args.length; i++) { + string += ' '+that.args[i]; + } + + for (var name in that.options) { + string += ' --'+name+'=\''+that.options[name]+'\''; + } + + return string; + }; + + return that; +}; + +IPA.batch_command = function (spec) { + + spec = spec || {}; + + spec.method = 'batch'; + + var that = IPA.command(spec); + + that.commands = []; + that.errors = IPA.error_list(); + that.show_error = typeof spec.show_error == 'undefined' ? + true : spec.show_error; + + that.add_command = function(command) { + that.commands.push(command); + that.add_arg(command.to_json()); + }; + + that.add_commands = function(commands) { + for (var i=0; i<commands.length; i++) { + that.add_command(commands[i]); + } + }; + + that.execute = function() { + that.errors.clear(); + + var command = IPA.command({ + name: that.name, + entity: that.entity, + method: that.method, + args: that.args, + options: that.options, + retry: that.retry + }); + + command.on_success = that.batch_command_on_success; + command.on_error = that.batch_command_on_error; + + command.execute(); + }; + + that.batch_command_on_success = function(data, text_status, xhr) { + + for (var i=0; i<that.commands.length; i++) { + var command = that.commands[i]; + var result = data.result.results[i]; + + var name = ''; + var message = ''; + + if (!result) { + name = IPA.get_message('errors.internal_error', 'Internal Error')+' '+xhr.status; + message = result ? xhr.statusText : IPA.get_message('errors.internal_error', 'Internal Error'); + + that.errors.add(command, name, message, text_status); + + if (command.on_error) command.on_error.call( + this, + xhr, + text_status, + { + name: name, + message: message + } + ); + + } else if (result.error) { + var code = result.error.code || result.error_code; + name = IPA.get_message('errors.ipa_error', 'IPA Error')+(code ? ' '+code : ''); + message = result.error.message || result.error; + + if (command.retry) that.errors.add(command, name, message, text_status); + + if (command.on_error) command.on_error.call( + this, + xhr, + text_status, + { + name: name, + code: code, + message: message, + data: result + } + ); + + } else { + var failed = that.get_failed(command, result, text_status, xhr); + that.errors.add_range(failed); + + if (command.on_success) command.on_success.call(this, result, text_status, xhr); + } + } + + //check for partial errors and show error dialog + if (that.show_error && that.errors.errors.length > 0) { + var ajax = this; + var dialog = IPA.error_dialog({ + xhr: xhr, + text_status: text_status, + error_thrown: { + name: IPA.get_message('dialogs.batch_error_title', 'Operations Error'), + message: that.error_message + }, + command: that, + errors: that.errors.errors, + visible_buttons: [ 'ok' ] + }); + + dialog.on_ok = function() { + dialog.close(); + if (that.on_success) that.on_success.call(ajax, data, text_status, xhr); + }; + + dialog.open(); + + } else { + if (that.on_success) that.on_success.call(this, data, text_status, xhr); + } + }; + + that.batch_command_on_error = function(xhr, text_status, error_thrown) { + // TODO: undefined behavior + if (that.on_error) { + that.on_error.call(this, xhr, text_status, error_thrown); + } + }; + + return that; +}; + + +IPA.concurrent_command = function(spec) { + + spec = spec || {}; + var that = {}; + + that.commands = []; + that.on_success = spec.on_success; + that.on_error = spec.on_error; + + that.add_commands = function(commands) { + + if(commands && commands.length) { + for(var i=0; i < commands.length; i++) { + that.commands.push({ + command: commands[i] + }); + } + } + }; + + that.execute = function() { + + var command_info, command, i; + + //prepare for execute + for(i=0; i < that.commands.length; i++) { + command_info = that.commands[i]; + command = command_info.command; + if(!command) { + var dialog = IPA.message_dialog({ + name: 'internal_error', + title: IPA.get_message('errors.error', 'Error'), + message: IPA.get_message('errors.internal_error', 'Internal error.') + }); + break; + } + command_info.completed = false; + command_info.success = false; + command_info.on_success = command_info.on_success || command.on_success; + command_info.on_error = command_info.on_error || command.on_error; + command.on_success = function(command_info) { + return function(data, text_status, xhr) { + that.success_handler.call(this, command_info, data, text_status, xhr); + }; + }(command_info); + command.on_error = function(command_info) { + return function(xhr, text_status, error_thrown) { + that.error_handler.call(this, command_info, xhr, text_status, error_thrown); + }; + }(command_info); + } + + //execute + for(i=0; i < that.commands.length; i++) { + command = that.commands[i].command; + command.execute(); + } + }; + + that.error_handler = function(command_info, xhr, text_status, error_thrown) { + + command_info.completed = true; + command_info.success = false; + command_info.xhr = xhr; + command_info.text_status = text_status; + command_info.error_thrown = error_thrown; + command_info.context = this; + that.command_completed(); + }; + + that.success_handler = function(command_info, data, text_status, xhr) { + + command_info.completed = true; + command_info.success = true; + command_info.data = data; + command_info.text_status = text_status; + command_info.xhr = xhr; + command_info.context = this; + that.command_completed(); + }; + + that.command_completed = function() { + + var all_completed = true; + var all_success = true; + + for(var i=0; i < that.commands.length; i++) { + var command_info = that.commands[i]; + all_completed = all_completed && command_info.completed; + all_success = all_success && command_info.success; + } + + if(all_completed) { + if(all_success) { + that.on_success_all(); + } else { + that.on_error_all(); + } + } + }; + + that.on_success_all = function() { + + for(var i=0; i < that.commands.length; i++) { + var command_info = that.commands[i]; + if(command_info.on_success) { + command_info.on_success.call( + command_info.context, + command_info.data, + command_info.text_status, + command_info.xhr); + } + } + + if(that.on_success) { + that.on_success(); + } + }; + + that.on_error_all = function() { + + if(that.on_error) { + that.on_error(); + + } else { + var dialog = IPA.message_dialog({ + name: 'operation_error', + title: IPA.get_message('dialogs.batch_error_title', 'Operations Error'), + message: IPA.get_message('dialogs.batch_error_message', 'Some operations failed.') + }); + + dialog.open(); + } + }; + + that.add_commands(spec.commands); + + return that; +}; + +IPA.builder = function(spec) { + + spec = spec || {}; + + var that = {}; + + that.factory = spec.factory || IPA.default_factory; + + that.build = function(spec) { + + var factory = spec.factory || that.factory; + + //when spec is a factory function + if (!spec.factory && typeof spec === 'function') { + factory = spec; + spec = {}; + } + + var obj = factory(spec); + return obj; + }; + + that.build_objects = function(specs) { + + var objects = []; + + for (var i=0; i<specs.length; i++) { + var spec = specs[i]; + var obj = that.build(spec); + objects.push(obj); + } + + return objects; + }; + + return that; +}; + +IPA.build = function(spec, builder_fac) { + + if (!spec) return null; + + if (!builder_fac) builder_fac = IPA.builder; + + var builder = builder_fac(); + var product; + + if ($.isArray(spec)) { + product = builder.build_objects(spec); + } else { + product = builder.build(spec); + } + + return product; +}; + +IPA.build_default = function(spec, def_spec) { + + var builder, factory, default_object; + + if (!spec && !def_spec) return null; + + if (typeof def_spec === 'function') { //factory function + factory = def_spec; + } else if (typeof def_spec === 'object') { + default_object = def_spec; + } + + builder = IPA.builder({ + factory: factory + }); + + var product; + spec = spec || default_object || {}; + + if ($.isArray(spec)) { + product = builder.build_objects(spec); + } else { + product = builder.build(spec); + } + + return product; +}; + +IPA.default_factory = function(spec) { + + spec = spec || {}; + + var that = {}; + + $.extend(that, spec); + + return that; +}; + +/* helper function used to retrieve information about an attribute */ +IPA.get_entity_param = function(entity_name, name) { + + var metadata = IPA.metadata.objects[entity_name]; + if (!metadata) { + return null; + } + + var params = metadata.takes_params; + if (!params) { + return null; + } + + for (var i=0; i<params.length; i++) { + if (params[i].name === name) { + return params[i]; + } + } + + return null; +}; + +IPA.get_command_arg = function(command_name, arg_name) { + + var metadata = IPA.metadata.commands[command_name]; + if (!metadata) { + return null; + } + + var args = metadata.takes_args; + if (!args) { + return null; + } + + for (var i=0; i<args.length; i++) { + if (args[i].name === arg_name) { + return args[i]; + } + } + + return null; +}; + +IPA.get_command_option = function(command_name, option_name) { + + var metadata = IPA.metadata.commands[command_name]; + if (!metadata) { + return null; + } + + var options = metadata.takes_options; + if (!options) { + return null; + } + + for (var i=0; i<options.length; i++) { + if (options[i].name === option_name) { + return options[i]; + } + } + + return null; +}; + +/* helper function used to retrieve attr name with members of type `member` */ +IPA.get_member_attribute = function(obj_name, member) { + + var obj = IPA.metadata.objects[obj_name]; + if (!obj) { + return null; + } + + var attribute_members = obj.attribute_members; + for (var a in attribute_members) { + var objs = attribute_members[a]; + for (var i = 0; i < objs.length; i += 1) { + if (objs[i] === member){ + return a; + } + } + } + + return null; +}; + +IPA.create_network_spinner = function(){ + var span = $('<span/>', { + 'class': 'network-activity-indicator' + }); + $('<img/>', { + src: 'images/spinner-small.gif' + }).appendTo(span); + return span; +}; + +IPA.dirty_dialog = function(spec) { + + spec = spec || {}; + spec.title = spec.title || IPA.messages.dialogs.dirty_title; + spec.width = spec.width || '25em'; + + var that = IPA.dialog(spec); + that.facet = spec.facet; + that.message = spec.message || IPA.messages.dialogs.dirty_message; + + that.create = function() { + that.container.append(that.message); + }; + + that.create_button({ + name: 'update', + label: IPA.messages.buttons.update, + click: function() { + that.facet.update(function() { + that.close(); + that.callback(); + }); + } + }); + + that.create_button({ + name: 'reset', + label: IPA.messages.buttons.reset, + click: function() { + that.facet.reset(); + that.close(); + that.callback(); + } + }); + + that.create_button({ + name: 'cancel', + label: IPA.messages.buttons.cancel, + click: function() { + that.close(); + } + }); + + that.callback = function() { + }; + + return that; +}; + +IPA.error_dialog = function(spec) { + + spec = spec || {}; + + spec.id = spec.id || 'error_dialog'; + spec.title = spec.error_thrown.name; + + var that = IPA.dialog(spec); + + IPA.confirm_mixin().apply(that); + + that.xhr = spec.xhr || {}; + that.text_status = spec.text_status || ''; + that.error_thrown = spec.error_thrown || {}; + that.command = spec.command; + that.errors = spec.errors; + that.visible_buttons = spec.visible_buttons || ['retry', 'cancel']; + + + 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', + text: lines[i].substr(1) + }).appendTo(container); + } else { + line_span = $('<p />', { + text: lines[i] + }).appendTo(container); + } + } + }; + + that.create = function() { + if (that.error_thrown.url) { + $('<p/>', { + text: IPA.get_message('errors.url', 'URL')+': '+that.error_thrown.url + }).appendTo(that.container); + } + + var error_message = $('<div />', {}); + that.beautify_message(error_message, that.error_thrown.message); + error_message.appendTo(that.container); + + if(that.errors && that.errors.length > 0) { + //render errors + var errors_title_div = $('<div />', { + 'class': 'errors_title' + }).appendTo(that.container); + + var show_details = $('<a />', { + href: '#', + title: IPA.messages.dialogs.show_details, + text: IPA.messages.dialogs.show_details + }).appendTo(errors_title_div); + + var hide_details = $('<a />', { + href: '#', + title: IPA.messages.dialogs.hide_details, + text: IPA.messages.dialogs.hide_details, + style : 'display: none' + }).appendTo(errors_title_div); + + var errors_container = $('<ul />', { + 'class' : 'error-container', + style : 'display: none' + }).appendTo(that.container); + + for(var i=0; i < that.errors.length; i++) { + var error = that.errors[i]; + if(error.message) { + var error_div = $('<li />', {}); + that.beautify_message(error_div, error.message); + error_div.appendTo(errors_container); + } + } + + show_details.click(function() { + errors_container.show(); + show_details.hide(); + hide_details.show(); + return false; + }); + + hide_details.click(function() { + errors_container.hide(); + hide_details.hide(); + show_details.show(); + return false; + }); + } + }; + + 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. + */ + + var visible = that.visible_buttons.indexOf('retry') > -1; + var label = IPA.get_message('buttons.retry', 'Retry'); + that.create_button({ + name: 'retry', + label: label, + visible: visible, + click: function() { + that.on_retry(); + } + }); + + visible = that.visible_buttons.indexOf('ok') > -1; + label = IPA.get_message('buttons.ok', 'OK'); + that.create_button({ + name: 'ok', + label: label, + visible: visible, + click: function() { + that.on_ok(); + } + }); + + visible = that.visible_buttons.indexOf('cancel') > -1; + label = IPA.get_message('buttons.cancel', 'Cancel'); + that.create_button({ + name: 'cancel', + label: label, + visible: visible, + click: function() { + that.on_cancel(); + } + }); + }; + + that.on_retry = function() { + that.close(); + that.command.execute(); + }; + + that.on_ok = function() { + that.close(); + }; + + that.on_cancel = function() { + that.close(); + }; + + that.on_confirm = function() { + if (that.visible_buttons.indexOf('retry') > -1) that.on_retry(); + else that.on_ok(); + }; + + that.create_buttons(); + + return that; +}; + +IPA.error_list = function() { + var that = {}; + + that.clear = function() { + that.errors = []; + }; + + that.add = function(command, name, message, status) { + that.errors.push({ + command: command, + name: name, + message: message, + status: status + }); + }; + + that.add_range = function(error_list) { + that.errors = that.errors.concat(error_list.errors); + }; + + that.is_empty = function () { + return that.errors.length === 0; + }; + + that.clear(); + return that; +}; + +IPA.create_4304_error_handler = function(adder_dialog) { + + var set_pkey = function(result) { + + var pkey_name = adder_dialog.entity.metadata.primary_key; + var args = adder_dialog.command.args; + var pkey = args[args.length-1]; + result[pkey_name] = pkey; + }; + + return function (xhr, text_status, error_thrown) { + + var ajax = this; + var command = adder_dialog.command; + var data = error_thrown.data; + var dialog = null; + + if (data && data.error && data.error.code === 4304) { + dialog = IPA.message_dialog({ + name: 'error_4304_info', + message: data.error.message, + title: adder_dialog.title, + on_ok: function() { + data.result = { result: {} }; + set_pkey(data.result.result); + command.on_success.call(ajax, data, text_status, xhr); + } + }); + } else { + dialog = IPA.error_dialog({ + xhr: xhr, + text_status: text_status, + error_thrown: error_thrown, + command: command + }); + } + + dialog.open(adder_dialog.container); + }; +}; + +IPA.unauthorized_dialog = function(spec) { + + spec = spec || {}; + + spec.sections = [ + { + name: 'login', + label: 'Login', + fields: [ + { + name: 'username', + label: IPA.get_message('login.username', "Username") + }, + { + name: 'password', + type: 'password', + label: IPA.get_message('login.password', "Password") + } + ] + }, + { + name: 'reset', + label: 'Reset', + fields: [ + { + name: 'username_r', + read_only: true, + label: IPA.get_message('login.username', "Username") + }, + { + name: 'new_password', + type: 'password', + required: true, + label: IPA.get_message('password.new_password)', "New Password") + }, + { + name: 'verify_password', + type: 'password', + required: true, + label: IPA.get_message('password.verify_password', "Verify Password"), + validators: [IPA.same_password_validator({ + other_field: 'new_password' + })] + } + ] + } + ]; + + spec.visible_buttons = spec.visible_buttons || ['retry']; + spec.name = spec.name || 'unauthorized_dialog'; + spec.id = spec.id || spec.name; + + var that = IPA.error_dialog(spec); + + that.title = spec.title || IPA.get_message('login.login', "Login"); + + that.message = spec.message || IPA.get_message('ajax.401.message', + "Your session has expired. Please re-login."); + + that.form_auth_msg = spec.form_auth_msg || IPA.get_message('login.form_auth', + "To login with username and password, enter them in the fields below then click Login."); + + that.krb_auth_msg = spec.krb_auth_msg || IPA.get_message('login.krb_auth_msg', + " To login with Kerberos, please make sure you" + + " have valid tickets (obtainable via kinit) and " + + "<a href='http://${host}/ipa/config/unauthorized.html'>configured</a>" + + " the browser correctly, then click Login. "); + + that.krb_auth_msg = that.krb_auth_msg.replace('${host}', window.location.hostname); + + 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>"; + + that.password_expired = "Your password has expired. Please enter a new password."; + + that.denied = "Sorry you are not allowed to access this service."; + + that.create = function() { + + that.session_expired_form(); + that.create_reset_form(); + }; + + that.session_expired_form = function() { + that.session_form = $('<div\>').appendTo(that.container); + + that.login_error_box = $('<div/>', { + 'class': 'error-box', + style: 'display:none', + html: that.form_auth_failed + }).appendTo(that.session_form); + + $('<p/>', { + html: that.message + }).appendTo(that.session_form); + + $('<p/>', { + html: that.krb_auth_msg + }).appendTo(that.session_form); + + $('<p/>', { + html: that.form_auth_msg + }).appendTo(that.session_form); + + $('<div>', { + 'class': 'auth-dialog' + }).appendTo(that.session_form); + + + var section = that.widgets.get_widget('login'); + var div = $('<div/>', { + name: 'login', + 'class': 'dialog-section' + }).appendTo(that.session_form); + section.create(div); + + that.username_widget = that.widgets.get_widget('login.username'); + that.password_widget = that.widgets.get_widget('login.password'); + + that.username_widget.value_changed.attach(that.on_username_change); + }; + + that.create_reset_form = function() { + + that.reset_form = $('<div\>', { + style: 'display:none' + }).appendTo(that.container); + + that.reset_error_box = $('<div/>', { + 'class': 'error-box' + }).appendTo(that.reset_form); + + $('<p/>', { + html: that.password_expired + }).appendTo(that.reset_form); + + var section = that.widgets.get_widget('reset'); + var div = $('<div/>', { + name: 'reset', + 'class': 'dialog-section' + }).appendTo(that.reset_form); + section.create(div); + + that.username_r_widget = that.widgets.get_widget('reset.username_r'); + that.new_password_widget = that.widgets.get_widget('reset.new_password'); + that.verify_password_widget = that.widgets.get_widget('reset.verify_password'); + }; + + that.create_buttons = function() { + + that.buttons.empty(); + + var visible = that.visible_buttons.indexOf('login') > -1; + var label = IPA.get_message('login.login', "Login"); + that.create_button({ + name: 'login', + label: label, + visible: visible, + click: function() { + that.on_login(); + } + }); + + visible = that.visible_buttons.indexOf('reset') > -1; + label = IPA.get_message('buttons.reset_password_and_login', "Reset Password and Login"); + that.create_button({ + name: 'reset', + label: label, + visible: visible, + click: function() { + that.on_reset(); + } + }); + + visible = that.visible_buttons.indexOf('cancel') > -1; + label = IPA.get_message('buttons.cancel', "Cancel"); + that.create_button({ + name: 'cancel', + label: label, + visible: visible, + click: function() { + that.on_cancel(); + } + }); + }; + + that.open = function() { + that.dialog_open(); + that.show_session_form(); + that.check_error_reason(); + }; + + that.check_error_reason = function() { + if (this.xhr) { + var reason = this.xhr.getResponseHeader("X-IPA-Rejection-Reason"); + if (reason) { + that.show_login_error_message(reason); + } + } + }; + + that.on_username_change = function() { + + var password_field = that.fields.get_field('password'); + var user_specified = !IPA.is_empty(that.username_widget.save()); + password_field.set_required(user_specified); + if (!user_specified) that.password_widget.clear(); + }; + + that.enable_fields = function(field_names) { + + var field, fields, i, enable; + fields = that.fields.get_fields(); + for (i=0; i<fields.length; i++) { + field = fields[i]; + enable = field_names.indexOf(field.name) > -1; + field.set_enabled(enable); + } + }; + + that.show_session_form = function() { + + that.current_view = 'session'; + that.enable_fields(['username', 'password']); + that.session_form.css('display', 'block'); + that.reset_form.css('display', 'none'); + that.display_buttons(['login']); + that.username_widget.focus_input(); + }; + + that.show_reset_form = function() { + + that.current_view = 'reset'; + that.enable_fields(['new_password', 'verify_password']); + that.session_form.css('display', 'none'); + that.reset_form.css('display', 'block'); + that.display_buttons(['reset', 'cancel']); + + var username = that.username_widget.save(); + that.username_r_widget.update(username); + that.new_password_widget.focus_input(); + }; + + that.show_login_error_message = function(reason) { + var errors = { + 'invalid': that.form_auth_failed, + 'denied': that.denied + }; + + var message = errors[reason]; + + if (message) { + that.login_error_box.html(message); + that.login_error_box.css('display', 'block'); + } + }; + + that.on_cancel = function() { + + that.username_widget.clear(); + that.password_widget.clear(); + that.username_r_widget.clear(); + that.new_password_widget.clear(); + that.verify_password_widget.clear(); + + that.show_session_form(); + }; + + that.on_login = function() { + + var username = that.username_widget.save(); + var password = that.password_widget.save(); + + //if user doesn't specify username and password try kerberos auth + if (IPA.is_empty(username) && IPA.is_empty(password)) { + that.on_retry(); + return; + } + + if (!that.validate()) return; + + IPA.display_activity_icon(); + + var result = IPA.login_password(username[0], password[0]); + + IPA.hide_activity_icon(); + + if (result === 'success') { + that.on_login_success(); + } else if (result === 'password-expired') { + that.reset_error_box.css('display', 'none'); + that.show_reset_form(); + } else { + that.show_login_error_message(result); + } + }; + + that.on_login_success = function() { + that.login_error_box.css('display', 'none'); + + that.username_widget.clear(); + that.password_widget.clear(); + + that.on_retry(); + }; + + that.on_reset = function() { + if (!that.validate()) return; + + var username = that.username_widget.save(); + var password = that.password_widget.save(); + var new_password = that.new_password_widget.save(); + var verify_password = that.verify_password_widget.save(); + + that.reset_error_box.css('display', 'none'); + + var result = IPA.reset_password(username[0], + password[0], + new_password[0]); + + if (result.status === 'ok') { + that.on_reset_success(); + } else { + that.reset_error_box.html(result.message); + that.reset_error_box.css('display', 'block'); + } + }; + + that.on_reset_success = function() { + + that.login_error_box.css('display', 'none'); + that.reset_error_box.css('display', 'none'); + + that.password_widget.update(that.new_password_widget.save()); + + that.new_password_widget.clear(); + that.verify_password_widget.clear(); + + that.show_session_form(); + + //re-login + that.on_login(); + }; + + //replaces confirm_mixin method + that.on_key_up = function(event) { + + if (that.switching) { + that.switching = false; + return; + } + + if (that.current_view === 'session') { + if (event.keyCode === $.ui.keyCode.ENTER && !this.test_ignore(event)) { + that.on_login(); + event.preventDefault(); + } + } else { + if (event.keyCode === $.ui.keyCode.ENTER && !this.test_ignore(event)) { + that.on_reset(); + event.preventDefault(); + } else if (event.keyCode === $.ui.ESCAPE) { + that.on_cancel(); + event.preventDefault(); + } + } + }; + + that.create_buttons(); + + return that; +}; + +IPA.limit_text = function(value, max_length) { + + if (!value) return ''; + + var limited_text = value; + + if (value.length && value.length > max_length) { + limited_text = value.substring(0, max_length - 3)+'...'; + } + + return limited_text; +}; + +IPA.create_options = function(values) { + + var options = []; + + for (var i=0; i<values.length; i++) { + var val = values[i]; + var option = val; + + if (typeof val === 'string') { + option = { + value: val, + label: val + }; + } + + options.push(option); + } + + return options; +}; + +IPA.is_empty = function(value) { + + var empty = false; + + if (!value) empty = true; + + if (value instanceof Array) { + empty = empty || value.length === 0 || + (value.length === 1) && (value[0] === ''); + } + + if (value === '') empty = true; + + return empty; +}; + +IPA.defined = function(value, check_empty_str) { + return value !== null && value !== undefined && + ((check_empty_str && value !== '') || !check_empty_str); +}; + +IPA.array_diff = function(a, b) { + + if (a === b || (!a && !b)) return false; + + if (!a || !b) return true; + + if (a.length !== b.length) return true; + + for (var i=0; i<a.length; i++) { + if (a[i] !== b[i]) return true; + } + + return false; +}; + +IPA.confirm = function(msg) { + return window.confirm(msg); +}; + +IPA.notify_success = function(message, timeout) { + + if (!message) return; // don't show undefined, null and such + + function destroy_timeout() { + if (IPA.notify_success.timeout) window.clearTimeout(IPA.notify_success.timeout); + } + + var notification_area = $('.notification-area'); + + if (notification_area.length === 0) { + notification_area = $('<div/>', { + 'class': 'notification-area ui-corner-all ui-state-highlight', + click: function() { + destroy_timeout(); + notification_area.fadeOut(100); + } + }); + + notification_area.appendTo('#container'); + } + + notification_area.text(message); + + destroy_timeout(); + notification_area.fadeIn(IPA.config.message_fadein_time); + + IPA.notify_success.timeout = window.setTimeout(function() { + notification_area.fadeOut(IPA.config.message_fadeout_time); + }, timeout || IPA.config.message_timeout); +}; + +IPA.config = { + default_priority: 500, + message_timeout: 3000, // [ms] + message_fadeout_time: 800, // [ms] + message_fadein_time: 400 // [ms] +}; diff --git a/install/ui/src/freeipa/navigation.js b/install/ui/src/freeipa/navigation.js new file mode 100644 index 000000000..deef37dd8 --- /dev/null +++ b/install/ui/src/freeipa/navigation.js @@ -0,0 +1,455 @@ +/*jsl:import ipa.js */ + +/* Authors: + * Pavel Zuna <pzuna@redhat.com> + * Endi S. Dewata <edewata@redhat.com> + * + * Copyright (C) 2010 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/>. +*/ + +IPA.navigation = function(spec) { + + spec = spec || {}; + + var that = {}; + + that.name = spec.name; + + that.container = spec.container; + that.root = that.container.attr('id'); + + that.content = spec.content; + that.tab_class = spec.tab_class || 'tabs'; + that.max_depth = spec.max_depth || 3; + + that.tabs = []; + that.tabs_by_name = {}; + + that.path = {}; + + that.set_tabs = function(tabs) { + that.tabs = tabs; + that.tabs_by_name = {}; + + for (var i=0; i<tabs.length; i++) { + that.add_tab(tabs[i]); + } + }; + + that.add_tab = function(tab, parent) { + if (!tab.name) { + tab.name = tab.entity; + } + tab.parent = parent; + + that.tabs_by_name[tab.name] = tab; + + for (var i=0; tab.children && i<tab.children.length; i++) { + that.add_tab(tab.children[i], tab); + } + }; + + that.get_tab = function(name) { + return that.tabs_by_name[name]; + }; + + that.get_active_tab = function(state) { + var name = null; + var next = state[that.root]; + + while (next) { + name = next; + next = state[name]; + } + + return that.get_tab(name); + }; + + that.is_ancestor = function(tab, ancestor) { + var parent = tab.parent; + while (parent) { + if (parent == ancestor) return true; + parent = parent.parent; + } + return false; + }; + + that.get_path_state = function(name) { + + var path_state = {}; + + var tab = that.get_tab(name); + var parent = tab.parent; + + while (parent) { + path_state[parent.name] = tab.name; + + tab = parent; + parent = tab.parent; + } + + path_state[that.root] = tab.name; + + return path_state; + }; + + that.push_state = function(params) { + + var param_path = {}; + var param_state = {}; + + for (var key in params) { + var value = params[key]; + if (key.indexOf('-') < 0) { + param_path[key] = value; + } else { + param_state[key] = value; + } + } + + var state = {}; + + var prev_entity = IPA.current_entity; + var prev_facet = prev_entity ? prev_entity.facet : null; + + if (prev_facet) { + + if (prev_facet.is_dirty()) { + var dialog = IPA.dirty_dialog({ + facet: prev_facet + }); + + dialog.callback = function() { + + // Some facet's might not call reset before this call but after + // so they are still dirty. Calling reset prevent's opening of + // dirty dialog again. + if (prev_facet.is_dirty()) prev_facet.reset(); + $.bbq.pushState(params); + }; + + dialog.open(that.container); + + return false; + } + + // get prev facet state + $.extend(state, prev_facet.state); + } + + // merge existing path with new path + $.extend(that.path, param_path); + + // find the tab pointed by the path + var tab = that.get_active_tab(that.path); + + // find the active tab at the lowest level + while (!tab.entity) { + var index = tab.container.tabs('option', 'selected'); + tab = tab.children[index]; + } + + var facet_name; + if (tab.entity == prev_entity) { + // merge prev facet state with new state to find new facet name + $.extend(state, param_state); + facet_name = state[tab.entity.name+'-facet']; + + } else { + // find new facet name in the new state + facet_name = param_state[tab.entity.name+'-facet']; + } + + var facet = tab.entity.get_facet(facet_name); + + // update new facet state with new state + $.extend(facet.state, param_state); + + var entity = tab.entity.get_containing_entity(); + while (entity) { + var facet2 = entity.get_facet(); + + var key_names = entity.get_key_names(); + for (var i=0; i<key_names.length; i++) { + var key_name = key_names[i]; + var key_value = param_state[key_name]; + if (!key_value) key_value = facet2.state[key_name]; + if (key_value) facet.state[key_name] = key_value; + } + + entity = entity.get_containing_entity(); + } + + // push entity path and facet state + state = {}; + $.extend(state, that.get_path_state(tab.name)); + $.extend(state, facet.state); + $.bbq.pushState(state, 2); + + return true; + }; + + that.get_state = function(key) { + return $.bbq.getState(key); + }; + + that.remove_state = function(key) { + $.bbq.removeState(key); + }; + + that.show_tab = function(tab_name, pkey) { + + var tab = that.get_tab(tab_name); + + var state = that.get_path_state(tab.name); + + if (tab.entity) { + + if (tab.facet) { + state[tab.entity.name + '-facet'] = tab.facet; + } + + if (pkey) { + state[tab.entity.name + '-pkey'] = pkey; + } + } + + return that.push_state(state); + }; + + that.show_page = function(entity_name, facet_name, pkey) { + var state = that.get_path_state(entity_name); + + if (facet_name) { + state[entity_name + '-facet'] = facet_name; + } + + if (pkey) { + state[entity_name + '-pkey'] = pkey; + } + + return that.push_state(state); + }; + + /*like show page, but works for nested entities */ + that.show_entity_page = function(entity, facet_name, pkeys) { + var state = that.get_path_state(entity.name); + + if (facet_name) { + state[entity.name + '-facet'] = facet_name; + } + + if (pkeys) { + if (pkeys instanceof Array){ + var current_entity = entity; + while (current_entity){ + state[current_entity.name + '-pkey'] = pkeys.pop(); + current_entity = current_entity.get_containing_entity(); + } + }else{ + state[entity.name + '-pkey'] = pkeys; + } + } + + return that.push_state(state); + }; + + that.show_top_level_page = function() { + jQuery.bbq.pushState({}, 2); + }; + + that.get_tab_facet = function(tab_name) { + + var facet = null; + var tab = that.get_tab(tab_name); + + if (tab.entity) { + if (tab.facet) { + facet = tab.entity.get_facet(tab.facet); + } else { + facet = tab.entity.get_facet(tab.entity.redirect_facet); + } + } + + return facet; + }; + + + that.create = function() { + + var container = $('<div/>', { + name: that.root + }).appendTo(that.container); + + that._create(that.tabs, container, 1); + + var tabs = $('.' + that.tab_class, that.container); + tabs.tabs({ + select: function(event, ui) { + + // get the selected tab + var panel = $(ui.panel); + var name = panel.attr('name'); + var selected_tab = that.get_tab(name); + + // get the tab specified in the URL state + var state = that.get_state(); + var url_tab = that.get_active_tab(state); + + if (url_tab) { + // if they are the same, the selection is triggered by hash change + if (url_tab == selected_tab) { + // use the URL state to update internal state + return that.push_state(state); + + // if the selection is for the ancestor + } else if (that.is_ancestor(url_tab, selected_tab)) { + // let the tab be updated and don't change the state + return true; + } + } + + // selection is triggered by mouse click, update the URL state + return that.show_tab(name); + } + }); + }; + + that._create = function(tabs, container, depth) { + + var parent_name = container.attr('name'); + that.path[parent_name] = tabs[0].name; + + container.addClass(that.tab_class); + container.addClass('tabs'+depth); + + var ul = $('<ul/>').appendTo(container); + var created_count = 0; + + for (var i=0; i<tabs.length; i++) { + var tab = tabs[i]; + tab.container = container; + + var tab_id = that.root+'-'+tab.name; + + if (tab.entity) { + var entity = IPA.get_entity(tab.entity); + if (!entity){ + tabs.splice(i, 1); + i--; + continue; + } + tab.entity = entity; + + if (!tab.label) { + tab.label = entity.label; + } + } + + var tab_li = $('<li/>').append($('<a/>', { + href: '#'+tab_id, + title: tab.label, + html: tab.label + })); + + if (tab.hidden) { + tab_li.css('display', 'none'); + } + + tab.children_container = $('<div/>', { + id: tab_id, + name: tab.name + }); + + if (tab.children && tab.children.length) { + var kids = + that._create(tab.children, tab.children_container, depth+1); + /*If there are no child tabs, remove the container */ + if (kids === 0) { + tabs.splice(i, 1); + i -= 1; + continue; + } + } + created_count += 1; + tab_li.appendTo(ul); + tab.children_container.appendTo(container); + } + return created_count; + }; + + that.update = function() { + for (var i=1; i<=that.max_depth; i++) { + that.container.removeClass(that.tab_class+'-'+i); + that.content.removeClass(that.tab_class+'-'+i); + } + $('.entity', that.content).css('display', 'none'); + + var container = $('div[name='+that.root+']', that.container); + that._update(that.tabs, container, 1); + }; + + that._update = function(tabs, container, depth) { + + var parent_name = container.attr('name'); + var tab_name = that.get_state(parent_name); + if (!tab_name) tab_name = that.path[parent_name]; + that.path[parent_name] = tab_name; + + var index = 0; + while (index < tabs.length && tabs[index].name != tab_name) index++; + if (index >= tabs.length) index = 0; + + container.tabs('select', index); + + var tab = tabs[index]; + if (tab.depth !== undefined) { + depth += tab.depth; + } + + if (tab.children && tab.children.length) { + var next_depth = depth + 1; + that._update(tab.children, tab.children_container, next_depth); + + } else if (tab.entity) { + + that.container.addClass(that.tab_class+'-'+depth); + that.content.addClass(that.tab_class+'-'+depth); + + var entity_container = $('.entity[name="'+tab.entity.name+'"]', + that.content); + if (!entity_container.length) { + tab.content = $('<div/>', { + name: tab.entity.name, + title: tab.entity.label, + 'class': 'entity' + }).appendTo(that.content); + tab.entity.create(tab.content); + } + + entity_container.css('display', 'block'); + tab.entity.display(tab.content); + } + }; + + // methods that should be invoked by subclasses + that.navigation_update = that.update; + + that.set_tabs(spec.tabs); + + return that; +}; diff --git a/install/ui/src/freeipa/net.js b/install/ui/src/freeipa/net.js new file mode 100644 index 000000000..b8674919f --- /dev/null +++ b/install/ui/src/freeipa/net.js @@ -0,0 +1,394 @@ +/* Authors: + * Petr Vobornik <pvoborni@redhat.com> + * + * Copyright (C) 2010 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/>. + */ + +var NET = {}; + +NET.ip_address = function(spec) { + + spec = spec || {}; + + if (typeof spec === 'string') { + spec = { + address: spec + }; + } + + var that = {}; + + that.input = spec.address; + + that.type = spec.type; + that.parts = spec.parts; + that.reverse_address = ''; + that.only_decimal = spec.only_decimal !== undefined? spec.only_decimal : + false; //for parsing IPv4 address + + that.parse = function() { + + if (!that.input && !that.parts) { + that.set_error('no input'); + return false; + } + + if (!that.type) { + that.type = that.detect_type(); + } + + if (that.type === 'v4-quads') { + return that.parse_v4_quads(); + } else if (that.type === 'v4-int') { + return that.parse_v4_int(); + } else if (that.type === 'v6') { + return that.parse_v6(); + } + + that.set_error('not an ip address'); + return false; + }; + + that.detect_type = function() { + + var type; + + if (!that.input) return null; + + if (that.input.indexOf(':') > -1) type = 'v6'; + else if (that.input.indexOf('.') > -1) type = 'v4-quads'; + else type = 'v4-int'; + + return type; + }; + + that.parse_v4_int = function() { + + var part = { value: that.input }; + if(!that.is_part_valid_v4(part, 32, that.only_decimal)) return false; + + that.parts = []; + that.make_quads(part.decimal_value, that.parts); + + that.valid = true; + return true; + }; + + that.parse_v4_quads = function() { + + if (!that.parts) { + that.parts = that.input.split('.'); + } + + if (that.parts.length !== 4) { + return that.set_error('invalid number of parts'); + } + + for (var i=0; i<4; i++) { + + var part = { value: that.parts[i] }; + + if (!that.is_part_valid_v4(part, 8, that.only_decimal)) { + return false; + } + that.parts[i] = part.decimal_value.toString(10); + } + + that.valid = true; + return true; + }; + + that.parse_v6 = function() { + + if (!that.parts) { + that.parts = that.input.split(':'); + } + + var total_parts = that.parts.length; + var ipv4_present = false; + var double_colon = false; + var double_colon_position; + + var i; + + //usecases like ':' + if (that.parts.length <= 2) { + return that.set_error('invalid format'); + } + + for (i=0; i<that.parts.length; i++) { + var part = that.parts[i]; + + if (i === that.parts.length -1 && part.indexOf('.') > -1) { + ipv4_present = true; + total_parts++; //ipv4 part consists of 4 octects (two parts) + } + + //checking for :: + if (part.length === 0) { + + if (!double_colon || //first occurance + (double_colon && i === 1) || //still at the beginning + (double_colon && i === that.parts.length - 1 && + double_colon_position === i -1)) { //still at the end + + part = '0000'; + that.parts[i] = part; + double_colon = true; + double_colon_position = i; + } else { //second occurance of :: + return that.set_error('invalid format: mupltiple ::'); + } + } + + //add missing zeros for not empty parts + if (part.length !== 0 && part.length < 4) { + part = add_leading_zeros(part, 4 - part.length); + that.parts[i] = part; + } + } + + //add missing empty parts + if (double_colon) { + var parts_to_add = 8 - total_parts; + + for (i=0; i<parts_to_add; i++) { + that.parts.splice(double_colon_position, 0, '0000'); + } + } + + //change ipv4 part + if (ipv4_present) { + var ipv4_address = NET.ip_address(); + ipv4_address.input = that.parts[that.parts.length -1]; + ipv4_address.only_decimal = true; + if (ipv4_address.parse() && ipv4_address.type === 'v4-quads') { + var v4_parts = ipv4_address.parts; + var oct1 = dec_2_hex(v4_parts[0]); + var oct2 = dec_2_hex(v4_parts[1]); + var oct3 = dec_2_hex(v4_parts[2]); + var oct4 = dec_2_hex(v4_parts[3]); + + //replace IPv4 part with two IPv6 parts (4 octets) + that.parts[that.parts.length -1] = oct1+oct2; + that.parts.push(oct3+oct4); + } else { + return that.set_error('invalid IPv4 part'); + } + } + + //validate length after modifications + if (that.parts.length !== 8) { + return that.set_error('invalid number of parts'); + } + + //validate each part + for (i=0; i<8; i++) { + + if (!that.is_part_valid_v6(that.parts[i])) { + return false; + } + } + + that.valid = true; + return true; + }; + + function dec_2_hex(val) { + var dec = parseInt(val, 10); + var hex = dec.toString(16); + hex = add_leading_zeros(hex, 2 - hex.length); + return hex; + } + + function add_leading_zeros(val, num) { + for (var i=0; i<num; i++) { + val='0'+val; + } + return val; + } + + that.get_reverse = function() { + + if (!that.valid) return 'invalid input address'; + + if (that.type === 'v4-quads' || that.type === 'v4-int') { + return that.get_v4_reverse(); + } else if (that.type === 'v6') { + return that.get_v6_reverse(); + } + + return ''; + }; + + that.get_v4_reverse = function() { + + that.reverse_parts = []; + + for (var i=3; i>=0; i--) { + that.reverse_parts.push(that.parts[i]); + } + + that.reverse_parts.push('in-addr'); + that.reverse_parts.push('arpa'); + + return that.reverse_parts.join('.'); + }; + + that.get_v6_reverse = function() { + + that.reverse_parts = []; + + var address = that.parts.join(''); + + for (var i=31; i>=0; i--) { + that.reverse_parts.push(address[i]); + } + + that.reverse_parts.push('ip6'); + that.reverse_parts.push('arpa'); + + return that.reverse_parts.join('.'); + }; + + that.set_error = function(msg) { + that.valid = false; + that.error = msg; + return false; + }; + + that.is_part_valid_v6 = function(str) { + + if (str.length === 0) { + return that.set_error('not a number'); + } + + if (str.length > 4) { + return that.set_error('wrong format - too long'); + } + + for (var i=0; i<str.length; i++) { + + var digit = parseInt(str[i], 16); + + //check if character is digit + if (isNaN(digit)) { + return that.set_error('invalid format: \''+digit+'\''); + } + } + + return true; + }; + + /* + * Checks if part.value is valid IPv4 integer of given size (in bits). + * Validation can be limited only to decimal values by only_decimal argument. + * Sets its decimal representation to part.decimal_value. + */ + that.is_part_valid_v4 = function(part, bits, only_decimal) { + + if (!part.value || part.value.length === 0) { + return that.set_error('not a number'); + } + + var radix = that.get_radix(part.value); + + var number = part.value; + + if (radix === 16) number = part.value.substring(2); + else if (radix === 8) number = part.value.substring(1); + + if (radix !== 10 && only_decimal) { + return that.set_error('not a decimal number'); + } + + for (var i=0; i<number.length; i++) { + + var digit = parseInt(number[i], radix); + + //check if character is digit in its radix + if (isNaN(digit)) { + return that.set_error('invalid format: \''+digit+'\''); + } + + //check for leading zeros + if (i === 0 && digit === 0 && number.length > 1) { + return that.set_error('invalid format: leading zeros'); + } + } + + var max_value = Math.pow(2, bits) - 1; + + part.decimal_value = parseInt(part.value, radix); + + if (part.decimal_value > max_value) { + return that.set_error('value out of range'); + } + + return true; + }; + + that.get_radix = function(str) { + + var normalized = str.toLowerCase(); + + if (normalized.length > 2 && + normalized[0] === '0' && + normalized[1] === 'x') { + return 16; + + } else if (normalized.length > 1 && normalized[0] === '0') { + return 8; + } + + return 10; + }; + + that.make_quads = function(integer, quads) { + + var hex_str = integer.toString(16); + if (hex_str.length < 8) { + hex_str = add_leading_zeros(hex_str, 8 - hex_str.length); + } + + for (var i=0; i<hex_str.length; i+=2) { + var quad_hex = hex_str.substring(i,i+2); + var quad = parseInt(quad_hex, 16); + quads.push(quad.toString(10)); + } + }; + + that.get_radix = function(str) { + + var normalized = str.toLowerCase(); + + if (normalized.length > 2 && + normalized[0] === '0' && + normalized[1] === 'x') { + return 16; + + } else if (normalized.length > 1 && normalized[0] === '0') { + return 8; + } + + return 10; + }; + + that.parse(); + + return that; +};
\ No newline at end of file diff --git a/install/ui/src/freeipa/netgroup.js b/install/ui/src/freeipa/netgroup.js new file mode 100644 index 000000000..73deec100 --- /dev/null +++ b/install/ui/src/freeipa/netgroup.js @@ -0,0 +1,306 @@ +/*jsl:import ipa.js */ + +/* Authors: + * Pavel Zuna <pzuna@redhat.com> + * + * Copyright (C) 2010 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/>. + */ + +/* REQUIRES: ipa.js, details.js, search.js, add.js, facet.js, entity.js */ + +IPA.netgroup = { + remove_method_priority: IPA.config.default_priority - 1, + enable_priority: IPA.config.default_priority + 1 +}; + +IPA.netgroup.entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + that.entity_init(); + + that.builder.facet_groups(['settings', 'member', 'memberof']). + search_facet({ + columns: [ + 'cn', + 'description' + ] + }). + details_facet({ + factory: IPA.netgroup.details_facet, + entity: that, + command_mode: 'info' + }). + association_facet({ + name: 'memberof_netgroup', + associator: IPA.serial_associator + }). + standard_association_facets(). + adder_dialog({ + fields: [ + 'cn', + { + type: 'textarea', + name: 'description' + } + ] + }); + }; + + return that; +}; + +IPA.netgroup.details_facet = function(spec) { + + var entity_name = spec.entity.name; + + // + // Identity + // + + spec.fields = [ + { + name: 'cn', + widget: 'identity.cn' + }, + { + type: 'textarea', + name: 'description', + widget: 'identity.description' + }, + { + name: 'nisdomainname', + widget: 'identity.nisdomainname' + } + ]; + + spec.widgets = [ + { + type: 'details_table_section', + name: 'identity', + label: IPA.messages.details.general, + widgets: [ + { + name: 'cn' + }, + { + type: 'textarea', + name: 'description' + }, + { + name: 'nisdomainname', + widget: 'general.nisdomainname' + } + ] + } + ]; + + // + // Users + // + + spec.fields.push( + { + type: 'radio', + name: 'usercategory', + widget: 'user.rule.usercategory' + }, + { + type: 'rule_association_table', + name: 'memberuser_user', + widget: 'user.rule.memberuser_user', + priority: IPA.netgroup.remove_method_priority + }, + { + type: 'rule_association_table', + name: 'memberuser_group', + widget: 'user.rule.memberuser_group', + priority: IPA.netgroup.remove_method_priority + } + ); + + spec.widgets.push( + { + factory: IPA.collapsible_section, + name: 'user', + label: IPA.messages.objects.netgroup.user, + widgets: [ + { + factory: IPA.rule_details_widget, + name: 'rule', + radio_name: 'usercategory', + options: [ + { value: 'all', + label: IPA.messages.objects.netgroup.anyone }, + { value: '', + label: IPA.messages.objects.netgroup.specified_users } + ], + tables: [ + { name: 'memberuser_user' }, + { name: 'memberuser_group' } + ], + widgets: [ + { + type: 'rule_association_table', + id: entity_name+'-memberuser_user', + name: 'memberuser_user', + add_method: 'add_member', + remove_method: 'remove_member', + add_title: IPA.messages.association.add.member, + remove_title: IPA.messages.association.remove.member, + columns: [ + { + name: 'memberuser_user', + label: IPA.messages.objects.netgroup.users, + link: true + } + ] + }, + { + type: 'rule_association_table', + id: entity_name+'-memberuser_group', + name: 'memberuser_group', + add_method: 'add_member', + remove_method: 'remove_member', + add_title: IPA.messages.association.add.member, + remove_title: IPA.messages.association.remove.member, + columns: [ + { + name: 'memberuser_group', + label: IPA.messages.objects.netgroup.usergroups, + link: true + } + ] + } + ] + } + ] + } + ); + + // + // Hosts + // + + spec.fields.push( + { + type: 'radio', + name: 'hostcategory', + widget: 'host.rule.hostcategory' + }, + { + type: 'rule_association_table', + name: 'memberhost_host', + widget: 'host.rule.memberhost_host', + priority: IPA.netgroup.remove_method_priority, + external: 'externalhost' + }, + { + type: 'rule_association_table', + name: 'memberhost_hostgroup', + widget: 'host.rule.memberhost_hostgroup', + priority: IPA.netgroup.remove_method_priority + } + ); + + spec.widgets.push( + { + factory: IPA.collapsible_section, + name: 'host', + label: IPA.messages.objects.netgroup.host, + widgets: [ + { + factory: IPA.rule_details_widget, + name: 'rule', + radio_name: 'hostcategory', + options: [ + { + 'value': 'all', + 'label': IPA.messages.objects.netgroup.any_host + }, + { + 'value': '', + 'label': IPA.messages.objects.netgroup.specified_hosts + } + ], + tables: [ + { 'name': 'memberhost_host' }, + { 'name': 'memberhost_hostgroup' } + ], + widgets: [ + { + type: 'rule_association_table', + id: entity_name+'-memberhost_host', + name: 'memberhost_host', + add_method: 'add_member', + remove_method: 'remove_member', + external: 'externalhost', + add_title: IPA.messages.association.add.member, + remove_title: IPA.messages.association.remove.member, + columns: [ + { + name: 'memberhost_host', + label: IPA.messages.objects.netgroup.hosts, + link: true + }, + { + name: 'externalhost', + label: IPA.messages.objects.netgroup.external, + formatter: IPA.boolean_formatter(), + width: '200px' + } + ] + }, + { + type: 'rule_association_table', + id: entity_name+'-memberhost_hostgroup', + name: 'memberhost_hostgroup', + add_method: 'add_member', + remove_method: 'remove_member', + add_title: IPA.messages.association.add.member, + remove_title: IPA.messages.association.remove.member, + columns: [ + { + name: 'memberhost_hostgroup', + label: IPA.messages.objects.netgroup.hostgroups, + link: true + } + ] + } + ] + } + ] + } + ); + + var that = IPA.details_facet(spec); + + that.update_on_success = function(data, text_status, xhr) { + that.refresh(); + that.on_update.notify(); + that.nofify_update_success(); + }; + + that.update_on_error = function(xhr, text_status, error_thrown) { + that.refresh(); + }; + + return that; +}; + +IPA.register('netgroup', IPA.netgroup.entity); diff --git a/install/ui/src/freeipa/policy.js b/install/ui/src/freeipa/policy.js new file mode 100644 index 000000000..c109163e4 --- /dev/null +++ b/install/ui/src/freeipa/policy.js @@ -0,0 +1,122 @@ +/*jsl:import ipa.js */ +/*jsl:import search.js */ + +/* Authors: + * Adam Young <ayoung@redhat.com> + * + * Copyright (C) 2010 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/>. + */ + +/* REQUIRES: ipa.js, details.js, search.js, add.js, facet.js, entity.js */ + +IPA.pwpolicy = {}; + +IPA.pwpolicy.entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + that.entity_init(); + + that.builder.search_facet({ + sort_enabled: false, + columns:['cn','cospriority'] + }). + details_facet({ + sections:[ + { + name : 'identity', + fields:[ + { + type: 'link', + name: 'cn', + other_entity: 'group' + }, + 'krbmaxpwdlife', + 'krbminpwdlife', + { + name: 'krbpwdhistorylength', + measurement_unit: 'number_of_passwords' + }, + 'krbpwdmindiffchars', + 'krbpwdminlength', + 'krbpwdmaxfailure', + { + name: 'krbpwdfailurecountinterval', + measurement_unit: 'seconds' + }, + { + name: 'krbpwdlockoutduration', + measurement_unit: 'seconds' + }, + 'cospriority' + ] + }]}). + standard_association_facets(). + adder_dialog({ + fields: [ + { + type: 'entity_select', + name: 'cn', + other_entity: 'group', + other_field: 'cn', + required: true + }, + 'cospriority' + ], + height: 300 + }); + }; + + return that; +}; + +IPA.krbtpolicy = {}; + +IPA.krbtpolicy.entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + that.entity_init(); + + that.builder.details_facet({ + title: IPA.metadata.objects.krbtpolicy.label, + sections: [ + { + name: 'identity', + fields: [ + { + name: 'krbmaxrenewableage', + measurement_unit: 'seconds' + }, + { + name: 'krbmaxticketlife', + measurement_unit: 'seconds' + } + ] + } + ], + needs_update: true + }); + }; + + return that; +}; + +IPA.register('pwpolicy', IPA.pwpolicy.entity); +IPA.register('krbtpolicy', IPA.krbtpolicy.entity); diff --git a/install/ui/src/freeipa/rule.js b/install/ui/src/freeipa/rule.js new file mode 100644 index 000000000..dfe710b91 --- /dev/null +++ b/install/ui/src/freeipa/rule.js @@ -0,0 +1,254 @@ +/*jsl:import ipa.js */ + +/* Authors: + * Endi Sukma Dewata <edewata@redhat.com> + * + * Copyright (C) 2010 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/>. + */ + +/* REQUIRES: ipa.js, details.js, search.js, add.js, facet.js, entity.js */ + +IPA.rule_details_widget = function(spec) { + + spec = spec || {}; + + var that = IPA.composite_widget(spec); + + that.radio_name = spec.radio_name; + that.options = spec.options || []; + that.tables = spec.tables || []; + that.columns = spec.columns; + + that.init = function() { + + that.enable_radio = IPA.radio_widget({ + name: that.radio_name, + options: that.options + }); + + that.widgets.add_widget(that.enable_radio); + that.enable_radio.value_changed.attach(that.on_enable_radio_changed); + }; + + that.on_enable_radio_changed = function(value) { + if(value.length > 0) { + var enabled = ('' === value[0]); + for (var i=0; i<that.tables.length; i++) { + var table = that.tables[i]; + + var table_widget = that.widgets.get_widget(table.name); + table_widget.set_enabled(enabled); + } + } + }; + + that.create = function(container) { + + that.container = container; + + //enable radios + var param_info = IPA.get_entity_param(that.entity.name, that.radio_name); + var title = param_info ? param_info.doc : that.radio_name; + var enable_radio_container = $('<div/>', { + name: that.radio_name, + title: title, + 'class': 'field' + }).appendTo(container); + + enable_radio_container.append(title+': '); + that.enable_radio.create(enable_radio_container); + + //tables + for (var j=0; j<that.tables.length; j++) { + var table = that.tables[j]; + + var metadata = IPA.get_entity_param(that.entity.name, table.name); + + var table_container = $('<div/>', { + name: table.name, + title: metadata ? metadata.doc : table.name, + 'class': 'field' + }).appendTo(container); + + var widget = that.widgets.get_widget(table.name); + widget.create(table_container); + } + }; + + that.init(); + + return that; +}; + + +IPA.rule_association_table_widget = function(spec) { + + spec = spec || {}; + + var that = IPA.association_table_widget(spec); + + that.external = spec.external; + + that.enabled = spec.enabled !== undefined ? spec.enabled : true; + + that.setup_column = function(column, div, record) { + var suppress_link = false; + if (that.external) { + suppress_link = record[that.external] === 'true'; + } + column.setup(div, record, suppress_link); + }; + + that.create_columns = function() { + + if (!that.columns.length) { + that.association_table_widget_create_columns(); + if (that.external) { + that.create_column({ + name: that.external, + label: IPA.messages.objects.sudorule.external, + entity: that.other_entity, + formatter: IPA.boolean_formatter(), + width: '200px' + }); + } + } + }; + + that.create_add_dialog = function() { + + var entity_label = that.entity.metadata.label_singular; + var pkey = IPA.nav.get_state(that.entity.name+'-pkey'); + var other_entity_label = that.other_entity.metadata.label; + + var title = that.add_title; + title = title.replace('${entity}', entity_label); + title = title.replace('${primary_key}', pkey); + title = title.replace('${other_entity}', other_entity_label); + + return IPA.rule_association_adder_dialog({ + title: title, + pkey: pkey, + other_entity: that.other_entity, + attribute_member: that.attribute_member, + entity: that.entity, + external: that.external, + exclude: that.values + }); + }; + + return that; +}; + +IPA.rule_association_table_field = function(spec) { + + spec = spec || {}; + + var that = IPA.association_table_field(spec); + + that.external = spec.external; + + that.set_values_external = function(values, external) { + + for (var i=0; i<values.length; i++) { + + var record = values[i]; + + if (typeof record !== 'object') { + record = {}; + record[that.param] = values[i]; + } + + record[that.external] = external; + + values[i] = record; + } + }; + + that.load = function(result) { + that.values = result[that.param] || []; + + if (that.external) { + that.set_values_external(that.values, ''); + var external_values = result[that.external] || []; + that.set_values_external(external_values, 'true'); + $.merge(that.values, external_values); + } + + that.widget.update(that.values); + that.widget.unselect_all(); + }; + + that.get_update_info = function() { + + var update_info = IPA.update_info_builder.new_update_info(); + + //association_table_widget performs basic add and remove operation + //immediately. Rule association field test if its enabled and if not it + //performs delete operation. + + if (!that.widget.enabled) { + var values = that.save(); + + if (values.length > 0) { //no need to delete if has no values + + var command = IPA.command({ + entity: that.entity.name, + method: that.widget.remove_method, + args: that.entity.get_primary_key() + }); + + command.set_option(that.widget.other_entity.name, values); + update_info.append_command(command, that.priority); + } + } + + return update_info; + }; + + return that; +}; + +IPA.widget_factories['rule_association_table'] = IPA.rule_association_table_widget; +IPA.field_factories['rule_association_table'] = IPA.rule_association_table_field; + +IPA.rule_association_adder_dialog = function(spec) { + + spec = spec || {}; + + var that = IPA.association_adder_dialog(spec); + + that.external = spec.external; + + that.add = function() { + var rows = that.available_table.remove_selected_rows(); + that.selected_table.add_rows(rows); + + if (that.external) { + var pkey_name = that.other_entity.metadata.primary_key; + var value = that.external_field.val(); + if (!value) return; + + var record = {}; + record[pkey_name] = value; + that.selected_table.add_record(record); + that.external_field.val(''); + } + }; + + return that; +}; diff --git a/install/ui/src/freeipa/search.js b/install/ui/src/freeipa/search.js new file mode 100644 index 000000000..b54c375f7 --- /dev/null +++ b/install/ui/src/freeipa/search.js @@ -0,0 +1,491 @@ +/*jsl:import ipa.js */ + +/* Authors: + * Pavel Zuna <pzuna@redhat.com> + * Adam Young <ayoung@redhat.com> + * Endi S. Dewata <edewata@redhat.com> + * Petr Vobornik <pvoborni@redhat.com> + * + * Copyright (C) 2010 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/>. + */ + +/* REQUIRES: ipa.js */ + +IPA.search_facet = function(spec, no_init) { + + spec = spec || {}; + + spec.name = spec.name || 'search'; + spec.managed_entity = spec.managed_entity ? IPA.get_entity(spec.managed_entity) : spec.entity; + + spec.disable_breadcrumb = + spec.disable_breadcrumb === undefined ? true : spec.disable_breadcrumb; + spec.disable_facet_tabs = + spec.disable_facet_tabs === undefined ? true : spec.disable_facet_tabs; + + spec.actions = spec.actions || []; + spec.actions.unshift( + IPA.refresh_action, + IPA.batch_remove_action, + IPA.add_action); + + spec.control_buttons = spec.control_buttons || []; + spec.control_buttons.unshift( + { + name: 'refresh', + label: IPA.messages.buttons.refresh, + icon: 'reset-icon' + }, + { + name: 'remove', + label: IPA.messages.buttons.remove, + icon: 'remove-icon' + }, + { + name: 'add', + label: IPA.messages.buttons.add, + icon: 'add-icon' + }); + + spec.state = spec.state || {}; + spec.state.evaluators = spec.state.evaluators || []; + spec.state.evaluators.push( + IPA.selected_state_evaluator, + IPA.self_service_state_evaluator); + + var that = IPA.table_facet(spec, true); + + that.deleter_dialog = spec.deleter_dialog || IPA.search_deleter_dialog; + + that.create_header = function(container) { + + that.facet_create_header(container); + + var div = $('<div/>', { + 'class': 'right-aligned-facet-controls' + }).appendTo(that.controls); + + div.append(IPA.create_network_spinner()); + + var filter_container = $('<div/>', { + 'class': 'search-filter' + }).appendTo(div); + + that.filter = $('<input/>', { + type: 'text', + name: 'filter' + }).appendTo(filter_container); + + that.filter.keypress(function(e) { + /* if the key pressed is the enter key */ + if (e.which == 13) { + that.find(); + } + }); + + that.find_button = IPA.action_button({ + name: 'find', + icon: 'search-icon', + click: function() { + that.find(); + return false; + } + }).appendTo(filter_container); + + that.create_control_buttons(that.controls); + }; + + that.show = function() { + that.facet_show(); + + var filter = IPA.nav.get_state(that.entity.name+'-filter'); + that.old_filter = filter || ''; + that.old_pkeys = that.managed_entity.get_primary_key_prefix(); + + if (that.filter) { + that.filter.val(filter); + } + }; + + that.needs_update = function() { + if (that._needs_update !== undefined) return that._needs_update; + + var needs_update = that.facet_needs_update(); + + //check if state changed + var pkeys = that.managed_entity.get_primary_key_prefix(); + needs_update = needs_update || IPA.array_diff(pkeys, that.old_pkeys); + + return needs_update; + }; + + that.show_add_dialog = function() { + var dialog = that.managed_entity.get_dialog('add'); + dialog.open(that.container); + }; + + that.show_remove_dialog = function() { + + var values = that.get_selected_values(); + + var title; + if (!values.length) { + title = IPA.messages.dialogs.remove_empty; + alert(title); + return; + } + + var dialog = that.managed_entity.get_dialog('remove'); + + if (!dialog) { + dialog = that.deleter_dialog(); + } + + dialog.entity_name = that.managed_entity.name; + dialog.entity = that.managed_entity; + dialog.facet = that; + + title = IPA.messages.dialogs.remove_title; + var label = that.managed_entity.metadata.label; + dialog.title = title.replace('${entity}', label); + + dialog.set_values(values); + + dialog.open(that.container); + }; + + that.find = function() { + var filter = that.filter.val(); + var old_filter = IPA.nav.get_state(that.managed_entity.name+'-filter'); + var state = {}; + state[that.managed_entity.name + '-filter'] = filter; + + if (filter !== old_filter) that.set_expired_flag(); + + IPA.nav.push_state(state); + }; + + that.get_search_command_name = function() { + var name = that.managed_entity.name + '_find'; + if (that.pagination && !that.search_all_entries) { + name += '_pkeys'; + } + return name; + }; + + that.create_refresh_command = function() { + + var filter = that.managed_entity.get_primary_key_prefix(); + filter.push(IPA.nav.get_state(that.managed_entity.name+'-filter')); + + var command = IPA.command({ + name: that.get_search_command_name(), + entity: that.managed_entity.name, + method: 'find', + args: filter, + options: { + all: that.search_all_attributes + } + }); + + if (that.pagination) { + if (!that.search_all_entries) command.set_option('pkey_only', true); + command.set_option('sizelimit', 0); + } + + return command; + }; + + that.refresh = function() { + + var command = that.create_refresh_command(); + + command.on_success = function(data, text_status, xhr) { + if (!IPA.opened_dialogs.dialogs.length) that.filter.focus(); + that.load(data); + that.show_content(); + }; + + command.on_error = function(xhr, text_status, error_thrown) { + that.report_error(error_thrown); + }; + + command.execute(); + }; + + that.clear = function() { + if (that.needs_clear()) { + that.table.clear(); + } + }; + + that.needs_clear = function() { + var clear = false; + var filter = IPA.nav.get_state(that.entity.name+'-filter') || ''; + clear = that.old_filter !== '' || that.old_filter !== filter; + + var pkeys = that.managed_entity.get_primary_key_prefix(); + clear = clear || IPA.array_diff(pkeys, that.old_pkeys); + + return clear; + }; + + that.init_search_facet = function() { + + that.init_facet(); + that.init_table_columns(); + that.init_table(that.managed_entity); + }; + + if (!no_init) that.init_search_facet(); + + // methods that should be invoked by subclasses + that.search_facet_refresh = that.refresh; + that.search_facet_create_refresh_command = that.create_refresh_command; + + return that; +}; + +IPA.search_deleter_dialog = function(spec) { + + spec = spec || {}; + + var that = IPA.deleter_dialog(spec); + + that.create_command = function() { + var batch = IPA.batch_command({ + error_message: IPA.messages.search.partial_delete + }); + + var pkeys = that.entity.get_primary_key_prefix(); + + for (var i=0; i<that.values.length; i++) { + var command = IPA.command({ + entity: that.entity.name, + method: 'del' + }); + + for (var j=0; j<pkeys.length; j++) { + command.add_arg(pkeys[j]); + } + + var value = that.values[i]; + if (value instanceof Object) { + for (var key in value) { + if (value.hasOwnProperty(key)) { + if (key === 'pkey'){ + command.add_arg(value[key]); + } else { + command.set_option(key, value[key]); + } + } + } + } else { + command.add_arg(value); + } + + batch.add_command(command); + } + + return batch; + }; + + that.execute = function() { + + var batch = that.create_command(); + + batch.on_success = function(data, text_status, xhr) { + that.facet.refresh(); + that.facet.on_update.notify([],that.facet); + IPA.notify_success(IPA.messages.search.deleted); + }; + + batch.on_error = function() { + that.facet.refresh(); + }; + + batch.execute(); + }; + + that.search_deleter_dialog_create_command = that.create_command; + + return that; +}; + +/*TODO. this has much copied code from above. Refactor the search_facet +To either be nested or not nested. */ +IPA.nested_search_facet = function(spec) { + + spec = spec || {}; + + spec.managed_entity = IPA.get_entity(spec.nested_entity); + + spec.disable_breadcrumb = false; + spec.disable_facet_tabs = false; + + var that = IPA.search_facet(spec); + + that.show = function() { + that.facet_show(); + + that.header.set_pkey( + IPA.nav.get_state(IPA.current_entity.name+'-pkey')); + + var filter = IPA.nav.get_state(that.managed_entity.name+'-filter'); + that.old_filter = filter || ''; + that.old_pkeys = that.managed_entity.get_primary_key_prefix(); + + if (that.filter) { + that.filter.val(filter); + } + }; + + that.refresh = function() { + + var pkey = IPA.nav.get_state(that.entity.name+'-pkey'); + + if ((!pkey) && (that.entity.redirect_facet)) { + that.redirect(); + return; + } + + that.search_facet_refresh(); + }; + + return that; +}; + +IPA.batch_remove_action = function(spec) { + + spec = spec || {}; + spec.name = spec.name || 'remove'; + spec.label = spec.label || IPA.messages.buttons.remove; + spec.enable_cond = spec.enable_cond || ['item-selected']; + spec.hide_cond = spec.hide_cond || ['self-service']; + + var that = IPA.action(spec); + + that.execute_action = function(facet) { + facet.show_remove_dialog(); + }; + + return that; +}; + +IPA.add_action = function(spec) { + + spec = spec || {}; + spec.name = spec.name || 'add'; + spec.label = spec.label || IPA.messages.buttons.add; + spec.hide_cond = spec.hide_cond || ['self-service']; + + var that = IPA.action(spec); + + that.execute_action = function(facet) { + facet.show_add_dialog(); + }; + + return that; +}; + +/* + * Calls entity's disable command for each selected item in a batch. + * Usable in table facets. + */ +IPA.batch_items_action = function(spec) { + + spec = spec || {}; + + var that = IPA.action(spec); + + that.method = spec.method || 'disable'; + that.success_msg = spec.success_msg; + + that.execute_action = function(facet, on_success, on_error) { + + var entity = facet.managed_entity; + var pkeys = facet.get_selected_values(); + + that.batch = IPA.batch_command({ + name: entity.name + '_batch_'+ that.method, + on_success: that.get_on_success(facet, on_success) + }); + + for (var i=0; i<pkeys.length; i++) { + var pkey = pkeys[i]; + + var command = IPA.command({ + entity: entity.name, + method: that.method, + args: [pkey] + }); + + that.batch.add_command(command); + } + + that.batch.execute(); + }; + + that.on_success = function(facet, data, text_status, xhr) { + facet.on_update.notify(); + facet.refresh(); + + if (that.success_msg) { + var succeeded = that.batch.commands.length - that.batch.errors.errors.length; + var msg = that.success_msg.replace('${count}', succeeded); + IPA.notify_success(msg); + } + }; + + that.get_on_success = function(facet, on_success) { + return function(data, text_status, xhr) { + that.on_success(facet, data, text_status, xhr); + if (on_success) on_success.call(this, data, text_status, xhr); + }; + }; + + + return that; +}; + +IPA.batch_disable_action = function(spec) { + + spec = spec || {}; + + spec.name = spec.name || 'disable'; + spec.method = spec.method || 'disable'; + spec.needs_confirm = spec.needs_confirm === undefined ? true : spec.needs_confirm; + spec.enable_cond = spec.enable_cond || ['item-selected']; + spec.success_msg = spec.success_msg || IPA.messages.search.disabled; + spec.confirm_msg = spec.confirm_msg || IPA.messages.search.disable_confirm; + + return IPA.batch_items_action(spec); +}; + +IPA.batch_enable_action = function(spec) { + + spec = spec || {}; + + spec.name = spec.name || 'enable'; + spec.method = spec.method || 'enable'; + spec.needs_confirm = spec.needs_confirm === undefined ? true : spec.needs_confirm; + spec.enable_cond = spec.enable_cond || ['item-selected']; + spec.success_msg = spec.success_msg || IPA.messages.search.enabled; + spec.confirm_msg = spec.confirm_msg || IPA.messages.search.enable_confirm; + + return IPA.batch_items_action(spec); +};
\ No newline at end of file diff --git a/install/ui/src/freeipa/selinux.js b/install/ui/src/freeipa/selinux.js new file mode 100644 index 000000000..ca4700609 --- /dev/null +++ b/install/ui/src/freeipa/selinux.js @@ -0,0 +1,315 @@ +/*jsl:import ipa.js */ + +/* Authors: + * Petr Vobornik <pvoborni@redhat.com> + * + * Copyright (C) 2012 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/>. + */ + +/* REQUIRES: ipa.js, details.js, search.js, add.js, facet.js, entity.js */ + +IPA.selinux = { + remove_method_priority: IPA.config.default_priority - 1 +}; + +IPA.selinux.selinuxusermap_entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + that.entity_init(); + + that.builder.search_facet({ + row_enabled_attribute: 'ipaenabledflag', + search_all_attributes: true, + columns: [ + 'cn', + 'ipaselinuxuser', + { + name: 'ipaenabledflag', + label: IPA.messages.status.label, + formatter: IPA.boolean_status_formatter() + }, + 'description' + ], + actions: [ + IPA.batch_disable_action, + IPA.batch_enable_action + ], + control_buttons: [ + { + name: 'disable', + label: IPA.messages.buttons.disable, + icon: 'disabled-icon' + }, + { + name: 'enable', + label: IPA.messages.buttons.enable, + icon: 'enabled-icon' + } + ] + }). + details_facet({ + factory: IPA.selinux_details_facet, + entity: that, + command_mode: 'info', + actions: [ + IPA.select_action, + IPA.enable_action, + IPA.disable_action, + IPA.delete_action + ], + header_actions: ['select_action', 'enable', 'disable', 'delete'], + state: { + evaluators: [ + { + factory: IPA.enable_state_evaluator, + field: 'ipaenabledflag' + } + ], + summary_conditions: [ + IPA.enabled_summary_cond(), + IPA.disabled_summary_cond() + ] + } + }). + adder_dialog({ + fields: [ + 'cn', + 'ipaselinuxuser' + ] + }); + }; + + return that; +}; + +IPA.selinux_details_facet = function(spec) { + + var entity_name = spec.entity.name; + + // + // General + // + + spec.fields = [ + { + name: 'cn', + read_only: true, + widget: 'general.cn' + }, + { + type: 'textarea', + name: 'description', + widget: 'general.description' + }, + { + name: 'ipaselinuxuser', + widget: 'general.ipaselinuxuser' + }, + { + type: 'entity_select', + name: 'seealso', + widget: 'general.seealso' + } + ]; + + spec.widgets = [ + { + type: 'details_table_section', + name: 'general', + label: IPA.messages.details.general, + widgets: [ + { + name: 'cn' + }, + { + type: 'textarea', + name: 'description' + }, + { + name: 'ipaselinuxuser', + widget: 'general.ipaselinuxuser' + }, + { + type: 'entity_select', + name: 'seealso', + other_entity: 'hbacrule', + other_field: 'cn' + } + ] + } + ]; + + // + // Users + // + + spec.fields.push( + { + type: 'radio', + name: 'usercategory', + widget: 'user.rule.usercategory' + }, + { + type: 'rule_association_table', + name: 'memberuser_user', + widget: 'user.rule.memberuser_user', + priority: IPA.selinux.remove_method_priority + }, + { + type: 'rule_association_table', + name: 'memberuser_group', + widget: 'user.rule.memberuser_group', + priority: IPA.selinux.remove_method_priority + } + ); + + spec.widgets.push( + { + factory: IPA.collapsible_section, + name: 'user', + label: IPA.messages.objects.selinuxusermap.user, + widgets: [ + { + factory: IPA.rule_details_widget, + name: 'rule', + radio_name: 'usercategory', + options: [ + { value: 'all', + label: IPA.messages.objects.selinuxusermap.anyone }, + { value: '', + label: IPA.messages.objects.selinuxusermap.specified_users } + ], + tables: [ + { name: 'memberuser_user' }, + { name: 'memberuser_group' } + ], + widgets: [ + { + type: 'rule_association_table', + id: entity_name+'-memberuser_user', + name: 'memberuser_user', + add_method: 'add_user', + remove_method: 'remove_user', + add_title: IPA.messages.association.add.member, + remove_title: IPA.messages.association.remove.member + }, + { + type: 'rule_association_table', + id: entity_name+'-memberuser_group', + name: 'memberuser_group', + add_method: 'add_user', + remove_method: 'remove_user', + add_title: IPA.messages.association.add.member, + remove_title: IPA.messages.association.remove.member + } + ] + } + ] + } + ); + + // + // Hosts + // + + spec.fields.push( + { + type: 'radio', + name: 'hostcategory', + widget: 'host.rule.hostcategory' + }, + { + type: 'rule_association_table', + name: 'memberhost_host', + widget: 'host.rule.memberhost_host', + priority: IPA.selinux.remove_method_priority + }, + { + type: 'rule_association_table', + name: 'memberhost_hostgroup', + widget: 'host.rule.memberhost_hostgroup', + priority: IPA.selinux.remove_method_priority + } + ); + + spec.widgets.push( + { + factory: IPA.collapsible_section, + name: 'host', + label: IPA.messages.objects.selinuxusermap.host, + widgets: [ + { + factory: IPA.rule_details_widget, + name: 'rule', + radio_name: 'hostcategory', + options: [ + { + 'value': 'all', + 'label': IPA.messages.objects.selinuxusermap.any_host + }, + { + 'value': '', + 'label': IPA.messages.objects.selinuxusermap.specified_hosts + } + ], + tables: [ + { 'name': 'memberhost_host' }, + { 'name': 'memberhost_hostgroup' } + ], + widgets: [ + { + type: 'rule_association_table', + id: entity_name+'-memberuser_user', + name: 'memberhost_host', + add_method: 'add_host', + remove_method: 'remove_host', + add_title: IPA.messages.association.add.member, + remove_title: IPA.messages.association.remove.member + }, + { + type: 'rule_association_table', + id: entity_name+'-memberuser_group', + name: 'memberhost_hostgroup', + add_method: 'add_host', + remove_method: 'remove_host', + add_title: IPA.messages.association.add.member, + remove_title: IPA.messages.association.remove.member + } + ] + } + ] + } + ); + + var that = IPA.details_facet(spec); + + that.update_on_success = function(data, text_status, xhr) { + that.refresh(); + that.on_update.notify(); + that.nofify_update_success(); + }; + + that.update_on_error = function(xhr, text_status, error_thrown) { + that.refresh(); + }; + + return that; +}; + +IPA.register('selinuxusermap', IPA.selinux.selinuxusermap_entity);
\ No newline at end of file diff --git a/install/ui/src/freeipa/serverconfig.js b/install/ui/src/freeipa/serverconfig.js new file mode 100644 index 000000000..517aa7800 --- /dev/null +++ b/install/ui/src/freeipa/serverconfig.js @@ -0,0 +1,118 @@ +/*jsl:import ipa.js */ + +/* Authors: + * Endi Sukma Dewata <edewata@redhat.com> + * Adam Young <ayoung@redhat.com> + * + * Copyright (C) 2010 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/>. + */ + +/* REQUIRES: ipa.js, details.js, search.js, add.js, facet.js, entity.js */ + +IPA.serverconfig = {}; + +IPA.serverconfig.entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + that.entity_init(); + + that.builder.details_facet({ + title: IPA.metadata.objects.config.label, + sections: [ + { + name: 'search', + label: IPA.messages.objects.config.search, + fields: [ + 'ipasearchrecordslimit', + 'ipasearchtimelimit' + ] + }, + { + name: 'user', + label: IPA.messages.objects.config.user, + fields: [ + 'ipausersearchfields', + 'ipadefaultemaildomain', + { + type: 'entity_select', + name: 'ipadefaultprimarygroup', + other_entity: 'group', + other_field: 'cn' + }, + 'ipahomesrootdir', + 'ipadefaultloginshell', + 'ipamaxusernamelength', + 'ipapwdexpadvnotify', + { + name: 'ipaconfigstring', + type: 'checkboxes', + options: IPA.create_options([ + 'AllowLMhash', 'AllowNThash', + 'KDC:Disable Last Success', 'KDC:Disable Lockout' + ]) + }, + { + type: 'checkbox', + name: 'ipamigrationenabled' + }, + { + type: 'multivalued', + name: 'ipauserobjectclasses' + } + ] + }, + { + name: 'group', + label: IPA.messages.objects.config.group, + fields: [ + 'ipagroupsearchfields', + { + type: 'multivalued', + name: 'ipagroupobjectclasses' + } + ] + }, + { + name: 'selinux', + label: IPA.messages.objects.config.selinux, + fields: [ + 'ipaselinuxusermaporder', + 'ipaselinuxusermapdefault' + ] + }, + { + name: 'service', + label: IPA.messages.objects.config.service, + fields: [ + { + name: 'ipakrbauthzdata', + type: 'checkboxes', + options: IPA.create_options(['MS-PAC', 'PAD']) + } + ] + } + ], + needs_update: true + }); + }; + + return that; +}; + +IPA.register('config', IPA.serverconfig.entity); diff --git a/install/ui/src/freeipa/service.js b/install/ui/src/freeipa/service.js new file mode 100644 index 000000000..04e294bf0 --- /dev/null +++ b/install/ui/src/freeipa/service.js @@ -0,0 +1,467 @@ +/*jsl:import ipa.js */ +/*jsl:import certificate.js */ + +/* Authors: + * Endi Sukma Dewata <edewata@redhat.com> + * + * Copyright (C) 2010 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/>. + */ + +/* REQUIRES: ipa.js, details.js, search.js, add.js, facet.js, entity.js */ + +IPA.service = {}; + +IPA.service.entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + that.entity_init(); + + that.builder.search_facet({ + columns: [ 'krbprincipalname' ] + }). + details_facet({ + factory: IPA.service.details_facet, + sections: [ + { + name: 'details', + fields: [ + 'krbprincipalname', + { + type: 'service_name', + name: 'service', + label: IPA.messages.objects.service.service, + read_only: true + }, + { + type: 'service_host', + name: 'host', + label: IPA.messages.objects.service.host, + read_only: true + }, + { + name: 'ipakrbauthzdata', + type: 'checkboxes', + options: IPA.create_options(['MS-PAC', 'PAD']) + } + ] + }, + { + name: 'provisioning', + action_panel: { + factory: IPA.action_panel, + name: 'provisioning_actions', + actions: ['unprovision'] + }, + fields: [ + { + type: 'service_provisioning_status', + name: 'has_keytab', + label: IPA.messages.objects.service.status + } + ] + }, + { + name: 'certificate', + action_panel: { + factory: IPA.action_panel, + name: 'cert_actions', + actions: [ + 'request_cert', 'view_cert', 'get_cert', + 'revoke_cert', 'restore_cert' + ] + }, + fields: [ + { + type: 'certificate_status', + name: 'certificate_status', + label: IPA.messages.objects.service.status + } + ] + } + ], + actions: [ + IPA.service.unprovision_action, + IPA.cert.view_action, + IPA.cert.get_action, + IPA.cert.request_action, + IPA.cert.revoke_action, + IPA.cert.restore_action + ], + state: { + evaluators: [ + IPA.service.has_keytab_evaluator, + IPA.service.krbprincipalkey_acl_evaluator, + IPA.cert.certificate_evaluator + ] + }, + policies: [ + IPA.service.certificate_policy() + ] + }). + association_facet({ + name: 'managedby_host', + add_method: 'add_host', + remove_method: 'remove_host' + }). + standard_association_facets(). + adder_dialog({ + factory: IPA.service_adder_dialog, + height: 350, + sections: [ + { + fields: [ + { + type: 'combobox', + name: 'service', + label: IPA.messages.objects.service.service, + options: [ + 'cifs', + 'DNS', + 'ftp', + 'HTTP', + 'imap', + 'ldap', + 'libvirt', + 'nfs', + 'smtp', + 'qpidd' + ], + editable: true, + size: 10, + required: true, + z_index: 2 + }, + { + type: 'entity_select', + name: 'host', + other_entity: 'host', + other_field: 'fqdn', + label: IPA.messages.objects.service.host, + required: true, + z_index: 1 + }, + { + type: 'checkbox', + name: 'force', + metadata: IPA.get_command_option('service_add', 'force') + } + ] + } + ] + }); + }; + + return that; +}; + +IPA.service.details_facet = function(spec, no_init) { + + var that = IPA.details_facet(spec, true); + that.certificate_loaded = IPA.observer(); + + if (!no_init) that.init_details_facet(); + + return that; +}; + +IPA.service_adder_dialog = function(spec) { + + spec = spec || {}; + + var that = IPA.entity_adder_dialog(spec); + + var init = function() { + + //small hack - krbprincipalname should not be displayed. This way + //creation of associated widget is skipped. + //In future it would be better split section definion into widget and + //fields definition and create custom field with two associated + //widgets - 'service' and 'host' with this dialog's save logic. + that.builder.build_field({ + type: 'field', + name: 'krbprincipalname', + required: false + }); + }; + + that.save = function(record) { + + var field = that.fields.get_field('service'); + var service = field.save()[0]; + + field = that.fields.get_field('host'); + var host = field.save()[0]; + + record['krbprincipalname'] = [ service+'/'+host ]; + + field = that.fields.get_field('force'); + record['force'] = field.save(); + }; + + init(); + + return that; +}; + +IPA.service_name_field = function(spec) { + + spec = spec || {}; + + var that = IPA.field(spec); + + that.load = function(record) { + + that.field_load(record); + + var krbprincipalname = record.krbprincipalname[0]; + var value = krbprincipalname.replace(/\/.*$/, ''); + that.values = [value]; + + that.reset(); + }; + + return that; +}; + +IPA.field_factories['service_name'] = IPA.service_name_field; +IPA.widget_factories['service_name'] = IPA.text_widget; + + +IPA.service_host_field = function(spec) { + + spec = spec || {}; + + var that = IPA.field(spec); + + that.load = function(record) { + + that.field_load(record); + + var krbprincipalname = record.krbprincipalname[0]; + var value = krbprincipalname.replace(/^.*\//, '').replace(/@.*$/, ''); + that.values = [value]; + + that.reset(); + }; + + return that; +}; + +IPA.field_factories['service_host'] = IPA.service_host_field; +IPA.widget_factories['service_host'] = IPA.text_widget; + +IPA.service_provisioning_status_widget = function (spec) { + + spec = spec || {}; + + var that = IPA.input_widget(spec); + + that.create = function(container) { + + that.widget_create(container); + + that.status_valid = $('<div/>', { + name: 'kerberos-key-valid', + style: 'display: none;' + }).appendTo(container); + + $('<img/>', { + src: 'images/check-icon.png', + style: 'float: left;', + 'class': 'status-icon' + }).appendTo(that.status_valid); + + var content_div = $('<div/>', { + style: 'float: left;' + }).appendTo(that.status_valid); + + content_div.append('<b>'+IPA.messages.objects.service.valid+'</b>'); + + that.status_missing = $('<div/>', { + name: 'kerberos-key-missing', + style: 'display: none;' + }).appendTo(container); + + $('<img/>', { + src: 'images/caution-icon.png', + style: 'float: left;', + 'class': 'status-icon' + }).appendTo(that.status_missing); + + content_div = $('<div/>', { + style: 'float: left;' + }).appendTo(that.status_missing); + + content_div.append('<b>'+IPA.messages.objects.service.missing+'</b>'); + }; + + that.update = function(values) { + that.status = values && values.length ? values[0] : false; + set_status(that.status ? 'valid' : 'missing'); + }; + + that.clear = function() { + that.status_valid.css('display', 'none'); + that.status_missing.css('display', 'none'); + }; + + function set_status(status) { + that.status_valid.css('display', status == 'valid' ? 'inline' : 'none'); + that.status_missing.css('display', status == 'missing' ? 'inline' : 'none'); + } + + return that; +}; + +IPA.field_factories['service_provisioning_status'] = IPA.field; +IPA.widget_factories['service_provisioning_status'] = IPA.service_provisioning_status_widget; + + +IPA.service.unprovision_dialog = function(spec) { + + spec = spec || {}; + spec.title = spec.title || IPA.messages.objects.service.unprovision_title; + + var that = IPA.dialog(spec); + that.facet = spec.facet; + + var entity_singular = that.entity.metadata.label_singular; + that.title = that.title.replace('${entity}', entity_singular); + + that.create = function() { + that.container.append(IPA.messages.objects.service.unprovision_confirmation); + }; + + that.create_buttons = function() { + + that.create_button({ + name: 'unprovision', + label: IPA.messages.objects.service.unprovision, + click: function() { + that.unprovision(); + } + }); + + that.create_button({ + name: 'cancel', + label: IPA.messages.buttons.cancel, + click: function() { + that.close(); + } + }); + }; + + that.unprovision = function() { + + var principal_f = that.facet.fields.get_field('krbprincipalname'); + var pkey = principal_f.values[0]; + + IPA.command({ + entity: that.entity.name, + method: 'disable', + args: [pkey], + on_success: function(data, text_status, xhr) { + that.facet.refresh(); + that.close(); + IPA.notify_success(IPA.messages.objects.service.unprovisioned); + }, + on_error: function(xhr, text_status, error_thrown) { + that.close(); + } + }).execute(); + }; + + that.create_buttons(); + + return that; +}; + +IPA.service.unprovision_action = function(spec) { + + spec = spec || {}; + spec.name = spec.name || 'unprovision'; + spec.label = spec.label || IPA.messages.objects.service.delete_key_unprovision; + spec.enable_cond = spec.enable_cond || ['has_keytab', 'krbprincipalkey_w']; + + var that = IPA.action(spec); + + that.execute_action = function(facet) { + + var dialog = IPA.service.unprovision_dialog({ + entity: facet.entity, + facet: facet + }); + + dialog.open(); + }; + + return that; +}; + +IPA.service.krbprincipalkey_acl_evaluator = function(spec) { + + spec.name = spec.name || 'unprovision_acl_evaluator'; + spec.attribute = spec.attribute || 'krbprincipalkey'; + + var that = IPA.acl_state_evaluator(spec); + return that; +}; + +IPA.service.has_keytab_evaluator = function(spec) { + + spec.name = spec.name || 'has_keytab_evaluator'; + spec.attribute = spec.attribute || 'has_keytab'; + spec.value = spec.value || [true]; + spec.representation = spec.representation || 'has_keytab'; + + var that = IPA.value_state_evaluator(spec); + return that; +}; + +IPA.service.certificate_policy = function(spec) { + + spec = spec || {}; + + function get_pkey(result) { + var values = result.krbprincipalname; + return values ? values[0] : null; + } + + spec.get_pkey = spec.get_pkey || get_pkey; + + spec.get_name = spec.get_name || function(result) { + var value = get_pkey(result); + return value ? value.replace(/@.*$/, '') : null; + }; + + spec.get_principal = spec.get_principal || get_pkey; + + spec.get_hostname = spec.get_hostname || function(result) { + var value = get_pkey(result); + if (value) { + value = value.replace(/@.*$/, '').replace(/^.*\//, ''); + } + return value; + }; + + var that = IPA.cert.load_policy(spec); + return that; +}; + +IPA.register('service', IPA.service.entity); diff --git a/install/ui/src/freeipa/sudo.js b/install/ui/src/freeipa/sudo.js new file mode 100644 index 000000000..fddcda0b5 --- /dev/null +++ b/install/ui/src/freeipa/sudo.js @@ -0,0 +1,930 @@ +/*jsl:import ipa.js */ + +/* Authors: + * Endi Sukma Dewata <edewata@redhat.com> + * + * Copyright (C) 2010 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/>. + */ + +/* REQUIRES: ipa.js, details.js, search.js, add.js, facet.js, entity.js */ + +IPA.sudo = { + //priority of commands in details facet + remove_method_priority: IPA.config.default_priority - 1 +}; + +IPA.sudo.rule_entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + that.entity_init(); + + that.builder.search_facet({ + row_enabled_attribute: 'ipaenabledflag', + columns: [ + 'cn', + { + name: 'ipaenabledflag', + label: IPA.messages.status.label, + formatter: IPA.boolean_status_formatter() + }, + 'description' + ], + actions: [ + IPA.batch_disable_action, + IPA.batch_enable_action + ], + control_buttons: [ + { + name: 'disable', + label: IPA.messages.buttons.disable, + icon: 'disabled-icon' + }, + { + name: 'enable', + label: IPA.messages.buttons.enable, + icon: 'enabled-icon' + } + ] + }). + details_facet({ + factory: IPA.sudorule_details_facet, + entity: that, + command_mode: 'info', + actions: [ + IPA.select_action, + IPA.enable_action, + IPA.disable_action, + IPA.delete_action + ], + header_actions: ['select_action', 'enable', 'disable', 'delete'], + state: { + evaluators: [ + { + factory: IPA.enable_state_evaluator, + field: 'ipaenabledflag' + } + ], + summary_conditions: [ + IPA.enabled_summary_cond(), + IPA.disabled_summary_cond() + ] + } + }). + adder_dialog({ + fields: [ 'cn' ] + }); + }; + + return that; +}; + +IPA.sudo.command_entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + that.entity_init(); + + that.builder.search_facet({ + columns: [ + 'sudocmd', + 'description' + ] + }). + details_facet({ + sections: [ + { + name: 'general', + label: IPA.messages.details.general, + fields: [ + 'sudocmd', + { + type: 'textarea', + name: 'description' + } + ] + } + ] + }). + association_facet({ + name: 'memberof_sudocmdgroup', + associator: IPA.serial_associator, + columns:[ + 'cn', + 'description' + ], + adder_columns: [ + { + name: 'cn', + primary_key: true, + width: '100px' + }, + { + name: 'description', + width: '100px' + } + ] + }). + standard_association_facets(). + adder_dialog({ + fields: [ + 'sudocmd', + { + type: 'textarea', + name: 'description' + } + ] + }); + }; + + return that; +}; + +IPA.sudo.command_group_entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + that.entity_init(); + + that.builder.search_facet({ + columns: [ + 'cn', + 'description' + ] + }). + details_facet({ + sections: [ + { + name: 'general', + label: IPA.messages.details.general, + fields: [ + 'cn', + { + type: 'textarea', + name: 'description' + } + ] + } + ] + }). + association_facet({ + name: 'member_sudocmd', + columns: [ + 'sudocmd', + 'description' + ], + adder_columns: [ + { + name: 'sudocmd', + primary_key: true, + width: '100px' + }, + { + name: 'description', + width: '100px' + } + ] + }). + standard_association_facets(). + adder_dialog({ + fields: [ + 'cn', + { + type: 'textarea', + name: 'description' + } + ] + }); + }; + + return that; +}; + +IPA.sudorule_details_facet = function(spec) { + + var entity_name = spec.entity.name; + + // + // General + // + + spec.fields = [ + { + name: 'cn', + read_only: true, + widget: 'general.cn' + }, + { + type: 'textarea', + name: 'description', + widget: 'general.description' + } + ]; + + spec.widgets = [ + { + type: 'details_table_section', + name: 'general', + label: IPA.messages.details.general, + widgets: [ + { + name: 'cn' + }, + { + type: 'textarea', + name: 'description' + } + ] + } + ]; + + // + // Options + // + + spec.fields.push( + { + name: 'ipasudoopt', + widget: 'options.ipasudoopt' + } + ); + + spec.widgets.push( + { + factory: IPA.sudo.options_section, + name: 'options', + label: IPA.messages.objects.sudorule.options, + facet: that + } + ); + + // + // Users + // + + spec.fields.push( + { + type: 'radio', + name: 'usercategory', + widget: 'user.rule.usercategory' + }, + { + type: 'rule_association_table', + name: 'memberuser_user', + widget: 'user.rule.memberuser_user', + external: 'externaluser', + priority: IPA.sudo.remove_method_priority + }, + { + type: 'rule_association_table', + name: 'memberuser_group', + widget: 'user.rule.memberuser_group', + priority: IPA.sudo.remove_method_priority + } + ); + + spec.widgets.push( + { + factory: IPA.collapsible_section, + name: 'user', + label: IPA.messages.objects.sudorule.user, + widgets: [ + { + factory: IPA.rule_details_widget, + name: 'rule', + radio_name: 'usercategory', + options: [ + { + value: 'all', + label: IPA.messages.objects.sudorule.anyone + }, + { + value: '', + label: IPA.messages.objects.sudorule.specified_users + } + ], + tables: [ + { name: 'memberuser_user' }, + { name: 'memberuser_group' } + ], + widgets: [ + { + type: 'rule_association_table', + id: entity_name+'-memberuser_user', + name: 'memberuser_user', + add_method: 'add_user', + remove_method: 'remove_user', + external: 'externaluser', + add_title: IPA.messages.association.add.member, + remove_title: IPA.messages.association.remove.member + }, + { + type: 'rule_association_table', + id: entity_name+'-memberuser_group', + name: 'memberuser_group', + add_method: 'add_user', + remove_method: 'remove_user', + add_title: IPA.messages.association.add.member, + remove_title: IPA.messages.association.remove.member + } + ] + } + ] + } + ); + + // + // Hosts + // + + spec.fields.push( + { + type: 'radio', + name: 'hostcategory', + widget: 'host.rule.hostcategory' + }, + { + type: 'rule_association_table', + name: 'memberhost_host', + widget: 'host.rule.memberhost_host', + external: 'externalhost', + priority: IPA.sudo.remove_method_priority + }, + { + type: 'rule_association_table', + name: 'memberhost_hostgroup', + widget: 'host.rule.memberhost_hostgroup', + priority: IPA.sudo.remove_method_priority + } + ); + + spec.widgets.push( + { + factory: IPA.collapsible_section, + name: 'host', + label: IPA.messages.objects.sudorule.host, + widgets: [ + { + factory: IPA.rule_details_widget, + name: 'rule', + radio_name: 'hostcategory', + options: [ + { + 'value': 'all', + 'label': IPA.messages.objects.sudorule.any_host + }, + { + 'value': '', + 'label': IPA.messages.objects.sudorule.specified_hosts + } + ], + tables: [ + { 'name': 'memberhost_host' }, + { 'name': 'memberhost_hostgroup' } + ], + widgets: [ + { + type: 'rule_association_table', + id: entity_name+'-memberuser_user', + name: 'memberhost_host', + add_method: 'add_host', + remove_method: 'remove_host', + external: 'externalhost', + add_title: IPA.messages.association.add.member, + remove_title: IPA.messages.association.remove.member + }, + { + type: 'rule_association_table', + id: entity_name+'-memberuser_group', + name: 'memberhost_hostgroup', + add_method: 'add_host', + remove_method: 'remove_host', + add_title: IPA.messages.association.add.member, + remove_title: IPA.messages.association.remove.member + } + ] + } + ] + } + ); + + // + // Run Commands + // + + spec.fields.push( + { + type: 'radio', + name: 'cmdcategory', + widget: 'command.rule.cmdcategory' + }, + { + type: 'rule_association_table', + name: 'memberallowcmd_sudocmd', + widget: 'command.rule.memberallowcmd_sudocmd', + priority: IPA.sudo.remove_method_priority + }, + { + type: 'rule_association_table', + name: 'memberallowcmd_sudocmdgroup', + widget: 'command.rule.memberallowcmd_sudocmdgroup', + priority: IPA.sudo.remove_method_priority + }, + { + type: 'rule_association_table', + name: 'memberdenycmd_sudocmd', + widget: 'command.memberdenycmd_sudocmd', + priority: IPA.sudo.remove_method_priority + }, + { + type: 'rule_association_table', + name: 'memberdenycmd_sudocmdgroup', + widget: 'command.memberdenycmd_sudocmdgroup', + priority: IPA.sudo.remove_method_priority + } + ); + + spec.widgets.push( + { + factory: IPA.collapsible_section, + name: 'command', + label: IPA.messages.objects.sudorule.command, + widgets: [ + { + factory: IPA.header_widget, + name: 'allow_header', + text: IPA.messages.objects.sudorule.allow, + description: IPA.messages.objects.sudorule.allow + }, + { + factory: IPA.rule_details_widget, + name: 'rule', + radio_name: 'cmdcategory', + options: [ + { + value: 'all', + label: IPA.messages.objects.sudorule.any_command + }, + { + value: '', + label: IPA.messages.objects.sudorule.specified_commands + } + ], + tables: [ + { name: 'memberallowcmd_sudocmd' }, + { name: 'memberallowcmd_sudocmdgroup' } + ], + widgets: [ + { + type: 'rule_association_table', + id: entity_name+'-memberallowcmd_sudocmd', + name: 'memberallowcmd_sudocmd', + add_method: 'add_allow_command', + remove_method: 'remove_allow_command', + add_title: IPA.messages.association.add.memberallowcmd, + remove_title: IPA.messages.association.remove.memberallowcmd + }, + { + type: 'rule_association_table', + id: entity_name+'-memberallowcmd_sudocmdgroup', + name: 'memberallowcmd_sudocmdgroup', + add_method: 'add_allow_command', + remove_method: 'remove_allow_command', + add_title: IPA.messages.association.add.memberallowcmd, + remove_title: IPA.messages.association.remove.memberallowcmd + } + ] + }, + { + factory: IPA.header_widget, + name: 'deny_header', + text: IPA.messages.objects.sudorule.deny, + description: IPA.messages.objects.sudorule.deny + }, + { + type: 'rule_association_table', + id: entity_name+'-memberdenycmd_sudocmd', + name: 'memberdenycmd_sudocmd', + add_method: 'add_deny_command', + remove_method: 'remove_deny_command', + add_title: IPA.messages.association.add.memberdenycmd, + remove_title: IPA.messages.association.remove.memberdenycmd + }, + { + type: 'rule_association_table', + id: entity_name+'-memberdenycmd_sudocmdgroup', + name: 'memberdenycmd_sudocmdgroup', + add_method: 'add_deny_command', + remove_method: 'remove_deny_command', + add_title: IPA.messages.association.add.memberdenycmd, + remove_title: IPA.messages.association.remove.memberdenycmd + } + ] + } + ); + + // + // As whom + // + + spec.fields.push( + { + type: 'radio', + name: 'ipasudorunasusercategory', + widget: 'runas.runas_users.ipasudorunasusercategory' + }, + { + type: 'rule_association_table', + name: 'ipasudorunas_user', + widget: 'runas.runas_users.ipasudorunas_user', + external: 'ipasudorunasextuser', + priority: IPA.sudo.remove_method_priority + }, + { + type: 'rule_association_table', + name: 'ipasudorunas_group', + widget: 'runas.runas_users.ipasudorunas_group', + priority: IPA.sudo.remove_method_priority + }, + { + type: 'radio', + name: 'ipasudorunasgroupcategory', + widget: 'runas.runas_groups.ipasudorunasgroupcategory' + }, + { + type: 'rule_association_table', + name: 'ipasudorunasgroup_group', + widget: 'runas.runas_groups.ipasudorunasgroup_group', + external: 'ipasudorunasextgroup', + priority: IPA.sudo.remove_method_priority + } + ); + + spec.widgets.push( + { + factory: IPA.collapsible_section, + name: 'runas', + label: IPA.messages.objects.sudorule.runas, + widgets: [ + { + factory: IPA.rule_details_widget, + name: 'runas_users', + radio_name: 'ipasudorunasusercategory', + options: [ + { value: 'all', label: IPA.messages.objects.sudorule.anyone }, + { value: '', label: IPA.messages.objects.sudorule.specified_users } + ], + tables: [ + { name: 'ipasudorunas_user' }, + { name: 'ipasudorunas_group' } + ], + widgets: [ + { + type: 'rule_association_table', + id: entity_name+'-runasruser_user', + name: 'ipasudorunas_user', + external: 'ipasudorunasextuser', + add_method: 'add_runasuser', + remove_method: 'remove_runasuser', + add_title: IPA.messages.association.add.ipasudorunas, + remove_title: IPA.messages.association.remove.ipasudorunas + }, + { + type: 'rule_association_table', + id: entity_name+'-runasuser_group', + name: 'ipasudorunas_group', + add_method: 'add_runasuser', + remove_method: 'remove_runasuser', + add_title: IPA.messages.association.add.ipasudorunas, + remove_title: IPA.messages.association.remove.ipasudorunas + } + ] + }, + { + factory: IPA.rule_details_widget, + name: 'runas_groups', + radio_name: 'ipasudorunasgroupcategory', + options: [ + { value: 'all', label: IPA.messages.objects.sudorule.any_group }, + { value: '', label: IPA.messages.objects.sudorule.specified_groups } + ], + tables: [ + { name: 'ipasudorunasgroup_group' } + ], + widgets: [{ + type: 'rule_association_table', + id: entity_name+'-runasgroup_group', + name: 'ipasudorunasgroup_group', + external: 'ipasudorunasextgroup', + add_method: 'add_runasgroup', + remove_method: 'remove_runasgroup', + add_title: IPA.messages.association.add.ipasudorunasgroup, + remove_title: IPA.messages.association.remove.ipasudorunasgroup + }] + } + ] + } + ); + + var that = IPA.details_facet(spec); + + var init = function() { + var options = that.widgets.get_widget('options'); + options.facet = that; + }; + + that.update_on_success = function(data, text_status, xhr) { + that.refresh(); + that.on_update.notify(); + that.nofify_update_success(); + }; + + that.update_on_error = function(xhr, text_status, error_thrown) { + that.refresh(); + }; + + init(); + + return that; +}; + +IPA.sudo.options_section = function(spec) { + + spec = spec || {}; + + var that = IPA.collapsible_section(spec); + + function setup_table(){ + that.table = IPA.table_widget({ + name: 'ipasudoopt', + show_buttons: true + }); + + that.widgets.add_widget(that.table); + + that.table.create_column({ + name: 'ipasudoopt', + label: IPA.get_command_option('sudorule_add_option', 'ipasudoopt').label, + entity: that.entity, + primary_key: true + }); + + that.table.create = function(container) { + + that.table.table_create(container); + + that.remove_button = IPA.action_button({ + name: 'remove', + label: IPA.messages.buttons.remove, + icon: 'remove-icon', + 'class': 'action-button-disabled', + click: function() { + if (!that.remove_button.hasClass('action-button-disabled')) { + that.remove_handler(); + } + return false; + } + }).appendTo(that.table.buttons); + + that.add_button = IPA.action_button({ + name: 'add', + label: IPA.messages.buttons.add, + icon: 'add-icon', + click: function() { + if (!that.add_button.hasClass('action-button-disabled')) { + that.add_handler(); + } + return false; + } + }).appendTo(that.table.buttons); + }; + + that.table.select_changed = function() { + + var values = that.table.get_selected_values(); + + if (that.remove_button) { + if (values.length === 0) { + that.remove_button.addClass('action-button-disabled'); + } else { + that.remove_button.removeClass('action-button-disabled'); + } + } + }; + + that.table.update = function(values) { + + that.table.empty(); + + for (var i=0; i<values.length; i++) { + var value = values[i]; + if(!value || value === '') continue; + + var record = { + ipasudoopt: values[i] + }; + that.table.add_record(record); + } + + that.table.unselect_all(); + }; + } + + that.add_handler = function() { + if (that.facet.is_dirty()) { + var dialog = IPA.dirty_dialog({ + facet: that.facet + }); + + dialog.callback = function() { + that.show_add_dialog(); + }; + dialog.open(that.container); + + } else { + that.show_add_dialog(); + } + }; + + that.remove_handler = function() { + if (that.facet.is_dirty()) { + var dialog = IPA.dirty_dialog({ + facet: that.facet + }); + + dialog.callback = function() { + that.show_remove_dialog(); + }; + dialog.open(that.container); + + } else { + that.show_remove_dialog(); + } + }; + + that.show_add_dialog = function() { + + var label = IPA.get_command_option('sudorule_add_option', 'ipasudoopt').label; + + var title = IPA.messages.dialogs.add_title; + title = title.replace('${entity}', label); + + var dialog = IPA.dialog({ + name: 'option-adder-dialog', + title: title, + sections: [ + { + fields: [ + { + name: 'ipasudoopt', + label: label + } + ] + } + ] + }); + + dialog.create_button({ + name: 'add', + label: IPA.messages.buttons.add, + click: function() { + var ipasudoopt = dialog.fields.get_field('ipasudoopt'); + var value = ipasudoopt.save()[0]; + + var pkey = IPA.nav.get_state(that.entity.name+'-pkey'); + + var command = IPA.command({ + entity: 'sudorule', + method: 'add_option', + args: [pkey], + options: { + ipasudoopt: value + }, + on_success: function(data) { + that.table.load(data.result.result); + dialog.close(); + IPA.notify_success(IPA.messages.objects.sudorule.option_added); + }, + on_error: function(data) { + that.reload(); + dialog.close(); + } + }); + + command.execute(); + } + }); + + dialog.create_button({ + name: 'cancel', + label: IPA.messages.buttons.cancel, + click: function() { + dialog.close(); + } + }); + + dialog.open(that.container); + }; + + that.show_remove_dialog = function() { + + var label = IPA.get_command_option('sudorule_add_option', 'ipasudoopt').label; + var values = that.table.get_selected_values(); + + if (!values.length) { + var message = IPA.messages.dialogs.remove_empty; + alert(message); + return; + } + + var pkey = IPA.nav.get_state(that.entity.name+'-pkey'); + + var title = IPA.messages.dialogs.remove_title; + title = title.replace('${entity}', label); + + var dialog = IPA.deleter_dialog({ + title: title, + values: values + }); + + dialog.execute = function() { + + var batch = IPA.batch_command({ + on_success: function(data) { + //last successful result of batch results contains valid data + var result; + for(var i = data.result.results.length - 1; i > -1; i--) { + result = data.result.results[i].result; + if(result) break; + } + + if(result) { + that.table.load(result); + } else { + that.reload(); + } + + IPA.notify_success(IPA.messages.objects.sudorule.option_removed); + }, + on_error: function(data) { + that.reload(); + } + }); + + for (var i=0; i<values.length; i++) { + var command = IPA.command({ + entity: 'sudorule', + method: 'remove_option', + args: [pkey] + }); + + command.set_option('ipasudoopt', values[i]); + + batch.add_command(command); + } + + batch.execute(); + }; + + dialog.open(that.container); + }; + + that.reload = function() { + var command = IPA.command({ + entity: that.facet.entity.name, + method: 'show', + args: that.facet.get_primary_key(true), + on_success: function(data) { + that.table.load(data.result.result); + } + }); + + command.execute(); + }; + + /*initialization*/ + setup_table(); + + return that; +}; + +IPA.register('sudorule', IPA.sudo.rule_entity); +IPA.register('sudocmd', IPA.sudo.command_entity); +IPA.register('sudocmdgroup', IPA.sudo.command_group_entity); diff --git a/install/ui/src/freeipa/trust.js b/install/ui/src/freeipa/trust.js new file mode 100644 index 000000000..939bb59a3 --- /dev/null +++ b/install/ui/src/freeipa/trust.js @@ -0,0 +1,183 @@ +/*jsl:import ipa.js */ + +/* Authors: + * Petr Vobornik <pvoborni@redhat.com> + * + * Copyright (C) 2010 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/>. + */ + +/* REQUIRES: ipa.js, details.js, search.js, add.js, facet.js, entity.js */ + +IPA.trust = {}; + +IPA.trust.entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + that.entity_init(); + + that.builder.search_facet({ + columns: [ + 'cn' + ] + }). + details_facet({ + sections: [ + { + name: 'details', + label: IPA.messages.objects.trust.details, + fields: [ + 'cn', + { + name: 'ipantflatname', + label: IPA.messages.objects.trust.ipantflatname, + read_only: true + }, + { + name: 'ipanttrusteddomainsid', + label: IPA.messages.objects.trust.ipanttrusteddomainsid, + read_only: true + }, + { + name: 'trustdirection', + label: IPA.messages.objects.trust.trustdirection + }, + { + name: 'trusttype', + label: IPA.messages.objects.trust.trusttype + } +// trust status not supported by show command at the moment +// { +// name: 'truststatus', +// label: IPA.messages.objects.trust.truststatus +// } + ] + } + ] + }). + adder_dialog({ + factory: IPA.trust.adder_dialog, + fields: [ + { + name: 'cn', + label: IPA.messages.objects.trust.domain, + widget: 'realm.realm_server' + }, + { + name: 'realm_admin', + label: IPA.messages.objects.trust.account, + widget: 'method.realm_admin' + }, + { + type: 'password', + name: 'realm_passwd', + label: IPA.messages.password.password, + widget: 'method.realm_passwd' + }, + { + type: 'password', + name: 'trust_secret', + label: IPA.messages.password.password, + widget: 'method.trust_secret' + }, + { + type: 'password', + name: 'trust_secret_verify', + label: IPA.messages.password.verify_password, + widget: 'method.trust_secret_verify', + flags: ['no_command'], + validators: [IPA.same_password_validator({ + other_field: 'trust_secret' + })] + } + ], + widgets: [ + { + type: 'details_table_section_nc', + name: 'realm', + widgets: [ + 'realm_server' + ] + }, + { + type: 'multiple_choice_section', + name: 'method', + label: IPA.messages.objects.trust.establish_using, + choices: [ + { + name: 'admin-account', + label: IPA.messages.objects.trust.admin_account, + fields: ['realm_admin', 'realm_passwd'], + required: ['realm_admin', 'realm_passwd'], + enabled: true + }, + { + name: 'preshared_password', + label: IPA.messages.objects.trust.preshared_password, + fields: ['trust_secret', 'trust_secret_verify'], + required: ['trust_secret', 'trust_secret_verify'] + } + ], + widgets: [ + { + name: 'realm_admin' + }, + { + type: 'password', + name: 'realm_passwd' + }, + { + type: 'password', + name: 'trust_secret' + }, + { + type: 'password', + name: 'trust_secret_verify' + } + ] + } + ], + policies: [ + IPA.multiple_choice_section_policy({ + widget: 'method' + }) + ] + }); + }; + + return that; +}; + +IPA.trust.adder_dialog = function(spec) { + + spec = spec || {}; + + var that = IPA.entity_adder_dialog(spec); + + that.get_success_message = function(data) { + return that.entity_adder_dialog_get_success_message(data) + '. ' + data.result.result.truststatus[0]; + }; + + that.notify_success = function(data) { + IPA.notify_success(that.get_success_message(data), 5000); + }; + + return that; +}; + +IPA.register('trust', IPA.trust.entity); diff --git a/install/ui/src/freeipa/user.js b/install/ui/src/freeipa/user.js new file mode 100644 index 000000000..ac51db2ba --- /dev/null +++ b/install/ui/src/freeipa/user.js @@ -0,0 +1,663 @@ +/*jsl:import ipa.js */ + +/* Authors: + * Pavel Zuna <pzuna@redhat.com> + * Adam Young <ayoung@redhat.com> + * Endi Sukma Dewata <edewata@redhat.com> + * Petr Vobornik <pvoborni@redhat.com> + * + * Copyright (C) 2010 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/>. + */ + +/* REQUIRES: ipa.js, details.js, search.js, add.js, facet.js, entity.js */ + +IPA.user = {}; + +IPA.user.entity = function(spec) { + + var that = IPA.entity(spec); + + that.init = function() { + that.entity_init(); + + var self_service = IPA.nav.name === 'self-service'; + var link = self_service ? false : undefined; + + that.builder.search_facet({ + row_disabled_attribute: 'nsaccountlock', + columns: [ + 'uid', + 'givenname', + 'sn', + { + name: 'nsaccountlock', + label: IPA.messages.status.label, + formatter: IPA.boolean_status_formatter({ + invert_value: true + }) + }, + 'uidnumber', + 'mail', + 'telephonenumber', + 'title' + ], + actions: [ + { + factory: IPA.batch_disable_action, + hide_cond: ['self-service'] + }, + { + factory: IPA.batch_enable_action, + hide_cond: ['self-service'] + } + ], + control_buttons: [ + { + name: 'disable', + label: IPA.messages.buttons.disable, + icon: 'disabled-icon' + }, + { + name: 'enable', + label: IPA.messages.buttons.enable, + icon: 'enabled-icon' + } + ] + }). + details_facet({ + factory: IPA.user.details_facet, + sections: [ + { + name: 'identity', + label: IPA.messages.details.identity, + fields: [ + 'title', + 'givenname', + 'sn', + 'cn', + 'displayname', + 'initials' + ] + }, + { + name: 'account', + action_panel: { + factory: IPA.action_panel, + name: 'account_actions', + actions: ['reset_password'] + }, + fields: [ + 'uid', + { + factory: IPA.user_password_widget, + name: 'has_password', + metadata: IPA.get_entity_param('user', 'userpassword') + }, + { + name: 'krbpasswordexpiration', + label: IPA.messages.objects.user.krbpasswordexpiration, + read_only: true, + formatter: IPA.utc_date_formatter() + }, + 'uidnumber', + 'gidnumber', + 'loginshell', + 'homedirectory', + { + type: 'sshkeys', + name: 'ipasshpubkey', + label: IPA.messages.objects.sshkeystore.keys + } + ] + }, + { + name: 'pwpolicy', + label: IPA.messages.objects.pwpolicy.identity, + fields: [ + { + name: 'krbmaxpwdlife', + label: IPA.get_entity_param('pwpolicy', 'krbmaxpwdlife').label, + read_only: true + }, + { + name: 'krbminpwdlife', + label: IPA.get_entity_param('pwpolicy', 'krbminpwdlife').label, + read_only: true + }, + { + name: 'krbpwdhistorylength', + label: IPA.get_entity_param('pwpolicy', 'krbpwdhistorylength').label, + read_only: true, + measurement_unit: 'number_of_passwords' + }, + { + name: 'krbpwdmindiffchars', + label: IPA.get_entity_param('pwpolicy', 'krbpwdmindiffchars').label, + read_only: true + }, + { + name: 'krbpwdminlength', + label: IPA.get_entity_param('pwpolicy', 'krbpwdminlength').label, + read_only: true + }, + { + name: 'krbpwdmaxfailure', + label: IPA.get_entity_param('pwpolicy', 'krbpwdmaxfailure').label, + read_only: true + }, + { + name: 'krbpwdfailurecountinterval', + label: IPA.get_entity_param('pwpolicy', 'krbpwdfailurecountinterval').label, + read_only: true, + measurement_unit: 'seconds' + }, + { + name: 'krbpwdlockoutduration', + label: IPA.get_entity_param('pwpolicy', 'krbpwdlockoutduration').label, + read_only: true, + measurement_unit: 'seconds' + } + ] + }, + { + name: 'krbtpolicy', + label: IPA.messages.objects.krbtpolicy.identity, + fields: [ + { + name: 'krbmaxrenewableage', + label: IPA.get_entity_param('krbtpolicy', 'krbmaxrenewableage').label, + read_only: true, + measurement_unit: 'seconds' + }, + { + name: 'krbmaxticketlife', + label: IPA.get_entity_param('krbtpolicy', 'krbmaxticketlife').label, + read_only: true, + measurement_unit: 'seconds' + } + ] + }, + { + name: 'contact', + fields: [ + { type: 'multivalued', name: 'mail' }, + { type: 'multivalued', name: 'telephonenumber' }, + { type: 'multivalued', name: 'pager' }, + { type: 'multivalued', name: 'mobile' }, + { type: 'multivalued', name: 'facsimiletelephonenumber' } + ] + }, + { + name: 'mailing', + fields: ['street', 'l', 'st', 'postalcode'] + }, + { + name: 'employee', + fields: [ + 'ou', + { + type: 'entity_select', + name: 'manager', + other_entity: 'user', + other_field: 'uid' + } + ] + }, + { + name: 'misc', + fields: [ 'carlicense' ] + } + ], + actions: [ + IPA.select_action, + IPA.enable_action, + IPA.disable_action, + IPA.delete_action, + IPA.user.reset_password_action + ], + header_actions: ['select_action', 'enable', 'disable', 'delete'], + state: { + evaluators: [ + { + factory: IPA.enable_state_evaluator, + field: 'nsaccountlock', + invert_value: true + }, + IPA.user.reset_password_acl_evaluator + ], + summary_conditions: [ + IPA.enabled_summary_cond(), + IPA.disabled_summary_cond() + ] + } + }). + association_facet({ + name: 'memberof_group', + associator: IPA.serial_associator, + link: link, + read_only: self_service + }). + association_facet({ + name: 'memberof_netgroup', + associator: IPA.serial_associator, + link: link, + read_only: self_service + }). + association_facet({ + name: 'memberof_role', + associator: IPA.serial_associator, + link: link, + read_only: self_service + }). + association_facet({ + name: 'memberof_hbacrule', + associator: IPA.serial_associator, + add_method: 'add_user', + remove_method: 'remove_user', + link: link, + read_only: self_service + }). + association_facet({ + name: 'memberof_sudorule', + associator: IPA.serial_associator, + add_method: 'add_user', + remove_method: 'remove_user', + link: link, + read_only: self_service + }). + standard_association_facets({ + link: link + }). + adder_dialog({ + factory: IPA.user_adder_dialog, + sections: [ + { + fields: [ + { + name: 'uid', + required: false + }, + 'givenname', + 'sn' + ] + }, + { + fields: [ + { + name: 'userpassword', + label: IPA.messages.password.new_password, + type: 'password' + }, + { + name: 'userpassword2', + label: IPA.messages.password.verify_password, + type: 'password' + } + ] + } + ] + }); + }; + + return that; +}; + +IPA.user.details_facet = function(spec) { + + spec = spec || {}; + + var that = IPA.details_facet(spec); + + that.refresh_on_success = function(data, text_status, xhr) { + // do not load data from batch + + that.show_content(); + }; + + that.create_refresh_command = function() { + + var pkey = IPA.nav.get_state(that.entity.name+'-pkey'); + + var batch = IPA.batch_command({ + name: 'user_details_refresh' + }); + + var user_command = that.details_facet_create_refresh_command(); + + user_command.on_success = function(data, text_status, xhr) { + // create data that mimics user-show output + var user_data = {}; + user_data.result = data; + that.load(user_data); + }; + + batch.add_command(user_command); + + var pwpolicy_command = IPA.command({ + entity: 'pwpolicy', + method: 'show', + options: { + user: pkey, + all: true, + rights: true + } + }); + + pwpolicy_command.on_success = function(data, text_status, xhr) { + // TODO: Use nested fields: that.fields.get_field('pwpolicy').get_fields(); + var fields = that.fields.get_fields(); + for (var i=0; i<fields.length; i++) { + var field = fields[i]; + + // load result into pwpolicy fields + if (field.widget_name.match(/^pwpolicy\./)) { + field.load(data.result); + } + } + }; + + batch.add_command(pwpolicy_command); + + var krbtpolicy_command = IPA.command({ + entity: 'krbtpolicy', + method: 'show', + args: [ pkey ], + options: { + all: true, + rights: true + } + }); + + krbtpolicy_command.on_success = function(data, text_status, xhr) { + // TODO: Use nested fields: that.fields.get_field('krbtpolicy').get_fields(); + var fields = that.fields.get_fields(); + for (var i=0; i<fields.length; i++) { + var field = fields[i]; + + // load result into krbtpolicy fields + if (field.widget_name.match(/^krbtpolicy\./)) { + field.load(data.result); + } + } + }; + + batch.add_command(krbtpolicy_command); + + return batch; + }; + + return that; +}; + +IPA.user_adder_dialog = function(spec) { + + var that = IPA.entity_adder_dialog(spec); + + that.validate = function() { + var valid = that.dialog_validate(); + + var field1 = that.fields.get_field('userpassword'); + var field2 = that.fields.get_field('userpassword2'); + + var password1 = field1.save()[0]; + var password2 = field2.save()[0]; + + if (password1 !== password2) { + field2.show_error(IPA.messages.password.password_must_match); + valid = false; + } + + return valid; + }; + + that.save = function(record) { + that.dialog_save(record); + delete record.userpassword2; + }; + + return that; +}; + +IPA.user_password_widget = function(spec) { + + spec = spec || {}; + spec.read_only = true; + + var that = IPA.input_widget(spec); + that.set_value = spec.set_value || '******'; + that.unset_value = spec.unset_value || ''; + + that.create = function(container) { + + that.widget_create(container); + + that.display_control = $('<label/>', { + name: that.name + }).appendTo(container); + }; + + that.update = function(values) { + + if (values && values[0]) { + that.display_control.text(that.set_value); + } else { + that.display_control.text(that.unset_value); + } + }; + + that.clear = function() { + that.display_control.text(''); + }; + + return that; +}; + +IPA.user_password_dialog = function(spec) { + + spec = spec || {}; + + spec.width = spec.width || 400; + spec.title = spec.title || IPA.messages.password.reset_password; + spec.sections = spec.sections || []; + + spec.sections.push( + { + name: 'input', + fields: [ + { + name: 'current_password', + label: IPA.messages.password.current_password, + type: 'password', + required: true + }, + { + name: 'password1', + label: IPA.messages.password.new_password, + type: 'password', + required: true + }, + { + name: 'password2', + label: IPA.messages.password.verify_password, + type: 'password', + validators: [IPA.same_password_validator({ + other_field: 'password1' + })], + required: true + } + ] + }); + + var that = IPA.dialog(spec); + + IPA.confirm_mixin().apply(that); + + that.success_handler = spec.on_success; + that.error_handler = spec.on_error; + that.self_service = spec.self_service; //option to force self-service + + that.get_pkey = function() { + var pkey; + if (that.self_service) { + pkey = IPA.whoami.uid[0]; + } else { + pkey = IPA.nav.get_state('user-pkey'); + } + return pkey; + }; + + that.is_self_service = function() { + var pkey = that.get_pkey(); + var self_service = pkey === IPA.whoami.uid[0]; + return self_service; + }; + + that.open = function() { + + var self_service = that.is_self_service(); + var section = that.widgets.get_widget('input'); + var current_password_f = that.fields.get_field('current_password'); + + that.dialog_open(); + section.set_row_visible('current_password', self_service); + current_password_f.set_required(self_service); + that.focus_first_element(); + }; + + that.create_buttons = function() { + + that.create_button({ + name: 'reset_password', + label: IPA.messages.password.reset_password, + click: that.on_reset_click + }); + + that.create_button({ + name: 'cancel', + label: IPA.messages.buttons.cancel, + click: function() { + that.close(); + } + }); + }; + + that.on_confirm = function() { + that.on_reset_click(); + }; + + that.on_reset_click = function() { + + if (!that.validate()) return; + + var pkey = that.get_pkey(); + var self_service = that.is_self_service(); + + var record = {}; + that.save(record); + + var current_password = self_service ? record.current_password[0] : undefined; + var new_password = record.password1[0]; + var repeat_password = record.password2[0]; + + that.set_password( + pkey, + current_password, + new_password, + that.on_reset_success, + that.on_reset_error); + }; + + that.set_password = function(pkey, current_password, password, on_success, on_error) { + + var command = IPA.command({ + method: 'passwd', + args: [ pkey ], + options: { + current_password: current_password, + password: password + }, + on_success: on_success, + on_error: on_error + }); + + command.execute(); + }; + + that.on_reset_success = function(data, text_status, xhr) { + + if (that.success_handler) { + that.success_handler.call(this, data, text_status, xhr); + } else { + IPA.notify_success(IPA.messages.password.password_change_complete); + that.close(); + + // refresh password expiration field + var facet = IPA.current_entity.get_facet(); + facet.refresh(); + + if (that.is_self_service()) { + var command = IPA.get_whoami_command(); + command.execute(); + } + } + }; + + that.on_reset_error = function(xhr, text_status, error_thrown) { + + if (that.error_handler) { + that.error_handler.call(this, xhr, text_status, error_thrown); + } else { + that.close(); + } + }; + + that.create_buttons(); + + return that; +}; + +IPA.user.reset_password_action = function(spec) { + + spec = spec || {}; + spec.name = spec.name || 'reset_password'; + spec.label = spec.label || IPA.messages.password.reset_password; + spec.enable_cond = spec.enable_cond || ['userpassword_w']; + + var that = IPA.action(spec); + + that.execute_action = function(facet) { + + var dialog = IPA.user_password_dialog({ + entity: facet.entity + }); + + dialog.open(); + }; + + return that; +}; + +IPA.user.reset_password_acl_evaluator = function(spec) { + + spec.name = spec.name || 'reset_password_acl_evaluator'; + spec.attribute = spec.attribute || 'userpassword'; + + var that = IPA.acl_state_evaluator(spec); + return that; +}; + +IPA.register('user', IPA.user.entity);
\ No newline at end of file diff --git a/install/ui/src/freeipa/webui.js b/install/ui/src/freeipa/webui.js new file mode 100644 index 000000000..f381ab0be --- /dev/null +++ b/install/ui/src/freeipa/webui.js @@ -0,0 +1,204 @@ +/*jsl:import ipa.js */ +/*jsl:import navigation.js */ + +/* Authors: + * Pavel Zuna <pzuna@redhat.com> + * Endi S. Dewata <edewata@redhat.com> + * + * Copyright (C) 2010 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/>. + */ + +/* REQUIRES: everything, this file puts it all togheter */ + +/* tabs definition for IPA webUI */ + +IPA.admin_navigation = function(spec) { + + spec = spec || {}; + + spec.name = 'admin'; + + spec.tabs = [ + {name: 'identity', label: IPA.messages.tabs.identity, children: [ + {entity: 'user'}, + {entity: 'group'}, + {entity: 'host'}, + {entity: 'hostgroup'}, + {entity: 'netgroup'}, + {entity: 'service'}, + {name:'dns', label: IPA.messages.tabs.dns, children:[ + {entity: 'dnszone'}, + {entity: 'dnsconfig'}, + {entity: 'dnsrecord', hidden:true} + ] + } + ]}, + {name: 'policy', label: IPA.messages.tabs.policy, children: [ + {name: 'hbac', label: IPA.messages.tabs.hbac, children: [ + {entity: 'hbacrule'}, + {entity: 'hbacsvc'}, + {entity: 'hbacsvcgroup'}, + {entity: 'hbactest'} + ]}, + {name: 'sudo', label: IPA.messages.tabs.sudo, children: [ + {entity: 'sudorule'}, + {entity: 'sudocmd'}, + {entity: 'sudocmdgroup'} + ]}, + {name:'automount', + label: IPA.messages.tabs.automount, + children:[ + {entity: 'automountlocation', hidden:true, depth: -1}, + {entity: 'automountmap', hidden: true, depth: -1}, + {entity: 'automountkey', hidden: true, depth: -1}]}, + {entity: 'pwpolicy'}, + {entity: 'krbtpolicy'}, + {entity: 'selinuxusermap'}, + {name: 'automember', label: IPA.messages.tabs.automember, + children: [ + { name: 'amgroup', entity: 'automember', + facet: 'searchgroup', label: IPA.messages.objects.automember.usergrouprules}, + { name: 'amhostgroup', entity: 'automember', + facet: 'searchhostgroup', label: IPA.messages.objects.automember.hostgrouprules} + ]} + ]}, + {name: 'ipaserver', label: IPA.messages.tabs.ipaserver, children: [ + {name: 'rolebased', label: IPA.messages.tabs.role, children: [ + {entity: 'role'}, + {entity: 'privilege'}, + {entity: 'permission'} + ]}, + {entity: 'selfservice'}, + {entity: 'delegation'}, + {entity: 'idrange'}, + {entity: 'trust'}, + {entity: 'config'} + ]}]; + + var that = IPA.navigation(spec); + + return that; +}; + +IPA.self_serv_navigation = function(spec) { + + spec = spec || {}; + + spec.name = 'self-service'; + + spec.tabs = [ + {name: 'identity', label: IPA.messages.tabs.identity, children: [ + {entity: 'user'} + ]}]; + + var that = IPA.navigation(spec); + + that.update = function() { + var pkey = that.get_state('user-pkey'); + var facet = that.get_state('user-facet'); + + if (pkey && facet) { + that.navigation_update(); + + } else { + var state = { + 'navigation': 'identity', + 'identity': 'user', + 'user-pkey': pkey || IPA.whoami_pkey, + 'user-facet': facet || 'details' + }; + that.push_state(state); + } + }; + + return that; +}; + +/* main (document onready event handler) */ +$(function() { + + + + /* main loop (hashchange event handler) */ + function window_hashchange(evt){ + IPA.nav.update(); + } + + function create_navigation() { + var whoami = IPA.whoami; + var factory; + + + if (whoami.hasOwnProperty('memberof_group') && + whoami.memberof_group.indexOf('admins') !== -1) { + factory = IPA.admin_navigation; + } else if (whoami.hasOwnProperty('memberofindirect_group')&& + whoami.memberofindirect_group.indexOf('admins') !== -1) { + factory = IPA.admin_navigation; + } else if (whoami.hasOwnProperty('memberof_role') && + whoami.memberof_role.length > 0) { + factory = IPA.admin_navigation; + } else if (whoami.hasOwnProperty('memberofindirect_role') && + whoami.memberofindirect_role.length > 0) { + factory = IPA.admin_navigation; + } else { + factory = IPA.self_serv_navigation; + } + + return factory({ + container: $('#navigation'), + content: $('#content') + }); + } + + + function init_on_success(data, text_status, xhr) { + $(window).bind('hashchange', window_hashchange); + + var whoami = IPA.whoami; + IPA.whoami_pkey = whoami.uid[0]; + $('#loggedinas .login').text(whoami.cn[0]); + $('#loggedinas a').fragment( + {'user-facet': 'details', 'user-pkey': IPA.whoami_pkey}, 2); + + $('#logout').click(function() { + IPA.logout(); + return false; + }).text(IPA.messages.login.logout); + + $('.header-loggedinas').css('visibility','visible'); + IPA.update_password_expiration(); + + IPA.nav = create_navigation(); + IPA.nav.create(); + IPA.nav.update(); + + $('#login_header').html(IPA.messages.login.header); + } + + + function init_on_error(xhr, text_status, error_thrown) { + var container = $('#content').empty(); + container.append('<p>Error: '+error_thrown.name+'</p>'); + container.append('<p>'+error_thrown.message+'</p>'); + } + + IPA.init({ + on_success: init_on_success, + on_error: init_on_error + }); +}); diff --git a/install/ui/src/freeipa/widget.js b/install/ui/src/freeipa/widget.js new file mode 100644 index 000000000..8d2239d89 --- /dev/null +++ b/install/ui/src/freeipa/widget.js @@ -0,0 +1,3559 @@ +/*jsl:import ipa.js */ +/* Authors: + * Endi Sukma Dewata <edewata@redhat.com> + * Adam Young <ayoung@redhat.com> + * Pavel Zuna <pzuna@redhat.com> + * + * Copyright (C) 2010 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/>. + */ + +/* REQUIRES: ipa.js */ + +IPA.checkbox_column_width = 22; +IPA.required_indicator = '*'; + +IPA.widget = function(spec) { + + spec = spec || {}; + + var that = {}; + + that.name = spec.name; + that.id = spec.id; + that.label = spec.label; + that.tooltip = spec.tooltip; + that.measurement_unit = spec.measurement_unit; + that.entity = IPA.get_entity(spec.entity); //some old widgets still need it + that.facet = spec.facet; + + that.create = function(container) { + container.addClass('widget'); + that.container = container; + }; + + that.clear = function() { + }; + + that.set_visible = function(visible) { + + if (visible) { + that.container.show(); + } else { + that.container.hide(); + } + }; + + that.build_child = function(spec, factory) { + + if (typeof spec === 'function') { + spec = { + factory: spec + }; + } + + $.extend(spec, { + parent: that, + entity: that.entity, + facet: that.facet + }); + + var child = IPA.build(spec, factory); + return child; + }; + + that.widget_create = that.create; + + return that; +}; + +IPA.input_widget = function(spec) { + + spec = spec || {}; + + var that = IPA.widget(spec); + + that.width = spec.width; + that.height = spec.height; + + that.undo = spec.undo === undefined ? true : spec.undo; + that.writable = spec.writable === undefined ? true : spec.writable; + that.read_only = spec.read_only; + that.hidden = spec.hidden; + + //events + //each widget can contain several events + that.value_changed = IPA.observer(); + that.undo_clicked = IPA.observer(); + + + that.create_error_link = function(container) { + container.append(' '); + + $('<span/>', { + name: 'error_link', + 'class': 'ui-state-error ui-corner-all', + style: 'display:none' + }).appendTo(container); + }; + + that.create_required = function(container) { + that.required_indicator = $('<span/>', { + 'class': 'required-indicator', + text: IPA.required_indicator, + style: 'display: none;' + }).appendTo(container); + }; + + that.update = function() { + }; + + /** + * 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. + */ + that.save = function() { + return []; + }; + + /** + * This function creates an undo link in the container. + * 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. + */ + that.create_undo = function(container, on_undo) { + container.append(' '); + + that.undo_span = + $('<span/>', { + name: 'undo', + style: 'display: none;', + 'class': 'ui-state-highlight ui-corner-all undo', + html: IPA.messages.widget.undo + }).appendTo(container); + + if(on_undo === undefined) { + on_undo = function() { + that.undo_clicked.notify([], that); + }; + } + + if(typeof on_undo === 'function') { + that.undo_span.click(on_undo); + } + }; + + that.get_undo = function() { + return $(that.undo_span); + }; + + that.show_undo = function() { + that.get_undo().css('display', 'inline'); + }; + + that.hide_undo = function() { + $(that.undo_span).css('display', 'none'); + }; + + that.get_error_link = function() { + return $('span[name="error_link"]', that.container); + }; + + that.show_error = function(message) { + var error_link = that.get_error_link(); + error_link.html(message); + error_link.css('display', 'block'); + }; + + that.hide_error = function() { + var error_link = that.get_error_link(); + error_link.css('display', 'none'); + }; + + that.set_required = function(required) { + + that.required = required; + + if (that.required_indicator) { + that.required_indicator.css('display', that.required ? 'inline' : 'none'); + } + }; + + that.on_value_changed = function() { + var value = that.save(); + that.value_changed.notify([value], that); + }; + + that.focus_input = function() {}; + that.set_deleted = function() {}; + + // methods that should be invoked by subclasses + that.widget_hide_error = that.hide_error; + that.widget_show_error = that.show_error; + + return that; +}; + +/*uses a browser specific technique to select a range.*/ +IPA.select_range = function(input,start, end) { + input.focus(); + if (input[0].setSelectionRange) { + input[0].setSelectionRange(start, end); + } else if (input[0].createTextRange) { + var range = input[0].createTextRange(); + range.collapse(true); + range.moveEnd('character', end); + range.moveStart('character', start); + range.select(); + } +}; + + +IPA.text_widget = function(spec) { + + spec = spec || {}; + + var that = IPA.input_widget(spec); + + that.size = spec.size || 30; + that.input_type = spec.input_type || 'text'; + + that.select_range = function(start, end){ + IPA.select_range(that.input, start, end); + }; + + that.create = function(container) { + + that.widget_create(container); + + container.addClass('text-widget'); + + that.display_control = $('<label/>', { + name: that.name, + style: 'display: none;' + }).appendTo(container); + + that.input = $('<input/>', { + type: that.input_type, + name: that.name, + disabled: that.disabled, + size: that.size, + title: that.tooltip, + keyup: function() { + that.on_value_changed(); + } + }).appendTo(container); + + that.input.bind('input', function() { + that.on_value_changed(); + }); + + if (that.undo) { + that.create_undo(container); + } + + that.create_error_link(container); + }; + + that.update = function(values) { + var value = values && values.length ? values[0] : ''; + + if (that.read_only || !that.writable) { + that.display_control.text(value); + that.display_control.css('display', 'inline'); + that.input.css('display', 'none'); + + } else { + that.input.val(value); + that.display_control.css('display', 'none'); + that.input.css('display', 'inline'); + } + }; + + that.save = function() { + if (that.read_only || !that.writable) { + return null; + + } else { + var value = that.input.val(); + return value === '' ? [] : [value]; + } + }; + + that.set_enabled = function(value) { + + that.input.prop('disabled', !value); + }; + + that.clear = function() { + that.input.val(''); + that.display_control.text(''); + }; + + that.focus_input = function() { + that.input.focus(); + }; + + that.set_deleted = function(deleted) { + if(deleted) { + that.input.addClass('strikethrough'); + } else { + that.input.removeClass('strikethrough'); + } + }; + + // methods that should be invoked by subclasses + that.text_load = that.load; + + return that; +}; + +IPA.password_widget = function(spec) { + + spec = spec || {}; + spec.input_type = 'password'; + + var that = IPA.text_widget(spec); + return that; +}; + +IPA.multivalued_widget = function(spec) { + + spec = spec || {}; + + var that = IPA.input_widget(spec); + + that.widget_factory = spec.widget_factory || IPA.text_widget; + that.size = spec.size || 30; + that.undo_control; + that.initialized = false; + + that.rows = []; + + that.on_child_value_changed = function(row) { + if (that.test_dirty_row(row)) { + row.widget.show_undo(); + row.remove_link.hide(); + } else { + row.widget.hide_undo(); + row.remove_link.show(); + } + + that.value_changed.notify([], that); + }; + + 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(); + } + + row.widget.hide_undo(); + that.value_changed.notify([], that); + }; + + that.hide_undo = function() { + + $(that.undo_span).css('display', 'none'); + for(var i=0; i<that.rows.length; i++) { + var row = that.rows[i]; + row.widget.hide_undo(); + row.remove_link.show(); + } + }; + + + that.update_child = function(values, index) { + that.rows[index].widget.update(values); + }; + + that.show_child_undo = function(index) { + that.rows[index].widget.show_undo(); + that.show_undo(); + }; + + that.hide_error = function() { + + that.widget_hide_error(); + + for (var i=0; i<that.rows.length; i++) { + that.rows[i].widget.hide_error(); + } + }; + + that.show_child_error = function(index, error) { + + that.rows[index].widget.show_error(error); + }; + + that.get_saved_value_row_index = function(index) { + + for (var i=0; i<that.rows.length;i++) { + + if(that.rows[i].deleted) index++; + if(i === index) return i; + } + + return -1; //error state + }; + + that.save = function() { + + var values = []; + + for (var i=0; i<that.rows.length;i++) { + + if(that.rows[i].deleted) continue; + + values.push(that.extract_child_value(that.rows[i].widget.save())); + } + + return values; + }; + + that.extract_child_value = function(value) { + + if (value instanceof Array) { + if (value.length > 0) { + return value[0]; + } + return ''; + } + + if (value) return value; + + return ''; + }; + + that.focus_last = function() { + var last_row = that.rows[that.rows.length-1]; + last_row.widget.focus_input(); + }; + + that.add_row = function(values) { + var row = {}; + that.rows.push(row); + var row_index = that.rows.length - 1; + row.is_new = that.initialized; + + row.container = $('<div/>', { name: 'value'}); + + row.widget = that.widget_factory({ + name: that.name+'-'+row_index, + undo: that.undo || row.is_new, + read_only: that.read_only, + writable: that.writable + }); + + row.widget.create(row.container); + + row.original_values = values; + row.widget.update(values); + + row.widget.value_changed.attach(function() { + that.on_child_value_changed(row); + }); + row.widget.undo_clicked.attach(function() { + that.on_child_undo_clicked(row); + }); + + row.remove_link = $('<a/>', { + name: 'remove', + href: 'jslink', + title: IPA.messages.buttons.remove, + html: IPA.messages.buttons.remove, + click: function () { + that.remove_row(row); + that.value_changed.notify([], that); + return false; + } + }).appendTo(row.container); + + if(row.is_new) { + row.remove_link.hide(); + row.widget.show_undo(); + that.value_changed.notify([], that); + } + + row.container.insertBefore(that.add_link); + }; + + that.create = function(container) { + + container.addClass('multivalued-widget'); + + that.widget_create(container); + + that.create_error_link(container); + + that.add_link = $('<a/>', { + name: 'add', + href: 'jslink', + title: IPA.messages.buttons.add, + html: IPA.messages.buttons.add, + click: function() { + that.add_row(''); + that.focus_last(); + return false; + } + }).appendTo(container); + + + container.append(' '); + + that.undo_span = $('<span/>', { + name: 'undo_all', + style: 'display: none;', + 'class': 'ui-state-highlight ui-corner-all undo', + html: IPA.messages.widget.undo_all, + click: function() { + that.undo_clicked.notify([], that); + } + }).appendTo(container); + }; + + that.remove_row = function(row) { + if (row.is_new) { + row.container.remove(); + that.rows.splice(that.rows.indexOf(row), 1); //not supported by IE<9 + } else { + row.deleted = true; + row.widget.set_deleted(true); + row.remove_link.hide(); + row.widget.show_undo(); + } + }; + + that.remove_rows = function() { + for(var i=0; i < that.rows.length; i++) { + that.rows[i].container.remove(); + } + that.rows = []; + }; + + that.clear = function() { + that.remove_rows(); + }; + + that.test_dirty_row = function(row) { + + if (row.deleted || row.is_new) return true; + + var values = 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; + } + } + + 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; + + if (index === undefined) { + + that.initialized = false; + that.remove_rows(); + + for (var i=0; i<values.length; i++) { + value = [values[i]]; + if(value[0]) { + that.add_row(value); + } + } + + that.initialized = true; + + if (that.read_only || !that.writable) { + that.add_link.css('display', 'none'); + } else { + that.add_link.css('display', 'inline'); + } + + } else { + value = values[index]; + var row = that.rows[index]; + row.widget.update(values); + } + }; + + return that; +}; + +IPA.checkbox_widget = function (spec) { + + spec = spec || {}; + + var that = IPA.input_widget(spec); + + // default value + that.checked = spec.checked || false; + + that.create = function(container) { + + that.widget_create(container); + + container.addClass('checkbox-widget'); + + that.input = $('<input/>', { + type: 'checkbox', + name: that.name, + checked: that.checked, + title: that.tooltip, + change: function() { + that.value_changed.notify([that.save()], that); + } + }).appendTo(container); + + if (that.undo) { + that.create_undo(container); + } + + that.create_error_link(container); + }; + + that.save = function() { + var value = that.input.is(':checked'); + return [value]; + }; + + that.update = function(values) { + var value; + + if (values && values.length) { + value = values[0]; + } + + if (typeof value !== 'boolean') { + // use default value + value = that.checked; + } + + that.input.prop('checked', value); + }; + + that.clear = function() { + that.input.prop('checked', false); + }; + + that.checkbox_save = that.save; + + return that; +}; + +IPA.checkboxes_widget = function (spec) { + + spec = spec || {}; + + var that = IPA.input_widget(spec); + + that.options = spec.options || []; + that.direction = spec.direction || 'vertical'; + + that.create = function(container) { + + that.widget_create(container); + + container.addClass('checkboxes-widget'); + + var vertical = that.direction === 'vertical'; + + for (var i=0; i<that.options.length; i++) { + var option = that.options[i]; + $('<input/>', { + type: 'checkbox', + name: that.name, + value: option.value, + title: that.tooltip + }).appendTo(container); + + $('<label/>', { + text: option.label, + title: that.tooltip + }).appendTo(container); + + if (vertical) { + $('<br/>').appendTo(container); + } + } + + if (that.undo) { + that.create_undo(container); + } + + var input = $('input[name="'+that.name+'"]', that.container); + input.change(function() { + that.value_changed.notify([that.save()], that); + }); + + that.create_error_link(container); + }; + + that.save = function() { + var values = []; + + $('input[name="'+that.name+'"]:checked', that.container).each(function() { + values.push($(this).val()); + }); + + return values; + }; + + that.update = function(values) { + var inputs = $('input[name="'+that.name+'"]', that.container); + inputs.prop('checked', false); + + for (var j=0; values && j<values.length; j++) { + var value = values[j]; + var input = $('input[name="'+that.name+'"][value="'+value+'"]', that.container); + if (!input.length) continue; + input.prop('checked', true); + } + }; + + that.clear = function() { + $('input[name="'+that.name+'"]').prop('checked', false); + }; + + that.add_option = function(option) { + that.options.push(option); + }; + + // methods that should be invoked by subclasses + that.checkboxes_update = that.update; + + return that; +}; + +IPA.radio_widget = function(spec) { + + spec = spec || {}; + + var that = IPA.input_widget(spec); + + that.default_value = spec.default_value; + that.options = spec.options; + + that.create = function(container) { + + that.widget_create(container); + + container.addClass('radio-widget'); + + var name = IPA.html_util.get_next_id(that.name+'-'); + that.selector = 'input[name="'+name+'"]'; + + for (var i=0; i<that.options.length; i++) { + var option = that.options[i]; + + var id = name+'-'+i; + + $('<input/>', { + id: id, + type: 'radio', + name: name, + value: option.value + }).appendTo(container); + + $('<label/>', { + text: option.label, + 'for': id + }).appendTo(container); + } + + if (that.undo) { + that.create_undo(container); + } + + var input = $(that.selector, that.container); + input.change(function() { + that.value_changed.notify([that.save()], that); + }); + + that.create_error_link(container); + }; + + that.save = function() { + var input = $(that.selector+':checked', that.container); + if (!input.length) return []; + return [input.val()]; + }; + + that.update = function(values) { + + $(that.selector, that.container).each(function() { + var input = this; + input.checked = false; + }); + + var value = values && values.length ? values[0] : ''; + var input = $(that.selector+'[value="'+value+'"]', that.container); + if (input.length) { + input.prop('checked', true); + } else if (that.default_value) { + input = $(that.selector+'[value="'+that.default_value+'"]', that.container); + input.prop('checked', true); + } + + that.value_changed.notify([that.save()], that); + }; + + that.clear = function() { + $(that.selector, that.container).prop('checked', false); + + if (that.default_value) { + var input = $(that.selector+'[value="'+that.default_value+'"]', that.container); + input.prop('checked', true); + } + }; + + // methods that should be invoked by subclasses + that.radio_create = that.create; + that.radio_save = that.save; + + return that; +}; + +IPA.select_widget = function(spec) { + + spec = spec || {}; + + var that = IPA.input_widget(spec); + + that.options = spec.options || []; + + that.create = function(container) { + + that.widget_create(container); + + container.addClass('select-widget'); + + that.select = $('<select/>', { + name: that.name, + change: function() { + that.value_changed.notify([], that); + return false; + } + }).appendTo(container); + + that.create_options(); + + if (that.undo) { + that.create_undo(container); + } + + that.create_error_link(container); + }; + + that.create_options = function() { + + that.select.empty(); + + for (var i=0; i<that.options.length; i++) { + var option = that.options[i]; + + $('<option/>', { + text: option.label, + value: option.value + }).appendTo(that.select); + } + }; + + that.save = function() { + var value; + + if (that.select) { + value = that.select.val() || ''; + } else if (that.options.length > 0) { + value = that.options[0].value; //will be default value + } + + return [value]; + }; + + that.update = function(values) { + var value = values[0]; + var option = $('option[value="'+value+'"]', that.select); + if (!option.length) return; + option.prop('selected', true); + }; + + that.empty = function() { + $('option', that.select).remove(); + }; + + that.clear = function() { + $('option', that.select).prop('selected', false); + }; + + that.set_options_enabled = function(enabled, options) { + + if (!options) { + $('option', that.select).prop('disabled', !enabled); + } else { + for (var i=0; i<options.length;i++) { + var value = options[i]; + var option = $('option[value="'+value+'"]', that.select); + option.prop('disabled', !enabled); + } + } + }; + + that.enable_options = function(options) { + + that.set_options_enabled(true, options); + }; + + that.disable_options = function(options) { + + that.set_options_enabled(false, options); + }; + + // methods that should be invoked by subclasses + that.select_save = that.save; + that.select_update = that.update; + + return that; +}; + +IPA.textarea_widget = function (spec) { + + spec = spec || {}; + + var that = IPA.input_widget(spec); + + that.rows = spec.rows || 5; + that.cols = spec.cols || 40; + + that.create = function(container) { + + that.widget_create(container); + + container.addClass('textarea-widget'); + + that.input = $('<textarea/>', { + name: that.name, + rows: that.rows, + cols: that.cols, + disabled: that.disabled, + title: that.tooltip, + keyup: function() { + that.on_value_changed(); + } + }).appendTo(container); + + that.input.bind('input', function() { + that.on_value_changed(); + }); + + if (that.undo) { + that.create_undo(container); + } + + that.create_error_link(container); + }; + + that.save = function() { + var value = that.input.val(); + return [value]; + }; + + that.update = function(values) { + var value = values && values.length ? values[0] : ''; + that.input.val(value); + }; + + that.clear = function() { + that.input.val(''); + }; + + return that; +}; + +IPA.formatter = function(spec) { + + spec = spec || {}; + + var that = {}; + + that.type = spec.type; // default is text + + // parse attribute value into a normalized value + that.parse = function(value) { + return value; + }; + + // format normalized value + that.format = function(value) { + return value; + }; + + return that; +}; + +IPA.boolean_formatter = function(spec) { + + spec = spec || {}; + + var that = IPA.formatter(spec); + + that.true_value = spec.true_value || IPA.messages['true']; + that.false_value = spec.false_value || IPA.messages['false']; + that.show_false = spec.show_false; + that.invert_value = spec.invert_value; + + // convert string boolean value into real boolean value, or keep the original value + that.parse = function(value) { + + if (value === undefined || value === null) return ''; + + if (value instanceof Array) { + value = value[0]; + } + + if (typeof value === 'string') { + value = value.toLowerCase(); + + if (value === 'true') { + value = true; + } else if (value === 'false') { + value = false; + } // leave other values unchanged + } + + if (typeof value === 'boolean') { + if (that.invert_value) value = !value; + } + + return value; + }; + + // convert boolean value into formatted string, or keep the original value + that.format = function(value) { + + if (typeof value === 'boolean') { + if (value) { + value = that.true_value; + + } else { + if (that.show_false) { + value = that.false_value; + } else { + value = ''; + } + } + } + + return value; + }; + + that.boolean_formatter_parse = that.parse; + that.boolean_formatter_format = that.format; + + return that; +}; + +IPA.boolean_status_formatter = function(spec) { + + spec = spec || {}; + + var that = IPA.boolean_formatter(spec); + + that.true_value = spec.true_value || IPA.messages.status.enabled; + that.false_value = spec.false_value || IPA.messages.status.disabled; + that.show_false = true; + that.type = 'html'; + + that.format = function(value) { + var status = value ? 'enabled' : 'disabled'; + var formatted_value = that.boolean_formatter_format(value); + formatted_value = '<span class=\"icon '+status+'-icon\"/> '+formatted_value; + return formatted_value; + }; + + return that; +}; + +/* Take an LDAP format date in UTC and format it */ +IPA.utc_date_formatter = function(spec) { + + spec = spec || {}; + + var that = IPA.formatter(spec); + + that.format = function(value) { + + if (!value) return ''; + var date = IPA.parse_utc_date(value); + if (!date) return value; + return date.toString(); + }; + + return that; +}; + +/* + The entity name must be set in the spec either directly or via entity.name +*/ +IPA.column = function (spec) { + + spec = spec || {}; + + var that = {}; + + that.entity = IPA.get_entity(spec.entity); + that.name = spec.name; + + that.label = spec.label; + that.width = spec.width; + that.primary_key = spec.primary_key; + that.link = spec.link; + that.formatter = spec.formatter; + + if (!that.entity) { + throw { + expected: false, + message: 'Column created without an entity.' + }; + } + + that.setup = function(container, record, suppress_link) { + + container.empty(); + + var value = record[that.name]; + var type; + if (that.formatter) { + value = that.formatter.parse(value); + value = that.formatter.format(value); + type = that.formatter.type; + } + value = value ? value.toString() : ''; + + var c; + if (that.link && !suppress_link) { + c = $('<a/>', { + href: '#'+value, + click: function() { + return that.link_handler(value); + } + }).appendTo(container); + + } else { + c = container; + } + + if (type === 'html') { + c.html(value); + } else { + c.text(value); + } + }; + + that.link_handler = function(value) { + return false; + }; + + + /*column initialization*/ + if (that.entity && !that.label) { + var metadata = IPA.get_entity_param(that.entity.name, that.name); + if (metadata) { + that.label = metadata.label; + } + } + + + return that; +}; + +IPA.table_widget = function (spec) { + + spec = spec || {}; + + var that = IPA.input_widget(spec); + + that.scrollable = spec.scrollable; + that.selectable = spec.selectable === undefined ? true : spec.selectable; + that.save_values = spec.save_values === undefined ? true : spec.save_values; + that['class'] = spec['class']; + + that.pagination = spec.pagination; + that.current_page = 1; + that.total_pages = 1; + that.page_length = spec.page_length || 20; + + that.multivalued = spec.multivalued === undefined ? true : spec.multivalued; + + that.columns = $.ordered_map(); + that.value_attr_name = spec.value_attribute || that.name; + + that.get_columns = function() { + return that.columns.values; + }; + + that.get_column = function(name) { + return that.columns.get(name); + }; + + that.add_column = function(column) { + column.entity = that.entity; + that.columns.put(column.name, column); + }; + + that.set_columns = function(columns) { + that.clear_columns(); + for (var i=0; i<columns.length; i++) { + that.add_column(columns[i]); + } + }; + + that.clear_columns = function() { + that.columns.empty(); + }; + + that.create_column = function(spec) { + var column = IPA.column(spec); + that.add_column(column); + return column; + }; + + + that.create = function(container) { + + that.widget_create(container); + + container.addClass('table-widget'); + + that.table = $('<table/>', { + 'class': 'search-table' + }).appendTo(container); + + if (that['class']) that.table.addClass(that['class']); + + if (that.scrollable) { + that.table.addClass('scrollable'); + } + + that.thead = $('<thead/>').appendTo(that.table); + + var tr = $('<tr/>').appendTo(that.thead); + + var th; + + if (that.selectable) { + th = $('<th/>', { + 'style': 'width: '+IPA.checkbox_column_width+'px;' + }).appendTo(tr); + + if (that.multivalued) { + var select_all_checkbox = $('<input/>', { + type: 'checkbox', + name: that.name, + title: IPA.messages.search.select_all + }).appendTo(th); + + select_all_checkbox.change(function() { + if(select_all_checkbox.is(':checked')) { + that.select_all(); + } else { + that.unselect_all(); + } + return false; + }); + } + } + var columns = that.columns.values; + var column; + + var columns_without_width = 0; + var per_column_space = 16; //cell padding(2x6px), border (2x1px), spacing (2px) + var available_width = that.thead.width(); + available_width -= 2; //first cell spacing + + //subtract checkbox column + if(that.selectable) { + available_width -= IPA.checkbox_column_width; + available_width -= per_column_space; + } + + //subtract width of columns with their width set + for (i=0; i<columns.length; i++) { + column = columns[i]; + if (column.width) { + available_width -= parseInt( + column.width.substring(0, column.width.length-2),10); + available_width -= per_column_space; + } else { + columns_without_width++; + } + } + + //width for columns without width set + var new_column_width = (available_width - + per_column_space * columns_without_width) / + columns_without_width; + + + //set the new width, now all columns should have width set + for (i=0; i<columns.length; i++) { + column = columns[i]; + if (!column.width) { + column.width = new_column_width+"px"; + } + } + + for (i=0; i<columns.length; i++) { + column = columns[i]; + + th = $('<th/>').appendTo(tr); + + th.css('width', column.width); + th.css('max-width', column.width); + + var label = column.label; + + $('<div/>', { + 'style': 'float: left;', + 'html': label + }).appendTo(th); + + if (i == columns.length-1) { + that.buttons = $('<div/>', { + 'name': 'buttons', + 'style': 'float: right;' + }).appendTo(th); + } + + } + + that.tbody = $('<tbody/>').appendTo(that.table); + + // workaround for #2835 + if ($.browser.msie) { + that.tbody.mousedown(function(event) { + that.scroll_top = that.tbody.scrollTop(); + window.setTimeout(function() { + if (that.tbody.scrollTop() === 0) { + that.tbody.scrollTop(that.scroll_top); + } + }, 0); + }); + } + + if (that.height) { + that.tbody.css('height', that.height); + } + + that.row = $('<tr/>'); + + var td; + + if (that.selectable) { + td = $('<td/>', { + 'style': 'width: '+ (IPA.checkbox_column_width + 7) +'px;' + }).appendTo(that.row); + + if (that.multivalued) { + $('<input/>', { + type: 'checkbox', + name: that.name, + value: '' + }).appendTo(td); + } else { + $('<input/>', { + type: 'radio', + name: that.name, + value: '' + }).appendTo(td); + } + } + + var width; + + for (/* var */ i=0; i<columns.length; i++) { + /* var */ column = columns[i]; + + td = $('<td/>').appendTo(that.row); + if (column.width) { + width = parseInt( + column.width.substring(0, column.width.length-2),10); + width += 7; //data cells lack right padding + width += 'px'; + td.css('width', width); + td.css('max-width', width); + } + + $('<div/>', { + 'name': column.name + }).appendTo(td); + } + + that.tfoot = $('<tfoot/>').appendTo(that.table); + + tr = $('<tr/>').appendTo(that.tfoot); + + td = $('<td/>', { + colspan: columns.length + (that.selectable ? 1 : 0) + }).appendTo(tr); + + that.create_error_link(td); + + that.summary = $('<span/>', { + 'name': 'summary' + }).appendTo(td); + + that.pagination_control = $('<span/>', { + 'class': 'pagination-control' + }).appendTo(td); + + if (that.pagination) { + + $('<a/>', { + text: IPA.messages.widget.prev, + name: 'prev_page', + click: function() { + that.prev_page(); + return false; + } + }).appendTo(that.pagination_control); + + that.pagination_control.append(' '); + + $('<a/>', { + text: IPA.messages.widget.next, + name: 'next_page', + click: function() { + that.next_page(); + return false; + } + }).appendTo(that.pagination_control); + + that.pagination_control.append(' '); + that.pagination_control.append(IPA.messages.widget.page); + that.pagination_control.append(': '); + + that.current_page_input = $('<input/>', { + type: 'text', + name: 'current_page', + keypress: function(e) { + if (e.which == 13) { + var page = parseInt(that.current_page_input.val(), 10) || 1; + that.set_page(page); + } + } + }).appendTo(that.pagination_control); + + that.pagination_control.append(' / '); + + that.total_pages_span = $('<span/>', { + name: 'total_pages' + }).appendTo(that.pagination_control); + } + }; + + that.prev_page = function() { + if (that.current_page > 1) { + that.current_page--; + that.refresh(); + } + }; + + that.next_page = function() { + if (that.current_page < that.total_pages) { + that.current_page++; + that.refresh(); + } + }; + + that.set_page = function(page) { + if (page < 1) { + page = 1; + } else if (page > that.total_pages) { + page = that.total_pages; + } + that.current_page = page; + that.current_page_input.val(page); + that.refresh(); + }; + + that.select_changed = function() { + }; + + that.select_all = function() { + $('input[name="'+that.name+'"]', that.thead).prop('checked', true). + attr('title', IPA.messages.search.unselect_all); + $('input[name="'+that.name+'"]', that.tbody).prop('checked', true); + that.select_changed(); + }; + + that.unselect_all = function() { + $('input[name="'+that.name+'"]', that.thead).prop('checked', false). + attr('title', IPA.messages.search.select_all); + $('input[name="'+that.name+'"]', that.tbody).prop('checked', false); + that.select_changed(); + }; + + that.set_values = function(values) { + $('input[name="'+that.name+'"]', that.tbody).prop('checked', false); + for (var i=0; values && i<values.length; i++) { + var value = values[i]; + $('input[name="'+that.name+'"][value="'+value+'"]', that.tbody).prop('checked', true); + } + that.select_changed(); + }; + + that.empty = function() { + that.tbody.empty(); + }; + + that.load = function(result) { + + that.empty(); + + that.values = result[that.value_attr_name] || []; + for (var i=0; i<that.values.length; i++) { + var record = that.get_record(result, i); + that.add_record(record); + } + }; + + that.update = function(records) { + + that.empty(); + + that.values = []; + that.records = records; + + for (var i=0; i<records.length; i++) { + var record = records[i]; + that.values.push(record[that.value_attr_name]); + that.add_record(record); + } + }; + + that.save = function() { + if (that.save_values) { + var values = []; + + $('input[name="'+that.name+'"]', that.tbody).each(function() { + values.push($(this).val()); + }); + + return values; + + } else { + return null; + } + }; + + that.get_selected_values = function() { + var values = []; + + $('input[name="'+that.name+'"]:checked', that.tbody).each(function() { + values.push($(this).val()); + }); + + return values; + }; + + that.get_selected_rows = function() { + return $('input[name="'+that.name+'"]:checked', that.tbody).closest('tr'); + }; + + that.get_record = function(result, index) { + + var record = {}; + + var columns = that.columns.values; + for (var i=0; i<columns.length; i++){ + var name = columns[i].name; + var values = result[name]; + if (!values) continue; + + if (values instanceof Array){ + record[name] = values[index]; + } else { + record[name] = values; + } + } + + return record; + }; + + that.add_record = function(record) { + + var tr = that.row.clone(); + tr.appendTo(that.tbody); + + $('input[name="'+that.name+'"]', tr).click(function(){ + that.select_changed(); + }); + + var select_set = false; + var value; + var columns = that.columns.values; + + for (var i=0; i<columns.length; i++){ + var column = columns[i]; + + value = record[column.name]; + value = value ? value.toString() : ''; + + if (column.primary_key) { + $('input[name="'+that.name+'"]', tr).val(value); + select_set = true; + } + + var div = $('div[name="'+column.name+'"]', tr); + + that.setup_column(column, div, record); + } + + if (!select_set) { + value = record[that.value_attr_name]; + value = value ? value.toString() : ''; + $('input[name="'+that.name+'"]', tr).val(value); + } + + return tr; + }; + + that.set_row_enabled = function(tr, enabled) { + if (enabled) { + tr.removeClass('disabled'); + } else { + tr.addClass('disabled'); + } + }; + + that.setup_column = function(column, div, record) { + column.setup(div, record); + }; + + that.add_rows = function(rows) { + for (var i=0; i<rows.length; i++) { + var tr = rows[i]; + $('input', tr).attr('name', that.name); + that.tbody.append(tr); + } + }; + + that.remove_selected_rows = function() { + var rows = []; + that.tbody.children().each(function() { + var tr = $(this); + if (!$('input[name="'+that.name+'"]', tr).get(0).checked) return; + tr.detach(); + rows.push(tr); + }); + return rows; + }; + + that.show_error = function(message) { + var error_link = that.get_error_link(); + error_link.html(message); + error_link.css('display', 'inline'); + }; + + that.set_enabled = function(enabled) { + $('input[name="'+that.name+'"]', that.table).prop('disabled', !enabled); + }; + + that.clear = function() { + that.empty(); + that.summary.text(''); + }; + + //column initialization + if (spec.columns) { + for (var i=0; i<spec.columns; i++) { + that.create_column(spec.columns[i]); + } + } + + // methods that should be invoked by subclasses + that.table_create = that.create; + that.table_load = that.load; + that.table_next_page = that.next_page; + that.table_prev_page = that.prev_page; + that.table_set_enabled = that.set_enabled; + that.table_set_page = that.set_page; + that.table_show_error = that.show_error; + that.table_set_values = that.set_values; + that.table_update = that.update; + + return that; +}; + + +IPA.attribute_table_widget = function(spec) { + + + spec = spec || {}; + spec.columns = spec.columns || []; + + var that = IPA.table_widget(spec); + + that.attribute_name = spec.attribute_name || that.name; + that.adder_dialog_spec = spec.adder_dialog; + that.css_class = spec.css_class; + + that.add_command = spec.add_command; + that.remove_command = spec.remove_command; + + that.on_add = spec.on_add; + that.on_add_error = spec.on_add_error; + that.on_remove = spec.on_remove; + that.on_remove_error = spec.on_remove_error; + + that.create_column = function(spec) { + + if (typeof spec === 'string') { + spec = { + name: spec + }; + } + + spec.entity = that.entity; + + var factory = spec.factory || IPA.column; + + var column = factory(spec); + that.add_column(column); + return column; + }; + + that.create_columns = function() { + that.clear_columns(); + if (spec.columns) { + for (var i=0; i<spec.columns.length; i++) { + that.create_column(spec.columns[i]); + } + } + + that.post_create_columns(); + }; + + that.post_create_columns = function() { + }; + + that.create_buttons = function(container) { + + that.remove_button = IPA.action_button({ + name: 'remove', + label: IPA.messages.buttons.remove, + icon: 'remove-icon', + 'class': 'action-button-disabled', + click: function() { + if (!that.remove_button.hasClass('action-button-disabled')) { + that.remove_handler(); + } + return false; + } + }).appendTo(container); + + that.add_button = IPA.action_button({ + name: 'add', + label: IPA.messages.buttons.add, + icon: 'add-icon', + click: function() { + if (!that.add_button.hasClass('action-button-disabled')) { + that.add_handler(); + } + return false; + } + }).appendTo(container); + }; + + that.create = function(container) { + + that.create_columns(); + that.table_create(container); + if (that.css_class) + container.addClass(that.css_class); + that.create_buttons(that.buttons); + }; + + that.set_enabled = function(enabled) { + that.table_set_enabled(enabled); + if (enabled) { + if(that.add_button) { + that.add_button.removeClass('action-button-disabled'); + } + } else { + $('.action-button', that.table).addClass('action-button-disabled'); + that.unselect_all(); + } + that.enabled = enabled; + }; + + that.select_changed = function() { + + var values = that.get_selected_values(); + + if (that.remove_button) { + if (values.length === 0) { + that.remove_button.addClass('action-button-disabled'); + } else { + that.remove_button.removeClass('action-button-disabled'); + } + } + }; + + that.add_handler = function() { + var facet = that.entity.get_facet(); + + if (facet.is_dirty()) { + var dialog = IPA.dirty_dialog({ + entity:that.entity, + facet: facet + }); + + dialog.callback = function() { + that.show_add_dialog(); + }; + + dialog.open(that.container); + + } else { + that.show_add_dialog(); + } + }; + + that.remove_handler = function() { + var facet = that.entity.get_facet(); + + if (facet.is_dirty()) { + var dialog = IPA.dirty_dialog({ + entity:that.entity, + facet: facet + }); + + dialog.callback = function() { + that.show_remove_dialog(); + }; + + dialog.open(that.container); + + } else { + that.show_remove_dialog(); + } + }; + + that.show_remove_dialog = function() { + + var dialog = that.create_remove_dialog(); + if (dialog) dialog.open(that.container); + }; + + that.create_remove_dialog = function() { + var selected_values = that.get_selected_values(); + + if (!selected_values.length) { + var message = IPA.messages.dialogs.remove_empty; + alert(message); + return null; + } + + var dialog = IPA.deleter_dialog({ + entity: that.entity, + values: selected_values + }); + + dialog.execute = function() { + var command = that.create_remove_command( + selected_values, + function(data, text_status, xhr) { + var handler = that.on_remove || that.on_command_success; + handler.call(this, data, text_status, xhr); + }, + function(xhr, text_status, error_thrown) { + var handler = that.on_remove_error || that.on_command_error; + handler.call(this, xhr, text_status, error_thrown); + } + ); + command.execute(); + }; + + return dialog; + }; + + that.on_command_success = function(data) { + that.reload_facet(data); + }; + + that.on_command_error = function() { + that.refresh_facet(); + }; + + that.get_pkeys = function() { + var pkey = IPA.nav.get_state(that.entity.name+'-pkey'); + return [pkey]; + }; + + that.get_additional_options = function() { + return []; + }; + + that.create_remove_command = function(values, on_success, on_error) { + + var pkeys = that.get_pkeys(); + + var command = IPA.command({ + entity: that.entity.name, + method: that.remove_command || 'del', + args: pkeys, + on_success: on_success, + on_error: on_error + }); + + command.set_option(that.attribute_name, values); + + var additional_options = that.get_additional_options(); + for (var i=0; i<additional_options.length; i++) { + var option = additional_options[i]; + command.set_option(option.name, option.value); + } + + return command; + }; + + that.create_add_dialog = function() { + + var dialog_spec = { + entity: that.entity, + method: that.add_command + }; + + if (that.adder_dialog_spec) { + $.extend(dialog_spec, that.adder_dialog_spec); + } + + var label = that.entity.metadata.label_singular; + var pkey = IPA.nav.get_state(that.entity.name+'-pkey'); + dialog_spec.title = dialog_spec.title || IPA.messages.dialogs.add_title; + dialog_spec.title = dialog_spec.title.replace('${entity}', label); + dialog_spec.title = dialog_spec.title.replace('${pkey}', pkey); + + + var factory = dialog_spec.factory || IPA.entity_adder_dialog; + var dialog = factory(dialog_spec); + + var cancel_button = dialog.buttons.get('cancel'); + dialog.buttons.empty(); + + dialog.create_button({ + name: 'add', + label: IPA.messages.buttons.add, + click: function() { + dialog.hide_message(); + dialog.add( + function(data, text_status, xhr) { + var handler = that.on_add || that.on_command_success; + handler.call(this, data, text_status, xhr); + dialog.close(); + }, + dialog.on_error); + } + }); + + dialog.create_button({ + name: 'add_and_add_another', + label: IPA.messages.buttons.add_and_add_another, + click: function() { + dialog.hide_message(); + dialog.add( + function(data, text_status, xhr) { + var label = that.entity.metadata.label_singular; + var message = IPA.messages.dialogs.add_confirmation; + message = message.replace('${entity}', label); + dialog.show_message(message); + + var handler = that.on_add || that.on_command_success; + handler.call(this, data, text_status, xhr); + + dialog.reset(); + }, + dialog.on_error); + } + }); + + dialog.buttons.put('cancel', cancel_button); + + dialog.create_add_command = function(record) { + return that.adder_dialog_create_command(dialog, record); + }; + + return dialog; + }; + + that.adder_dialog_create_command = function(dialog, record) { + var command = dialog.entity_adder_dialog_create_add_command(record); + command.args = that.get_pkeys(); + + var additional_options = that.get_additional_options(); + for (var i=0; i<additional_options.length; i++) { + var option = additional_options[i]; + command.set_option(option.name, option.value); + } + + return command; + }; + + that.show_add_dialog = function() { + + var dialog = that.create_add_dialog(); + dialog.open(that.container); + }; + + that.update = function(values) { + that.table_update(values); + that.unselect_all(); + }; + + that.reload_facet = function(data) { + + //FIXME: bad approach - widget is directly manipulating with facet + var facet = IPA.current_entity.get_facet(); + facet.load(data); + }; + + that.refresh_facet = function() { + + //FIXME: bad approach + var facet = IPA.current_entity.get_facet(); + facet.refresh(); + }; + + that.attribute_table_adder_dialog_create_command = that.adder_dialog_create_command; + that.attribute_table_create_remove_command = that.create_remove_command; + that.attribute_table_update = that.update; + + return that; +}; + +IPA.combobox_widget = function(spec) { + + spec = spec || {}; + + var that = IPA.input_widget(spec); + + that.editable = 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.input_field_changed = IPA.observer(); + that.z_index = spec.z_index ? spec.z_index + 9000000 : 9000000; + + that.create = function(container) { + that.widget_create(container); + + container.addClass('combobox-widget'); + + $(document).keyup(function(e) { + if (e.which == 27) { // Escape + that.close(); + } + }); + + that.input_container = $('<div/>', { + 'class': 'combobox-widget-input' + }).appendTo(container); + + that.text = $('<label/>', { + name: that.name, + style: 'display: none;' + }).appendTo(that.input_container); + + that.input = $('<input/>', { + type: 'text', + name: that.name, + title: that.tooltip, + readonly: !that.editable || that.read_only, + keyup: function() { + that.input_field_changed.notify([], that); + }, + click: function() { + if (that.editable) return false; + if (that.is_open()) { + that.close(); + } else { + that.open(); + } + return false; + } + }).appendTo(that.input_container); + + that.input.bind('input', function() { + that.input_field_changed.notify([], that); + }); + + that.open_button = IPA.action_button({ + name: 'open', + icon: 'combobox-icon', + click: function() { + if (that.is_open()) { + that.close(); + } else { + that.open(); + } + return false; + } + }).appendTo(that.input_container); + + that.list_container = $('<div/>', { + 'class': 'combobox-widget-list', + css: { 'z-index': that.z_index } + }).appendTo(that.input_container); + + var div = $('<div/>', { + style: 'position: relative; width: 100%;' + }).appendTo(that.list_container); + + if (that.searchable) { + that.filter = $('<input/>', { + type: 'text', + name: 'filter', + keypress: function(e) { + if (e.which == 13) { // Enter + var filter = that.filter.val(); + that.search(filter); + } + } + }).appendTo(div); + + that.search_button = IPA.action_button({ + name: 'search', + icon: 'search-icon', + click: function() { + var filter = that.filter.val(); + that.search(filter); + return false; + } + }).appendTo(div); + + div.append('<br/>'); + } + + that.list = $('<select/>', { + name: 'list', + size: that.size, + style: 'width: 100%', + change: that.select_on_change + }).appendTo(div); + + if (that.undo) { + that.create_undo(container); + } + + that.create_error_link(container); + }; + + that.select_on_change = function() { + + if (!that.is_open()) return; + + var value = that.list.val(); + that.input.val(value); + IPA.select_range(that.input, 0, 0); + + that.close(); + that.value_changed.notify([[value]], that); + }; + + that.open = function() { + if (!that.read_only) + that.list_container.css('visibility', 'visible'); + }; + + that.close = function() { + that.list_container.css('visibility', 'hidden'); + }; + + that.is_open = function() { + return that.list_container.css('visibility') == 'visible'; + }; + + that.search = function(filter, on_success, on_error) { + + that.recreate_options(); + if (on_success) on_success.call(this); + }; + + that.set_options = function(options) { + that.options = options; + that.recreate_options(); + }; + + that.recreate_options = function() { + + that.remove_options(); + + if (that.empty_option) { + that.create_option(); + } + + for (var i=0; i<that.options.length; i++) { + var option = that.options[i]; + + var label, value; + if (option instanceof Object) { + label = option.label; + value = option.value; + } else { + label = option; + value = option; + } + + that.create_option(label, value); + } + }; + + that.update = function(values) { + that.close(); + + if (that.writable) { + that.text.css('display', 'none'); + that.input.css('display', 'inline'); + that.open_button.css('display', 'inline'); + } else { + that.text.css('display', 'inline'); + that.input.css('display', 'none'); + that.open_button.css('display', 'none'); + } + + if (that.searchable) { + that.filter.empty(); + } + + // In a details page the following code will get the stored value. + // In a dialog box the value will be null. + var value = values.length ? values[0] : null; + + // In a details page the following code will show the stored + // value immediately without waiting to populate the list. + // In a dialog box it will show blank. + that.set_value(value || ''); + + // In a details page the following code will populate the list + // and select the stored value. + // In a dialog box it will populate the list and select the first + // available option. + that.search( + null, + function(data, text_status, xhr) { + that.select(value); + } + ); + }; + + that.set_value = function(value) { + that.text.text(value); + that.input.val(value); + }; + + that.select = function(value) { + + var option; + + if (value) { + // select specified value + option = $('option[value="'+value+'"]', that.list); + } else { + // select first available option + option = $('option', that.list).first(); + } + + // if no option found, skip + if (!option.length) return; + + option.prop('selected', true); + + that.set_value(option.val()); + that.value_changed.notify([], that); + }; + + that.save = function() { + var value = that.input.val(); + return value === '' ? [] : [value]; + }; + + that.create_option = function(label, value) { + var option = $('<option/>', { + text: label, + value: value, + click: that.select_on_change + }).appendTo(that.list); + }; + + that.remove_options = function() { + that.list.empty(); + }; + + that.clear = function() { + that.input.val(''); + that.remove_options(); + }; + + return that; +}; + +IPA.entity_select_widget = function(spec) { + + spec = spec || {}; + spec.searchable = spec.searchable === undefined ? true : spec.searchable; + + 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.create_search_command = function(filter) { + return IPA.command({ + entity: that.other_entity.name, + method: 'find', + args: [filter] + }); + }; + + 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) { + + //get options + var options = []; + + var entries = data.result.result; + for (var i=0; i<data.result.count; i++) { + var entry = entries[i]; + var values = 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); + }; + + return that; +}; + + +IPA.link_widget = function(spec) { + var that = IPA.input_widget(spec); + + that.is_link = spec.is_link || false; + that.link_clicked = IPA.observer(); + + that.create = function(container) { + that.widget_create(container); + that.link = + $('<a/>', { + href: 'jslink', + title: '', + html: '', + click: function() { + that.link_clicked.notify([], that); + return false; + } + }).appendTo(container); + + that.nonlink = $('<label/>'). + appendTo(container); + }; + + that.update = function (values){ + + if (values || values.length > 0) { + that.nonlink.text(values[0]); + that.link.text(values[0]); + if(that.is_link) { + that.link.css('display','inline'); + that.nonlink.css('display','none'); + } else { + that.link.css('display','none'); + that.nonlink.css('display','inline'); + } + } else { + that.link.html(''); + that.nonlink.html(''); + that.link.css('display','none'); + that.nonlink.css('display','none'); + } + }; + + that.clear = function() { + that.nonlink.text(''); + that.link.text(''); + }; + + + return that; +}; + +IPA.action_button = function(spec) { + + spec = spec || {}; + + var button = $('<a/>', { + id: spec.id, + name: spec.name, + href: spec.href || '#' + (spec.name || 'button'), + title: spec.title || spec.label, + 'class': 'button action-button', + style: spec.style, + click: spec.click, + blur: spec.blur + }); + + if (spec['class']) button.addClass(spec['class']); + + if (spec.icon) { + $('<span/>', { + 'class': 'icon '+spec.icon + }).appendTo(button); + } + + if (spec.label) { + $('<span/>', { + 'class': 'button-label', + html: spec.label + }).appendTo(button); + } + + return button; +}; + +IPA.button = function(spec) { + + spec = spec || {}; + + var button = $('<a/>', { + id: spec.id, + name: spec.name, + href: spec.href || '#' + (spec.name || 'button') + }); + + var icons = { primary: spec.icon }; + var label = spec.label; + + button.button({ + icons: icons, + label: label + }); + + button.click(spec.click); + + return button; +}; + +IPA.button_widget = function(spec) { + + spec = spec || {}; + + var that = IPA.widget(spec); + + that.href = spec.href; + that.style = spec.style; + that.click = spec.click; + that['class'] = spec['class']; + that.disabled_class = 'button-disabled'; + + that.on_click = function() { + + if (that.click) { + that.click(); + } + return false; + }; + + that.create = function(container) { + that.button = IPA.button({ + id: that.id, + name: that.name, + href: that.href, + title: that.tooltip, + label: that.label, + 'class': that['class'], + style: that.style, + click: that.on_click + }).appendTo(container); + }; + + that.get_enabled = function() { + + var enabled = true; + + if (that.button) { + enabled = that.button.hasClass(that.disabled_class); + } + + return enabled; + }; + + that.set_enabled = function(enabled) { + + enabled ? that.enable() : that.disable(); + }; + + that.enable = function() { + if (that.button) { + that.button.removeClass(that.disabled_class); + } + }; + + that.disable = function() { + if (that.button) { + that.button.addClass(that.disabled_class); + } + }; + + return that; +}; + +IPA.html_widget = function(spec) { + + spec = spec || {}; + + var that = IPA.widget(spec); + + that.html = spec.html; + that.css_class = spec.css_class; + + that.create = function(container) { + + that.widget_create(container); + + if (that.css_class) { + container.addClass(that.css_class); + } + + if (that.html) { + container.append(that.html); + } + }; + + return that; +}; + +IPA.composite_widget = function(spec) { + + spec = spec || {}; + + var that = IPA.widget(spec); + + that.widgets = IPA.widget_container(); + + that.create = function(container) { + + that.widget_create(container); + that.widgets.create(container); + }; + + that.clear = function() { + + var widgets = that.widgets.get_widgets(); + + for (var i=0; i< widgets.length; i++) { + widgets[i].clear(); + } + }; + + that.composite_widget_create = that.create; + that.composite_widget_clear = that.clear; + + return that; +}; + +IPA.collapsible_section = function(spec) { + + spec = spec || {}; + + var that = IPA.composite_widget(spec); + + that.create = function(container) { + + that.widget_create(container); + + that.header = $('<h2/>', { + name: that.name, + title: that.label + }).appendTo(container); + + that.icon = $('<span/>', { + name: 'icon', + 'class': 'icon section-expand '+IPA.expanded_icon + }).appendTo(that.header); + + that.header.append(' '); + + that.header.append(that.label); + + that.content_container = $('<div/>', { + name: that.name, + 'class': 'details-section' + }).appendTo(container); + + that.header.click(function() { + var visible = that.content_container.is(":visible"); + that.toggle(!visible); + }); + + that.composite_widget_create(that.content_container); + }; + + that.toggle = function(visible) { + + that.icon.toggleClass(IPA.expanded_icon, visible); + that.icon.toggleClass(IPA.collapsed_icon, !visible); + + if (visible != that.content_container.is(":visible")) { + that.content_container.slideToggle('slow'); + } + }; + + return that; +}; + +IPA.details_section = IPA.collapsible_section; + +IPA.layout = function(spec) { + return {}; +}; + +// Creates list of widgets into table with two columns: label and widget +IPA.table_layout = function(spec) { + + spec = spec || {}; + + var that = IPA.layout(spec); + that.table_class = spec.table_class || 'section-table'; + that.label_cell_class = spec.label_cell_class || 'section-cell-label'; + that.field_cell_class = spec.field_cell_class || 'section-cell-field'; + that.label_class = spec.label_class || 'field-label'; + that.field_class = spec.field_class || 'field'; + + that.create = function(widgets) { + + that.rows = $.ordered_map(); + + var table = $('<table/>', { + 'class': that.table_class + }); + + for (var i=0; i<widgets.length; i++) { + var widget = widgets[i]; + var tr = $('<tr/>'); + that.rows.put(widget.name, tr); + + if (widget.hidden) { + tr.css('display', 'none'); + } + + tr.appendTo(table); + + var td = $('<td/>', { + 'class': that.label_cell_class, + title: widget.label + }).appendTo(tr); + + var label_text = widget.label + that.get_measurement_unit_text(widget) + ':'; + + $('<label/>', { + name: widget.name, + 'class': that.label_class, + text: label_text + }).appendTo(td); + + if(widget.create_required) { + widget.create_required(td); + } + + td = $('<td/>', { + 'class': that.field_cell_class, + title: widget.label + }).appendTo(tr); + + var widget_container = $('<div/>', { + name: widget.name, + 'class': that.field_class + }).appendTo(td); + + widget.create(widget_container); + } + return table; + }; + + + that.get_measurement_unit_text = function(widget) { + + if (widget.measurement_unit) { + var unit = IPA.messages.measurement_units[widget.measurement_unit]; + return ' (' + unit + ')'; + } + return ''; + }; + + return that; +}; + +IPA.details_table_section = function(spec) { + + spec = spec || {}; + + var that = IPA.details_section(spec); + that.layout = IPA.build_default(spec.layout, IPA.table_layout); + that.action_panel = that.build_child(spec.action_panel); + + that.rows = $.ordered_map(); + + that.composite_widget_create = function(container) { + + that.widget_create(container); + + if (that.action_panel) { + that.action_panel.create(container); + } + var widgets = that.widgets.get_widgets(); + var table = that.layout.create(widgets); + table.appendTo(container); + that.rows = that.layout.rows; + }; + + + that.add_row = function(name, row) { + that.rows.put(name, row); + }; + + that.get_row = function(name) { + return that.rows.get(name); + }; + + that.set_row_visible = function(name, visible) { + var row = that.get_row(name); + row.css('display', visible ? '' : 'none'); + }; + + that.table_section_create = that.composite_widget_create; + + return that; +}; + +//non-collabsible section +IPA.details_table_section_nc = function(spec) { + + spec = spec || {}; + + var that = IPA.details_table_section(spec); + + that.create = that.table_section_create; + + return that; +}; + +IPA.multiple_choice_section = function(spec) { + + spec = spec || {}; + + var that = IPA.composite_widget(spec); + that.choices = $.ordered_map().put_array(spec.choices, 'name'); + that.layout = IPA.build_default(spec.layout, IPA.table_layout); + + that.create = function(container) { + + var i, choice, choices; + + that.widget_create(container); + that.container.addClass('multiple-choice-section'); + + that.header_element = $('<div/>', { + 'class': 'multiple-choice-section-header', + text: that.label + }).appendTo(container); + + that.choice_container = $('<div/>', { + 'class': 'choices' + }).appendTo(container); + + choices = that.choices.values; + for (i=0; i<choices.length; i++) { + choice = choices[i]; + that.create_choice(choice); + } + }; + + that.create_choice = function(choice) { + + var widgets, i, widget, field, section, choice_el, header, radio, + enabled, radio_id; + + widgets = []; + + if (choice.widgets) { + for (i=0; i<choice.widgets.length; i++) { + widget = that.widgets.get_widget(choice.widgets[i]); + widgets.push(widget); + } + } else if (choice.fields) { + for (i=0; i<choice.fields.length; i++) { + field = that.facet.fields.get_field(choice.fields[i]); + widgets.push(field.widget); + } + } + + choice_el = $('<div/>',{ + 'class': 'choice', + name: choice.name + }); + + header = $('<div/>',{ + 'class': 'choice-header' + }).appendTo(choice_el); + + enabled = choice.enabled !== undefined ? choice.enabled : false; + + radio_id = that.name + '_' + choice.name; + + $('<input/>',{ + type: 'radio', + name: that.name, + id: radio_id, + value: choice.name, + checked: enabled, + change: function() { + that.select_choice(this.value); + } + }).appendTo(header); + + $('<label/>',{ + text: choice.label, + 'for': radio_id + }).appendTo(header); + + section = that.layout.create(widgets); + section.appendTo(choice_el); + choice_el.appendTo(that.choice_container); + }; + + that.select_choice = function(choice_name) { + + var i, choice, enabled; + + for (i=0; i<that.choices.values.length; i++) { + choice = that.choices.values[i]; + enabled = choice.name === choice_name; + that.set_enabled(choice, enabled); + } + }; + + that.set_enabled = function (choice, enabled) { + + var i, field_name, field, fields, required; + + fields = that.facet.fields; + + for (i=0; i<choice.fields.length; i++) { + field_name = choice.fields[i]; + field = fields.get_field(field_name); + field.set_enabled(enabled); + required = enabled && choice.required.indexOf(field_name) > -1; + field.set_required(required); + field.validate(); //hide validation errors + } + }; + + that.init_enabled = function() { + + var i, choice; + + for (i=0; i<that.choices.values.length; i++) { + choice = that.choices.values[i]; + if (choice.enabled) { + that.select_choice(choice.name); + break; + } + } + }; + + return that; +}; + +IPA.multiple_choice_section_policy = function(spec) { + + spec = spec || {}; + + var that = IPA.facet_policy(spec); + that.widget_name = spec.widget; + + that.init = function() { + that.widget = that.container.widgets.get_widget(that.widget_name); + }; + + that.post_create = function() { + that.widget.init_enabled(); + }; + + return that; +}; + +IPA.enable_widget = function(spec) { + + spec = spec || {}; + + var that = IPA.radio_widget(spec); + + return that; +}; + + +IPA.header_widget = function(spec) { + + spec = spec || {}; + + var that = IPA.widget(spec); + + that.level = spec.level || 3; + that.text = spec.text; + that.description = spec.description; + + that.create = function(container) { + container.append($('<h'+that.level+' />', { + text: that.text, + title: that.description + })); + }; + + return that; +}; + +IPA.observer = function(spec) { + + var that = {}; + + that.listeners = []; + + that.attach = function(callback) { + that.listeners.push(callback); + }; + + that.detach = function(callback) { + for(var i=0; i < that.listeners.length; i++) { + if(callback === that.listeners[i]) { + that.listeners.splice(i,1); + break; + } + } + }; + + that.notify = function(args, context) { + args = args || []; + context = context || this; + + for(var i=0; i < that.listeners.length; i++) { + that.listeners[i].apply(context, args); + } + }; + + return that; +}; + +IPA.html_util = function() { + + var that = {}; + that.id_count = 0; + + that.get_next_id = function(prefix) { + that.id_count++; + return prefix ? prefix + that.id_count : that.id_count; + }; + + return that; +}(); + +IPA.widget_container = function(spec) { + + spec = spec || {}; + + var that = {}; + + that.new_container_for_child = spec.new_container_for_child !== undefined ? + spec.new_container_for_child : true; + + that.widgets = $.ordered_map(); + that.widget_builder = spec.widget_builder || IPA.widget_builder(); + + that.add_widget = function(widget) { + that.widgets.put(widget.name, widget); + }; + + that.get_widget = function(path) { + + var path_len = path.length; + var i = path.indexOf('.'); + var name, child_path, widget, child; + + if (i >= 0) { + name = path.substring(0, i); + child_path = path.substring(i + 1); + + child = that.widgets.get(name); + widget = child.widgets.get_widget(child_path); + } else { + widget = that.widgets.get(path); + } + + return widget; + }; + + that.get_widgets = function() { + return that.widgets.values; + }; + + that.create = function(container) { + + var widgets = that.widgets.values; + for (var i=0; i<widgets.length; i++) { + var widget = widgets[i]; + + var child_container = container; + if(that.new_container_for_child) { + child_container = $('<div/>', { + name: widget.name, + title: widget.label, + 'class': widget['class'] + }).appendTo(container); + } + widget.create(child_container); + + if(i < widgets.length - 1) { + that.create_widget_delimiter(container); + } + } + }; + + that.clear = function() { + + var widgets = that.widgets.values; + for (var i=0; i<widgets.length; i++) { + widgets[i].clear(); + } + }; + + that.create_widget_delimiter = function(container) { + }; + + that.widget_container_create = that.create; + that.widget_container_clear = that.clear; + + return that; +}; + +IPA.widget_builder = function(spec) { + + spec = spec || {}; + + var that = {}; + + that.default_factory = spec.default_factory || IPA.text_widget; + that.container = spec.container; + that.widget_options = spec.widget_options || {}; + + that.get_widget_factory = function(spec) { + + var factory; + if (spec.factory) { + factory = spec.factory; + } else if(spec.type) { + factory = IPA.widget_factories[spec.type]; + } + + if (!factory) { + factory = that.default_factory; + } + + return factory; + }; + + that.build_widget = function(spec, container) { + + container = container || that.container; + + if(!(spec instanceof Object)) { + spec = { name: spec }; + } + + if(that.widget_options) { + $.extend(spec, that.widget_options); + } + + var factory = that.get_widget_factory(spec); + + var widget = factory(spec); + + if(container) { + container.add_widget(widget); + } + + if(spec.widgets) { + that.build_widgets(spec.widgets, widget.widgets); + } + + return widget; + }; + + that.build_widgets = function(specs, container) { + + container = container || that.container; + + for(var i=0; i<specs.length; i++) { + that.build_widget(specs[i], container); + } + }; + + return that; +}; + +IPA.sshkeys_widget = function(spec) { + + spec = spec || {}; + spec.widget_factory = IPA.sshkey_widget; + + var that = IPA.multivalued_widget(spec); + + that.test_dirty_row = function(row) { + + if(row.deleted || row.is_new) return true; + + var values = row.widget.save(); + + var key = values[0]; + var original_key = row.original_values[0]; + + if (original_key && original_key.key && original_key.key !== key) { + return true; + } + + return false; + }; + + return that; +}; + +IPA.sshkey_widget = function(spec) { + + spec = spec || {}; + + var that = IPA.input_widget(spec); + + that.key = null; + that.originally_set = false; + + that.create = function(container) { + + that.widget_create(container); + + container.addClass('text-widget'); + + that.status_label = $('<span />', { + 'class': 'sshkey-status', + text: '' + }).appendTo(container); + + that.link = $('<a/>', { + type: that.type, + 'class': 'sshkey-set', + name: that.name, + href: '#show-certificate', + title: that.tooltip, + text: IPA.messages.objects.sshkeystore.show_set_key, + click: function() { + that.open_edit_dialog(); + return false; + } + }).appendTo(container); + + if (that.undo) { + that.create_undo(container); + } + + that.create_error_link(container); + }; + + that.update = function(values) { + + var key = values && values.length ? values[0] : null; + + if (!key || key === '') { + key = {}; + } + + that.key = $.extend({}, key); + + if (that.key.key && that.key.key !== '' && + that.key.fingerprint && that.key.fingerprint !== '') { + that.originally_set = true; + that.original_key = that.key.key; + } + that.update_link(); + }; + + that.set_deleted = function(deleted) { + if (deleted) { + that.status_label.addClass('strikethrough'); + } else { + that.status_label.removeClass('strikethrough'); + } + }; + + that.save = function() { + var value = that.key.key; + value = value ? [value] : ['']; + return value; + }; + + that.update_link = function() { + var text = that.get_status(); + that.status_label.text(text); + }; + + that.get_status = function() { + + var text = ''; + var value = that.key.key; + + if (that.original_key) { + + if (value !== that.original_key) { + if (value === '') { + text = IPA.messages.objects.sshkeystore.status_mod_ns; + } else { + text = IPA.messages.objects.sshkeystore.status_mod_s; + } + } else { + text = that.key.fingerprint; + } + + } else { + + if (!value || value === '') { + text = IPA.messages.objects.sshkeystore.status_new_ns; + } else { + text = IPA.messages.objects.sshkeystore.status_new_s; + } + } + + return text; + }; + + that.set_user_value = function(value) { + + var previous = that.key.key; + that.key.key = value; + that.update_link(); + + if (value !== previous) { + that.value_changed.notify([], that); + } + }; + + that.open_edit_dialog = function() { + + var dialog = that.create_edit_dialog(); + dialog.open(); + }; + + that.create_edit_dialog = function() { + + var dialog = IPA.dialog({ + name: 'sshkey-edit-dialog', + title: IPA.messages.objects.sshkeystore.set_dialog_title, + width: 500, + height: 380 + }); + + dialog.message = IPA.messages.objects.sshkeystore.set_dialog_help; + + dialog.create_button({ + name: 'update', + label: IPA.messages.buttons.set, + click: function() { + var value = dialog.textarea.val(); + that.set_user_value(value); + dialog.close(); + } + }); + + dialog.create_button({ + name: 'cancel', + label: IPA.messages.buttons.cancel, + click: function() { + dialog.close(); + } + }); + + dialog.create = function() { + + dialog.container.append(dialog.message); + + dialog.textarea = $('<textarea/>', { + 'class': 'certificate', + readonly: that.read_only + }).appendTo(dialog.container); + + var key = that.key.key || ''; + dialog.textarea.val(key); + }; + + return dialog; + }; + + return that; +}; + +IPA.action_panel = function(spec) { + + spec = spec || {}; + spec.label = spec.label || IPA.messages.actions.title; + + var that = IPA.widget(spec); + + that.action_names = spec.actions; + that.actions = $.ordered_map(); + that.facet = spec.facet; + that.initialized = false; + + that.init = function() { + + for (var i=0; i<that.action_names.length; i++) { + var name = that.action_names[i]; + var action = that.facet.actions.get(name); + + that.add_action(action, true); + + that.actions.put(name, action); + } + + that.initialized = true; + }; + + that.add_action = function(action, batch) { + that.actions.put(action.name, action); + action.enabled_changed.attach(that.action_enabled_changed); + action.visible_changed.attach(that.action_visible_changed); + + if (!batch) { + that.create_items(); + } + }; + + that.create = function(container) { + + if (!that.initialized) that.init(); + + that.element = $('<div/>', { + 'data-name': that.name, + 'class': 'action-panel' + }); + + that.header_element = $('<h3/>', { + 'class': 'action-title' + }).appendTo(that.element); + + that.list_element = $('<ul/>', { + 'class': 'action-panel-list' + }).appendTo(that.element); + + that.element.appendTo(container); + + that.create_items(); + }; + + that.create_item = function(action) { + + var classes, state, li, a; + + if (!action.visible) return; + + classes = ['action']; + state = action.enabled ? 'enabled' : 'disabled'; + classes.push(state); + + li = $('<li/>'); + a = $('<a/>', { + 'data-name': action.name, + href: '#', + text: action.label, + 'class': classes.join(' '), + click: function() { + that.action_clicked(action); + return false; + } + }).appendTo(li); + li.appendTo(that.list_element); + }; + + that.clear_items = function() { + + that.list_element.empty(); + }; + + that.create_items = function() { + + if (!that.element) return; + + that.clear_items(); + + var actions = that.actions.values; + + for (var i=0; i<actions.length; i++) { + var action = actions[i]; + that.create_item(action); + } + + that.header_element.text(that.label); + }; + + that.action_clicked = function(action) { + + if (!action.enabled || !action.visible) return; + + action.execute(that.facet); + }; + + that.action_enabled_changed = function() { + + that.create_items(); + }; + + that.action_visible_changed = function() { + + that.create_items(); + }; + + + return that; +}; + +IPA.value_map_widget = function(spec) { + + spec = spec || {}; + spec.read_only = true; + + var that = IPA.input_widget(spec); + that.value_map = spec.value_map || {}; + that.default_label = spec.default_label || ''; + + that.create = function(container) { + that.widget_create(container); + container.addClass('status-widget'); + + that.display_control = $('<span/>', { + name: that.name + }).appendTo(container); + }; + + that.update = function(values) { + + var value, found, label; + + found = false; + + if ($.isArray(values)) { + for (value in that.value_map) { + + if (!that.value_map.hasOwnProperty(value)) continue; + + if (values.indexOf(value) > -1) { + label = that.value_map[value]; + found = true; + } + } + } + + if (!found) { + label = that.default_label; + } + + that.display_control.text(label); + }; + + that.clear = function() { + that.display_control.text(''); + }; + + return that; +}; + +IPA.widget_factories['attribute_table'] = IPA.attribute_table_widget; +IPA.widget_factories['button'] = IPA.button_widget; +IPA.widget_factories['checkbox'] = IPA.checkbox_widget; +IPA.widget_factories['checkboxes'] = IPA.checkboxes_widget; +IPA.widget_factories['combobox'] = IPA.combobox_widget; +IPA.widget_factories['composite_widget'] = IPA.composite_widget; +IPA.widget_factories['details_table_section'] = IPA.details_table_section; +IPA.widget_factories['details_table_section_nc'] = IPA.details_table_section_nc; +IPA.widget_factories['multiple_choice_section'] = IPA.multiple_choice_section; +IPA.widget_factories['enable'] = IPA.enable_widget; +IPA.widget_factories['entity_select'] = IPA.entity_select_widget; +IPA.widget_factories['header'] = IPA.header_widget; +IPA.widget_factories['html'] = IPA.html_widget; +IPA.widget_factories['link'] = IPA.link_widget; +IPA.widget_factories['multivalued'] = IPA.multivalued_widget; +IPA.widget_factories['password'] = IPA.password_widget; +IPA.widget_factories['radio'] = IPA.radio_widget; +IPA.widget_factories['select'] = IPA.select_widget; +IPA.widget_factories['sshkeys'] = IPA.sshkeys_widget; +IPA.widget_factories['textarea'] = IPA.textarea_widget; +IPA.widget_factories['text'] = IPA.text_widget; +IPA.widget_factories['value_map'] = IPA.value_map_widget;
\ No newline at end of file |