* view should be displayed (if there is one active).
*/
searchable: false,
- readonly : false,
- form_template: "FormView",
+ template: "FormView",
display_name: _lt('Form'),
/**
* @constructs openerp.web.FormView
this.model = dataset.model;
this.view_id = view_id || false;
this.fields_view = {};
- this.widgets = {};
- this.widgets_counter = 0;
this.fields = {};
this.fields_order = [];
this.datarecord = {};
this.mutating_mutex = new $.Mutex();
this.on_change_mutex = new $.Mutex();
this.reload_mutex = new $.Mutex();
+ this.set({"force_readonly": false});
+ this.rendering_engine = new openerp.web.FormRenderingEngine(this);
},
start: function() {
this._super();
}, this.on_loaded);
}
},
- stop: function() {
+ destroy: function() {
if (this.sidebar) {
- this.sidebar.attachments.stop();
- this.sidebar.stop();
+ this.sidebar.attachments.destroy();
+ this.sidebar.destroy();
}
- _.each(this.widgets, function(w) {
- w.stop();
+ _.each(this.get_widgets(), function(w) {
+ w.destroy();
});
this._super();
},
},
on_loaded: function(data) {
var self = this;
- if (data) {
- this.fields_order = [];
- this.fields_view = data;
- var frame = new (this.registry.get_object('frame'))(this, this.fields_view.arch);
-
- this.rendered = QWeb.render(this.form_template, { 'frame': frame, 'widget': this });
+ if (!data) {
+ throw new Error("No data provided.");
}
- this.$element.html(this.rendered);
- _.each(this.widgets, function(w) {
- w.start();
- });
+ if (this.arch) {
+ throw "Form view does not support multiple calls to on_loaded";
+ }
+ this.fields_order = [];
+ this.fields_view = data;
+
+ this.rendering_engine.set_fields_view(data);
+ this.rendering_engine.render_to(this.$element.find('.oe_form_container'));
+
this.$form_header = this.$element.find('.oe_form_header:first');
this.$form_header.find('div.oe_form_pager button[data-pager-action]').click(function() {
var action = $(this).data('pager-action');
if (self.sidebar) {
self.sidebar.attachments.do_update();
}
- if (self.default_focus_field && !self.embedded_view) {
+ if (self.default_focus_field) {
self.default_focus_field.focus();
}
if (record.id) {
});
},
on_form_changed: function() {
- for (var w in this.widgets) {
- w = this.widgets[w];
+ _.each(this.get_widgets(), function(w) {
w.process_modifiers();
if (w.field) {
w.validate();
}
w.update_dom();
- }
+ });
},
do_notify_change: function() {
this.$element.addClass('oe_form_dirty');
}
// form field
if (self.fields[field]) {
- var value = self.fields[field].get_on_change_value();
+ var value = self.fields[field].get_value();
return value == null ? false : value;
}
// parent field
value = response.value[fieldname];
} else {
// otherwise get form value for field
- value = self.fields[fieldname].get_on_change_value();
+ value = self.fields[fieldname].get_value();
}
var condition = fieldname + '=' + value;
this.on_form_changed();
}
if (!_.isEmpty(result.warning)) {
- $(QWeb.render("CrashManagerWarning", result.warning)).dialog({
+ openerp.web.dialog($(QWeb.render("CrashManagerWarning", result.warning)), {
modal: true,
buttons: [
{text: _t("Ok"), click: function() { $(this).dialog("close"); }}
if (!first_invalid_field) {
first_invalid_field = f;
}
- } else if (f.name !== 'id' && !f.readonly && (!self.datarecord.id || f.is_dirty())) {
+ } else if (f.name !== 'id' && !f.get("readonly") && (!self.datarecord.id || f.is_dirty())) {
// Special case 'id' field, do not save this field
// on 'create' : save all non readonly fields
// on 'edit' : save non readonly modified fields
var msg = "<ul>";
_.each(this.fields, function(f) {
if (!f.is_valid()) {
- msg += "<li>" + f.string + "</li>";
+ msg += "<li>" + f.node.attrs.string + "</li>";
}
});
msg += "</ul>";
}
});
},
+ get_widgets: function() {
+ return _.filter(this.getChildren(), function(obj) {
+ return obj instanceof openerp.web.form.Widget;
+ });
+ },
get_fields_values: function(blacklist) {
blacklist = blacklist || [];
var values = {};
// or m2m
if (!value
|| field.invisible
- || field.readonly
+ || field.get("readonly")
|| field.field.type === 'one2many'
|| field.field.type === 'many2many') {
return false;
return {
name: name,
- string: field.string,
+ string: field.node_atts.string,
value: value,
displayed: displayed,
// convert undefined to false
}
})
.compact()
- .sortBy(function (field) { return field.string; })
+ .sortBy(function (field) { return field.node_atts.string; })
.value();
var conditions = _.chain(fields)
.filter(function (field) { return field.change_default; })
d.open();
}
});
+
+/**
+ * Interface to be implemented by rendering engines for the form view.
+ */
+openerp.web.FormRenderingEngineInterface = {
+ set_fields_view: function(fields_view) {},
+ render_to: function($element) {},
+};
+
+/**
+ * Default rendering engine for the form view.
+ *
+ * It is necessary to set the view using set_view() before usage.
+ */
+openerp.web.FormRenderingEngine = openerp.web.Class.extend({
+ init: function(view) {
+ this.view = view;
+ this.legacy_mode = false;
+ },
+ set_fields_view: function(fvg) {
+ this.fvg = fvg;
+ this.legacy_mode = (this.fvg.arch.tag === 'form');
+ },
+ set_registry: function(registry) {
+ this.registry = registry;
+ },
+ render_to: function($element) {
+ var self = this;
+ this.$element = $element;
+
+ // TODO: I know this will save the world and all the kitten for a moment,
+ // but one day, we will have to get rid of xml2json
+ var xml = openerp.web.json_node_to_xml(this.fvg.arch);
+ this.$form = $(xml);
+
+ this.process(this.$form);
+
+ this.$form.appendTo(this.$element);
+ // OpenERP views spec :
+ // - @width is obsolete ?
+
+ this.$element.find('field, button').each(function() {
+ var $elem = $(this),
+ key = $elem.attr('widget') || $elem[0].tagName.toLowerCase();
+ if (self.view.registry.contains(key)) {
+ var obj = self.view.registry.get_object(key);
+ var w = new (obj)(self.view, openerp.web.xml_to_json($elem[0]));
+ self.alter_field(w);
+ w.replace($elem);
+ }
+ });
+ $('<button>Debug layout</button>').appendTo(this.$element).click($.proxy(this.toggle_layout_debugging, this));
+ },
+ render_element: function(template, dict) {
+ dict = dict || {};
+ dict.legacy_mode = this.legacy_mode;
+ return $(QWeb.render(template, dict));
+ },
+ alter_field: function(field) {},
+ toggle_layout_debugging: function() {
+ if (!this.$element.has('.oe_layout_debug_cell:first').length) {
+ this.$element.find('.oe_form_group_cell').each(function() {
+ var text = 'W:' + ($(this).attr('width') || '') + ' - C:' + $(this).attr('colspan'),
+ $span = $('<span class="oe_layout_debug_cell"/>').text(text);
+ $span.prependTo($(this));
+ });
+ }
+ this.$element.toggleClass('oe_layout_debugging');
+
+ },
+ process: function($tag) {
+ var self = this;
+ var tagname = $tag[0].nodeName.toLowerCase();
+ var fn = self['process_' + tagname];
+ if (this.registry && this.registry.contains(tagname)) {
+ fn = this.registry.get_object(tagname);
+ }
+ if (fn) {
+ var args = [].slice.call(arguments);
+ args[0] = $tag;
+ return fn.apply(self, args);
+ } else {
+ // generic tag handling, just process children
+ $tag.children().each(function() {
+ self.process($(this));
+ });
+ return $tag;
+ }
+ },
+ process_form: function($form) {
+ var $new_form = this.render_element('FormRenderingForm', $form.getAttributes());
+ $dst = this.legacy_mode ? $new_form.find('group:first') : $new_form;
+ $form.children().appendTo($dst);
+ if ($form[0] === this.$form[0]) {
+ // If root element, replace it
+ this.$form = $new_form;
+ } else {
+ $form.before($new_form).remove();
+ }
+ this.process($new_form);
+ },
+ preprocess_field: function($field) {
+ var name = $field.attr('name'),
+ field_orm = this.fvg.fields[name],
+ field_string = $field.attr('string') || field_orm.string || '',
+ field_help = $field.attr('help') || field_orm.help || '',
+ field_colspan = parseInt($field.attr('colspan'), 10);
+ if ($field.attr('nolabel') !== '1') {
+ $field.attr('nolabel', '1');
+ var $label = this.$form.find('label[for="' + name + '"]');
+ if (!$label.length) {
+ field_string = $label.attr('string') || $label.text() || field_string;
+ field_help = $label.attr('help') || field_help;
+ $label = $('<label/>').attr({
+ 'for' : name,
+ 'string' : field_string,
+ 'help' : field_help
+ });
+ $label.insertBefore($field);
+ if (field_colspan > 1) {
+ $field.attr('colspan', field_colspan - 1);
+ }
+ }
+ return $label;
+ }
+ },
+ process_field: function($field) {
+ var $label = this.preprocess_field($field);
+ if ($label)
+ this.process($label);
+ var name = $field.attr('name'),
+ field_orm = this.fvg.fields[name],
+ field_string = $field.attr('string') || field_orm.string || '',
+ field_help = $field.attr('help') || field_orm.help || '',
+ field_colspan = parseInt($field.attr('colspan'), 10);
+
+ if (!field_orm) {
+ throw new Error("Field '" + name + "' specified in view could not be found.");
+ }
+
+ $field.attr({
+ 'widget' : $field.attr('widget') || field_orm.type,
+ 'string' : field_string,
+ 'help' : field_help
+ });
+ return $field;
+ },
+ process_group: function($group) {
+ var self = this;
+ $group.children('field').each(function() {
+ self.preprocess_field($(this));
+ });
+ var $new_group = $(QWeb.render('FormRenderingGroup', $group.getAttributes())),
+ $table;
+ if ($new_group.is('table')) {
+ $table = $new_group;
+ } else {
+ $table = $new_group.find('table:first');
+ }
+ $table.addClass('oe_form_group');
+ var $tr, $td,
+ cols = parseInt($group.attr('col') || 4, 10),
+ row_cols = cols;
+
+ var children = [];
+ $group.children().each(function(a,b,c) {
+ var $child = $(this),
+ colspan = parseInt($child.attr('colspan') || 1, 10),
+ tagName = $child[0].tagName.toLowerCase();
+ if (tagName === 'newline') {
+ $tr = null;
+ return;
+ }
+ if (!$tr || row_cols < colspan) {
+ $tr = $('<tr/>').addClass('oe_form_group_row').appendTo($table);
+ row_cols = cols;
+ }
+ row_cols -= colspan;
+ $td = $('<td/>').addClass('oe_form_group_cell').attr('colspan', colspan);
+ $tr.append($td.append($child));
+ children.push($child[0]);
+ });
+ if (row_cols) {
+ $td.attr('colspan', parseInt($td.attr('colspan'), 10) + row_cols);
+ }
+ $group.before($new_group).remove();
+
+ // Now compute width of cells
+ $table.find('tbody > tr').each(function() {
+ var to_compute = [],
+ row_cols = cols,
+ total = 100;
+ $(this).children().each(function() {
+ var $td = $(this),
+ $child = $td.children(':first');
+ switch ($child[0].tagName.toLowerCase()) {
+ case 'separator':
+ if ($child.attr('orientation') === 'vertical') {
+ $td.addClass('oe_vertical_separator').attr('width', '1');
+ $td.empty();
+ row_cols--;
+ }
+ break;
+ case 'label':
+ if ($child.attr('for')) {
+ $td.attr('width', '1%');
+ row_cols--;
+ total--;
+ }
+ break;
+ default:
+ to_compute.push($td);
+ }
+ });
+ var unit = Math.floor(total / row_cols);
+ _.each(to_compute, function($td, i) {
+ var width = parseInt($td.attr('colspan'), 10) * unit;
+ $td.attr('width', ((i == to_compute.length - 1) ? total : width) + '%');
+ total -= width;
+ });
+ });
+ _.each(children, function(el) {
+ self.process($(el));
+ });
+ return $new_group;
+ },
+ process_notebook: function($notebook) {
+ var self = this;
+ var pages = [];
+ $notebook.find('> page').each(function() {
+ var $page = $(this),
+ page_attrs = $page.getAttributes();
+ page_attrs.id = _.uniqueId('notebook_page_');
+ pages.push(page_attrs);
+ var $new_page = self.render_element('FormRenderingNotebookPage', page_attrs),
+ $dst = self.legacy_mode ? $new_page.find('group:first') : $new_page;
+ $page.children().appendTo($dst);
+ $page.before($new_page).remove();
+ });
+ var $new_notebook = $(QWeb.render('FormRenderingNotebook', { pages : pages }));
+ $notebook.children().appendTo($new_notebook);
+ $notebook.before($new_notebook).remove();
+ $new_notebook.children().each(function() {
+ self.process($(this));
+ });
+ $new_notebook.tabs();
+ return $new_notebook;
+ },
+ process_separator: function($separator) {
+ var $new_separator = $(QWeb.render('FormRenderingSeparator', $separator.getAttributes()));
+ $separator.before($new_separator).remove();
+ return $new_separator;
+ },
+ process_label: function($label) {
+ var dict = $label.getAttributes();
+ var align = parseFloat(dict.align);
+ if (isNaN(align) || align === 1) {
+ align = 'right';
+ } else if (align === 0) {
+ align = 'left';
+ } else {
+ align = 'center';
+ }
+ dict.align = align;
+ var $new_label = $(QWeb.render('FormRenderingLabel', dict));
+ $label.before($new_label).remove();
+ return $new_label;
+ }
+});
+
openerp.web.FormDialog = openerp.web.Dialog.extend({
init: function(parent, options, view_id, dataset) {
this._super(parent, options);
return _.all(stack, _.identity);
};
-openerp.web.form.Widget = openerp.web.OldWidget.extend(/** @lends openerp.web.form.Widget# */{
- template: 'Widget',
+openerp.web.form.Widget = openerp.web.Widget.extend(/** @lends openerp.web.form.Widget# */{
/**
* @constructs openerp.web.form.Widget
- * @extends openerp.web.OldWidget
+ * @extends openerp.web.Widget
*
* @param view
* @param node
this.node = node;
this.modifiers = JSON.parse(this.node.attrs.modifiers || '{}');
this.always_invisible = (this.modifiers.invisible && this.modifiers.invisible === true);
- this.type = this.type || node.tag;
- this.element_name = this.element_name || this.type;
- this.element_class = [
- 'formview', this.view.view_id, this.element_name,
- this.view.widgets_counter++].join("_");
this._super(view);
- this.view.widgets[this.element_class] = this;
- this.children = node.children;
- this.colspan = parseInt(node.attrs.colspan || 1, 10);
- this.decrease_max_width = 0;
-
- this.string = this.string || node.attrs.string;
- this.help = this.help || node.attrs.help;
this.invisible = this.modifiers['invisible'] === true;
- this.classname = 'oe_form_' + this.type;
-
- this.align = parseFloat(this.node.attrs.align);
- if (isNaN(this.align) || this.align === 1) {
- this.align = 'right';
- } else if (this.align === 0) {
- this.align = 'left';
- } else {
- this.align = 'center';
- }
-
-
- this.width = this.node.attrs.width;
- },
- start: function() {
- this.$element = this.view.$element.find(
- '.' + this.element_class.replace(/[^\r\n\f0-9A-Za-z_-]/g, "\\$&"));
},
- stop: function() {
+ destroy: function() {
this._super.apply(this, arguments);
$.fn.tipsy.clear();
},
process_modifiers: function() {
var compute_domain = openerp.web.form.compute_domain;
+ var to_set = {};
for (var a in this.modifiers) {
- this[a] = compute_domain(this.modifiers[a], this.view.fields);
+ var val = compute_domain(this.modifiers[a], this.view.fields);
+ this[a] = val;
+ to_set[a] = val;
}
+ this.set(to_set);
},
update_dom: function() {
this.$element.toggle(!this.invisible);
},
- render: function() {
- var template = this.template;
- return QWeb.render(template, { "widget": this });
- },
do_attach_tooltip: function(widget, trigger, options) {
widget = widget || this;
trigger = trigger || this.$element;
}
});
-openerp.web.form.WidgetFrame = openerp.web.form.Widget.extend({
- template: 'WidgetFrame',
- init: function(view, node) {
- this._super(view, node);
- this.columns = parseInt(node.attrs.col || 4, 10);
- this.x = 0;
- this.y = 0;
- this.table = [];
- this.add_row();
- for (var i = 0; i < node.children.length; i++) {
- var n = node.children[i];
- if (n.tag == "newline") {
- this.add_row();
- } else {
- this.handle_node(n);
- }
- }
- this.set_row_cells_with(this.table[this.table.length - 1]);
- },
- add_row: function(){
- if (this.table.length) {
- this.set_row_cells_with(this.table[this.table.length - 1]);
- }
- var row = [];
- this.table.push(row);
- this.x = 0;
- this.y += 1;
- return row;
- },
- set_row_cells_with: function(row) {
- var bypass = 0,
- max_width = 100,
- row_length = row.length;
- for (var i = 0; i < row.length; i++) {
- if (row[i].always_invisible) {
- row_length--;
- } else {
- bypass += row[i].width === undefined ? 0 : 1;
- max_width -= row[i].decrease_max_width;
- }
- }
- var size_unit = Math.round(max_width / (this.columns - bypass)),
- colspan_sum = 0;
- for (var i = 0; i < row.length; i++) {
- var w = row[i];
- if (w.always_invisible) {
- continue;
- }
- colspan_sum += w.colspan;
- if (w.width === undefined) {
- var width = (i === row_length - 1 && colspan_sum === this.columns) ? max_width : Math.round(size_unit * w.colspan);
- max_width -= width;
- w.width = width + '%';
- }
- }
- },
- handle_node: function(node) {
- var type = {};
- if (node.tag == 'field') {
- type = this.view.fields_view.fields[node.attrs.name] || {};
- if (node.attrs.widget == 'statusbar' && node.attrs.nolabel !== '1') {
- // This way we can retain backward compatibility between addons and old clients
- node.attrs.colspan = (parseInt(node.attrs.colspan, 10) || 1) + 1;
- node.attrs.nolabel = '1';
- }
- }
- var widget = new (this.view.registry.get_any(
- [node.attrs.widget, type.type, node.tag])) (this.view, node);
- if (node.tag == 'field') {
- if (!this.view.default_focus_field || node.attrs.default_focus == '1') {
- this.view.default_focus_field = widget;
- }
- if (node.attrs.nolabel != '1') {
- var label = new (this.view.registry.get_object('label')) (this.view, node);
- label["for"] = widget;
- this.add_widget(label, widget.colspan + 1);
- }
- }
- this.add_widget(widget);
- },
- add_widget: function(widget, colspan) {
- var current_row = this.table[this.table.length - 1];
- if (!widget.always_invisible) {
- colspan = colspan || widget.colspan;
- if (current_row.length && (this.x + colspan) > this.columns) {
- current_row = this.add_row();
- }
- this.x += widget.colspan;
- }
- current_row.push(widget);
- return widget;
- }
-});
-
-openerp.web.form.WidgetGroup = openerp.web.form.WidgetFrame.extend({
- template: 'WidgetGroup'
-}),
-
-openerp.web.form.WidgetNotebook = openerp.web.form.Widget.extend({
- template: 'WidgetNotebook',
- init: function(view, node) {
- this._super(view, node);
- this.pages = [];
- for (var i = 0; i < node.children.length; i++) {
- var n = node.children[i];
- if (n.tag == "page") {
- var page = new (this.view.registry.get_object('notebookpage'))(
- this.view, n, this, this.pages.length);
- this.pages.push(page);
- }
- }
- },
- start: function() {
- var self = this;
- this._super.apply(this, arguments);
- this.$element.find('> ul > li').each(function (index, tab_li) {
- var page = self.pages[index],
- id = _.uniqueId(self.element_name + '-');
- page.element_id = id;
- $(tab_li).find('a').attr('href', '#' + id);
- });
- this.$element.find('> div').each(function (index, page) {
- page.id = self.pages[index].element_id;
- });
- this.$element.tabs();
- this.view.on_button_new.add_first(this.do_select_first_visible_tab);
- if (openerp.connection.debug) {
- this.do_attach_tooltip(this, this.$element.find('ul:first'), {
- gravity: 's'
- });
- }
- },
- do_select_first_visible_tab: function() {
- for (var i = 0; i < this.pages.length; i++) {
- var page = this.pages[i];
- if (page.invisible === false) {
- this.$element.tabs('select', page.index);
- break;
- }
- }
- }
-});
-
-openerp.web.form.WidgetNotebookPage = openerp.web.form.WidgetFrame.extend({
- template: 'WidgetNotebookPage',
- init: function(view, node, notebook, index) {
- this.notebook = notebook;
- this.index = index;
- this.element_name = 'page_' + index;
- this._super(view, node);
- },
- start: function() {
- this._super.apply(this, arguments);
- this.$element_tab = this.notebook.$element.find(
- '> ul > li:eq(' + this.index + ')');
- },
- update_dom: function() {
- if (this.invisible && this.index === this.notebook.$element.tabs('option', 'selected')) {
- this.notebook.do_select_first_visible_tab();
- }
- this.$element_tab.toggle(!this.invisible);
- this.$element.toggle(!this.invisible);
- }
-});
-
-openerp.web.form.WidgetSeparator = openerp.web.form.Widget.extend({
- template: 'WidgetSeparator',
- init: function(view, node) {
- this._super(view, node);
- this.orientation = node.attrs.orientation || 'horizontal';
- if (this.orientation === 'vertical') {
- this.width = '1';
- }
- this.classname += '_' + this.orientation;
- }
-});
-
openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({
template: 'WidgetButton',
init: function(view, node) {
this._super(view, node);
this.force_disabled = false;
- if (this.string) {
- // We don't have button key bindings in the webclient
- this.string = this.string.replace(/_/g, '');
- }
- if (node.attrs.default_focus == '1') {
+ this.string = (this.node.attrs.string || '').replace(/_/g, '');
+ if (this.node.attrs.default_focus == '1') {
// TODO fme: provide enter key binding to widgets
this.view.default_focus_button = this;
}
},
start: function() {
this._super.apply(this, arguments);
- this.$element.find("button").click(this.on_click);
- if (this.help || openerp.connection.debug) {
+ this.$element.click(this.on_click);
+ if (this.node.attrs.help || openerp.connection.debug) {
this.do_attach_tooltip();
}
},
var exec_action = function() {
if (self.node.attrs.confirm) {
var def = $.Deferred();
- var dialog = $('<div>' + self.node.attrs.confirm + '</div>').dialog({
+ var dialog = openerp.web.dialog($('<div/>').text(self.node.attrs.confirm), {
title: _t('Confirm'),
modal: true,
buttons: [
this.check_disable();
},
check_disable: function() {
- var disabled = (this.readonly || this.force_disabled || !this.view.is_interactible_record());
- this.$element.find('button').prop('disabled', disabled);
- this.$element.find("button").css('color', disabled ? 'grey' : '');
+ var disabled = (this.force_disabled || !this.view.is_interactible_record());
+ this.$element.prop('disabled', disabled);
+ this.$element.css('color', disabled ? 'grey' : '');
}
});
-openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({
- template: 'WidgetLabel',
- init: function(view, node) {
- this.element_name = 'label_' + node.attrs.name;
+/**
+ * Interface implemented by the form view or any other object
+ * able to provide the features necessary for the fields to work.
+ *
+ * Properties:
+ * - ...
+ */
- this._super(view, node);
+openerp.web.form.FieldManagerInterface = {
- if (this.node.tag == 'label' && !this.string && this.node.children.length) {
- this.string = this.node.children[0];
- this.align = 'left';
- }
-
- if (this.node.tag == 'label' && (this.align === 'left' || this.node.attrs.colspan || (this.string && this.string.length > 32))) {
- this.template = "WidgetParagraph";
- this.colspan = parseInt(this.node.attrs.colspan || 1, 10);
- // Widgets default to right-aligned, but paragraph defaults to
- // left-aligned
- if (isNaN(parseFloat(this.node.attrs.align))) {
- this.align = 'left';
- }
+};
- this.multilines = this.string && _.str.lines(this.string).length > 1;
- } else {
- this.colspan = 1;
- this.width = '1%';
- this.decrease_max_width = 1;
- this.nowrap = true;
- }
- },
- render: function () {
- if (this['for'] && this.type !== 'label') {
- return QWeb.render(this.template, {widget: this['for']});
- }
- // Actual label widgets should not have a false and have type label
- return QWeb.render(this.template, {widget: this});
- },
- start: function() {
- this._super();
- var self = this;
- if (this['for'] && (this['for'].help || openerp.connection.debug)) {
- this.do_attach_tooltip(self['for']);
- }
- this.$element.find("label").dblclick(function() {
- var widget = self['for'] || self;
- openerp.log(widget.element_class , widget);
- window.w = widget;
- });
- }
-});
+/**
+ * Interface to be implemented by fields.
+ *
+ * Properties:
+ * - readonly: boolean. If set to true the field should appear in readonly mode.
+ * - force_readonly: boolean, When it is true, the field should always appear
+ * in read only mode, no matter what the value of the "readonly" property can be.
+ * Events:
+ * - ...
+ *
+ */
+openerp.web.form.FieldInterface = {
+ /**
+ * Constructor takes 2 arguments:
+ * - field_manager: Implements FieldManagerInterface
+ * - node: the "<field>" node in json form
+ */
+ init: function(field_manager, node) {},
+ /**
+ * Called by the form view to indicate the value of the field.
+ *
+ * set_value() may return an object that can be passed to $.when() that represents the moment when
+ * the field has finished all operations necessary before the user can effectively use the widget.
+ *
+ * Multiple calls to set_value() can occur at any time and must be handled correctly by the implementation,
+ * regardless of any asynchronous operation currently running and the status of any promise that a
+ * previous call to set_value() could have returned.
+ *
+ * set_value() must be able, at any moment, to handle the syntax returned by the "read" method of the
+ * osv class in the OpenERP server as well as the syntax used by the set_value() (see below). It must
+ * also be able to handle any other format commonly used in the _defaults key on the models in the addons
+ * as well as any format commonly returned in a on_change. It must be able to autodetect those formats as
+ * no information is ever given to know which format is used.
+ */
+ set_value: function(value) {},
+ /**
+ * Get the current value of the widget.
+ *
+ * Must always return a syntaxically correct value to be passed to the "write" method of the osv class in
+ * the OpenERP server, although it is not assumed to respect the constraints applied to the field.
+ * For example if the field is marqued as "required", a call to get_value() can return false.
+ *
+ * get_value() can also be called *before* a call to set_value() and, in that case, is supposed to
+ * return a defaut value according to the type of field.
+ *
+ * This method is always assumed to perform synchronously, it can not return a promise.
+ *
+ * If there was no user interaction to modify the value of the field, it is always assumed that
+ * get_value() return the same semantic value than the one passed in the last call to set_value(),
+ * altough the syntax can be different. This can be the case for type of fields that have a different
+ * syntax for "read" and "write" (example: m2o: set_value([0, "Administrator"]), get_value() => 0).
+ */
+ get_value: function() {},
+};
-openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.form.Field# */{
+/**
+ * Abstract class for classes implementing FieldInterface.
+ *
+ * Properties:
+ * - effective_readonly: when it is true, the widget is displayed as readonly. Vary depending
+ * the values of the "readonly" property and the "force_readonly" property on the field manager.
+ *
+ */
+openerp.web.form.AbstractField = openerp.web.form.Widget.extend(/** @lends openerp.web.form.AbstractField# */{
/**
- * @constructs openerp.web.form.Field
+ * @constructs openerp.web.form.AbstractField
* @extends openerp.web.form.Widget
*
- * @param view
+ * @param field_manager
* @param node
*/
- init: function(view, node) {
- this.name = node.attrs.name;
- this.value = undefined;
- view.fields[this.name] = this;
- view.fields_order.push(this.name);
- this.type = node.attrs.widget || view.fields_view.fields[node.attrs.name].type;
- this.element_name = "field_" + this.name + "_" + this.type;
-
- this._super(view, node);
-
- if (node.attrs.nolabel != '1' && this.colspan > 1) {
- this.colspan--;
- }
- this.field = view.fields_view.fields[node.attrs.name] || {};
- this.string = node.attrs.string || this.field.string;
- this.help = node.attrs.help || this.field.help;
- this.nolabel = (this.field.nolabel || node.attrs.nolabel) === '1';
- this.readonly = this.modifiers['readonly'] === true;
+ init: function(field_manager, node) {
+ this._super(field_manager, node);
+ this.name = this.node.attrs.name;
+ this.value = false;
+ this.view.fields[this.name] = this;
+ this.view.fields_order.push(this.name);
+ this.type = this.node.attrs.widget;
+ this.field = this.view.fields_view.fields[this.name] || {};
this.required = this.modifiers['required'] === true;
this.invalid = this.dirty = false;
-
- this.classname = 'oe_form_field_' + this.type;
+
+ // some events to make the property "effective_readonly" sync automatically with "readonly" and
+ // "force_readonly"
+ this.set({"readonly": this.modifiers['readonly'] === true});
+ var test_effective_readonly = function() {
+ this.set({"effective_readonly": this.get("readonly") || this.get("force_readonly")});
+ };
+ this.on("change:readonly", this, test_effective_readonly);
+ this.on("change:force_readonly", this, test_effective_readonly);
+ _.bind(test_effective_readonly, this)();
+
+ // TODO: do something good about this
+ if (this.view) {
+ this.$label = this.view.$element.find('label[for="' + this.name + '"]');
+ if (this.$label.length) {
+ this.id_for_label = _.uniqueId(['field', this.type, this.name, ''].join('_'));
+ this.$label.attr('for', this.id_for_label);
+ } else {
+ this.$label;
+ }
+ }
},
start: function() {
this._super.apply(this, arguments);
this.$element.addClass('oe_form_field_translatable');
this.$element.find('.oe_field_translate').click(this.on_translate);
}
- if (this.nolabel && openerp.connection.debug) {
+ if (this.node.attrs.nolabel && openerp.connection.debug) {
this.do_attach_tooltip(this, this.$element);
}
},
return !this.invalid;
},
is_dirty: function() {
- return this.dirty && !this.readonly;
- },
- get_on_change_value: function() {
- return this.get_value();
+ return this.dirty && !this.get("effective_readonly");
},
update_dom: function(show_invalid) {
this._super.apply(this, arguments);
this.$element.find('.oe_field_translate').toggle(!!this.view.datarecord.id);
}
if (!this.disable_utility_classes) {
- this.$element.toggleClass('disabled', this.readonly);
+ this.$element.toggleClass('disabled', this.get("effective_readonly"));
this.$element.toggleClass('required', this.required);
if (show_invalid) {
this.$element.toggleClass('invalid', !this.is_valid());
}
});
-openerp.web.form.FieldChar = openerp.web.form.Field.extend({
+/**
+ * A mixin to apply on any field that has to completely re-render when its readonly state
+ * switch.
+ */
+openerp.web.form.ReinitializeFieldMixin = {
+ /**
+ * Default implementation of start(), use it or call explicitly initialize_field().
+ */
+ start: function() {
+ this._super();
+ this.initialize_field();
+ },
+ initialize_field: function() {
+ this.on("change:effective_readonly", this, function() {
+ this.destroy_content();
+ this.renderElement();
+ this.initialize_content();
+ this.render_value();
+ });
+ this.initialize_content();
+ this.render_value();
+ },
+ /**
+ * Called to destroy anything that could have been created previously, called before a
+ * re-initialization.
+ */
+ destroy_content: function() {},
+ /**
+ * Called to initialize the content.
+ */
+ initialize_content: function() {},
+ /**
+ * Called to render the value. Should also be explicitly called at the end of a set_value().
+ */
+ render_value: function() {},
+};
+
+openerp.web.form.FieldChar = openerp.web.form.AbstractField.extend(_.extend({}, openerp.web.form.ReinitializeFieldMixin, {
template: 'FieldChar',
init: function (view, node) {
this._super(view, node);
this.password = this.node.attrs.password === 'True' || this.node.attrs.password === '1';
},
- start: function() {
- this._super.apply(this, arguments);
+ initialize_content: function() {
this.$element.find('input').change(this.on_ui_change);
},
set_value: function(value) {
this._super.apply(this, arguments);
- var show_value = openerp.web.format_value(value, this, '');
- this.$element.find('input').val(show_value);
- return show_value;
+ this.render_value();
},
- update_dom: function() {
- this._super.apply(this, arguments);
- this.$element.find('input').prop('readonly', this.readonly);
+ render_value: function() {
+ var show_value = openerp.web.format_value(this.value, this, '');
+ if (!this.get("effective_readonly")) {
+ this.$element.find('input').val(show_value);
+ } else {
+ if (this.password) {
+ show_value = new Array(show_value.length + 1).join('*');
+ }
+ this.$element.text(show_value);
+ }
},
set_value_from_ui: function() {
this.value = openerp.web.parse_value(this.$element.find('input').val(), this);
},
validate: function() {
this.invalid = false;
- try {
- var value = openerp.web.parse_value(this.$element.find('input').val(), this, '');
- this.invalid = this.required && value === '';
- } catch(e) {
- this.invalid = true;
+ if (!this.get("effective_readonly")) {
+ try {
+ var value = openerp.web.parse_value(this.$element.find('input').val(), this, '');
+ this.invalid = this.required && value === '';
+ } catch(e) {
+ this.invalid = true;
+ }
}
},
focus: function($element) {
this._super($element || this.$element.find('input:first'));
}
-});
+}));
openerp.web.form.FieldID = openerp.web.form.FieldChar.extend({
- update_dom: function() {
- this._super.apply(this, arguments);
- this.$element.find('input').prop('readonly', true);
- }
+
});
openerp.web.form.FieldEmail = openerp.web.form.FieldChar.extend({
template: 'FieldEmail',
- start: function() {
- this._super.apply(this, arguments);
+ initialize_content: function() {
+ this._super();
this.$element.find('button').click(this.on_button_clicked);
},
+ render_value: function() {
+ if (!this.get("effective_readonly")) {
+ this._super();
+ } else {
+ this.$element.find('a')
+ .attr('href', 'mailto:' + this.value)
+ .text(this.value);
+ }
+ },
on_button_clicked: function() {
if (!this.value || !this.is_valid()) {
this.do_warn("E-mail error", "Can't send email to invalid e-mail address");
openerp.web.form.FieldUrl = openerp.web.form.FieldChar.extend({
template: 'FieldUrl',
- start: function() {
- this._super.apply(this, arguments);
+ initialize_content: function() {
+ this._super();
this.$element.find('button').click(this.on_button_clicked);
},
+ render_value: function() {
+ if (!this.get("effective_readonly")) {
+ this._super();
+ } else {
+ var tmp = this.value;
+ var s = /(\w+):(.+)/.exec(tmp);
+ if (!s) {
+ tmp = "http://" + this.value;
+ }
+ this.$element.find('a').attr('href', tmp).text(tmp);
+ }
+ },
on_button_clicked: function() {
if (!this.value) {
this.do_warn("Resource error", "This resource is empty");
openerp.web.form.FieldFloat = openerp.web.form.FieldChar.extend({
init: function (view, node) {
this._super(view, node);
- if (node.attrs.digits) {
- this.parse_digits(node.attrs.digits);
+ this.value = 0;
+ if (this.node.attrs.digits) {
+ this.digits = py.eval(this.node.attrs.digits).toJSON();
} else {
- this.digits = view.fields_view.fields[node.attrs.name].digits;
+ this.digits = this.field.digits;
}
},
- parse_digits: function (digits_attr) {
- // could use a Python parser instead.
- var match = /^\s*[\(\[](\d+),\s*(\d+)/.exec(digits_attr);
- return [parseInt(match[1], 10), parseInt(match[2], 10)];
- },
set_value: function(value) {
if (value === false || value === undefined) {
// As in GTK client, floats default to 0
showButtonPanel: true
});
this.$element.find('img.oe_datepicker_trigger').click(function() {
- if (!self.readonly && !self.picker('widget').is(':visible')) {
+ if (!self.get("effective_readonly") && !self.picker('widget').is(':visible')) {
self.picker('setDate', self.value ? openerp.web.auto_str_to_date(self.value) : new Date());
self.$input_picker.show();
self.picker('show');
type_of_date: "date"
});
-openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({
+openerp.web.form.FieldDatetime = openerp.web.form.AbstractField.extend(_.extend({}, openerp.web.form.ReinitializeFieldMixin, {
template: "EmptyComponent",
build_widget: function() {
return new openerp.web.DateTimeWidget(this);
},
- start: function() {
- var self = this;
- this._super.apply(this, arguments);
- this.datewidget = this.build_widget();
- this.datewidget.on_change.add_last(this.on_ui_change);
- this.datewidget.appendTo(this.$element);
+ destroy_content: function() {
+ if (this.datewidget) {
+ this.datewidget.destroy();
+ this.datewidget = undefined;
+ }
+ },
+ initialize_content: function() {
+ if (!this.get("effective_readonly")) {
+ this.datewidget = this.build_widget();
+ this.datewidget.on_change.add_last(this.on_ui_change);
+ this.datewidget.appendTo(this.$element);
+ }
},
set_value: function(value) {
this._super(value);
- this.datewidget.set_value(value);
+ this.render_value();
},
- get_value: function() {
- return this.datewidget.get_value();
+ render_value: function() {
+ if (!this.get("effective_readonly")) {
+ this.datewidget.set_value(this.value);
+ } else {
+ this.$element.text(openerp.web.format_value(this.value, this, ''));
+ }
},
- update_dom: function() {
- this._super.apply(this, arguments);
- this.datewidget.set_readonly(this.readonly);
+ get_value: function() {
+ if (!this.get("effective_readonly")) {
+ return this.datewidget.get_value();
+ } else {
+ return this.value;
+ }
},
validate: function() {
- this.invalid = !this.datewidget.is_valid(this.required);
+ this.invalid = this.get("effective_readonly") || !this.datewidget.is_valid(this.required);
},
focus: function($element) {
- this._super($element || this.datewidget.$input);
+ this._super($element || (this.datewidget && this.datewidget.$input));
}
-});
+}));
openerp.web.form.FieldDate = openerp.web.form.FieldDatetime.extend({
build_widget: function() {
}
});
-openerp.web.form.FieldText = openerp.web.form.Field.extend({
+openerp.web.form.FieldText = openerp.web.form.AbstractField.extend(_.extend({}, openerp.web.form.ReinitializeFieldMixin, {
template: 'FieldText',
- start: function() {
- this._super.apply(this, arguments);
- this.$element.find('textarea').change(this.on_ui_change);
- this.resized = false;
+ initialize_content: function() {
+ this.$textarea = undefined;
+ if (!this.get("effective_readonly")) {
+ this.$textarea = this.$element.find('textarea').change(this.on_ui_change);
+ this.resized = false;
+ }
},
set_value: function(value) {
this._super.apply(this, arguments);
- var show_value = openerp.web.format_value(value, this, '');
- this.$element.find('textarea').val(show_value);
- if (!this.resized && this.view.options.resize_textareas) {
- this.do_resize(this.view.options.resize_textareas);
- this.resized = true;
- }
+ this.render_value();
},
- update_dom: function() {
- this._super.apply(this, arguments);
- this.$element.find('textarea').prop('readonly', this.readonly);
+ render_value: function() {
+ var show_value = openerp.web.format_value(this.value, this, '');
+ if (!this.get("effective_readonly")) {
+ this.$textarea.val(show_value);
+ if (!this.resized && this.view.options.resize_textareas) {
+ this.do_resize(this.view.options.resize_textareas);
+ this.resized = true;
+ }
+ } else {
+ this.$element.text(show_value);
+ }
},
set_value_from_ui: function() {
- this.value = openerp.web.parse_value(this.$element.find('textarea').val(), this);
+ this.value = openerp.web.parse_value(this.$textarea.val(), this);
this._super();
},
validate: function() {
this.invalid = false;
- try {
- var value = openerp.web.parse_value(this.$element.find('textarea').val(), this, '');
- this.invalid = this.required && value === '';
- } catch(e) {
- this.invalid = true;
+ if (!this.get("effective_readonly")) {
+ try {
+ var value = openerp.web.parse_value(this.$textarea.val(), this, '');
+ this.invalid = this.required && value === '';
+ } catch(e) {
+ this.invalid = true;
+ }
}
},
focus: function($element) {
- this._super($element || this.$element.find('textarea:first'));
+ this._super($element || this.$textarea);
},
do_resize: function(max_height) {
max_height = parseInt(max_height, 10);
- var $input = this.$element.find('textarea'),
+ var $input = this.$textarea,
$div = $('<div style="position: absolute; z-index: 1000; top: 0"/>').width($input.width()),
new_height;
$div.text($input.val());
reset: function() {
this.resized = false;
}
-});
+}));
-openerp.web.form.FieldBoolean = openerp.web.form.Field.extend({
+openerp.web.form.FieldBoolean = openerp.web.form.AbstractField.extend({
template: 'FieldBoolean',
start: function() {
- var self = this;
this._super.apply(this, arguments);
- this.$element.find('input').click(self.on_ui_change);
+ this.$checkbox = $("input", this.$element);
+ this.$element.click(this.on_ui_change);
+ var check_readonly = function() {
+ this.$checkbox.prop('disabled', this.get("effective_readonly"));
+ };
+ this.on("change:effective_readonly", this, check_readonly);
+ _.bind(check_readonly, this)();
},
set_value: function(value) {
this._super.apply(this, arguments);
- this.$element.find('input')[0].checked = value;
+ this.$checkbox[0].checked = value;
},
set_value_from_ui: function() {
- this.value = this.$element.find('input').is(':checked');
- this._super();
- },
- update_dom: function() {
+ this.value = this.$checkbox.is(':checked');
this._super.apply(this, arguments);
- this.$element.find('input').prop('disabled', this.readonly);
},
focus: function($element) {
- this._super($element || this.$element.find('input:first'));
+ this._super($element || this.$checkbox);
}
});
-openerp.web.form.FieldProgressBar = openerp.web.form.Field.extend({
+openerp.web.form.FieldProgressBar = openerp.web.form.AbstractField.extend({
template: 'FieldProgressBar',
start: function() {
this._super.apply(this, arguments);
- this.$element.find('div').progressbar({
+ this.$element.progressbar({
value: this.value,
- disabled: this.readonly
+ disabled: this.get("effective_readonly")
});
},
set_value: function(value) {
show_value = 0;
}
var formatted_value = openerp.web.format_value(show_value, { type : 'float' }, '0');
- this.$element.find('div').progressbar('option', 'value', show_value).find('span').html(formatted_value + '%');
+ this.$element.progressbar('option', 'value', show_value).find('span').html(formatted_value + '%');
}
});
-openerp.web.form.FieldTextXml = openerp.web.form.Field.extend({
+openerp.web.form.FieldTextXml = openerp.web.form.AbstractField.extend({
// to replace view editor
});
-openerp.web.form.FieldSelection = openerp.web.form.Field.extend({
+openerp.web.form.FieldSelection = openerp.web.form.AbstractField.extend(_.extend({}, openerp.web.form.ReinitializeFieldMixin, {
template: 'FieldSelection',
init: function(view, node) {
var self = this;
});
this.values.unshift([false, '']);
},
- start: function() {
+ initialize_content: function() {
// Flag indicating whether we're in an event chain containing a change
// event on the select, in order to know what to do on keyup[RETURN]:
// * If the user presses [RETURN] as part of changing the value of a
// changing the selected value), takes the action as validating the
// row
var ischanging = false;
- this._super.apply(this, arguments);
this.$element.find('select')
.change(this.on_ui_change)
.change(function () { ischanging = true; })
value = value === null ? false : value;
value = value instanceof Array ? value[0] : value;
this._super(value);
- var index = 0;
- for (var i = 0, ii = this.values.length; i < ii; i++) {
- if (this.values[i][0] === value) index = i;
+ this.render_value();
+ },
+ render_value: function() {
+ if (!this.get("effective_readonly")) {
+ var index = 0;
+ for (var i = 0, ii = this.values.length; i < ii; i++) {
+ if (this.values[i][0] === this.value) index = i;
+ }
+ this.$element.find('select')[0].selectedIndex = index;
+ } else {
+ var self = this;
+ var option = _(this.values)
+ .detect(function (record) { return record[0] === self.value; });
+ this.$element.text(option ? option[1] : this.values[0][1]);
}
- this.$element.find('select')[0].selectedIndex = index;
},
set_value_from_ui: function() {
this.value = this.values[this.$element.find('select')[0].selectedIndex][0];
this._super();
},
- update_dom: function() {
- this._super.apply(this, arguments);
- this.$element.find('select').prop('disabled', this.readonly);
- },
validate: function() {
+ if (this.get("effective_readonly")) {
+ this.invalid = false;
+ return;
+ }
var value = this.values[this.$element.find('select')[0].selectedIndex];
this.invalid = !(value && !(this.required && value[0] === false));
},
focus: function($element) {
this._super($element || this.$element.find('select:first'));
}
-});
+}));
// jquery autocomplete tweak to allow html
(function() {
return dialog.$element;
};
-openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({
- template: 'FieldMany2One',
+openerp.web.form.FieldMany2One = openerp.web.form.AbstractField.extend(_.extend({}, openerp.web.form.ReinitializeFieldMixin, {
+ template: "FieldMany2One",
init: function(view, node) {
this._super(view, node);
this.limit = 7;
this.last_search = [];
this.tmp_value = undefined;
},
- start: function() {
- this._super();
+ initialize_content: function() {
+ if (!this.get("effective_readonly"))
+ this.render_editable();
+ this.render_value();
+ },
+ render_editable: function() {
var self = this;
this.$input = this.$element.find("input");
this.$drop_down = this.$element.find(".oe-m2o-drop-down-button");
$cmenu.append(QWeb.render("FieldMany2One.context_menu", {widget: self}));
var bindings = {};
bindings[self.cm_id + "_search"] = function() {
- if (self.readonly)
+ if (self.get("effective_readonly"))
return;
self._search_create_popup("search");
};
bindings[self.cm_id + "_create"] = function() {
- if (self.readonly)
+ if (self.get("effective_readonly"))
return;
self._search_create_popup("form");
};
} else {
$("#" + self.cm_id + " .oe_m2o_menu_item_mandatory").addClass("oe-m2o-disabled-cm");
}
- if (!self.readonly) {
+ if (!self.get("effective_readonly")) {
$("#" + self.cm_id + " .oe_m2o_menu_item_noreadonly").removeClass("oe-m2o-disabled-cm");
} else {
$("#" + self.cm_id + " .oe_m2o_menu_item_noreadonly").addClass("oe-m2o-disabled-cm");
}
});
this.$drop_down.click(function() {
- if (self.readonly)
+ if (self.get("effective_readonly"))
return;
if (self.$input.autocomplete("widget").is(":visible")) {
self.$input.autocomplete("close");
minLength: 0,
delay: 0
});
+ this.$input.autocomplete("widget").addClass("openerp openerp2");
// used to correct a bug when selecting an element by pushing 'enter' in an editable list
this.$input.keyup(function(e) {
if (e.which === 13) {
},
_change_int_ext_value: function(value) {
this._change_int_value(value);
- this.$input.val(this.value ? this.value[1] : "");
+ this.render_value();
+ },
+ render_value: function() {
+ var self = this;
+ if (!this.get("effective_readonly")) {
+ this.$input.val(this.value ? this.value[1] : "");
+ } else {
+ self.$element.find('a')
+ .unbind('click')
+ .text(this.value ? this.value[1] : '')
+ .click(function () {
+ self.do_action({
+ type: 'ir.actions.act_window',
+ res_model: self.field.relation,
+ res_id: self.value[0],
+ context: self.build_context(),
+ views: [[false, 'page'], [false, 'form']],
+ target: 'current'
+ });
+ return false;
+ });
+ }
},
_change_int_value: function(value) {
this.value = value;
},
focus: function ($element) {
this._super($element || this.$input);
- },
- update_dom: function() {
- this._super.apply(this, arguments);
- this.$input.prop('readonly', this.readonly);
}
-});
+}));
/*
# Values: (0, 0, { fields }) create
return [6, false, ids];
}
};
-openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({
- template: 'FieldOne2Many',
+openerp.web.form.FieldOne2Many = openerp.web.form.AbstractField.extend({
multi_selection: false,
init: function(view, node) {
this._super(view, node);
this.is_setted.then(function() {
self.load_views();
});
+ this.is_loaded.then(function() {
+ self.on("change:effective_readonly", self, function() {
+ self.is_loaded = self.is_loaded.pipe(function() {
+ self.viewmanager.destroy();
+ return $.when(self.load_views()).then(function() {
+ self.reload_current_view();
+ });
+ });
+ });
+ });
},
trigger_on_change: function() {
var tmp = this.doing_on_change;
this.on_ui_change();
this.doing_on_change = tmp;
},
- is_readonly: function() {
- return this.readonly || this.force_readonly;
- },
load_views: function() {
var self = this;
}
if(view.view_type === "list") {
view.options.selectable = self.multi_selection;
- if (self.is_readonly()) {
+ if (self.get("effective_readonly")) {
view.options.addable = null;
view.options.deletable = null;
view.options.isClarkGable = false;
}
} else if (view.view_type === "form") {
- if (self.is_readonly()) {
+ if (self.get("effective_readonly")) {
view.view_type = 'page';
}
view.options.not_interactible_on_create = true;
this.viewmanager.on_controller_inited.add_last(function(view_type, controller) {
if (view_type == "list") {
controller.o2m = self;
- if (self.is_readonly())
+ if (self.get("effective_readonly"))
controller.set_editable(false);
} else if (view_type == "form" || view_type == 'page') {
- if (view_type == 'page' || self.is_readonly()) {
+ if (view_type == 'page' || self.get("effective_readonly")) {
$(".oe_form_buttons", controller.$element).children().remove();
}
controller.on_record_loaded.add_last(function() {
is_dirty: function() {
this.save_any_view();
return this._super();
- },
- update_dom: function() {
- this._super.apply(this, arguments);
- var self = this;
- if (this.previous_readonly !== this.readonly) {
- this.previous_readonly = this.readonly;
- if (this.viewmanager) {
- this.is_loaded = this.is_loaded.pipe(function() {
- self.viewmanager.stop();
- return $.when(self.load_views()).then(function() {
- self.reload_current_view();
- });
- });
- }
- }
}
});
return self.o2m.dataset.read_ids.apply(self.o2m.dataset, arguments);
},
form_view_options: {'not_interactible_on_create':true},
- readonly: self.o2m.is_readonly()
+ readonly: self.o2m.get("effective_readonly")
});
pop.on_write.add(function(id, data) {
self.o2m.dataset.write(id, data, {}, function(r) {
}
});
-openerp.web.form.FieldMany2Many = openerp.web.form.Field.extend({
- template: 'FieldMany2Many',
+/*
+ * TODO niv: clean those deferred stuff, it could be better
+ */
+openerp.web.form.FieldMany2Many = openerp.web.form.AbstractField.extend({
multi_selection: false,
init: function(view, node) {
this._super(view, node);
- this.list_id = _.uniqueId("many2many");
this.is_loaded = $.Deferred();
this.initial_is_loaded = this.is_loaded;
this.is_setted = $.Deferred();
this.is_setted.then(function() {
self.load_view();
});
+ this.is_loaded.then(function() {
+ self.on("change:effective_readonly", self, function() {
+ self.is_loaded = self.is_loaded.pipe(function() {
+ self.list_view.destroy();
+ return $.when(self.load_view()).then(function() {
+ self.reload_content();
+ });
+ });
+ });
+ })
},
set_value: function(value) {
value = value || [];
validate: function() {
this.invalid = false;
},
- is_readonly: function() {
- return this.readonly || this.force_readonly;
- },
load_view: function() {
var self = this;
this.list_view = new openerp.web.form.Many2ManyListView(this, this.dataset, false, {
- 'addable': self.is_readonly() ? null : _t("Add"),
- 'deletable': self.is_readonly() ? false : true,
+ 'addable': self.get("effective_readonly") ? null : _t("Add"),
+ 'deletable': self.get("effective_readonly") ? false : true,
'selectable': self.multi_selection,
- 'isClarkGable': self.is_readonly() ? false : true
+ 'isClarkGable': self.get("effective_readonly") ? false : true
});
var embedded = (this.field.views || {}).tree;
if (embedded) {
loaded.resolve();
});
$.async_when().then(function () {
- self.list_view.appendTo($("#" + self.list_id));
+ self.list_view.appendTo(self.$element);
});
return loaded;
},
return self.list_view.reload_content();
});
},
- update_dom: function() {
- this._super.apply(this, arguments);
- var self = this;
- if (this.previous_readonly !== this.readonly) {
- this.previous_readonly = this.readonly;
- if (this.list_view) {
- this.is_loaded = this.is_loaded.pipe(function() {
- self.list_view.stop();
- return $.when(self.load_view()).then(function() {
- self.reload_content();
- });
- });
- }
- }
- }
});
openerp.web.form.Many2ManyDataSet = openerp.web.DataSetStatic.extend({
);
var self = this;
pop.on_select_elements.add(function(element_ids) {
- _.each(element_ids, function(element_id) {
- if(! _.detect(self.dataset.ids, function(x) {return x == element_id;})) {
- self.dataset.set_ids([].concat(self.dataset.ids, [element_id]));
+ _.each(element_ids, function(one_id) {
+ if(! _.detect(self.dataset.ids, function(x) {return x == one_id;})) {
+ self.dataset.set_ids([].concat(self.dataset.ids, [one_id]));
self.m2m_field.on_ui_change();
self.reload_content();
}
var pop = new openerp.web.form.FormOpenPopup(this);
pop.show_element(this.dataset.model, id, this.m2m_field.build_context(), {
title: _t("Open: ") + this.name,
- readonly: this.widget_parent.is_readonly()
+ readonly: this.getParent().get("effective_readonly")
});
pop.on_write_completed.add_last(function() {
self.reload_content();
}, read_function: null});
this.initial_ids = this.options.initial_ids;
this.created_elements = [];
- this.render_element();
+ this.renderElement();
openerp.web.form.dialog(this.$element, {
close: function() {
self.check_exit();
setup_search_view: function(search_defaults) {
var self = this;
if (this.searchview) {
- this.searchview.stop();
+ this.searchview.destroy();
}
this.searchview = new openerp.web.SearchView(this,
this.dataset, false, search_defaults);
'selectable': !self.options.disable_multiple_selection
}, self.options.list_view_options || {}));
self.view_list.popup = self;
- self.view_list.appendTo($("#" + self.element_id + "_view_list")).pipe(function() {
+ self.view_list.appendTo($(".oe-select-create-popup-view-list", self.$element)).pipe(function() {
self.view_list.do_show();
}).pipe(function() {
self.searchview.do_search();
$buttons.prepend(QWeb.render("SelectCreatePopup.search.buttons"));
var $cbutton = $buttons.find(".oe_selectcreatepopup-search-close");
$cbutton.click(function() {
- self.stop();
+ self.destroy();
});
var $sbutton = $buttons.find(".oe_selectcreatepopup-search-select");
if(self.options.disable_multiple_selection) {
}
$sbutton.click(function() {
self.on_select_elements(self.selected_ids);
- self.stop();
+ self.destroy();
});
});
});
- this.searchview.appendTo($("#" + this.element_id + "_search"));
+ this.searchview.appendTo($(".oe-select-create-popup-view-list", self.$element));
},
do_search: function(domains, contexts, groupbys) {
var self = this;
if (this.options.alternative_form_view) {
this.view_form.set_embedded_view(this.options.alternative_form_view);
}
- this.view_form.appendTo(this.$element.find("#" + this.element_id + "_view_form"));
+ this.view_form.appendTo(this.$element.find(".oe-select-create-popup-view-form"));
this.view_form.on_loaded.add_last(function() {
var $buttons = self.view_form.$element.find(".oe_form_buttons");
$buttons.html(QWeb.render("SelectCreatePopup.form.buttons", {widget:self}));
if (this.created_elements.length > 0) {
this.on_select_elements(this.created_elements);
}
- this.stop();
+ this.destroy();
},
on_default_get: function(res) {}
});
},
select_record: function(index) {
this.popup.on_select_elements([this.dataset.ids[index]]);
- this.popup.stop();
+ this.popup.destroy();
},
do_select: function(ids, records) {
this._super(ids, records);
this.row_id = row_id;
this.context = context || {};
this.options = _.defaults(options || {}, {"auto_write": true});
- this.render_element();
- this.$element.dialog({
+ this.renderElement();
+ openerp.web.dialog(this.$element, {
title: options.title || '',
modal: true,
width: 960,
if (this.options.alternative_form_view) {
this.view_form.set_embedded_view(this.options.alternative_form_view);
}
- this.view_form.appendTo(this.$element.find("#" + this.element_id + "_view_form"));
+ this.view_form.appendTo(this.$element.find(".oe-form-open-popup-form-view"));
this.view_form.on_loaded.add_last(function() {
var $buttons = self.view_form.$element.find(".oe_form_buttons");
$buttons.html(QWeb.render("FormOpenPopup.form.buttons"));
var $nbutton = $buttons.find(".oe_formopenpopup-form-save");
$nbutton.click(function() {
self.view_form.do_save().then(function() {
- self.stop();
+ self.destroy();
});
});
var $cbutton = $buttons.find(".oe_formopenpopup-form-close");
$cbutton.click(function() {
- self.stop();
+ self.destroy();
});
if (self.options.readonly) {
$nbutton.hide();
}
});
-openerp.web.form.FieldReference = openerp.web.form.Field.extend({
+openerp.web.form.FieldReference = openerp.web.form.AbstractField.extend(_.extend({}, openerp.web.form.ReinitializeFieldMixin, {
template: 'FieldReference',
init: function(view, node) {
this._super(view, node);
this.get_selected_ids = view.get_selected_ids;
this.do_onchange = this.on_form_changed = this.do_notify_change = this.on_nop;
this.dataset = this.view.dataset;
- this.widgets_counter = 0;
this.view_id = 'reference_' + _.uniqueId();
- this.widgets = {};
this.fields = {};
this.fields_order = [];
- this.selection = new openerp.web.form.FieldSelection(this, { attrs: {
- name: 'selection',
- widget: 'selection'
- }});
this.reference_ready = true;
- this.selection.on_value_changed.add_last(this.on_selection_changed);
- this.m2o = new openerp.web.form.FieldMany2One(this, { attrs: {
- name: 'm2o',
- widget: 'many2one'
- }});
- this.m2o.on_ui_change.add_last(this.on_ui_change);
},
on_nop: function() {
},
this.m2o.$element.toggle(sel !== false);
}
},
- start: function() {
- this._super();
- this.selection.start();
+ destroy_content: function() {
+ if (this.selection) {
+ this.selection.destroy();
+ this.selection = undefined;
+ }
+ if (this.m2o) {
+ this.m2o.destroy();
+ this.m2o.undefined;
+ }
+ },
+ initialize_content: function() {
+ if (!this.get("effective_readonly")) {
+ this.selection = new openerp.web.form.FieldSelection(this, { attrs: {
+ name: 'selection',
+ widget: 'selection'
+ }});
+ this.selection.on_value_changed.add_last(this.on_selection_changed);
+
+ this.selection.$element = $(".oe_form_view_reference_selection", this.$element);
+ this.selection.renderElement();
+ this.selection.start();
+ }
+ this.m2o = new openerp.web.form.FieldMany2One(this, { attrs: {
+ name: 'm2o',
+ widget: 'many2one'
+ }});
+ this.m2o.set({"readonly": this.get("effective_readonly")});
+ this.m2o.on_ui_change.add_last(this.on_ui_change);
+ this.m2o.$element = $(".oe_form_view_reference_m2o", this.$element);
+ this.m2o.renderElement();
this.m2o.start();
},
is_valid: function() {
},
set_value: function(value) {
this._super(value);
+ this.render_value();
+ },
+ render_value: function() {
this.reference_ready = false;
var vals = [], sel_val, m2o_val;
- if (typeof(value) === 'string') {
- vals = value.split(',');
+ if (typeof(this.value) === 'string') {
+ vals = this.value.split(',');
}
sel_val = vals[0] || false;
m2o_val = vals[1] ? parseInt(vals[1], 10) : false;
- this.selection.set_value(sel_val);
+ if (!this.get("effective_readonly")) {
+ this.selection.set_value(sel_val);
+ }
this.m2o.field.relation = sel_val;
this.m2o.set_value(m2o_val);
this.m2o.$element.toggle(sel_val !== false);
return false;
}
}
-});
+}));
-openerp.web.form.FieldBinary = openerp.web.form.Field.extend({
+openerp.web.form.FieldBinary = openerp.web.form.AbstractField.extend(_.extend({}, openerp.web.form.ReinitializeFieldMixin, {
init: function(view, node) {
this._super(view, node);
this.iframe = this.element_id + '_iframe';
this.binary_value = false;
},
- start: function() {
- this._super.apply(this, arguments);
+ initialize_content: function() {
this.$element.find('input.oe-binary-file').change(this.on_file_change);
this.$element.find('button.oe-binary-file-save').click(this.on_save_as);
this.$element.find('.oe-binary-file-clear').click(this.on_clear);
}
return false;
}
-});
+}));
openerp.web.form.FieldBinaryFile = openerp.web.form.FieldBinary.extend({
template: 'FieldBinaryFile',
- update_dom: function() {
- this._super.apply(this, arguments);
- this.$element.find('.oe-binary-file-set, .oe-binary-file-clear').toggle(!this.readonly);
- this.$element.find('input[type=text]').prop('readonly', this.readonly);
+ initialize_content: function() {
+ this._super();
+ if (this.get("effective_readonly")) {
+ var self = this;
+ this.$element.find('a').click(function() {
+ if (self.value) {
+ self.on_save_as();
+ }
+ return false;
+ });
+ }
},
set_value: function(value) {
this._super.apply(this, arguments);
- var show_value;
- if (this.node.attrs.filename) {
- show_value = this.view.datarecord[this.node.attrs.filename] || '';
+ this.render_value();
+ },
+ render_value: function() {
+ if (!this.get("effective_readonly")) {
+ var show_value;
+ if (this.node.attrs.filename) {
+ show_value = this.view.datarecord[this.node.attrs.filename] || '';
+ } else {
+ show_value = (this.value != null && this.value !== false) ? this.value : '';
+ }
+ this.$element.find('input').eq(0).val(show_value);
} else {
- show_value = (value != null && value !== false) ? value : '';
+ this.$element.find('a').show(!!this.value);
+ if (this.value) {
+ var show_value = _t("Download") + " " + (this.view.datarecord[this.node.attrs.filename] || '');
+ this.$element.find('a').text(show_value);
+ }
}
- this.$element.find('input').eq(0).val(show_value);
},
on_file_uploaded_and_valid: function(size, name, content_type, file_base64) {
this.value = file_base64;
openerp.web.form.FieldBinaryImage = openerp.web.form.FieldBinary.extend({
template: 'FieldBinaryImage',
- start: function() {
- this._super.apply(this, arguments);
- this.$image = this.$element.find('img.oe-binary-image');
- },
- update_dom: function() {
- this._super.apply(this, arguments);
- this.$element.find('.oe-binary').toggle(!this.readonly);
+ initialize_content: function() {
+ this._super();
+ this.$placeholder = $(".oe_form_field-binary-image-placeholder", this.$element);
+ if (!this.get("effective_readonly"))
+ this.$element.find('.oe-binary').show();
+ else
+ this.$element.find('.oe-binary').hide();
},
set_value: function(value) {
this._super.apply(this, arguments);
- this.set_image_maxwidth();
- var url = '/web/binary/image?session_id=' + this.session.session_id + '&model=' +
- this.view.dataset.model +'&id=' + (this.view.datarecord.id || '') + '&field=' + this.name + '&t=' + (new Date().getTime());
- this.$image.attr('src', url);
- },
- set_image_maxwidth: function() {
- this.$image.css('max-width', this.$element.width());
+ this.render_value();
+ },
+ render_value: function() {
+ var url;
+ if (this.value && this.value.substr(0, 10).indexOf(' ') == -1) {
+ url = 'data:image/png;base64,' + this.value;
+ } else if (this.value) {
+ url = '/web/binary/image?session_id=' + this.session.session_id + '&model=' +
+ this.view.dataset.model +'&id=' + (this.view.datarecord.id || '') + '&field=' + this.name + '&t=' + (new Date().getTime());
+ } else {
+ url = "/web/static/src/img/placeholder.png";
+ }
+ var rendered = QWeb.render("FieldBinaryImage-img", {widget: this, url: url});;
+ this.$placeholder.html(rendered);
},
on_file_change: function() {
- this.set_image_maxwidth();
+ this.render_value();
this._super.apply(this, arguments);
},
on_file_uploaded_and_valid: function(size, name, content_type, file_base64) {
this.value = file_base64;
this.binary_value = true;
- this.$image.attr('src', 'data:' + (content_type || 'image/png') + ';base64,' + file_base64);
+ this.render_value();
},
on_clear: function() {
this._super.apply(this, arguments);
- this.$image.attr('src', '/web/static/src/img/placeholder.png');
+ this.render_value();
}
});
-openerp.web.form.FieldStatus = openerp.web.form.Field.extend({
+openerp.web.form.FieldStatus = openerp.web.form.AbstractField.extend({
template: "EmptyComponent",
start: function() {
this._super();
}
});
-openerp.web.form.WidgetHtml = openerp.web.form.Widget.extend({
- render: function () {
- var $root = $('<div class="oe_form_html_view">');
- this.render_children(this, $root);
- return $root.html();
- },
- render_children: function (object, $into) {
- var self = this,
- fields = this.view.fields_view.fields;
- _(object.children).each(function (child) {
- if (typeof child === 'string') {
- $into.text(child);
- } else if (child.tag === 'field') {
- $into.append(
- new (self.view.registry.get_object('frame'))(
- self.view, {tag: 'ueule', attrs: {}, children: [child] })
- .render());
- } else {
- var $child = $(document.createElement(child.tag))
- .attr(child.attrs)
- .appendTo($into);
- self.render_children(child, $child);
- }
- });
- }
-});
-
-
/**
* Registry of form widgets, called by :js:`openerp.web.FormView`
*/
openerp.web.form.widgets = new openerp.web.Registry({
- 'frame' : 'openerp.web.form.WidgetFrame',
- 'group' : 'openerp.web.form.WidgetGroup',
- 'notebook' : 'openerp.web.form.WidgetNotebook',
- 'notebookpage' : 'openerp.web.form.WidgetNotebookPage',
- 'separator' : 'openerp.web.form.WidgetSeparator',
- 'label' : 'openerp.web.form.WidgetLabel',
'button' : 'openerp.web.form.WidgetButton',
'char' : 'openerp.web.form.FieldChar',
'id' : 'openerp.web.form.FieldID',
'progressbar': 'openerp.web.form.FieldProgressBar',
'image': 'openerp.web.form.FieldBinaryImage',
'binary': 'openerp.web.form.FieldBinaryFile',
- 'statusbar': 'openerp.web.form.FieldStatus',
- 'html': 'openerp.web.form.WidgetHtml'
+ 'statusbar': 'openerp.web.form.FieldStatus'
});
-
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: