init: function(parent, field, $node) { this._super(parent); this.field = field; this.$node = $node; this.options = pyeval.py_eval(this.$node.attr("options") || '{}'); this.set("value", field.raw_value); },
var recurse = function(node) { if (node.tag === "field" && node.attrs.name == self.view.group_by) { options = pyeval.py_eval(node.attrs.options || '{}'); return; } _.each(node.children, function(child) { recurse(child); }); };
_.each(modifiersNames, function (a) { if (node.attrs[a]) { var v = pyeval.py_eval(node.attrs[a]) ? true: false; if (inTreeView && a === 'invisible') { modifiers['tree_invisible'] = v; } else if (v || !(a in modifiers) || !_.isArray(modifiers[a])) { modifiers[a] = v; } } });
_.each(modifiersNames, function (a) { if (node.attrs[a]) { var pyevalContext = window.py.dict.fromJSON(context || {}); var v = pyeval.py_eval(node.attrs[a], {context: pyevalContext}) ? true: false; if (inTreeView && a === 'invisible') { modifiers['column_invisible'] = v; } else if (v || !(a in modifiers) || !_.isArray(modifiers[a])) { modifiers[a] = v; } } });
init: function(field_manager, node) { this._super(field_manager, node); this.name = this.node.attrs.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 = pyeval.py_eval(this.node.attrs.options || '{}'); this.set({'value': false}); this.on("change:value", this, function() { this.trigger('changed_value'); this._check_css_flags(); }); this.$translate = $(); // Enterprise compatibility },
init: function(field_manager, node) { this._super(field_manager, node); this.name = this.node.attrs.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 = pyeval.py_eval(this.node.attrs.options || '{}'); this.set({'value': false}); this.on("change:value", this, function() { this.trigger('changed_value'); this._check_css_flags(); }); this.$translate = (_t.database.multi_lang && this.field.translate) ? $('<button/>', { type: 'button', }).addClass('o_field_translate fa fa-globe btn btn-link') : $(); },
var recurse = function(node) { if (node.tag === "field" && node.attrs && node.attrs.options) { var options = pyeval.py_eval(node.attrs.options); var states_fields_to_read = _.map( options && options.states_legend || {}, function (value, key, list) { return value; }); var tooltip_fields_to_read = _.map( options && options.group_by_tooltip || {}, function (value, key, list) { return key; }); group_by_fields_to_read = _.union( group_by_fields_to_read, states_fields_to_read, tooltip_fields_to_read); } _.each(node.children, function(child) { recurse(child); }); };
var handler = function (action) { // show effect if button have effect attribute // Rainbowman can be displayed from two places : from attribute on a button, or from python. // Code below handles the first case i.e 'effect' attribute on button. var effect = false; if (action_data.effect) { effect = pyeval.py_eval(action_data.effect); }; if (action && action.constructor === Object) { // filter out context keys that are specific to the current action. // Wrong default_* and search_default_* values will no give the expected result // Wrong group_by values will simply fail and forbid rendering of the destination view var ncontext = new Context( _.object(_.reject(_.pairs(self.env.context), function(pair) { return pair[0].match('^(?:(?:default_|search_default_|show_).+|.+_view_ref|group_by|group_by_no_leaf|active_id|active_ids)$') !== null; })) ); ncontext.add(action_data.context || {}); ncontext.add({active_model: env.model}); if (recordID) { ncontext.add({ active_id: recordID, active_ids: [recordID], }); } ncontext.add(action.context || {}); action.context = ncontext; // In case effect data is returned from python and also there is rainbow // attribute on button, priority is given to button attribute action.effect = effect || action.effect; return self.do_action(action, { on_close: result_handler, }); } else { // If action doesn't return anything, but have effect // attribute on button, display rainbowman self.do_action({"type":"ir.actions.act_window_close", 'effect': effect}); return result_handler(); } };
_processField: function (viewType, field, attrs) { var self = this; attrs.Widget = this._getFieldWidgetClass(viewType, field, attrs); if (!_.isObject(attrs.options)) { // parent arch could have already been processed (TODO this should not happen) attrs.options = attrs.options ? pyeval.py_eval(attrs.options) : {}; } if (attrs.on_change && !field.onChange) { field.onChange = "1"; } if (!_.isEmpty(field.views)) { // process the inner fields_view as well to find the fields they use. // register those fields' description directly on the view. // for those inner views, the list of all fields isn't necessary, so // basically the field_names will be the keys of the fields obj. // don't use _ to iterate on fields in case there is a 'length' field, // as _ doesn't behave correctly when there is a length key in the object attrs.views = {}; _.each(field.views, function (innerFieldsView, viewType) { viewType = viewType === 'tree' ? 'list' : viewType; innerFieldsView.type = viewType; attrs.views[viewType] = self._processFieldsView(_.extend({}, innerFieldsView)); }); delete field.views; } if (field.type === 'one2many' || field.type === 'many2many') { if (attrs.Widget.prototype.useSubview) { if (!attrs.views) { attrs.views = {}; } var mode = attrs.mode; if (!mode) { if (attrs.views.tree && attrs.views.kanban) { mode = 'tree'; } else if (!attrs.views.tree && attrs.views.kanban) { mode = 'kanban'; } else { mode = 'tree,kanban'; } } if (mode.indexOf(',') !== -1) { mode = config.device.size_class !== config.device.SIZES.XS ? 'tree' : 'kanban'; } if (mode === 'tree') { mode = 'list'; if (!attrs.views.list && attrs.views.tree) { attrs.views.list = attrs.views.tree; } } attrs.mode = mode; } if (attrs.Widget.prototype.fetchSubFields) { attrs.relatedFields = { display_name: {type: 'char'}, //id: {type: 'integer'}, }; attrs.fieldsInfo = {}; attrs.fieldsInfo.default = {display_name: {}, id: {}}; attrs.viewType = 'default'; if (attrs.color || 'color') { attrs.relatedFields[attrs.color || 'color'] = {type: 'integer'}; attrs.fieldsInfo.default.color = {}; } } } return attrs; },
_processField: function (viewType, field, attrs) { var self = this; attrs.Widget = this._getFieldWidgetClass(viewType, field, attrs); if (!_.isObject(attrs.options)) { // parent arch could have already been processed (TODO this should not happen) attrs.options = attrs.options ? pyeval.py_eval(attrs.options) : {}; } if (attrs.on_change && !field.onChange) { field.onChange = "1"; } // the relational data of invisible relational fields should not be // fetched (e.g. name_gets of invisible many2ones), at least those that // are always invisible. // the invisible attribute of a field is supposed to be static ("1" in // general), but not totally as it may use keys of the context // ("context.get('some_key')"). It is evaluated server-side, and the // result is put inside the modifiers as a value of the '(tree_)invisible' // key, and the raw value is left in the invisible attribute (it is used // in debug mode for informational purposes). // this should change, for instance the server might set the evaluated // value in invisible, which could then be seen as static by the client, // and add another key in debug mode containing the raw value. // for now, we do an hack to detect if the value is static and retrieve // it from the modifiers, if (attrs.invisible && attrs.modifiers.match('"(?:tree_)?invisible": ?true')) { attrs.__no_fetch = true; } if (!_.isEmpty(field.views)) { // process the inner fields_view as well to find the fields they use. // register those fields' description directly on the view. // for those inner views, the list of all fields isn't necessary, so // basically the field_names will be the keys of the fields obj. // don't use _ to iterate on fields in case there is a 'length' field, // as _ doesn't behave correctly when there is a length key in the object attrs.views = {}; _.each(field.views, function (innerFieldsView, viewType) { viewType = viewType === 'tree' ? 'list' : viewType; innerFieldsView.type = viewType; attrs.views[viewType] = self._processFieldsView(_.extend({}, innerFieldsView)); // default_order is like: // 'name,id desc' // but we need it like: // [{name: 'id', asc: false}, {name: 'name', asc: true}] var defaultOrder = innerFieldsView.arch.attrs.default_order; if (defaultOrder) { attrs.orderedBy = _.map(defaultOrder.split(','), function (order) { order = order.trim().split(' '); return {name: order[0], asc: order[1] !== 'desc'}; }); } }); delete field.views; } if (field.type === 'one2many' || field.type === 'many2many') { if (attrs.Widget.prototype.useSubview) { if (!attrs.views) { attrs.views = {}; } var mode = attrs.mode; if (!mode) { if (attrs.views.tree && attrs.views.kanban) { mode = 'tree'; } else if (!attrs.views.tree && attrs.views.kanban) { mode = 'kanban'; } else { mode = 'tree,kanban'; } } if (mode.indexOf(',') !== -1) { mode = config.device.size_class !== config.device.SIZES.XS ? 'tree' : 'kanban'; } if (mode === 'tree') { mode = 'list'; if (!attrs.views.list && attrs.views.tree) { attrs.views.list = attrs.views.tree; } } attrs.mode = mode; } if (attrs.Widget.prototype.fieldsToFetch) { attrs.viewType = 'default'; attrs.relatedFields = attrs.Widget.prototype.fieldsToFetch; attrs.fieldsInfo = { default: _.mapObject(attrs.Widget.prototype.fieldsToFetch, function () { return {}; }), }; if (attrs.options.color_field) { // used by m2m tags attrs.relatedFields[attrs.options.color_field] = { type: 'integer' }; attrs.fieldsInfo.default[attrs.options.color_field] = {}; } } } return attrs; },
this._traverse(arch, function (node) { if (typeof node === "string") { return false; } var modifiers = {}; var isField = (node.tag === 'field'); if (isField) { fieldNodes[node.attrs.name] = node; // 'transfer_field_to_modifiers' simulation var field = fields[node.attrs.name]; var defaultValues = {}; var stateExceptions = {}; _.each(modifiersNames, function (attr) { stateExceptions[attr] = []; defaultValues[attr] = !!field[attr]; }); _.each(field['states'] || {}, function (modifs, state) { _.each(modifs, function (modif) { if (defaultValues[modif[0]] !== modif[1]) { stateExceptions[modif[0]].append(state); } }); }); _.each(defaultValues, function (defaultValue, attr) { if (stateExceptions[attr].length) { modifiers[attr] = [("state", defaultValue ? "not in" : "in", stateExceptions[attr])]; } else { modifiers[attr] = defaultValue; } }); } // 'transfer_node_to_modifiers' simulation if (node.attrs.attrs) { var attrs = pyeval.py_eval(node.attrs.attrs); _.extend(modifiers, attrs); delete node.attrs.attrs; } if (node.attrs.states) { if (!modifiers.invisible) { modifiers.invisible = []; } modifiers.invisible.push(["state", "not in", node.attrs.states.split(",")]); } _.each(modifiersNames, function (a) { if (node.attrs[a]) { var v = pyeval.py_eval(node.attrs[a]) ? true: false; if (inTreeView && a === 'invisible') { modifiers['tree_invisible'] = v; } else if (v || !(a in modifiers) || !_.isArray(modifiers[a])) { modifiers[a] = v; } } }); // 'transfer_modifiers_to_node' simulation _.each(modifiersNames, function (a) { if (a in modifiers && (!!modifiers[a] === false || (_.isArray(modifiers[a]) && !modifiers[a].length))) { delete modifiers[a]; } }); node.attrs.modifiers = JSON.stringify(modifiers); return !isField; });
this._traverse(doc, function (node) { if (node.nodeType === Node.TEXT_NODE) { return false; } var modifiers = {}; var isField = (node.tagName === 'field'); if (isField) { var fieldName = node.getAttribute('name'); fieldNodes[fieldName] = node; // 'transfer_field_to_modifiers' simulation var field = fields[fieldName]; if (!field) { throw new Error("Field " + fieldName + " does not exist"); } var defaultValues = {}; var stateExceptions = {}; _.each(modifiersNames, function (attr) { stateExceptions[attr] = []; defaultValues[attr] = !!field[attr]; }); _.each(field.states || {}, function (modifs, state) { _.each(modifs, function (modif) { if (defaultValues[modif[0]] !== modif[1]) { stateExceptions[modif[0]].append(state); } }); }); _.each(defaultValues, function (defaultValue, attr) { if (stateExceptions[attr].length) { modifiers[attr] = [("state", defaultValue ? "not in" : "in", stateExceptions[attr])]; } else { modifiers[attr] = defaultValue; } }); } // 'transfer_node_to_modifiers' simulation var attrs = node.getAttribute('attrs'); if (attrs) { attrs = pyeval.py_eval(attrs); _.extend(modifiers, attrs); node.removeAttribute('attrs'); } var states = node.getAttribute('states'); if (states) { if (!modifiers.invisible) { modifiers.invisible = []; } modifiers.invisible.push(["state", "not in", states.split(",")]); } _.each(modifiersNames, function (a) { var mod = node.getAttribute(a); if (mod) { var pyevalContext = window.py.dict.fromJSON(context || {}); var v = pyeval.py_eval(mod, {context: pyevalContext}) ? true: false; if (inTreeView && a === 'invisible') { modifiers.column_invisible = v; } else if (v || !(a in modifiers) || !_.isArray(modifiers[a])) { modifiers[a] = v; } } }); _.each(modifiersNames, function (a) { if (a in modifiers && (!!modifiers[a] === false || (_.isArray(modifiers[a]) && !modifiers[a].length))) { delete modifiers[a]; } }); if (Object.keys(modifiers).length) { node.setAttribute('modifiers', JSON.stringify(modifiers)); } return !isField; });