From 273904d103c3a13499521b71772d794e5e5d400b Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Mon, 1 Oct 2012 17:03:05 +0200 Subject: [PATCH 01/15] [IMP] orm: be more vebose when accessing an invalid field of browse_record objects bzr revid: chs@openerp.com-20121001150305-recozdvfc68wc766 --- openerp/osv/orm.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index f324ed67522..47afda39e5e 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -51,6 +51,7 @@ import pickle import re import simplejson import time +import traceback import types from lxml import etree @@ -373,6 +374,8 @@ class browse_record(object): else: error_msg = "Field '%s' does not exist in object '%s'" % (name, self) self.__logger.warning(error_msg) + if self.__logger.isEnabledFor(logging.DEBUG): + self.__logger.debug(''.join(traceback.format_stack())) raise KeyError(error_msg) # if the field is a classic one or a many2one, we'll fetch all classic and many2one fields From 5763e40506f9f155e09b2fccbc7ffa4247b02b9b Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Mon, 1 Oct 2012 17:14:21 +0200 Subject: [PATCH 02/15] [IMP] orm: remove trailing spaces bzr revid: chs@openerp.com-20121001151421-vdfkzuf864rz8q20 --- openerp/osv/orm.py | 60 +++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 47afda39e5e..8b78621db03 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -372,7 +372,7 @@ class browse_record(object): else: return attr else: - error_msg = "Field '%s' does not exist in object '%s'" % (name, self) + error_msg = "Field '%s' does not exist in object '%s'" % (name, self) self.__logger.warning(error_msg) if self.__logger.isEnabledFor(logging.DEBUG): self.__logger.debug(''.join(traceback.format_stack())) @@ -712,7 +712,7 @@ class BaseModel(object): def log(self, cr, uid, id, message, secondary=False, context=None): return _logger.warning("log() is deprecated. Please use OpenChatter notification system instead of the res.log mechanism.") - + def view_init(self, cr, uid, fields_list, context=None): """Override this method to do specific things when a view on the object is opened.""" pass @@ -902,7 +902,7 @@ class BaseModel(object): # If new class defines a constraint with # same function name, we let it override # the old one. - + new[c2] = c exist = True break @@ -1596,7 +1596,7 @@ class BaseModel(object): def user_has_groups(self, cr, uid, groups, context=None): """Return true if the user is at least member of one of the groups in groups_str. Typically used to resolve ``groups`` attribute - in view and model definitions. + in view and model definitions. :param str groups: comma-separated list of fully-qualified group external IDs, e.g.: ``base.group_user,base.group_system`` @@ -1639,7 +1639,7 @@ class BaseModel(object): the field should be completely removed from the view, as it is completely unavailable for non-members - :return: True if field should be included in the result of fields_view_get + :return: True if field should be included in the result of fields_view_get """ if node.tag == 'field' and node.get('name') in self._all_columns: column = self._all_columns[node.get('name')].column @@ -1914,7 +1914,7 @@ class BaseModel(object): def _get_default_calendar_view(self, cr, user, context=None): """ Generates a default calendar view by trying to infer calendar fields from a number of pre-set attribute names - + :param cr: database cursor :param int user: user id :param dict context: connection context @@ -1993,7 +1993,7 @@ class BaseModel(object): def raise_view_error(error_msg, child_view_id): view, child_view = self.pool.get('ir.ui.view').browse(cr, user, [view_id, child_view_id], context) - error_msg = error_msg % {'parent_xml_id': view.xml_id} + error_msg = error_msg % {'parent_xml_id': view.xml_id} raise AttributeError("View definition error for inherited view '%s' on model '%s': %s" % (child_view.xml_id, self._name, error_msg)) @@ -2029,7 +2029,7 @@ class BaseModel(object): if all(node.get(attr) == spec.get(attr) \ for attr in spec.attrib if attr not in ('position','version')): - # Version spec should match parent's root element's version + # Version spec should match parent's root element's version if spec.get('version') and spec.get('version') != source.get('version'): return None return node @@ -2103,7 +2103,7 @@ class BaseModel(object): raise_view_error("Mismatching view API version for element '%s': %r vs %r in parent view '%%(parent_xml_id)s'" % \ (tag, spec.get('version'), source.get('version')), inherit_id) raise_view_error("Element '%s' not found in parent view '%%(parent_xml_id)s'" % tag, inherit_id) - + return source def apply_view_inheritance(cr, user, source, inherit_id): @@ -2318,7 +2318,7 @@ class BaseModel(object): or ``'='``. :param int limit: optional max number of records to return :rtype: list - :return: list of pairs ``(id,text_repr)`` for all matching records. + :return: list of pairs ``(id,text_repr)`` for all matching records. """ return self._name_search(cr, user, name, args, operator, context, limit) @@ -2448,7 +2448,7 @@ class BaseModel(object): # This is useful to implement kanban views for instance, where all columns # should be displayed even if they don't contain any record. - # Grab the list of all groups that should be displayed, including all present groups + # Grab the list of all groups that should be displayed, including all present groups present_group_ids = [x[groupby][0] for x in read_group_result if x[groupby]] all_groups,folded = self._group_by_full[groupby](self, cr, uid, present_group_ids, domain, read_group_order=read_group_order, @@ -2824,28 +2824,28 @@ class BaseModel(object): def _m2o_fix_foreign_key(self, cr, source_table, source_field, dest_model, ondelete): # Find FK constraint(s) currently established for the m2o field, - # and see whether they are stale or not + # and see whether they are stale or not cr.execute("""SELECT confdeltype as ondelete_rule, conname as constraint_name, cl2.relname as foreign_table FROM pg_constraint as con, pg_class as cl1, pg_class as cl2, pg_attribute as att1, pg_attribute as att2 - WHERE con.conrelid = cl1.oid - AND cl1.relname = %s - AND con.confrelid = cl2.oid - AND array_lower(con.conkey, 1) = 1 - AND con.conkey[1] = att1.attnum - AND att1.attrelid = cl1.oid - AND att1.attname = %s - AND array_lower(con.confkey, 1) = 1 - AND con.confkey[1] = att2.attnum - AND att2.attrelid = cl2.oid - AND att2.attname = %s + WHERE con.conrelid = cl1.oid + AND cl1.relname = %s + AND con.confrelid = cl2.oid + AND array_lower(con.conkey, 1) = 1 + AND con.conkey[1] = att1.attnum + AND att1.attrelid = cl1.oid + AND att1.attname = %s + AND array_lower(con.confkey, 1) = 1 + AND con.confkey[1] = att2.attnum + AND att2.attrelid = cl2.oid + AND att2.attname = %s AND con.contype = 'f'""", (source_table, source_field, 'id')) constraints = cr.dictfetchall() if constraints: if len(constraints) == 1: # Is it the right constraint? - cons, = constraints + cons, = constraints if cons['ondelete_rule'] != POSTGRES_CONFDELTYPES.get((ondelete or 'set null').upper(), 'a')\ or cons['foreign_table'] != dest_model._table: _schema.debug("Table '%s': dropping obsolete FK constraint: '%s'", @@ -3218,7 +3218,7 @@ class BaseModel(object): def _o2m_raise_on_missing_reference(self, cr, f): # TODO this check should be a method on fields.one2many. - + other = self.pool.get(f._obj) if other: # TODO the condition could use fields_get_keys(). @@ -3735,7 +3735,7 @@ class BaseModel(object): the length of `ids`, and raise an appropriate exception if it does not. """ if cr.rowcount != len(ids): - # Attempt to distinguish record rule restriction vs deleted records, + # Attempt to distinguish record rule restriction vs deleted records, # to provide a more specific error message cr.execute('SELECT id FROM ' + self._table + ' WHERE id IN %s', (tuple(ids),)) if cr.rowcount != len(ids): @@ -3790,13 +3790,13 @@ class BaseModel(object): self._check_record_rules_result_count(cr, uid, sub_ids, operation, context=context) def _workflow_trigger(self, cr, uid, ids, trigger, context=None): - """Call given workflow trigger as a result of a CRUD operation""" + """Call given workflow trigger as a result of a CRUD operation""" wf_service = netsvc.LocalService("workflow") for res_id in ids: getattr(wf_service, trigger)(uid, self._name, res_id, cr) def _workflow_signal(self, cr, uid, ids, signal, context=None): - """Send given workflow signal""" + """Send given workflow signal""" wf_service = netsvc.LocalService("workflow") for res_id in ids: wf_service.trg_validate(uid, self._name, res_id, signal, cr) @@ -3827,7 +3827,7 @@ class BaseModel(object): self.check_access_rights(cr, uid, 'unlink') ir_property = self.pool.get('ir.property') - + # Check if the records are used as default properties. domain = [('res_id', '=', False), ('value_reference', 'in', ['%s,%s' % (self._name, i) for i in ids]), @@ -4955,7 +4955,7 @@ class BaseModel(object): :return: map of ids to their fully qualified XML ID, defaulting to an empty string when there's none - (to be usable as a function field), + (to be usable as a function field), e.g.:: { 'id': 'module.ext_id', From b7e5519b4c0b4c212ef1268a09dacdfb26b704d8 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Mon, 1 Oct 2012 17:37:53 +0200 Subject: [PATCH 03/15] [FIX] orm: handle fields.char() with not size correctly bzr revid: chs@openerp.com-20121001153753-um68vpz1caojm17r --- openerp/osv/orm.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 8b78621db03..ec995c6a8cc 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -2967,14 +2967,14 @@ class BaseModel(object): ('numeric', 'float', get_pg_type(f)[1], '::'+get_pg_type(f)[1]), ('float8', 'float', get_pg_type(f)[1], '::'+get_pg_type(f)[1]), ] - if f_pg_type == 'varchar' and f._type == 'char' and f_pg_size < f.size: + if f_pg_type == 'varchar' and f._type == 'char' and ((f.size is None and f_pg_size) or f_pg_size < f.size): cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO temp_change_size' % (self._table, k)) cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, pg_varchar(f.size))) cr.execute('UPDATE "%s" SET "%s"=temp_change_size::%s' % (self._table, k, pg_varchar(f.size))) cr.execute('ALTER TABLE "%s" DROP COLUMN temp_change_size CASCADE' % (self._table,)) cr.commit() _schema.debug("Table '%s': column '%s' (type varchar) changed size from %s to %s", - self._table, k, f_pg_size, f.size) + self._table, k, f_pg_size or 'unlimited', f.size or 'unlimited') for c in casts: if (f_pg_type==c[0]) and (f._type==c[1]): if f_pg_type != f_obj_type: @@ -3207,8 +3207,7 @@ class BaseModel(object): # attlen is the number of bytes necessary to represent the type when # the type has a fixed size. If the type has a varying size attlen is # -1 and atttypmod is the size limit + 4, or -1 if there is no limit. - # Thus the query can return a negative size for a unlimited varchar. - cr.execute("SELECT c.relname,a.attname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,t.typname,CASE WHEN a.attlen=-1 THEN a.atttypmod-4 ELSE a.attlen END as size " \ + cr.execute("SELECT c.relname,a.attname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,t.typname,CASE WHEN a.attlen=-1 THEN (CASE WHEN a.atttypmod=-1 THEN 0 ELSE a.atttypmod-4 END) ELSE a.attlen END as size " \ "FROM pg_class c,pg_attribute a,pg_type t " \ "WHERE c.relname=%s " \ "AND c.oid=a.attrelid " \ From 6427f6b4d46c03725ac064f4be000fde3f83788e Mon Sep 17 00:00:00 2001 From: Tejas Tank Date: Wed, 3 Oct 2012 18:26:29 +0530 Subject: [PATCH 04/15] [FIX] Data exporting wizard crashes & Export wizard buttons. bzr revid: tta@openerp.com-20121003125629-x6jxmerj5tqc65xp --- addons/web/static/src/js/chrome.js | 2 +- addons/web/static/src/js/data_export.js | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index bed4a159535..01bfb63a580 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -146,8 +146,8 @@ instance.web.Dialog = instance.web.Widget.extend({ this.$buttons = $('
'); this.$el.dialog("widget").append(this.$buttons); } - var res = this.start(); this.dialog_inited = true; + var res = this.start(); return res; }, close: function() { diff --git a/addons/web/static/src/js/data_export.js b/addons/web/static/src/js/data_export.js index 29aea6362f7..1353f59d4a5 100644 --- a/addons/web/static/src/js/data_export.js +++ b/addons/web/static/src/js/data_export.js @@ -5,7 +5,15 @@ instance.web.DataExport = instance.web.Dialog.extend({ template: 'ExportTreeView', dialog_title: {toString: function () { return _t("Export Data"); }}, init: function(parent, dataset) { - this._super(parent); + var self = this; + options = { + buttons : [ + {text: _t("Close"), click: function() { self.close(); }}, + {text: _t("Export To File"), click: function() { self.on_click_export_data(); }} + ], + close: function(event, ui){ self.close();} + } + this._super(parent, options); this.records = {}; this.dataset = dataset; this.exports = new instance.web.DataSetSearch( @@ -14,13 +22,7 @@ instance.web.DataExport = instance.web.Dialog.extend({ start: function() { var self = this; this._super.apply(this, arguments); - this.open({ - buttons : [ - {text: _t("Close"), click: function() { self.close(); }}, - {text: _t("Export To File"), click: function() { self.on_click_export_data(); }} - ], - close: function(event, ui){ self.close();} - }); + this.open(); self.$el.removeClass('ui-dialog-content ui-widget-content'); self.$el.find('#add_field').click(function() { if ($('#field-tree-structure tr.ui-selected')) { From c69757aae17c996f48f900e1fe127f9534725f6e Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Thu, 4 Oct 2012 15:28:32 +0200 Subject: [PATCH 05/15] [ADD] Add on_reverse_breadcrumb callback bzr revid: fme@openerp.com-20121004132832-7qllln1awi4fcot1 --- addons/web/static/src/js/views.js | 36 ++++++++++++++++++------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index 7b5fcc70289..99f4d3e35e8 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -99,6 +99,10 @@ instance.web.ActionManager = instance.web.Widget.extend({ this.select_breadcrumb(index, subindex); }, select_breadcrumb: function(index, subindex) { + var next_item = this.breadcrumbs[index + 1]; + if (next_item && next_item.on_reverse_breadcrumb) { + next_item.on_reverse_breadcrumb(this.breadcrumbs[index].widget); + } for (var i = this.breadcrumbs.length - 1; i >= 0; i--) { if (i > index) { if (this.remove_breadcrumb(i) === false) { @@ -220,14 +224,14 @@ instance.web.ActionManager = instance.web.Widget.extend({ } }); }, - do_action: function(action, on_close, clear_breadcrumbs) { + do_action: function(action, on_close, clear_breadcrumbs, on_reverse_breadcrumb) { if (_.isString(action) && instance.web.client_actions.contains(action)) { var action_client = { type: "ir.actions.client", tag: action }; - return this.do_action(action_client, on_close, clear_breadcrumbs); + return this.do_action(action_client, on_close, clear_breadcrumbs, on_reverse_breadcrumb); } else if (_.isNumber(action) || _.isString(action)) { var self = this; return self.rpc("/web/action/load", { action_id: action }).pipe(function(result) { - return self.do_action(result.result, on_close, clear_breadcrumbs); + return self.do_action(result.result, on_close, clear_breadcrumbs, on_reverse_breadcrumb); }); } if (!action.type) { @@ -249,7 +253,7 @@ instance.web.ActionManager = instance.web.Widget.extend({ console.error("Action manager can't handle action of type " + action.type, action); return $.Deferred().reject(); } - return this[type](action, on_close, clear_breadcrumbs); + return this[type](action, on_close, clear_breadcrumbs, on_reverse_breadcrumb); }, null_action: function() { this.dialog_stop(); @@ -300,24 +304,24 @@ instance.web.ActionManager = instance.web.Widget.extend({ return this.inner_widget.appendTo(this.$el); } }, - ir_actions_act_window: function (action, on_close, clear_breadcrumbs) { + ir_actions_act_window: function (action, on_close, clear_breadcrumbs, on_reverse_breadcrumb) { var self = this; return this.ir_actions_common({ widget: function () { return new instance.web.ViewManagerAction(self, action); }, action: action, klass: 'oe_act_window', - post_process: function (widget) { widget.add_breadcrumb(); } - }, on_close, clear_breadcrumbs); + post_process: function (widget) { widget.add_breadcrumb(on_reverse_breadcrumb); } + }, on_close, clear_breadcrumbs, on_reverse_breadcrumb); }, - ir_actions_client: function (action, on_close, clear_breadcrumbs) { + ir_actions_client: function (action, on_close, clear_breadcrumbs, on_reverse_breadcrumb) { var self = this; var ClientWidget = instance.web.client_actions.get_object(action.tag); if (!(ClientWidget.prototype instanceof instance.web.Widget)) { var next; if (next = ClientWidget(this, action.params)) { - return this.do_action(next, on_close, clear_breadcrumbs); + return this.do_action(next, on_close, clear_breadcrumbs, on_reverse_breadcrumb); } return $.when(); } @@ -329,13 +333,14 @@ instance.web.ActionManager = instance.web.Widget.extend({ post_process: function(widget) { self.push_breadcrumb({ widget: widget, - title: action.name + title: action.name, + on_reverse_breadcrumb: on_reverse_breadcrumb, }); if (action.tag !== 'reload') { self.do_push_state({}); } } - }, on_close, clear_breadcrumbs); + }, on_close, clear_breadcrumbs, on_reverse_breadcrumb); }, ir_actions_act_window_close: function (action, on_closed) { if (!this.dialog && on_closed) { @@ -343,13 +348,13 @@ instance.web.ActionManager = instance.web.Widget.extend({ } this.dialog_stop(); }, - ir_actions_server: function (action, on_closed, clear_breadcrumbs) { + ir_actions_server: function (action, on_closed, clear_breadcrumbs, on_reverse_breadcrumb) { var self = this; this.rpc('/web/action/run', { action_id: action.id, context: action.context || {} }).then(function (action) { - self.do_action(action, on_closed, clear_breadcrumbs) + self.do_action(action, on_closed, clear_breadcrumbs, on_reverse_breadcrumb) }); }, ir_actions_report_xml: function(action, on_closed) { @@ -544,7 +549,7 @@ instance.web.ViewManager = instance.web.Widget.extend({ set_title: function(title) { this.$el.find('.oe_view_title_text:first').text(title); }, - add_breadcrumb: function() { + add_breadcrumb: function(on_reverse_breadcrumb) { var self = this; var views = [this.active_view || this.views_src[0].view_type]; this.on_mode_switch.add(function(mode) { @@ -589,7 +594,8 @@ instance.web.ViewManager = instance.web.Widget.extend({ titles.pop(); } return titles; - } + }, + on_reverse_breadcrumb: on_reverse_breadcrumb, }); }, /** From 1e843a1e83e05172105da26075d1c7cbb2af2218 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 4 Oct 2012 15:38:15 +0200 Subject: [PATCH 06/15] [FIX] arguments forwarding in Widget#do_action bzr revid: xmo@openerp.com-20121004133815-vvqn18kntl8z2m3y --- addons/web/static/src/js/corelib.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/addons/web/static/src/js/corelib.js b/addons/web/static/src/js/corelib.js index 62ac48f0c61..da7e8b584ef 100644 --- a/addons/web/static/src/js/corelib.js +++ b/addons/web/static/src/js/corelib.js @@ -820,9 +820,10 @@ instance.web.Widget = instance.web.Class.extend(instance.web.WidgetMixin, { * the action manager can be found amongst the ancestors of the current widget. * If that's not the case this method will simply return `false`. */ - do_action: function(action, on_finished) { - if (this.getParent()) { - return this.getParent().do_action(action, on_finished); + do_action: function() { + var parent = this.getParent(); + if (parent) { + return parent.do_action.apply(parent, arguments); } return false; }, From ba4e817379f592bd376795fa4f848be45b335d3d Mon Sep 17 00:00:00 2001 From: "vta vta@openerp.com" <> Date: Thu, 4 Oct 2012 15:40:56 +0200 Subject: [PATCH 07/15] [FIX] Fixed return type in action.load controller. bzr revid: vta@openerp.com-20121004134056-je53a1m4tpxecho0 --- addons/web/controllers/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index d1feb19e3d1..d93aef6526c 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1622,7 +1622,7 @@ class Action(openerpweb.Controller): action = req.session.model(action_type).read([action_id], False, ctx) if action: value = clean_action(req, action[0], do_not_eval) - return {'result': value} + return value @openerpweb.jsonrequest def run(self, req, action_id): From 37dd0e03195867555e00f0ff069a1a1ee52c46b2 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Thu, 4 Oct 2012 15:52:11 +0200 Subject: [PATCH 08/15] [REV] Revert recent commit on do_action return type, broke trunk bzr revid: odo@openerp.com-20121004135211-txo513kgfbyrjjtz --- addons/web/controllers/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index d93aef6526c..d1feb19e3d1 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1622,7 +1622,7 @@ class Action(openerpweb.Controller): action = req.session.model(action_type).read([action_id], False, ctx) if action: value = clean_action(req, action[0], do_not_eval) - return value + return {'result': value} @openerpweb.jsonrequest def run(self, req, action_id): From 83a16a237479e45177f9a2d0b638fb52b1b6a128 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Thu, 4 Oct 2012 16:13:51 +0200 Subject: [PATCH 09/15] [IMP] minor modifs in form view for future refactoring bzr revid: nicolas.vanhoren@openerp.com-20121004141351-4m0bjan8dax2tfcz --- addons/web/static/src/js/view_form.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 1c32d5ef63e..68e79d60cea 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -1694,8 +1694,9 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi */ init: function(field_manager, node) { this._super(field_manager); - this.view = field_manager; this.field_manager = field_manager; + if (this.field_manager instanceof instance.web.FormView) + this.view = this.field_manager; this.node = node; this.modifiers = JSON.parse(this.node.attrs.modifiers || '{}'); instance.web.form.InvisibilityChangerMixin.init.call(this, this.field_manager, this.modifiers.invisible); @@ -2015,11 +2016,11 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w renderElement: function() { var self = this; this._super(); - if (this.field.translate) { + if (this.field.translate && this.view) { this.$el.addClass('oe_form_field_translatable'); this.$el.find('.oe_field_translate').click(this.on_translate); } - this.$label = this.view.$el.find('label[for=' + this.id_for_label + ']'); + this.$label = this.view ? this.view.$el.find('label[for=' + this.id_for_label + ']') : $(); if (instance.session.debug) { this.do_attach_tooltip(this, this.$label[0] || this.$el); this.$label.off('dblclick').on('dblclick', function() { @@ -3879,7 +3880,7 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(in }, render_value: function() { var self = this; - var dataset = new instance.web.DataSetStatic(this, this.field.relation, self.view.dataset.get_context()); + var dataset = new instance.web.DataSetStatic(this, this.field.relation, self.build_context()); var values = self.get("value") var handle_names = function(data) { var indexed = {}; From 5a108df9b6d3a1cd7a2b2c4f82767c43f37a68c0 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Thu, 4 Oct 2012 16:33:37 +0200 Subject: [PATCH 10/15] Removed usage of view to compute domain bzr revid: nicolas.vanhoren@openerp.com-20121004143337-hoon3hzh6y1qlfub --- addons/web/static/src/js/view_form.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 68e79d60cea..43aac69c3bd 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -44,6 +44,13 @@ instance.web.form.FieldManagerMixin = { @return (Deferred) Is resolved after all the values are setted. */ set_values: function(values) {}, + /** + Computes an OpenERP domain. + + @param (list) expression An OpenERP domain. + @return (boolean) The computed value of the domain. + */ + compute_domain: function(expression) {}, }; instance.web.views.add('form', 'instance.web.FormView'); @@ -1109,6 +1116,9 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM get_field_value: function(field_name) { return this.fields[field_name].get_value(); }, + compute_domain: function(expression) { + return instance.web.form.compute_domain(expression, this.fields); + }, }); /** @@ -1641,9 +1651,7 @@ instance.web.form.InvisibilityChangerMixin = { this._ic_invisible_modifier = invisible_domain; this._ic_field_manager.on("view_content_has_changed", this, function() { var result = self._ic_invisible_modifier === undefined ? false : - instance.web.form.compute_domain( - self._ic_invisible_modifier, - self._ic_field_manager.fields); + self._ic_field_manager.compute_domain(self._ic_invisible_modifier); self.set({"invisible": result}); }); this.set({invisible: this._ic_invisible_modifier === true, force_invisible: false}); @@ -1738,12 +1746,11 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi }); }, process_modifiers: function() { - var compute_domain = instance.web.form.compute_domain; var to_set = {}; for (var a in this.modifiers) { if (!this.modifiers.hasOwnProperty(a)) { continue; } if (!_.include(["invisible"], a)) { - var val = compute_domain(this.modifiers[a], this.view.fields); + var val = this.field_manager.compute_domain(this.modifiers[a]); to_set[a] = val; } } From ac827d7325689de88188ee72b0b64b915796660b Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Thu, 4 Oct 2012 16:52:06 +0200 Subject: [PATCH 11/15] removed dependency to view to build context and domains bzr revid: nicolas.vanhoren@openerp.com-20121004145206-1j9lsidw7g8upssn --- addons/web/static/src/js/view_form.js | 50 ++++++++++++++++----------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 43aac69c3bd..d2d16550805 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -51,6 +51,14 @@ instance.web.form.FieldManagerMixin = { @return (boolean) The computed value of the domain. */ compute_domain: function(expression) {}, + /** + Builds an evaluation context for the resolution of the fields' contexts. Please note + the field are only supposed to use this context to evualuate their own, they should not + extend it. + + @return (CompoundContext) An OpenERP context. + */ + build_eval_context: function() {}, }; instance.web.views.add('form', 'instance.web.FormView'); @@ -1119,6 +1127,25 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM compute_domain: function(expression) { return instance.web.form.compute_domain(expression, this.fields); }, + _build_view_fields_values: function(blacklist) { + var a_dataset = this.dataset; + var fields_values = this.get_fields_values(blacklist); + var active_id = a_dataset.ids[a_dataset.index]; + _.extend(fields_values, { + active_id: active_id || false, + active_ids: active_id ? [active_id] : [], + active_model: a_dataset.model, + parent: {} + }); + if (a_dataset.parent_view) { + fields_values.parent = a_dataset.parent_view.get_fields_values([a_dataset.child_name]); + } + return fields_values; + }, + build_eval_context: function(blacklist) { + var a_dataset = this.dataset; + return new instance.web.CompoundContext(a_dataset.get_context(), this._build_view_fields_values(blacklist)); + }, }); /** @@ -1779,25 +1806,6 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi }, options || {}); $(trigger).tipsy(options); }, - _build_view_fields_values: function(blacklist) { - var a_dataset = this.view.dataset; - var fields_values = this.view.get_fields_values(blacklist); - var active_id = a_dataset.ids[a_dataset.index]; - _.extend(fields_values, { - active_id: active_id || false, - active_ids: active_id ? [active_id] : [], - active_model: a_dataset.model, - parent: {} - }); - if (a_dataset.parent_view) { - fields_values.parent = a_dataset.parent_view.get_fields_values([a_dataset.child_name]); - } - return fields_values; - }, - _build_eval_context: function(blacklist) { - var a_dataset = this.view.dataset; - return new instance.web.CompoundContext(a_dataset.get_context(), this._build_view_fields_values(blacklist)); - }, /** * Builds a new context usable for operations related to fields by merging * the fields'context with the action's context. @@ -1810,7 +1818,7 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi } if (v_context.__ref || true) { //TODO: remove true - var fields_values = this._build_eval_context(blacklist); + var fields_values = this.field_manager.build_eval_context(blacklist); v_context = new instance.web.CompoundContext(v_context).set_eval_context(fields_values); } return v_context; @@ -1821,7 +1829,7 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi // if there is a domain on the node, overrides the model's domain var final_domain = n_domain !== null ? n_domain : f_domain; if (!(final_domain instanceof Array) || true) { //TODO: remove true - var fields_values = this._build_eval_context(); + var fields_values = this.field_manager.build_eval_context(); final_domain = new instance.web.CompoundDomain(final_domain).set_eval_context(fields_values); } return final_domain; From aa66a76e773fa8d27a177ffdb469d3d9379c572d Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Thu, 4 Oct 2012 17:28:37 +0200 Subject: [PATCH 12/15] Changed a lot of 'view' usages to fit when the view is not disponible bzr revid: nicolas.vanhoren@openerp.com-20121004152837-2qiul7svgdbflyvo --- addons/web/static/src/js/view_form.js | 34 ++++++++++++++------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index d2d16550805..58046fb9ff6 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -2900,7 +2900,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc self.focus(); return; } - var pop = new instance.web.form.FormOpenPopup(self.view); + var pop = new instance.web.form.FormOpenPopup(self); pop.show_element( self.field.relation, self.get("value"), @@ -3495,7 +3495,7 @@ instance.web.form.One2ManyViewManager = instance.web.ViewManager.extend({ } var self = this; var id = self.o2m.dataset.index !== null ? self.o2m.dataset.ids[self.o2m.dataset.index] : null; - var pop = new instance.web.form.FormOpenPopup(self.o2m.view); + var pop = new instance.web.form.FormOpenPopup(this); pop.show_element(self.o2m.field.relation, id, self.o2m.build_context(), { title: _t("Open: ") + self.o2m.string, create_function: function(data) { @@ -3611,7 +3611,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({ }, do_activate_record: function(index, id) { var self = this; - var pop = new instance.web.form.FormOpenPopup(self.o2m.view); + var pop = new instance.web.form.FormOpenPopup(self); pop.show_element(self.o2m.field.relation, id, self.o2m.build_context(), { title: _t("Open: ") + self.o2m.string, write_function: function(id, data) { @@ -3639,7 +3639,10 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({ var parent_form = this.o2m.view; var self = this; this.ensure_saved().pipe(function () { - return parent_form.do_save(); + if (parent_form) + return parent_form.do_save(); + else + return $.when(); }).then(function () { self.handle_button(name, id, callback); }); @@ -4165,7 +4168,7 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend( }); } else { var id = self.dataset.ids[self.dataset.index]; - var pop = new instance.web.form.FormOpenPopup(self.view); + var pop = new instance.web.form.FormOpenPopup(this); pop.show_element(self.field.relation, id, self.build_context(), { title: _t("Open: ") + self.string, write_function: function(id, data, options) { @@ -4627,7 +4630,7 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan get_field: function(name) { if (name === "selection") { return { - selection: this.view.fields_view.fields[this.name].selection, + selection: this.field_manager.get_field(this.name).selection, type: "selection", }; } else if (name === "m2o") { @@ -4725,9 +4728,10 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance. }, set_filename: function(value) { var filename = this.node.attrs.filename; - if (this.view.fields[filename]) { - this.view.fields[filename].set_value(value); - this.view.fields[filename].on_ui_change(); + if (filename) { + var tmp = {}; + tmp[filename] = value; + this.field_manager.set_values(tmp); } }, on_clear: function() { @@ -4769,7 +4773,9 @@ instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({ } else { this.$el.find('a').show(!!this.get('value')); if (this.get('value')) { - var show_value = _t("Download") + " " + (this.view.datarecord[this.node.attrs.filename] || ''); + var show_value = _t("Download") + if (this.view) + show_value += " " + (this.view.datarecord[this.node.attrs.filename] || ''); this.$el.find('a').text(show_value); } } @@ -4781,12 +4787,6 @@ instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({ this.$el.find('input').eq(0).val(show_value); this.set_filename(name); }, - set_filename: function(value_) { - var filename = this.node.attrs.filename; - if (this.view.fields[filename]) { - this.view.fields[filename].set({value: value_}); - } - }, on_clear: function() { this._super.apply(this, arguments); this.$el.find('input').eq(0).val(''); @@ -4835,10 +4835,12 @@ instance.web.form.FieldBinaryImage = instance.web.form.FieldBinary.extend({ this.set({'value': file_base64}); this.binary_value = true; this.render_value(); + this.set_filename(name); }, on_clear: function() { this._super.apply(this, arguments); this.render_value(); + this.set_filename(''); } }); From 18cc6542b63110ec2f63a5c2af14e903c14d5f28 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Thu, 4 Oct 2012 17:34:00 +0200 Subject: [PATCH 13/15] Renammed get_field to get_field_desc bzr revid: nicolas.vanhoren@openerp.com-20121004153400-odaxckppdi1od6mw --- addons/web/static/src/js/view_form.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 58046fb9ff6..f5e701a5dce 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -30,7 +30,7 @@ instance.web.form.FieldManagerMixin = { /** * Must return the asked field as in fields_get. */ - get_field: function(field_name) {}, + get_field_desc: function(field_name) {}, /** * Returns the current value of a field present in the view. See the get_value() method * method in FieldInterface for further information. @@ -1100,7 +1100,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM field.on('focused', null, this.proxy('widgetFocused')) .on('blurred', null, this.proxy('widgetBlurred')); - if (this.get_field(name).translate) { + if (this.get_field_desc(name).translate) { this.translatable_fields.push(field); } field.on('changed_value', this, function() { @@ -1118,7 +1118,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM } }); }, - get_field: function(field_name) { + get_field_desc: function(field_name) { return this.fields_view.fields[field_name]; }, get_field_value: function(field_name) { @@ -2017,7 +2017,7 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w var self = this this._super(field_manager, node); this.name = this.node.attrs.name; - this.field = this.field_manager.get_field(this.name); + this.field = this.field_manager.get_field_desc(this.name); this.widget = this.node.attrs.widget; this.string = this.node.attrs.string || this.field.string || this.name; this.options = JSON.parse(this.node.attrs.options || '{}'); @@ -4630,7 +4630,7 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan get_field: function(name) { if (name === "selection") { return { - selection: this.field_manager.get_field(this.name).selection, + selection: this.field_manager.get_field_desc(this.name).selection, type: "selection", }; } else if (name === "m2o") { From 8bc67000e62ac8757b1edd31d5db16e515fa2a72 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Thu, 4 Oct 2012 18:14:33 +0200 Subject: [PATCH 14/15] added default field manager bzr revid: nicolas.vanhoren@openerp.com-20121004161433-pbtx50nk7hg4ihw0 --- addons/web/static/src/js/view_form.js | 36 +++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index f5e701a5dce..f180311f7a5 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -1565,6 +1565,42 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt }, }); +/** + Welcome. + + If you read this documentation, it probably means that you were asked to use a form view widget outside of + a form view. Before going further, you must understand that those fields were never really created for + that usage. Don't think that this class will hold the answer to all your problems, at best it will allow + you to hack the system with more style. +*/ +instance.web.form.DefaultFieldManager = instance.web.CallbackEnabled.extend({ + init: function(parent, eval_context) { + this._super(parent); + this.field_descs = {}; + this.eval_context = eval_context || {}; + }, + get_field_desc: function(field_name) { + if (this.field_descs[field_name] === undefined) { + this.field_descs[field_name] = { + string: "I have no name", + }; + } + return this.field_descs[field_name]; + }, + get_field_value: function(field_name) { + return false; + }, + set_values: function(values) { + // nothing + }, + compute_domain: function(expression) { + return instance.web.form.compute_domain(expression, {}); + }, + build_eval_context: function() { + return new instance.web.CompoundContext(this.eval_context); + }, +}); + instance.web.form.FormDialog = instance.web.Dialog.extend({ init: function(parent, options, view_id, dataset) { this._super(parent, options); From 78e47437f645c5557a7d5d38fd26ba52e1bee8c2 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Thu, 4 Oct 2012 18:29:28 +0200 Subject: [PATCH 15/15] fixed references bzr revid: nicolas.vanhoren@openerp.com-20121004162928-ge6hggc7zz03r2sc --- addons/web/static/src/js/view_form.js | 41 ++++++++++++++------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index f180311f7a5..4d69e07489a 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -1573,7 +1573,7 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt that usage. Don't think that this class will hold the answer to all your problems, at best it will allow you to hack the system with more style. */ -instance.web.form.DefaultFieldManager = instance.web.CallbackEnabled.extend({ +instance.web.form.DefaultFieldManager = instance.web.Widget.extend({ init: function(parent, eval_context) { this._super(parent); this.field_descs = {}; @@ -1582,11 +1582,17 @@ instance.web.form.DefaultFieldManager = instance.web.CallbackEnabled.extend({ get_field_desc: function(field_name) { if (this.field_descs[field_name] === undefined) { this.field_descs[field_name] = { - string: "I have no name", + string: field_name, }; } return this.field_descs[field_name]; }, + extend_field_desc: function(fields) { + var self = this; + _.each(fields, function(v, k) { + _.extend(self.get_field_desc(k), v); + }); + }, get_field_value: function(field_name) { return false; }, @@ -4606,11 +4612,21 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan }, initialize_content: function() { var self = this; - this.selection = new instance.web.form.FieldSelection(this, { attrs: { + var fm = new instance.web.form.DefaultFieldManager(this); + fm.extend_field_desc({ + "selection": { + selection: this.field_manager.get_field_desc(this.name).selection, + type: "selection", + }, + "m2o": { + relation: null, + type: "many2one", + }, + }); + this.selection = new instance.web.form.FieldSelection(fm, { attrs: { name: 'selection', modifiers: JSON.stringify({readonly: this.get('effective_readonly')}), }}); - this.selection.view = this.view; this.selection.on("change:value", this, this.on_selection_changed); this.selection.setElement(this.$(".oe_form_view_reference_selection")); this.selection.renderElement(); @@ -4619,11 +4635,10 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan .on('focused', null, function () {self.trigger('focused')}) .on('blurred', null, function () {self.trigger('blurred')}); - this.m2o = new instance.web.form.FieldMany2One(this, { attrs: { + this.m2o = new instance.web.form.FieldMany2One(fm, { attrs: { name: 'm2o', modifiers: JSON.stringify({readonly: this.get('effective_readonly')}), }}); - this.m2o.view = this.view; this.m2o.on("change:value", this, this.data_changed); this.m2o.setElement(this.$(".oe_form_view_reference_m2o")); this.m2o.renderElement(); @@ -4663,20 +4678,6 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan this.set({'value': false}); } }, - get_field: function(name) { - if (name === "selection") { - return { - selection: this.field_manager.get_field_desc(this.name).selection, - type: "selection", - }; - } else if (name === "m2o") { - return { - relation: null, - type: "many2one", - }; - } - throw Exception("Should not happen"); - }, }); instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {