diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index d5eaafaecee..56ce25476c5 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -140,7 +140,7 @@ class WebClient(openerpweb.Controller): return content @openerpweb.httprequest - def home(self, req, s_action=None): + def home(self, req, s_action=None, **kw): # script tags jslist = ['/base/webclient/js'] if req.debug: @@ -423,25 +423,25 @@ def eval_context_and_domain(session, context, domain=None): return e_context, e_domain -def load_actions_from_ir_values(req, key, key2, models, meta, context): +def load_actions_from_ir_values(req, key, key2, models, meta): + context = req.session.eval_context(req.context) Values = req.session.model('ir.values') actions = Values.get(key, key2, models, meta, context) - return [(id, name, clean_action(action, req.session, context=context)) + return [(id, name, clean_action(req, action)) for id, name, action in actions] -def clean_action(action, session, context=None): +def clean_action(req, action): + context = req.session.eval_context(req.context) + eval_ctx = req.session.evaluation_context(context) action.setdefault('flags', {}) - if action['type'] != 'ir.actions.act_window': - return action + # values come from the server, we can just eval them if isinstance(action.get('context'), basestring): - localvars = session.evaluation_context(context=context) - action['context'] = eval( action['context'], localvars ) or {} + action['context'] = eval( action['context'], eval_ctx ) or {} if isinstance(action.get('domain'), basestring): - localvars = session.evaluation_context( action.get('context', {})) - action['domain'] = eval( action['domain'], localvars ) or [] + action['domain'] = eval( action['domain'], eval_ctx ) or [] return fix_view_modes(action) @@ -559,8 +559,7 @@ class Menu(openerpweb.Controller): @openerpweb.jsonrequest def action(self, req, menu_id): actions = load_actions_from_ir_values(req,'action', 'tree_but_open', - [('ir.ui.menu', menu_id)], False, - req.session.eval_context(req.context)) + [('ir.ui.menu', menu_id)], False) return {"action": actions} class DataSet(openerpweb.Controller): @@ -572,15 +571,15 @@ class DataSet(openerpweb.Controller): req.session.eval_context(req.context))} @openerpweb.jsonrequest - def search_read(self, request, model, fields=False, offset=0, limit=False, domain=None, sort=None): - return self.do_search_read(request, model, fields, offset, limit, domain, sort) - def do_search_read(self, request, model, fields=False, offset=0, limit=False, domain=None + def search_read(self, req, model, fields=False, offset=0, limit=False, domain=None, sort=None): + return self.do_search_read(req, model, fields, offset, limit, domain, sort) + def do_search_read(self, req, model, fields=False, offset=0, limit=False, domain=None , sort=None): """ Performs a search() followed by a read() (if needed) using the provided search criteria - :param request: a JSON-RPC request object - :type request: openerpweb.JsonRequest + :param req: a JSON-RPC request object + :type req: openerpweb.JsonRequest :param str model: the name of the model to search on :param fields: a list of the fields to return in the result records :type fields: [str] @@ -593,10 +592,10 @@ class DataSet(openerpweb.Controller): matching fields selection set) :rtype: list """ - Model = request.session.model(model) + Model = req.session.model(model) context, domain = eval_context_and_domain( - request.session, request.context, domain) + req.session, req.context, domain) ids = Model.search(domain, 0, False, sort or False, context) # need to fill the dataset with all ids for the (domain, context) pair, @@ -618,22 +617,22 @@ class DataSet(openerpweb.Controller): @openerpweb.jsonrequest - def read(self, request, model, ids, fields=False): - return self.do_search_read(request, model, ids, fields) + def read(self, req, model, ids, fields=False): + return self.do_search_read(req, model, ids, fields) @openerpweb.jsonrequest - def get(self, request, model, ids, fields=False): - return self.do_get(request, model, ids, fields) + def get(self, req, model, ids, fields=False): + return self.do_get(req, model, ids, fields) - def do_get(self, request, model, ids, fields=False): + def do_get(self, req, model, ids, fields=False): """ Fetches and returns the records of the model ``model`` whose ids are in ``ids``. The results are in the same order as the inputs, but elements may be missing (if there is no record left for the id) - :param request: the JSON-RPC2 request object - :type request: openerpweb.JsonRequest + :param req: the JSON-RPC2 request object + :type req: openerpweb.JsonRequest :param model: the model to read from :type model: str :param ids: a list of identifiers @@ -644,8 +643,8 @@ class DataSet(openerpweb.Controller): :returns: a list of records, in the same order as the list of ids :rtype: list """ - Model = request.session.model(model) - records = Model.read(ids, fields, request.session.eval_context(request.context)) + Model = req.session.model(model) + records = Model.read(ids, fields, req.session.eval_context(req.context)) record_map = dict((record['id'], record) for record in records) @@ -673,9 +672,9 @@ class DataSet(openerpweb.Controller): return {'result': r} @openerpweb.jsonrequest - def unlink(self, request, model, ids=()): - Model = request.session.model(model) - return Model.unlink(ids, request.session.eval_context(request.context)) + def unlink(self, req, model, ids=()): + Model = req.session.model(model) + return Model.unlink(ids, req.session.eval_context(req.context)) def call_common(self, req, model, method, args, domain_id=None, context_id=None): domain = args[domain_id] if domain_id and len(args) - 1 >= domain_id else [] @@ -696,7 +695,7 @@ class DataSet(openerpweb.Controller): def call_button(self, req, model, method, args, domain_id=None, context_id=None): action = self.call_common(req, model, method, args, domain_id, context_id) if isinstance(action, dict) and action.get('type') != '': - return {'result': clean_action(action, req.session)} + return {'result': clean_action(req, action)} return {'result': False} @openerpweb.jsonrequest @@ -718,9 +717,9 @@ class DataSet(openerpweb.Controller): class DataGroup(openerpweb.Controller): _cp_path = "/base/group" @openerpweb.jsonrequest - def read(self, request, model, fields, group_by_fields, domain=None, sort=None): - Model = request.session.model(model) - context, domain = eval_context_and_domain(request.session, request.context, domain) + def read(self, req, model, fields, group_by_fields, domain=None, sort=None): + Model = req.session.model(model) + context, domain = eval_context_and_domain(req.session, req.context, domain) return Model.read_group( domain or [], fields, group_by_fields, 0, False, @@ -729,13 +728,13 @@ class DataGroup(openerpweb.Controller): class View(openerpweb.Controller): _cp_path = "/base/view" - def fields_view_get(self, request, model, view_id, view_type, + def fields_view_get(self, req, model, view_id, view_type, transform=True, toolbar=False, submenu=False): - Model = request.session.model(model) - context = request.session.eval_context(request.context) + Model = req.session.model(model) + context = req.session.eval_context(req.context) fvg = Model.fields_view_get(view_id, view_type, context, toolbar, submenu) # todo fme?: check that we should pass the evaluated context here - self.process_view(request.session, fvg, context, transform) + self.process_view(req.session, fvg, context, transform) return fvg def process_view(self, session, fvg, context, transform): @@ -768,20 +767,20 @@ class View(openerpweb.Controller): field["context"] = self.parse_context(field["context"], session) @openerpweb.jsonrequest - def add_custom(self, request, view_id, arch): - CustomView = request.session.model('ir.ui.view.custom') + def add_custom(self, req, view_id, arch): + CustomView = req.session.model('ir.ui.view.custom') CustomView.create({ - 'user_id': request.session._uid, + 'user_id': req.session._uid, 'ref_id': view_id, 'arch': arch - }, request.session.eval_context(request.context)) + }, req.session.eval_context(req.context)) return {'result': True} @openerpweb.jsonrequest - def undo_custom(self, request, view_id, reset=False): - CustomView = request.session.model('ir.ui.view.custom') - context = request.session.eval_context(request.context) - vcustom = CustomView.search([('user_id', '=', request.session._uid), ('ref_id' ,'=', view_id)], + def undo_custom(self, req, view_id, reset=False): + CustomView = req.session.model('ir.ui.view.custom') + context = req.session.eval_context(req.context) + vcustom = CustomView.search([('user_id', '=', req.session._uid), ('ref_id' ,'=', view_id)], 0, False, False, context) if vcustom: if reset: @@ -1040,13 +1039,13 @@ class Action(openerpweb.Controller): action = req.session.model(action_type[0]['type']).read([action_id], False, context) if action: - value = clean_action(action[0], req.session) + value = clean_action(req, action[0]) return {'result': value} @openerpweb.jsonrequest def run(self, req, action_id): - return clean_action(req.session.model('ir.actions.server').run( - [action_id], req.session.eval_context(req.context)), req.session) + return clean_action(req, req.session.model('ir.actions.server').run( + [action_id], req.session.eval_context(req.context))) class TreeView(View): _cp_path = "/base/treeview" @@ -1059,7 +1058,7 @@ class TreeView(View): def action(self, req, model, id): return load_actions_from_ir_values( req,'action', 'tree_but_open',[(model, id)], - False, req.session.eval_context(req.context)) + False) def export_csv(fields, result): fp = StringIO() diff --git a/addons/base/static/src/css/base.css b/addons/base/static/src/css/base.css index 242167480f3..46364c4fa1d 100644 --- a/addons/base/static/src/css/base.css +++ b/addons/base/static/src/css/base.css @@ -870,6 +870,9 @@ label.error { .openerp .oe_forms input[type="text"], .openerp .oe_forms input[type="password"], .openerp .oe_forms select, .openerp .oe_forms .button { height: 22px; } +.openerp .oe_forms input.field_datetime { + min-width: 11em; +} .openerp .oe_forms .button { color: #4c4c4c; white-space: nowrap; @@ -878,13 +881,20 @@ label.error { position: relative; vertical-align: top; } -.openerp img.ui-datepicker-trigger, .openerp img.ui-datepicker-trigger { +.openerp img.ui-datepicker-trigger { margin-left: -20px; vertical-align: middle; cursor: pointer; position: relative; top: -1px; } +.openerp img.oe_field_translate { + margin-left: -21px; + vertical-align: top; + cursor: pointer; + position: relative; + top: 4px; +} /* http://www.quirksmode.org/dom/inputfile.html * http://stackoverflow.com/questions/2855589/replace-input-type-file-by-an-image diff --git a/addons/base/static/src/js/chrome.js b/addons/base/static/src/js/chrome.js index 954e2024965..a4a3b73cc77 100644 --- a/addons/base/static/src/js/chrome.js +++ b/addons/base/static/src/js/chrome.js @@ -590,22 +590,27 @@ openerp.base.Header = openerp.base.Widget.extend({ sc.binding = {}; $(sc.binding).bind({ 'add': function (e, attrs) { - var $shortcut = $('
  • ', { + shortcuts_ds.create(attrs, function (out) { + $('
  • ', { + 'data-shortcut-id':out.result, 'data-id': attrs.res_id }).text(attrs.name) - .appendTo(self.$element.find('.oe-shortcuts ul')); - shortcuts_ds.create(attrs, function (out) { - $shortcut.data('shortcut-id', out.result); + .appendTo(self.$element.find('.oe-shortcuts ul')); + attrs.id = out.result; + sc.push(attrs); }); }, 'remove-current': function () { var menu_id = self.session.active_id; var $shortcut = self.$element - .find('.oe-shortcuts li[data-id=' + menu_id + ']'); + .find('.oe-shortcuts li[data-id=' + menu_id + ']'); var shortcut_id = $shortcut.data('shortcut-id'); $shortcut.remove(); shortcuts_ds.unlink([shortcut_id]); - } + var sc_new = _.reject(sc, function(shortcut){ return shortcut_id === shortcut.id}); + sc.splice(0, sc.length); + sc.push.apply(sc, sc_new); + } }); } return this.rpc('/base/session/sc_list', {}, function(shortcuts) { @@ -615,6 +620,7 @@ openerp.base.Header = openerp.base.Widget.extend({ self.$element.find('.oe-shortcuts') .html(QWeb.render('Shortcuts', {'shortcuts': shortcuts})) .undelegate('li', 'click') + .delegate('li', 'click', function(e) { e.stopPropagation(); var id = $(this).data('id'); diff --git a/addons/base/static/src/js/core.js b/addons/base/static/src/js/core.js index fca993ffef9..57b3a8cb637 100644 --- a/addons/base/static/src/js/core.js +++ b/addons/base/static/src/js/core.js @@ -567,8 +567,8 @@ openerp.base.Session = openerp.base.CallbackEnabled.extend( /** @lends openerp.b self.do_load_js(file_list.concat(files)); }); } else { - self.do_load_css(file_list.concat(["/base/webclient/css?mods="+modules])); - self.do_load_js(["/base/webclient/js?mods="+modules]); + self.do_load_css(["/base/webclient/css?mods="+modules]); + self.do_load_js(file_list.concat(["/base/webclient/js?mods="+modules])); } openerp._modules_loaded = true; }); diff --git a/addons/base/static/src/js/data.js b/addons/base/static/src/js/data.js index 2548fad1e8a..20456d3db45 100644 --- a/addons/base/static/src/js/data.js +++ b/addons/base/static/src/js/data.js @@ -396,18 +396,18 @@ openerp.base.DataSet = openerp.base.Widget.extend( /** @lends openerp.base.Data * * @param {String} method * @param {Array} [args] - * @param {Number} [domain_id] index of a domain to evaluate in the args array - * @param {Number} [context_id] index of a context to evaluate in the args array + * @param {Number} [domain_index] index of a domain to evaluate in the args array + * @param {Number} [context_index] index of a context to evaluate in the args array * @param {Function} callback * @param {Function }error_callback * @returns {$.Deferred} */ - call_and_eval: function (method, args, domain_id, context_id, callback, error_callback) { + call_and_eval: function (method, args, domain_index, context_index, callback, error_callback) { return this.rpc('/base/dataset/call', { model: this.model, method: method, - domain_id: domain_id || null, - context_id: context_id || null, + domain_id: domain_index || null, + context_id: context_index || null, args: args || [] }, callback, error_callback); }, diff --git a/addons/base/static/src/js/form.js b/addons/base/static/src/js/form.js index 5da048a17b9..212f48717bd 100644 --- a/addons/base/static/src/js/form.js +++ b/addons/base/static/src/js/form.js @@ -38,6 +38,7 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi this.registry = openerp.base.form.widgets; this.has_been_loaded = $.Deferred(); this.$form_header = null; + this.translatable_fields = []; _.defaults(this.options, {"always_show_new_button": true}); }, start: function() { @@ -440,6 +441,10 @@ openerp.base.FormView = openerp.base.View.extend( /** @lends openerp.base.FormVi values[key] = val; }); return values; + }, + get_selected_ids: function() { + var id = this.dataset.ids[this.dataset.index]; + return id ? [id] : []; } }); @@ -461,7 +466,7 @@ openerp.base.form.SidebarAttachments = openerp.base.Widget.extend({ ['res_model', '=', this.view.dataset.model], ['res_id', '=', this.view.datarecord.id], ['type', 'in', ['binary', 'url']] - ])).read_slice(['name', 'url', 'type'], this.on_attachments_loaded); + ])).read_slice(['name', 'url', 'type'], {}, this.on_attachments_loaded); } }, on_attachments_loaded: function(attachments) { @@ -839,6 +844,13 @@ openerp.base.form.Field = openerp.base.form.Widget.extend({ this.invalid = false; this.dirty = false; }, + start: function() { + this._super.apply(this, arguments); + if (this.field.translate) { + this.view.translatable_fields.push(this); + this.$element.find('.oe_field_translate').click(this.on_translate); + } + }, set_value: function(value) { this.value = value; this.invalid = false; @@ -850,6 +862,9 @@ openerp.base.form.Field = openerp.base.form.Widget.extend({ }, on_value_changed: function() { }, + on_translate: function() { + this.view.open_translate_dialog(this); + }, get_value: function() { return this.value; }, @@ -938,7 +953,7 @@ openerp.base.form.FieldChar = openerp.base.form.Field.extend({ }, set_value: function(value) { this._super.apply(this, arguments); - var show_value = (value != null && value !== false) ? value : ''; + var show_value = openerp.base.format_value(value, this, ''); this.$element.find('input').val(show_value); }, update_dom: function() { @@ -946,16 +961,16 @@ openerp.base.form.FieldChar = openerp.base.form.Field.extend({ this.$element.find('input').attr('disabled', this.readonly); }, set_value_from_ui: function() { - this.value = this.$element.find('input').val(); + this.value = openerp.base.parse_value(this.$element.find('input').val(), this); this._super(); }, validate: function() { this.invalid = false; - var value = this.$element.find('input').val(); - if (value === "") { - this.invalid = this.required; - } else if (this.validation_regex) { - this.invalid = !this.validation_regex.test(value); + try { + var value = openerp.base.parse_value(this.$element.find('input').val(), this, ''); + this.invalid = this.required && value === ''; + } catch(e) { + this.invalid = true; } }, focus: function() { @@ -967,7 +982,6 @@ openerp.base.form.FieldEmail = openerp.base.form.FieldChar.extend({ init: function(view, node) { this._super(view, node); this.template = "FieldEmail"; - this.validation_regex = /@/; }, start: function() { this._super.apply(this, arguments); @@ -982,8 +996,7 @@ openerp.base.form.FieldEmail = openerp.base.form.FieldChar.extend({ }, set_value: function(value) { this._super.apply(this, arguments); - var show_value = (value != null && value !== false) ? value : ''; - this.$element.find('a').attr('href', 'mailto:' + show_value); + this.$element.find('a').attr('href', 'mailto:' + this.$element.find('input').val()); } }); @@ -1006,44 +1019,13 @@ openerp.base.form.FieldUrl = openerp.base.form.FieldChar.extend({ }); openerp.base.form.FieldFloat = openerp.base.form.FieldChar.extend({ - init: function(view, node) { - this._super(view, node); - this.validation_regex = /^-?\d+(\.\d+)?$/; - }, set_value: function(value) { - this._super.apply(this, [value]); if (value === false || value === undefined) { // As in GTK client, floats default to 0 value = 0; this.dirty = true; } - var show_value = value.toFixed(2); - this.$element.find('input').val(show_value); - }, - set_value_from_ui: function() { - this.value = Number(this.$element.find('input').val().replace(/,/g, '.')); - this._super(); - } -}); - -openerp.base.form.FieldInteger = openerp.base.form.FieldFloat.extend({ - init: function(view, node) { - this._super(view, node); - this.validation_regex = /^-?\d+$/; - }, - set_value: function(value) { this._super.apply(this, [value]); - if (value === false || value === undefined) { - // TODO fme: check if GTK client default integers to 0 (like it does with floats) - value = 0; - this.dirty = true; - } - var show_value = parseInt(value, 10); - this.$element.find('input').val(show_value); - }, - set_value_from_ui: function() { - this.value = Number(this.$element.find('input').val()); - this._super(); } }); @@ -1052,7 +1034,6 @@ openerp.base.form.FieldDatetime = openerp.base.form.Field.extend({ this._super(view, node); this.template = "FieldDate"; this.jqueryui_object = 'datetimepicker'; - this.validation_regex = /^\d+-\d+-\d+( \d+:\d+(:\d+)?)?$/; }, start: function() { this._super.apply(this, arguments); @@ -1092,8 +1073,6 @@ openerp.base.form.FieldDatetime = openerp.base.form.Field.extend({ var value = this.$element.find('input').val(); if (value === "") { this.invalid = this.required; - } else if (this.validation_regex) { - this.invalid = !this.validation_regex.test(value); } else { this.invalid = !this.$element.find('input')[this.jqueryui_object]('getDate'); } @@ -1109,39 +1088,15 @@ openerp.base.form.FieldDate = openerp.base.form.FieldDatetime.extend({ init: function(view, node) { this._super(view, node); this.jqueryui_object = 'datepicker'; - this.validation_regex = /^\d+-\d+-\d+$/; }, parse: openerp.base.str_to_date, format: openerp.base.date_to_str }); -openerp.base.form.FieldFloatTime = openerp.base.form.FieldChar.extend({ - init: function(view, node) { - this._super(view, node); - this.validation_regex = /^\d+:\d+$/; - }, - set_value: function(value) { - this._super.apply(this, [value]); - if (value === false || value === undefined) { - // As in GTK client, floats default to 0 - value = 0; - this.dirty = true; - } - var show_value = _.sprintf("%02d:%02d", Math.floor(value), Math.round((value % 1) * 60)); - this.$element.find('input').val(show_value); - }, - set_value_from_ui: function() { - var time = this.$element.find('input').val().split(':'); - this.set_value(parseInt(time[0], 10) + parseInt(time[1], 10) / 60); - this._super(); - } -}); - openerp.base.form.FieldText = openerp.base.form.Field.extend({ init: function(view, node) { this._super(view, node); this.template = "FieldText"; - this.validation_regex = null; }, start: function() { this._super.apply(this, arguments); @@ -1149,7 +1104,7 @@ openerp.base.form.FieldText = openerp.base.form.Field.extend({ }, set_value: function(value) { this._super.apply(this, arguments); - var show_value = (value != null && value !== false) ? value : ''; + var show_value = openerp.base.format_value(value, this, ''); this.$element.find('textarea').val(show_value); }, update_dom: function() { @@ -1157,16 +1112,16 @@ openerp.base.form.FieldText = openerp.base.form.Field.extend({ this.$element.find('textarea').attr('disabled', this.readonly); }, set_value_from_ui: function() { - this.value = this.$element.find('textarea').val(); + this.value = openerp.base.parse_value(this.$element.find('textarea').val(), this); this._super(); }, validate: function() { this.invalid = false; - var value = this.$element.find('textarea').val(); - if (value === "") { - this.invalid = this.required; - } else if (this.validation_regex) { - this.invalid = !this.validation_regex.test(value); + try { + var value = openerp.base.parse_value(this.$element.find('textarea').val(), this, ''); + this.invalid = this.required && value === ''; + } catch(e) { + this.invalid = true; } }, focus: function() { @@ -1355,36 +1310,56 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({ this.$input = this.$element.find("input"); this.$drop_down = this.$element.find(".oe-m2o-drop-down-button"); this.$menu_btn = this.$element.find(".oe-m2o-cm-button"); - + // context menu - var bindings = {}; - bindings[this.cm_id + "_search"] = function() { - self._search_create_popup("search"); - }; - bindings[this.cm_id + "_create"] = function() { - self._search_create_popup("form"); - }; - bindings[this.cm_id + "_open"] = function() { - if (!self.value) { - return; - } - var pop = new openerp.base.form.FormOpenPopup(self.view); - pop.show_element(self.field.relation, self.value[0],self.build_context(), {}); - pop.on_write_completed.add_last(function() { - self.set_value(self.value[0]); + var init_context_menu_def = $.Deferred().then(function(e) { + var rdataset = new openerp.base.DataSetStatic(self, "ir.values", self.build_context()); + rdataset.call("get", ['action', 'client_action_relate', + [[self.field.relation, false]], false, rdataset.get_context()], false, 0) + .then(function(result) { + self.related_entries = result; + + var $cmenu = $("#" + self.cm_id); + $cmenu.append(QWeb.render("FieldMany2One.context_menu", {widget: self})); + var bindings = {}; + bindings[self.cm_id + "_search"] = function() { + self._search_create_popup("search"); + }; + bindings[self.cm_id + "_create"] = function() { + self._search_create_popup("form"); + }; + bindings[self.cm_id + "_open"] = function() { + if (!self.value) { + return; + } + var pop = new openerp.base.form.FormOpenPopup(self.view); + pop.show_element(self.field.relation, self.value[0],self.build_context(), {}); + pop.on_write_completed.add_last(function() { + self.set_value(self.value[0]); + }); + }; + _.each(_.range(self.related_entries.length), function(i) { + bindings[self.cm_id + "_related_" + i] = function() { + self.open_related(self.related_entries[i]); + }; + }); + var cmenu = self.$menu_btn.contextMenu(self.cm_id, {'leftClickToo': true, + bindings: bindings, itemStyle: {"color": ""}, + onContextMenu: function() { + if(self.value) { + $("#" + self.cm_id + " .oe_m2o_menu_item_mandatory").removeClass("oe-m2o-disabled-cm"); + } else { + $("#" + self.cm_id + " .oe_m2o_menu_item_mandatory").addClass("oe-m2o-disabled-cm"); + } + return true; + }, menuStyle: {width: "200px"} + }); + setTimeout(function() {self.$menu_btn.trigger(e);}, 0); }); - }; - var cmenu = this.$menu_btn.contextMenu(this.cm_id, {'leftClickToo': true, - bindings: bindings, itemStyle: {"color": ""}, - onContextMenu: function() { - if(self.value) { - $("#" + self.cm_id + "_open").removeClass("oe-m2o-disabled-cm"); - } else { - $("#" + self.cm_id + "_open").addClass("oe-m2o-disabled-cm"); - } - return true; - } }); + var ctx_callback = function(e) {init_context_menu_def.resolve(e); e.preventDefault()}; + this.$menu_btn.bind('contextmenu', ctx_callback); + this.$menu_btn.click(ctx_callback); // some behavior for input this.$input.keyup(function() { @@ -1578,6 +1553,21 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({ if (this.value === null) { this.invalid = this.required; } + }, + open_related: function(related) { + var self = this; + if (!self.value) + return; + self.rpc("/base/action/load", { + action_id: related[2].id, + context: { + active_id: self.value[0], + active_ids: [self.value[0]], + active_model: self.field.relation + } + }, function(result) { + self.do_action(result.result); + }); } }); @@ -2429,9 +2419,9 @@ openerp.base.form.widgets = new openerp.base.Registry({ 'reference' : 'openerp.base.form.FieldReference', 'boolean' : 'openerp.base.form.FieldBoolean', 'float' : 'openerp.base.form.FieldFloat', - 'integer': 'openerp.base.form.FieldInteger', + 'integer': 'openerp.base.form.FieldFloat', + 'float_time': 'openerp.base.form.FieldFloat', 'progressbar': 'openerp.base.form.FieldProgressBar', - 'float_time': 'openerp.base.form.FieldFloatTime', 'image': 'openerp.base.form.FieldBinaryImage', 'binary': 'openerp.base.form.FieldBinaryFile' }); diff --git a/addons/base/static/src/js/formats.js b/addons/base/static/src/js/formats.js index c141aad908c..ed25b28c16d 100644 --- a/addons/base/static/src/js/formats.js +++ b/addons/base/static/src/js/formats.js @@ -45,8 +45,8 @@ openerp.base.format_value = function (value, descriptor, value_if_empty) { if (typeof(value) == "string") value = openerp.base.str_to_datetime(value); try { - return value.format(_.sprintf("%s %s", openerp.base._t.database.parameters.date_format, - openerp.base._t.database.parameters.time_format)); + return value.toString(_.sprintf("%s %s", Date.CultureInfo.formatPatterns.shortDate, + Date.CultureInfo.formatPatterns.longTime)); } catch (e) { return value.format("%m/%d/%Y %H:%M:%S"); } @@ -55,7 +55,7 @@ openerp.base.format_value = function (value, descriptor, value_if_empty) { if (typeof(value) == "string") value = openerp.base.str_to_date(value); try { - return value.format(openerp.base._t.database.parameters.date_format); + return value.toString(Date.CultureInfo.formatPatterns.shortDate); } catch (e) { return value.format("%m/%d/%Y"); } @@ -63,7 +63,7 @@ openerp.base.format_value = function (value, descriptor, value_if_empty) { if (typeof(value) == "string") value = openerp.base.str_to_time(value); try { - return value.format(openerp.base._t.database.parameters.time_format); + return value.toString(Date.CultureInfo.formatPatterns.longTime); } catch (e) { return value.format("%H:%M:%S"); } @@ -108,17 +108,27 @@ openerp.base.parse_value = function (value, descriptor, value_if_empty) { case 'progressbar': return openerp.base.parse_value(value, {type: "float"}); case 'datetime': - var tmp = Date.parse(value); + var tmp = Date.parseExact(value, _.sprintf("%s %s", Date.CultureInfo.formatPatterns.shortDate, + Date.CultureInfo.formatPatterns.longTime)); + if (tmp !== null) + return tmp; + tmp = Date.parse(value); if (tmp !== null) return tmp; throw value + " is not a valid datetime"; case 'date': - var tmp = Date.parse(value); + var tmp = Date.parseExact(Date.CultureInfo.formatPatterns.shortDate); + if (tmp !== null) + return tmp; + tmp = Date.parse(value); if (tmp !== null) return tmp; throw value + " is not a valid date"; case 'time': - var tmp = Date.parse(value); + var tmp = Date.parseExact(Date.CultureInfo.formatPatterns.longTime); + if (tmp !== null) + return tmp; + tmp = Date.parse(value); if (tmp !== null) return tmp; throw value + " is not a valid time"; diff --git a/addons/base/static/src/js/list-editable.js b/addons/base/static/src/js/list-editable.js index 8810d902346..1cd43fd6ea8 100644 --- a/addons/base/static/src/js/list-editable.js +++ b/addons/base/static/src/js/list-editable.js @@ -92,7 +92,7 @@ openerp.base.list_editable = function (openerp) { if (!this.options.editable) { return this._super(event); } - this.edit_record(); + this.edit_record($(event.currentTarget).data('id')); }, /** * Checks if a record is being edited, and if so cancels it @@ -104,8 +104,8 @@ openerp.base.list_editable = function (openerp) { return cancelled.promise(); } - if (this.edition_index !== null) { - this.reload_record(this.edition_index).then(function () { + if (this.edition_id != null) { + this.reload_record(self.records.get(this.edition_id)).then(function () { cancelled.resolve(); }); } else { @@ -115,13 +115,14 @@ openerp.base.list_editable = function (openerp) { self.edition_form.stop(); self.edition_form.$element.remove(); delete self.edition_form; - delete self.edition_index; + delete self.edition_id; delete self.edition; }); return cancelled.promise(); }, /** - * Adapts this list's view description to be suitable to the inner form view of a row being edited. + * Adapts this list's view description to be suitable to the inner form + * view of a row being edited. * * @returns {Object} fields_view_get's view section suitable for putting into form view of editable rows. */ @@ -140,10 +141,10 @@ openerp.base.list_editable = function (openerp) { render_row_as_form: function (row) { var self = this; this.cancel_pending_edition().then(function () { + var record_id = $(row).data('id'); var $new_row = $('', { id: _.uniqueId('oe-editable-row-'), - 'data-id': $(row).data('id'), - 'data-index': $(row).data('index'), + 'data-id': record_id, 'class': $(row).attr('class') + ' oe_forms', click: function (e) {e.stopPropagation();} }) @@ -176,7 +177,7 @@ openerp.base.list_editable = function (openerp) { self.$current.append($new_row); } self.edition = true; - self.edition_index = self.dataset.index; + self.edition_id = record_id; self.edition_form = _.extend(new openerp.base.FormView( self, $new_row.attr('id'), self.dataset, false), { template: 'ListView.row.form', @@ -201,6 +202,25 @@ openerp.base.list_editable = function (openerp) { }); }); }, + handle_onwrite: function (source_record_id) { + var self = this; + var on_write_callback = self.view.fields_view.arch.attrs.on_write; + if (!on_write_callback) { return; } + this.dataset.call(on_write_callback, [source_record_id], function (ids) { + _(ids).each(function (id) { + var record = self.records.get(id); + if (!record) { + // insert after the source record + var index = self.records.indexOf( + self.records.get(source_record_id)) + 1; + record = new openerp.base.list.Record({id: id}); + self.records.add(record, {at: index}); + self.dataset.ids.splice(index, 0, id); + } + self.reload_record(record); + }); + }); + }, /** * Saves the current row, and triggers the edition of its following * sibling if asked. @@ -210,11 +230,16 @@ openerp.base.list_editable = function (openerp) { save_row: function (edit_next) { var self = this; this.edition_form.do_save(function (result) { - if (result.created && !self.edition_index) { + if (result.created && !self.edition_id) { self.records.add({id: result.result}, {at: self.options.editable === 'top' ? 0 : null}); - self.edition_index = self.dataset.index; + self.edition_id = result.result; } + var edited_record = self.records.get(self.edition_id), + next_record = self.records.at( + self.records.indexOf(edited_record) + 1); + + self.handle_onwrite(self.edition_id); self.cancel_pending_edition().then(function () { $(self).trigger('saved', [self.dataset]); if (!edit_next) { @@ -224,8 +249,16 @@ openerp.base.list_editable = function (openerp) { self.new_record(); return; } - self.dataset.next(); - self.edit_record(); + var next_record_id; + if (next_record) { + next_record_id = next_record.get('id'); + self.dataset.index = _(self.dataset.ids) + .indexOf(next_record_id); + } else { + self.dataset.index = 0; + next_record_id = self.records.at(0).get('id'); + } + self.edit_record(next_record_id); }); }, this.options.editable === 'top'); }, @@ -238,12 +271,12 @@ openerp.base.list_editable = function (openerp) { /** * Edits record currently selected via dataset */ - edit_record: function () { + edit_record: function (record_id) { this.render_row_as_form( - this.$current.children(':eq(' + this.dataset.index + ')')); + this.$current.find('[data-id=' + record_id + ']')); $(this).trigger( 'edit', - [this.records.at(this.dataset.index).get('id'), this.dataset]); + [record_id, this.dataset]); }, new_record: function () { this.dataset.index = null; diff --git a/addons/base/static/src/js/list.js b/addons/base/static/src/js/list.js index 95db36f82de..786c8cb2068 100644 --- a/addons/base/static/src/js/list.js +++ b/addons/base/static/src/js/list.js @@ -120,7 +120,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi */ start: function() { this.$element.addClass('oe-listview'); - return this.reload_view(); + return this.reload_view(null, null, true); }, /** * Called after loading the list view's description, sets up such things @@ -362,7 +362,7 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi * @param {Boolean} [grouped] Should the list be displayed grouped * @param {Object} [context] context to send the server while loading the view */ - reload_view: function (grouped, context) { + reload_view: function (grouped, context, initial) { var self = this; var callback = function (field_view_get) { self.on_loaded(field_view_get, grouped); @@ -382,10 +382,19 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi * re-renders the content of the list view */ reload_content: function () { + var self = this; this.records.reset(); this.$element.find('.oe-listview-content').append( - this.groups.render( - $.proxy(this, 'compute_aggregates'))); + this.groups.render(function () { + if (self.dataset.index == null) { + var has_one = false; + self.records.each(function () { has_one = true; }); + if (has_one) { + self.dataset.index = 0; + } + } + self.compute_aggregates(); + })); }, /** * Event handler for a search, asks for the computation/folding of domains @@ -398,8 +407,8 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi */ do_search: function (domains, contexts, groupbys) { return this.rpc('/base/session/eval_domain_and_context', { - domains: domains, - contexts: contexts, + domains: [this.dataset.get_domain()].concat(domains), + contexts: [this.dataset.get_context()].concat(contexts), group_by_seq: groupbys }, $.proxy(this, 'do_actual_search')); }, @@ -588,8 +597,11 @@ openerp.base.ListView = openerp.base.View.extend( /** @lends openerp.base.ListVi $footer_cells.filter(_.sprintf('[data-field=%s]', column.id)) .html(openerp.base.format_cell(aggregation, column)); }); + }, + get_selected_ids: function() { + var ids = this.groups.get_selection().ids; + return ids; } - // TODO: implement reorder (drag and drop rows) }); openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base.ListView.List# */{ /** @@ -641,12 +653,22 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base. 'reset': $.proxy(this, 'on_records_reset'), 'change': function (event, record) { var $row = self.$current.find('[data-id=' + record.get('id') + ']'); - $row.replaceWith(self.render_record($row.data('index'))); + $row.replaceWith(self.render_record(record)); }, 'add': function (ev, records, record, index) { - $('').attr({ + var $new_row = $('').attr({ 'data-id': record.get('id') - }).insertAfter(self.$current.children(':eq(' + index + ')')); + }); + + if (index === 0) { + $new_row.prependTo(self.$current); + } else { + var previous_record = records.at(index-1), + $previous_sibling = self.$current.find( + '[data-id=' + previous_record.get('id') + ']'); + $new_row.insertAfter($previous_sibling); + } + self.refresh_zebra(index, 1); } }; @@ -672,16 +694,17 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base. var $target = $(e.currentTarget), field = $target.closest('td').data('field'), $row = $target.closest('tr'), - record_id = self.row_id($row), - index = self.row_position($row); + record_id = self.row_id($row); $(self).trigger('action', [field, record_id, function () { - return self.reload_record(index); + return self.reload_record(self.records.get(record_id)); }]); }) .delegate('tr', 'click', function (e) { e.stopPropagation(); - self.dataset.index = self.row_position(e.currentTarget); + self.dataset.index = self.records.indexOf( + self.records.get( + self.row_id(e.currentTarget))); self.row_clicked(e); }); }, @@ -718,15 +741,6 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base. }); return result; }, - /** - * Returns the index of the row in the list of rows. - * - * @param {Object} row the selected row - * @returns {Number} the position of the row in this.rows - */ - row_position: function (row) { - return $(row).data('index'); - }, /** * Returns the identifier of the object displayed in the provided table * row @@ -755,48 +769,43 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base. }); }, /** - * Reloads the record at index ``row_index`` in the list's rows. + * Reloads the provided record by re-reading its content from the server. * - * By default, simply re-renders the record. If the ``fetch`` parameter is - * provided and ``true``, will first fetch the record anew. - * - * @param {Number} record_index index of the record to reload - * @param {Boolean} fetch fetches the record from remote before reloading it + * @param {Record} record + * @returns {$.Deferred} promise to the finalization of the reloading */ - reload_record: function (record_index) { - var r = this.records.at(record_index); - + reload_record: function (record) { return this.dataset.read_ids( - [r.get('id')], + [record.get('id')], _.pluck(_(this.columns).filter(function (r) { return r.tag === 'field'; }), 'name'), - function (record) { - _(record[0]).each(function (value, key) { - r.set(key, value, {silent: true}); + function (records) { + _(records[0]).each(function (value, key) { + record.set(key, value, {silent: true}); }); - r.trigger('change', r); + record.trigger('change', record); } ); }, /** * Renders a list record to HTML * - * @param {Number} record_index index of the record to render in ``this.rows`` + * @param {Record} record index of the record to render in ``this.rows`` * @returns {String} QWeb rendering of the selected record */ - render_record: function (record_index) { + render_record: function (record) { + var index = this.records.indexOf(record); return QWeb.render('ListView.row', { columns: this.columns, options: this.options, - record: this.records.at(record_index), - row_parity: (record_index % 2 === 0) ? 'even' : 'odd', - row_index: record_index, + record: record, + row_parity: (index % 2 === 0) ? 'even' : 'odd', render_cell: openerp.base.format_cell }); }, /** - * Fixes @data-index on all table rows, and fixes the even/odd classes + * Fixes fixes the even/odd classes * * @param {Number} [from_index] index from which to resequence * @param {Number} [offset = 0] selection offset for DOM, in case there are rows to ignore in the table @@ -810,8 +819,7 @@ openerp.base.ListView.List = openerp.base.Class.extend( /** @lends openerp.base. var index = from_index + i; // reset record-index accelerators on rows and even/odd var even = index%2 === 0; - $(e).attr('data-index', index) - .toggleClass('even', even) + $(e).toggleClass('even', even) .toggleClass('odd', !even); }); } @@ -1043,25 +1051,25 @@ openerp.base.ListView.Groups = openerp.base.Class.extend( /** @lends openerp.bas var fields = _.pluck(_.select(this.columns, function(x) {return x.tag == "field"}), 'name'); var options = { offset: page * limit, limit: limit }; dataset.read_slice(fields, options , function (records) { - if (!self.datagroup.openable) { - view.configure_pager(dataset); - } else { - var pages = Math.ceil(dataset.ids.length / limit); - self.$row - .find('.oe-pager-state') - .text(_.sprintf('%d/%d', page + 1, pages)) - .end() - .find('button[data-pager-action=previous]') - .attr('disabled', page === 0) - .end() - .find('button[data-pager-action=next]') - .attr('disabled', page === pages - 1); - } + if (!self.datagroup.openable) { + view.configure_pager(dataset); + } else { + var pages = Math.ceil(dataset.ids.length / limit); + self.$row + .find('.oe-pager-state') + .text(_.sprintf('%d/%d', page + 1, pages)) + .end() + .find('button[data-pager-action=previous]') + .attr('disabled', page === 0) + .end() + .find('button[data-pager-action=next]') + .attr('disabled', page === pages - 1); + } - self.records.add(records, {silent: true}); - list.render(); - d.resolve(list); - }); + self.records.add(records, {silent: true}); + list.render(); + d.resolve(list); + }); return d.promise(); }, setup_resequence_rows: function (list, dataset) { @@ -1075,14 +1083,12 @@ openerp.base.ListView.Groups = openerp.base.Class.extend( /** @lends openerp.bas // ondrop, move relevant record & fix sequences list.$current.sortable({ stop: function (event, ui) { - var from = ui.item.data('index'), - to = ui.item.prev().data('index') || 0; - if (from === to) { return; } - var to_move = list.records.at(from); - list.records.remove(to_move); - list.records.add(to_move, {at: to}); + var to_move = list.records.get(ui.item.data('id')), + target_id = ui.item.prev().data('id'); - list.refresh_zebra(); + list.records.remove(to_move); + var to = target_id ? list.records.indexOf(list.records.get(target_id)) : 0; + list.records.add(to_move, { at: to }); // resequencing time! var record, index = to, @@ -1096,6 +1102,8 @@ openerp.base.ListView.Groups = openerp.base.Class.extend( /** @lends openerp.bas dataset.write(record.get('id'), {sequence: seq}); record.set('sequence', seq); } + + list.refresh_zebra(); } }); }, @@ -1440,6 +1448,11 @@ var Collection = openerp.base.Class.extend(/** @lends Collection# */{ }); return results; }, + pluck: function (fieldname) { + return this.map(function (record) { + return record.get(fieldname); + }); + }, indexOf: function (record) { return _(this.records).indexOf(record); } diff --git a/addons/base/static/src/js/search.js b/addons/base/static/src/js/search.js index 63ccec088a6..e3d51e2b439 100644 --- a/addons/base/static/src/js/search.js +++ b/addons/base/static/src/js/search.js @@ -590,6 +590,20 @@ openerp.base.search.Field = openerp.base.search.Input.extend( /** @lends openerp {}, context, {own_values: {self: val}}); }, + /** + * Function creating the returned domain for the field, override this + * methods in children if you only need to customize the field's domain + * without more complex alterations or tests (and without the need to + * change override the handling of filter_domain) + * + * @param {String} name the field's name + * @param {String} operator the field's operator (either attribute-specified or default operator for the field + * @param {Number|String} value parsed value for the field + * @returns {Array} domain to include in the resulting search + */ + make_domain: function (name, operator, value) { + return [[name, operator, value]]; + }, get_domain: function () { var val = this.get_value(); if (val === null || val === '') { @@ -598,11 +612,10 @@ openerp.base.search.Field = openerp.base.search.Input.extend( /** @lends openerp var domain = this.attrs['filter_domain']; if (!domain) { - return [[ + return this.make_domain( this.attrs.name, this.attrs.operator || this.default_operator, - this.get_value() - ]]; + val); } return _.extend({}, domain, {own_values: {self: val}}); } @@ -723,7 +736,22 @@ openerp.base.search.DateField = openerp.base.search.Field.extend( /** @lends ope return this.$element.val(); } }); -openerp.base.search.DateTimeField = openerp.base.search.DateField.extend({ +/** + * Implementation of the ``datetime`` openerp field type: + * + * * Uses the same widget as the ``date`` field type (a simple date) + * + * * Builds a slighly more complex, it's a datetime range (includes time) + * spanning the whole day selected by the date widget + * + * @class + * @extends openerp.base.DateField + */ +openerp.base.search.DateTimeField = openerp.base.search.DateField.extend(/** @lends openerp.base.search.DateTimeField# */{ + make_domain: function (name, operator, value) { + return ['&', [name, '>=', value + ' 00:00:00'], + [name, '<=', value + ' 23:59:59']]; + } }); openerp.base.search.ManyToOneField = openerp.base.search.CharField.extend({ init: function (view_section, field, view) { @@ -773,6 +801,8 @@ openerp.base.search.ManyToOneField = openerp.base.search.CharField.extend({ render: function (defaults) { if (defaults[this.attrs.name]) { this.id = defaults[this.attrs.name]; + if (this.id instanceof Array) + this.id = this.id[0]; // TODO: maybe this should not be completely removed delete defaults[this.attrs.name]; this.dataset.name_get([this.id], $.proxy(this, 'on_name_get')); @@ -781,16 +811,16 @@ openerp.base.search.ManyToOneField = openerp.base.search.CharField.extend({ } return this._super(defaults); }, - get_domain: function () { + make_domain: function (name, operator, value) { if (this.id && this.name) { - if (this.$element.val() === this.name) { - return [[this.attrs.name, '=', this.id]]; + if (value === this.name) { + return [[name, '=', this.id]]; } else { delete this.id; delete this.name; } } - return this._super(); + return this._super(name, operator, value); } }); diff --git a/addons/base/static/src/js/views.js b/addons/base/static/src/js/views.js index cb9b7d364cf..689a72ef7f1 100644 --- a/addons/base/static/src/js/views.js +++ b/addons/base/static/src/js/views.js @@ -4,6 +4,8 @@ openerp.base.views = function(openerp) { +var _t = openerp.base._t; + /** * Registry for all the client actions key: tag value: widget */ @@ -323,8 +325,9 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({ } }, on_mode_switch: function (view_type) { - this._super(view_type); - this.shortcut_check(this.views[view_type]); + return $.when( + this._super(view_type), + this.shortcut_check(this.views[view_type])); }, shortcut_check : function(view) { var self = this; @@ -347,20 +350,22 @@ openerp.base.ViewManagerAction = openerp.base.ViewManager.extend({ shortcut_add_remove: function() { var self = this; var $shortcut_toggle = this.$element.find('.oe-shortcut-toggle'); - $shortcut_toggle.click(function() { - if ($shortcut_toggle.hasClass("oe-shortcut-remove")) { - $(self.session.shortcuts.binding).trigger('remove-current'); - $shortcut_toggle.removeClass("oe-shortcut-remove"); - } else { - $(self.session.shortcuts.binding).trigger('add', { - 'user_id': self.session.uid, - 'res_id': self.session.active_id, - 'resource': 'ir.ui.menu', - 'name': self.action.name - }); - $shortcut_toggle.addClass("oe-shortcut-remove"); - } - }); + $shortcut_toggle + .unbind("click") + .click(function() { + if ($shortcut_toggle.hasClass("oe-shortcut-remove")) { + $(self.session.shortcuts.binding).trigger('remove-current'); + $shortcut_toggle.removeClass("oe-shortcut-remove"); + } else { + $(self.session.shortcuts.binding).trigger('add', { + 'user_id': self.session.uid, + 'res_id': self.session.active_id, + 'resource': 'ir.ui.menu', + 'name': self.action.name + }); + $shortcut_toggle.addClass("oe-shortcut-remove"); + } + }); } }); @@ -371,8 +376,8 @@ openerp.base.Sidebar = openerp.base.Widget.extend({ this.sections = {}; }, start: function() { + this._super(this); var self = this; - this._super(this, arguments); this.$element.html(QWeb.render('Sidebar')); this.$element.find(".toggle-sidebar").click(function(e) { self.do_toggle(); @@ -428,9 +433,23 @@ openerp.base.Sidebar = openerp.base.Widget.extend({ item.callback(); } if (item.action) { - item.action.flags = item.action.flags || {}; - item.action.flags.new_window = true; - self.do_action(item.action); + var ids = self.widget_parent.get_selected_ids(); + if (ids.length == 0) { + //TODO niv: maybe show a warning? + return false; + } + self.rpc("/base/action/load", { + action_id: item.action.id, + context: { + active_id: ids[0], + active_ids: ids, + active_model: self.widget_parent.dataset.model + } + }, function(result) { + result.result.flags = result.result.flags || {}; + result.result.flags.new_window = true; + self.do_action(result.result); + }); } return false; }); @@ -450,6 +469,106 @@ openerp.base.Sidebar = openerp.base.Widget.extend({ } }); +openerp.base.TranslateDialog = openerp.base.Dialog.extend({ + dialog_title: _t("Translations"), + init: function(view) { + this['on_button' + _t("Save")] = this.on_button_Save; + this['on_button' + _t("Close")] = this.on_button_Close; + this._super(view, { + width: '80%', + height: '80%' + }); + this.view = view; + this.view_type = view.fields_view.type || ''; + this.$fields_form = null; + this.$view_form = null; + this.$sidebar_form = null; + this.translatable_fields_keys = _.map(this.view.translatable_fields || [], function(i) { return i.name }); + this.languages = null; + this.languages_loaded = $.Deferred(); + (new openerp.base.DataSetSearch(this, 'res.lang', this.view.dataset.get_context(), + [['translatable', '=', '1']])).read_slice(['code', 'name'], { sort: 'id' }, this.on_languages_loaded); + }, + start: function() { + var self = this; + this._super(); + $.when(this.languages_loaded).then(function() { + self.$element.html(QWeb.render('TranslateDialog', { widget: self })); + self.$element.tabs(); + if (!(self.view.translatable_fields && self.view.translatable_fields.length)) { + self.hide_tabs('fields'); + self.select_tab('view'); + } + self.$fields_form = self.$element.find('.oe_translation_form'); + }); + return this; + }, + on_languages_loaded: function(langs) { + this.languages = langs; + this.languages_loaded.resolve(); + }, + do_load_fields_values: function(callback) { + var self = this, + deffered = []; + this.$fields_form.find('.oe_trad_field').val('').removeClass('touched'); + _.each(self.languages, function(lg) { + var deff = $.Deferred(); + deffered.push(deff); + self.rpc('/base/dataset/get', { + model: self.view.dataset.model, + ids: [self.view.datarecord.id], + fields: self.translatable_fields_keys, + context: self.view.dataset.get_context({ + 'lang': lg.code + }) + }, function(values) { + _.each(self.translatable_fields_keys, function(f) { + self.$fields_form.find('.oe_trad_field[name="' + lg.code + '-' + f + '"]').val(values[0][f] || ''); + }); + deff.resolve(); + }); + }); + $.when.apply(null, deffered).then(callback); + }, + show_tabs: function() { + for (var i = 0; i < arguments.length; i++) { + this.$element.find('ul.oe_translate_tabs li a[href$="' + arguments[i] + '"]').parent().show(); + } + }, + hide_tabs: function() { + for (var i = 0; i < arguments.length; i++) { + this.$element.find('ul.oe_translate_tabs li a[href$="' + arguments[i] + '"]').parent().hide(); + } + }, + select_tab: function(name) { + this.show_tabs(name); + var index = this.$element.find('ul.oe_translate_tabs li a[href$="' + arguments[i] + '"]').parent().index() - 1; + this.$element.tabs('select', index); + }, + open: function(field) { + var self = this, + sup = this._super; + $.when(this.languages_loaded).then(function() { + if (self.view.translatable_fields && self.view.translatable_fields.length) { + self.do_load_fields_values(function() { + sup.call(self); + if (field) { + // TODO: focus and scroll to field + } + }); + } else { + sup.call(self); + } + }); + }, + on_button_Save: function() { + this.close(); + }, + on_button_Close: function() { + this.close(); + } +}); + openerp.base.View = openerp.base.Widget.extend({ set_default_options: function(options) { this.options = options || {}; @@ -461,6 +580,12 @@ openerp.base.View = openerp.base.Widget.extend({ action_views_ids: {} }); }, + open_translate_dialog: function(field) { + if (!this.translate_dialog) { + this.translate_dialog = new openerp.base.TranslateDialog(this).start(); + } + this.translate_dialog.open(field); + }, /** * Fetches and executes the action identified by ``action_data``. * @@ -542,7 +667,7 @@ openerp.base.View = openerp.base.Widget.extend({ }, { label: "Translate", callback: this.on_sidebar_translate, - classname: 'oe_hide oe_sidebar_translate' + classname: 'oe_sidebar_translate' }, { label: "View Log", callback: this.on_sidebar_view_log, @@ -570,6 +695,7 @@ openerp.base.View = openerp.base.Widget.extend({ export_view.start(); }, on_sidebar_translate: function() { + this.open_translate_dialog(); }, on_sidebar_view_log: function() { } diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index 89b452a6760..0c17e70cfc7 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -453,6 +453,40 @@ + + +
    + + + + + + + + + +
    +
    Field
    +
    +
    +
    + + + + +
    +
    +
    + Translate view +
    +
    + Translate sidebar +
    +

    +
    @@ -735,11 +770,13 @@
    - + - + + +
      +
    • Open...
    • +
    • Create...
    • +
    • Search...
    • + + +
    • + ... +
    • + + +
    +
    diff --git a/addons/base_calendar/static/src/js/calendar.js b/addons/base_calendar/static/src/js/calendar.js index 19818b66380..7a0b561ccef 100644 --- a/addons/base_calendar/static/src/js/calendar.js +++ b/addons/base_calendar/static/src/js/calendar.js @@ -327,6 +327,10 @@ openerp.base_calendar.CalendarView = openerp.base.View.extend({ if (this.sidebar) { this.sidebar.$element.hide(); } + }, + get_selected_ids: function() { + // no way to select a record anyway + return []; } }); diff --git a/addons/base_dashboard/controllers.py b/addons/base_dashboard/controllers.py index d24649a21cc..b0f5fbf817e 100644 --- a/addons/base_dashboard/controllers.py +++ b/addons/base_dashboard/controllers.py @@ -1,11 +1,28 @@ # -*- coding: utf-8 -*- import openerpweb +WIDGET_CONTENT_PATTERN = """ + + [[Widget %(id)d]] + + %(content)s + + + +""" class Widgets(openerpweb.openerpweb.Controller): _cp_path = '/base_dashboard/widgets' @openerpweb.httprequest def content(self, request, widget_id): - return request.session.model('res.widget').read( + return WIDGET_CONTENT_PATTERN % request.session.model('res.widget').read( [widget_id], ['content'], request.session.eval_context(request.context) - )[0]['content'] + )[0] diff --git a/addons/base_dashboard/static/src/js/dashboard.js b/addons/base_dashboard/static/src/js/dashboard.js index 8cf94a43362..3f9da51b157 100644 --- a/addons/base_dashboard/static/src/js/dashboard.js +++ b/addons/base_dashboard/static/src/js/dashboard.js @@ -321,7 +321,7 @@ openerp.base_dashboard.ConfigOverview = openerp.base.View.extend({ $progress.progressbar({value: $progress.data('completion')}); var self = this; - this.$element.find('div.oe-dashboard-config-overview ul') + this.$element.find('dl') .delegate('input', 'click', function (e) { // switch todo status e.stopImmediatePropagation(); @@ -372,39 +372,36 @@ openerp.base_dashboard.ApplicationTiles = openerp.base.View.extend({ } }); openerp.base.client_actions.add( - 'board.home.widgets', 'openerp.base_dashboard.Widgets'); -openerp.base_dashboard.Widgets = openerp.base.View.extend({ - template: 'HomeWidgets', - start: function () { - return new openerp.base.DataSetSearch( - this, 'res.widget.user', null, - ['|', ['user_id', '=', false], - ['user_id', '=', parseInt(this.session.uid, 10)]]) - .read_slice(['widget_id', 'user_id'], {}, this.on_widgets_list_loaded); + 'board.home.widgets', 'openerp.base_dashboard.Widget'); +openerp.base_dashboard.Widget = openerp.base.View.extend(/** @lends openerp.base_dashboard.Widgets# */{ + template: 'HomeWidget', + /** + * Initializes a "HomeWidget" client widget: handles the display of a given + * res.widget objects in an OpenERP view (mainly a dashboard). + * + * @constructs + * @extends openerp.base.View + * @param {Object} parent + * @param {Object} options + * @param {Number} options.widget_id + */ + init: function (parent, options) { + this._super(parent); + this.widget_id = options.widget_id; }, - on_widgets_list_loaded: function (user_widgets) { - var self = this; - var widget_user = {}; - _(user_widgets).each(function (widget) { - widget['widget_id'] = widget['widget_id'][0]; - widget_user[widget['widget_id']] = { - removable: widget['user_id'] !== false, - user_widget_id: widget['id'] - }; - }); - new openerp.base.DataSetSearch(this, 'res.widget') - .read_ids(_(user_widgets).pluck('widget_id'), ['title'], function (widgets) { - _(widgets).each(function (widget) { - _.extend(widget, widget_user[widget['id']]); - }); - var url = _.sprintf( - '/base_dashboard/widgets/content?session_id=%s&widget_id=', - self.session.session_id); - self.$element.html(QWeb.render('HomeWidgets', { - widgets: widgets, - url: url - })); - }); + start: function () { + return new openerp.base.DataSet(this, 'res.widget').read_ids( + [this.widget_id], ['title'], this.on_widget_loaded); + }, + on_widget_loaded: function (widgets) { + var widget = widgets[0]; + var url = _.sprintf( + '/base_dashboard/widgets/content?session_id=%s&widget_id=%d', + this.session.session_id, widget.id); + this.$element.html(QWeb.render('HomeWidget.content', { + widget: widget, + url: url + })); } }); }; diff --git a/addons/base_dashboard/static/src/xml/base_dashboard.xml b/addons/base_dashboard/static/src/xml/base_dashboard.xml index 51657dfd068..b60e466964b 100644 --- a/addons/base_dashboard/static/src/xml/base_dashboard.xml +++ b/addons/base_dashboard/static/src/xml/base_dashboard.xml @@ -107,10 +107,9 @@ -
    -
    +
    +

    -