[REF] web,*: move fvg processing from DataManager to Views
*: mail, web_diagram
This commit is contained in:
committed by
Aaron Bohy
parent
12fa4e565a
commit
cb339842d4
@@ -6,10 +6,10 @@ var BasicView = require('web.BasicView');
|
|||||||
var mailWidgets = ['mail_followers', 'mail_thread', 'mail_activity', 'kanban_activity'];
|
var mailWidgets = ['mail_followers', 'mail_thread', 'mail_activity', 'kanban_activity'];
|
||||||
|
|
||||||
BasicView.include({
|
BasicView.include({
|
||||||
init: function (viewInfo) {
|
init: function () {
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
this.mailFields = {};
|
this.mailFields = {};
|
||||||
var fieldsInfo = viewInfo.fieldsInfo[this.viewType];
|
var fieldsInfo = this.fieldsInfo[this.viewType];
|
||||||
for (var fieldName in fieldsInfo) {
|
for (var fieldName in fieldsInfo) {
|
||||||
var fieldInfo = fieldsInfo[fieldName];
|
var fieldInfo = fieldsInfo[fieldName];
|
||||||
if (_.contains(mailWidgets, fieldInfo.widget)) {
|
if (_.contains(mailWidgets, fieldInfo.widget)) {
|
||||||
|
|||||||
@@ -347,11 +347,13 @@ ActionManager.include({
|
|||||||
_.each(action.views, function (view) {
|
_.each(action.views, function (view) {
|
||||||
var viewType = view[1];
|
var viewType = view[1];
|
||||||
var fieldsView = fieldsViews[viewType];
|
var fieldsView = fieldsViews[viewType];
|
||||||
var View = view_registry.get(fieldsView.arch.attrs.js_class || viewType);
|
var parsedXML = new DOMParser().parseFromString(fieldsView.arch, "text/xml");
|
||||||
|
var key = parsedXML.documentElement.getAttribute('js_class');
|
||||||
|
var View = view_registry.get(key || viewType);
|
||||||
if (View) {
|
if (View) {
|
||||||
views.push({
|
views.push({
|
||||||
accessKey: View.prototype.accessKey,
|
accessKey: View.prototype.accessKey,
|
||||||
fieldsView: fieldsViews[viewType],
|
fieldsView: fieldsView,
|
||||||
icon: View.prototype.icon,
|
icon: View.prototype.icon,
|
||||||
isMobileFriendly: View.prototype.mobile_friendly,
|
isMobileFriendly: View.prototype.mobile_friendly,
|
||||||
multiRecord: View.prototype.multi_record,
|
multiRecord: View.prototype.multi_record,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ var FilterMenu = require('web.FilterMenu');
|
|||||||
var GroupByMenu = require('web.GroupByMenu');
|
var GroupByMenu = require('web.GroupByMenu');
|
||||||
var pyeval = require('web.pyeval');
|
var pyeval = require('web.pyeval');
|
||||||
var search_inputs = require('web.search_inputs');
|
var search_inputs = require('web.search_inputs');
|
||||||
|
var utils = require('web.utils');
|
||||||
var Widget = require('web.Widget');
|
var Widget = require('web.Widget');
|
||||||
var _t = core._t;
|
var _t = core._t;
|
||||||
|
|
||||||
@@ -274,7 +275,8 @@ var SearchView = Widget.extend({
|
|||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.dataset = dataset;
|
this.dataset = dataset;
|
||||||
this.fields_view = fvg;
|
this.fields_view = this._processFieldsView(_.clone(fvg));
|
||||||
|
|
||||||
this.fields = this.fields_view.fields;
|
this.fields = this.fields_view.fields;
|
||||||
this.query = undefined;
|
this.query = undefined;
|
||||||
this.title = this.options.action && this.options.action.name;
|
this.title = this.options.action && this.options.action.name;
|
||||||
@@ -603,6 +605,26 @@ var SearchView = Widget.extend({
|
|||||||
current_category = filter.category;
|
current_category = filter.category;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
// Private
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes a fieldsView in place. In particular, parses its arch.
|
||||||
|
*
|
||||||
|
* @todo: this function is also defined in AbstractView ; this code
|
||||||
|
* duplication could be removed once the SearchView will be rewritten.
|
||||||
|
* @private
|
||||||
|
* @param {Object} fv
|
||||||
|
* @param {string} fv.arch
|
||||||
|
* @returns {Object} the processed fieldsView
|
||||||
|
*/
|
||||||
|
_processFieldsView: function (fv) {
|
||||||
|
var doc = $.parseXML(fv.arch).documentElement;
|
||||||
|
fv.arch = utils.xml_to_json(doc, true);
|
||||||
|
return fv;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
_.extend(SearchView, {
|
_.extend(SearchView, {
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
odoo.define('web.DataManager', function (require) {
|
odoo.define('web.DataManager', function (require) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var config = require('web.config');
|
|
||||||
var core = require('web.core');
|
var core = require('web.core');
|
||||||
var fieldRegistry = require('web.field_registry');
|
|
||||||
var pyeval = require('web.pyeval');
|
|
||||||
var rpc = require('web.rpc');
|
var rpc = require('web.rpc');
|
||||||
var utils = require('web.utils');
|
var utils = require('web.utils');
|
||||||
|
|
||||||
@@ -100,13 +97,18 @@ return core.Class.extend({
|
|||||||
model: model,
|
model: model,
|
||||||
method: 'load_views',
|
method: 'load_views',
|
||||||
}).then(function (result) {
|
}).then(function (result) {
|
||||||
// Postprocess fields_views and insert them into the fields_views cache
|
// Freeze the fields dict as it will be shared between views and
|
||||||
result.fields_views = _.mapObject(result.fields_views, self._postprocess_fvg.bind(self));
|
// no one should edit it
|
||||||
self.processViews(result.fields_views, result.fields);
|
utils.deepFreeze(result.fields);
|
||||||
|
|
||||||
|
// Insert views into the fields_views cache
|
||||||
_.each(views_descr, function (view_descr) {
|
_.each(views_descr, function (view_descr) {
|
||||||
var toolbar = options.toolbar && view_descr[1] !== 'search';
|
var toolbar = options.toolbar && view_descr[1] !== 'search';
|
||||||
var fv_key = self._gen_key(model, view_descr[0], view_descr[1], toolbar, context);
|
var fv_key = self._gen_key(model, view_descr[0], view_descr[1], toolbar, context);
|
||||||
self._cache.fields_views[fv_key] = $.when(result.fields_views[view_descr[1]]);
|
var fvg = result.fields_views[view_descr[1]];
|
||||||
|
fvg.viewFields = fvg.fields;
|
||||||
|
fvg.fields = result.fields;
|
||||||
|
self._cache.fields_views[fv_key] = $.when(fvg);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Insert filters, if any, into the filters cache
|
// Insert filters, if any, into the filters cache
|
||||||
@@ -184,57 +186,6 @@ return core.Class.extend({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Processes fields and fields_views. For each field, writes its name inside
|
|
||||||
* the field description to make it self-contained. For each fields_view,
|
|
||||||
* completes its fields with the missing ones.
|
|
||||||
*
|
|
||||||
* @param {Object} fieldsViews object of fields_views (keys are view types)
|
|
||||||
* @param {Object} fields all the fields of the model
|
|
||||||
*/
|
|
||||||
processViews: function (fieldsViews, fields) {
|
|
||||||
var fieldName, fieldsView, viewType;
|
|
||||||
// write the field name inside the description for all fields
|
|
||||||
for (fieldName in fields) {
|
|
||||||
fields[fieldName].name = fieldName;
|
|
||||||
}
|
|
||||||
for (viewType in fieldsViews) {
|
|
||||||
fieldsView = fieldsViews[viewType];
|
|
||||||
// write the field name inside the description for fields in view
|
|
||||||
for (fieldName in fieldsView.fields) {
|
|
||||||
fieldsView.fields[fieldName].name = fieldName;
|
|
||||||
}
|
|
||||||
// complete fields (in view) with missing ones
|
|
||||||
_.defaults(fieldsView.fields, fields);
|
|
||||||
// process the fields_view
|
|
||||||
_.extend(fieldsView, this._processFieldsView({
|
|
||||||
type: viewType,
|
|
||||||
arch: fieldsView.arch,
|
|
||||||
fields: fieldsView.fields,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Private function that postprocesses fields_view (mainly parses the arch attribute)
|
|
||||||
*/
|
|
||||||
_postprocess_fvg: function (fields_view) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
// Parse arch
|
|
||||||
var doc = $.parseXML(fields_view.arch).documentElement;
|
|
||||||
fields_view.arch = utils.xml_to_json(doc, (doc.nodeName.toLowerCase() !== 'kanban'));
|
|
||||||
|
|
||||||
// Process inner views (x2manys)
|
|
||||||
_.each(fields_view.fields, function(field) {
|
|
||||||
_.each(field.views || {}, function(view) {
|
|
||||||
self._postprocess_fvg(view);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return fields_view;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private function that generates a cache key from its arguments
|
* Private function that generates a cache key from its arguments
|
||||||
*/
|
*/
|
||||||
@@ -253,224 +204,6 @@ return core.Class.extend({
|
|||||||
_invalidate: function (cache, key) {
|
_invalidate: function (cache, key) {
|
||||||
delete cache[key];
|
delete cache[key];
|
||||||
},
|
},
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process a field node, in particular, put a flag on the field to give
|
|
||||||
* special directives to the BasicModel.
|
|
||||||
*
|
|
||||||
* @param {string} viewType
|
|
||||||
* @param {Object} field - the field properties
|
|
||||||
* @param {Object} attrs - the field attributes (from the xml)
|
|
||||||
* @returns {Object} 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 '(column_)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 look inside the modifiers and consider the value only if
|
|
||||||
// it is static (=== true),
|
|
||||||
if (attrs.modifiers.invisible === true || attrs.modifiers.column_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));
|
|
||||||
});
|
|
||||||
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 (mode in attrs.views) {
|
|
||||||
var view = attrs.views[mode];
|
|
||||||
var defaultOrder = view.arch.attrs.default_order;
|
|
||||||
if (defaultOrder) {
|
|
||||||
// process the default_order, which is like 'name,id desc'
|
|
||||||
// but we need it like [{name: 'name', asc: true}, {name: 'id', asc: false}]
|
|
||||||
attrs.orderedBy = _.map(defaultOrder.split(','), function (order) {
|
|
||||||
order = order.trim().split(' ');
|
|
||||||
return {name: order[0], asc: order[1] !== 'desc'};
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// if there is a field with widget `handle`, the x2many
|
|
||||||
// needs to be ordered by this field to correctly display
|
|
||||||
// the records
|
|
||||||
var handleField = _.find(view.arch.children, function (child) {
|
|
||||||
return child.attrs && child.attrs.widget === 'handle';
|
|
||||||
});
|
|
||||||
if (handleField) {
|
|
||||||
attrs.orderedBy = [{name: handleField.attrs.name, asc: true}];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
attrs.columnInvisibleFields = {};
|
|
||||||
_.each(view.arch.children, function (child) {
|
|
||||||
if (child.attrs && child.attrs.modifiers) {
|
|
||||||
attrs.columnInvisibleFields[child.attrs.name] =
|
|
||||||
child.attrs.modifiers.column_invisible || false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (attrs.Widget.prototype.fieldsToFetch) {
|
|
||||||
attrs.viewType = 'default';
|
|
||||||
attrs.relatedFields = _.extend({}, 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] = {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attrs.Widget.prototype.fieldDependencies) {
|
|
||||||
attrs.fieldDependencies = attrs.Widget.prototype.fieldDependencies;
|
|
||||||
}
|
|
||||||
|
|
||||||
return attrs;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Visit all nodes in the arch field and process each fields
|
|
||||||
*
|
|
||||||
* @param {string} viewType
|
|
||||||
* @param {Object} arch
|
|
||||||
* @param {Object} fields
|
|
||||||
* @returns {Object} fieldsInfo
|
|
||||||
*/
|
|
||||||
_processFields: function (viewType, arch, fields) {
|
|
||||||
var self = this;
|
|
||||||
var fieldsInfo = Object.create(null);
|
|
||||||
utils.traverse(arch, function (node) {
|
|
||||||
if (typeof node === 'string') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!_.isObject(node.attrs.modifiers)) {
|
|
||||||
node.attrs.modifiers = node.attrs.modifiers ? JSON.parse(node.attrs.modifiers) : {};
|
|
||||||
}
|
|
||||||
if (!_.isObject(node.attrs.options) && node.tag === 'button') {
|
|
||||||
node.attrs.options = node.attrs.options ? JSON.parse(node.attrs.options) : {};
|
|
||||||
}
|
|
||||||
if (node.tag === 'field') {
|
|
||||||
fieldsInfo[node.attrs.name] = self._processField(viewType,
|
|
||||||
fields[node.attrs.name], node.attrs ? _.clone(node.attrs) : {});
|
|
||||||
|
|
||||||
if (fieldsInfo[node.attrs.name].fieldDependencies) {
|
|
||||||
var deps = fieldsInfo[node.attrs.name].fieldDependencies;
|
|
||||||
for (var dependency_name in deps) {
|
|
||||||
var dependency_dict = {name: dependency_name, type: deps[dependency_name].type};
|
|
||||||
if (!(dependency_name in fieldsInfo)) {
|
|
||||||
fieldsInfo[dependency_name] = _.extend({}, dependency_dict, {options: deps[dependency_name].options || {}});
|
|
||||||
}
|
|
||||||
if (!(dependency_name in fields)) {
|
|
||||||
fields[dependency_name] = dependency_dict;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return node.tag !== 'arch';
|
|
||||||
});
|
|
||||||
return fieldsInfo;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Visit all nodes in the arch field and process each fields and inner views
|
|
||||||
*
|
|
||||||
* @param {Object} viewInfo
|
|
||||||
* @param {Object} viewInfo.arch
|
|
||||||
* @param {Object} viewInfo.fields
|
|
||||||
* @returns {Object} viewInfo
|
|
||||||
*/
|
|
||||||
_processFieldsView: function (viewInfo) {
|
|
||||||
var viewFields = this._processFields(viewInfo.type, viewInfo.arch, viewInfo.fields);
|
|
||||||
viewInfo.fieldsInfo = {};
|
|
||||||
viewInfo.fieldsInfo[viewInfo.type] = viewFields;
|
|
||||||
utils.deepFreeze(viewInfo.fields);
|
|
||||||
return viewInfo;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Returns the AbstractField specialization that should be used for the
|
|
||||||
* given field informations. If there is no mentioned specific widget to
|
|
||||||
* use, determine one according the field type.
|
|
||||||
*
|
|
||||||
* @param {string} viewType
|
|
||||||
* @param {Object} field
|
|
||||||
* @param {Object} attrs
|
|
||||||
* @returns {function|null} AbstractField specialization Class
|
|
||||||
*/
|
|
||||||
_getFieldWidgetClass: function (viewType, field, attrs) {
|
|
||||||
var Widget;
|
|
||||||
if (attrs.widget) {
|
|
||||||
Widget = fieldRegistry.getAny([viewType + "." + attrs.widget, attrs.widget]);
|
|
||||||
if (!Widget) {
|
|
||||||
console.warn("Missing widget: ", attrs.widget, " for field", attrs.name, "of type", field.type);
|
|
||||||
}
|
|
||||||
} else if (viewType === 'kanban' && field.type === 'many2many') {
|
|
||||||
// we want to display the widget many2manytags in kanban even if it
|
|
||||||
// is not specified in the view
|
|
||||||
Widget = fieldRegistry.get('kanban.many2many_tags');
|
|
||||||
}
|
|
||||||
return Widget || fieldRegistry.getAny([viewType + "." + field.type, field.type, "abstract"]);
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ var Class = require('web.Class');
|
|||||||
var AbstractModel = require('web.AbstractModel');
|
var AbstractModel = require('web.AbstractModel');
|
||||||
var AbstractRenderer = require('web.AbstractRenderer');
|
var AbstractRenderer = require('web.AbstractRenderer');
|
||||||
var AbstractController = require('web.AbstractController');
|
var AbstractController = require('web.AbstractController');
|
||||||
|
var utils = require('web.utils');
|
||||||
|
|
||||||
var AbstractView = Class.extend({
|
var AbstractView = Class.extend({
|
||||||
// name displayed in view switchers
|
// name displayed in view switchers
|
||||||
@@ -85,18 +86,32 @@ var AbstractView = Class.extend({
|
|||||||
* @param {boolean} [params.action.views]
|
* @param {boolean} [params.action.views]
|
||||||
*/
|
*/
|
||||||
init: function (viewInfo, params) {
|
init: function (viewInfo, params) {
|
||||||
|
// in general, the fieldsView has to be processed by the View (e.g. the
|
||||||
|
// arch is a string that needs to be parsed) ; the only exception is for
|
||||||
|
// inline form views inside form views, as they are processed alongside
|
||||||
|
// the main view, but they are opened in a FormViewDialog which
|
||||||
|
// instantiates another FormView (unlike kanban or list subviews for
|
||||||
|
// which only a Renderer is instantiated)
|
||||||
|
if (typeof viewInfo.arch === 'string') {
|
||||||
|
this.fieldsView = this._processFieldsView(viewInfo);
|
||||||
|
} else {
|
||||||
|
this.fieldsView = viewInfo;
|
||||||
|
}
|
||||||
|
this.fields = this.fieldsView.viewFields;
|
||||||
|
this.arch = this.fieldsView.arch;
|
||||||
|
|
||||||
this.rendererParams = {
|
this.rendererParams = {
|
||||||
arch: viewInfo.arch,
|
arch: this.arch,
|
||||||
noContentHelp: params.action && params.action.help,
|
noContentHelp: params.action && params.action.help,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.controllerParams = {
|
this.controllerParams = {
|
||||||
modelName: params.modelName,
|
modelName: params.modelName,
|
||||||
activeActions: {
|
activeActions: {
|
||||||
edit: viewInfo.arch.attrs.edit ? JSON.parse(viewInfo.arch.attrs.edit) : true,
|
edit: this.arch.attrs.edit ? JSON.parse(this.arch.attrs.edit) : true,
|
||||||
create: viewInfo.arch.attrs.create ? JSON.parse(viewInfo.arch.attrs.create) : true,
|
create: this.arch.attrs.create ? JSON.parse(this.arch.attrs.create) : true,
|
||||||
delete: viewInfo.arch.attrs.delete ? JSON.parse(viewInfo.arch.attrs.delete) : true,
|
delete: this.arch.attrs.delete ? JSON.parse(this.arch.attrs.delete) : true,
|
||||||
duplicate: viewInfo.arch.attrs.duplicate ? JSON.parse(viewInfo.arch.attrs.duplicate) : true,
|
duplicate: this.arch.attrs.duplicate ? JSON.parse(this.arch.attrs.duplicate) : true,
|
||||||
},
|
},
|
||||||
groupable: this.groupable,
|
groupable: this.groupable,
|
||||||
controllerID: params.controllerID,
|
controllerID: params.controllerID,
|
||||||
@@ -134,7 +149,7 @@ var AbstractView = Class.extend({
|
|||||||
// 'name,id desc'
|
// 'name,id desc'
|
||||||
// but we need it like:
|
// but we need it like:
|
||||||
// [{name: 'id', asc: false}, {name: 'name', asc: true}]
|
// [{name: 'id', asc: false}, {name: 'name', asc: true}]
|
||||||
var defaultOrder = viewInfo.arch.attrs.default_order;
|
var defaultOrder = this.arch.attrs.default_order;
|
||||||
if (defaultOrder) {
|
if (defaultOrder) {
|
||||||
this.loadParams.orderedBy = _.map(defaultOrder.split(','), function (order) {
|
this.loadParams.orderedBy = _.map(defaultOrder.split(','), function (order) {
|
||||||
order = order.trim().split(' ');
|
order = order.trim().split(' ');
|
||||||
@@ -235,6 +250,22 @@ var AbstractView = Class.extend({
|
|||||||
var model = this.getModel(parent);
|
var model = this.getModel(parent);
|
||||||
return model.load(this.loadParams);
|
return model.load(this.loadParams);
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Processes a fieldsView. In particular, parses its arch.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {Object} fieldsView
|
||||||
|
* @param {string} fieldsView.arch
|
||||||
|
* @returns {Object} the processed fieldsView
|
||||||
|
*/
|
||||||
|
_processFieldsView: function (fieldsView) {
|
||||||
|
var fv = _.extend({}, fieldsView);
|
||||||
|
var doc = $.parseXML(fv.arch).documentElement;
|
||||||
|
var stripWhitespaces = doc.nodeName.toLowerCase() !== 'kanban';
|
||||||
|
fv.arch = utils.xml_to_json(doc, stripWhitespaces);
|
||||||
|
fv.viewFields = _.defaults({}, fv.viewFields, fv.fields);
|
||||||
|
return fv;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return AbstractView;
|
return AbstractView;
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ odoo.define('web.BasicView', function (require) {
|
|||||||
var AbstractView = require('web.AbstractView');
|
var AbstractView = require('web.AbstractView');
|
||||||
var BasicController = require('web.BasicController');
|
var BasicController = require('web.BasicController');
|
||||||
var BasicModel = require('web.BasicModel');
|
var BasicModel = require('web.BasicModel');
|
||||||
|
var config = require('web.config');
|
||||||
|
var fieldRegistry = require('web.field_registry');
|
||||||
|
var pyeval = require('web.pyeval');
|
||||||
|
var utils = require('web.utils');
|
||||||
|
|
||||||
var BasicView = AbstractView.extend({
|
var BasicView = AbstractView.extend({
|
||||||
config: _.extend({}, AbstractView.prototype.config, {
|
config: _.extend({}, AbstractView.prototype.config, {
|
||||||
@@ -35,6 +39,9 @@ var BasicView = AbstractView.extend({
|
|||||||
init: function (viewInfo, params) {
|
init: function (viewInfo, params) {
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
|
|
||||||
|
this.fieldsInfo = {};
|
||||||
|
this.fieldsInfo[this.viewType] = this.fieldsView.fieldsInfo[this.viewType];
|
||||||
|
|
||||||
this.rendererParams.viewType = this.viewType;
|
this.rendererParams.viewType = this.viewType;
|
||||||
|
|
||||||
this.controllerParams.confirmOnDelete = true;
|
this.controllerParams.confirmOnDelete = true;
|
||||||
@@ -42,10 +49,10 @@ var BasicView = AbstractView.extend({
|
|||||||
this.controllerParams.hasButtons =
|
this.controllerParams.hasButtons =
|
||||||
'action_buttons' in params ? params.action_buttons : true;
|
'action_buttons' in params ? params.action_buttons : true;
|
||||||
|
|
||||||
this.loadParams.fieldsInfo = viewInfo.fieldsInfo;
|
this.loadParams.fieldsInfo = this.fieldsInfo;
|
||||||
this.loadParams.fields = viewInfo.fields;
|
this.loadParams.fields = this.fields;
|
||||||
this.loadParams.context = params.context || {};
|
this.loadParams.context = params.context || {};
|
||||||
this.loadParams.limit = parseInt(viewInfo.arch.attrs.limit, 10) || params.limit;
|
this.loadParams.limit = parseInt(this.arch.attrs.limit, 10) || params.limit;
|
||||||
this.loadParams.viewType = this.viewType;
|
this.loadParams.viewType = this.viewType;
|
||||||
this.loadParams.parentID = params.parentID;
|
this.loadParams.parentID = params.parentID;
|
||||||
this.recordID = params.recordID;
|
this.recordID = params.recordID;
|
||||||
@@ -57,6 +64,31 @@ var BasicView = AbstractView.extend({
|
|||||||
// Private
|
// Private
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the AbstractField specialization that should be used for the
|
||||||
|
* given field informations. If there is no mentioned specific widget to
|
||||||
|
* use, determines one according the field type.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {string} viewType
|
||||||
|
* @param {Object} field
|
||||||
|
* @param {Object} attrs
|
||||||
|
* @returns {function|null} AbstractField specialization Class
|
||||||
|
*/
|
||||||
|
_getFieldWidgetClass: function (viewType, field, attrs) {
|
||||||
|
var FieldWidget;
|
||||||
|
if (attrs.widget) {
|
||||||
|
FieldWidget = fieldRegistry.getAny([viewType + "." + attrs.widget, attrs.widget]);
|
||||||
|
if (!FieldWidget) {
|
||||||
|
console.warn("Missing widget: ", attrs.widget, " for field", attrs.name, "of type", field.type);
|
||||||
|
}
|
||||||
|
} else if (viewType === 'kanban' && field.type === 'many2many') {
|
||||||
|
// we want to display the widget many2manytags in kanban even if it
|
||||||
|
// is not specified in the view
|
||||||
|
FieldWidget = fieldRegistry.get('kanban.many2many_tags');
|
||||||
|
}
|
||||||
|
return FieldWidget || fieldRegistry.getAny([viewType + "." + field.type, field.type, "abstract"]);
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* In some cases, we already have a preloaded record
|
* In some cases, we already have a preloaded record
|
||||||
*
|
*
|
||||||
@@ -67,6 +99,15 @@ var BasicView = AbstractView.extend({
|
|||||||
_loadData: function () {
|
_loadData: function () {
|
||||||
if (this.recordID) {
|
if (this.recordID) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
// Add the fieldsInfo of the current view to the given recordID,
|
||||||
|
// as it will be shared between two views, and it must be able to
|
||||||
|
// handle changes on fields that are only on this view.
|
||||||
|
this.model.addFieldsInfo(this.recordID, {
|
||||||
|
fields: this.fields,
|
||||||
|
fieldsInfo: this.fieldsInfo,
|
||||||
|
});
|
||||||
|
|
||||||
var record = this.model.get(this.recordID);
|
var record = this.model.get(this.recordID);
|
||||||
var viewType = this.viewType;
|
var viewType = this.viewType;
|
||||||
var viewFields = Object.keys(record.fieldsInfo[viewType]);
|
var viewFields = Object.keys(record.fieldsInfo[viewType]);
|
||||||
@@ -135,6 +176,205 @@ var BasicView = AbstractView.extend({
|
|||||||
}
|
}
|
||||||
return this._super.apply(this, arguments);
|
return this._super.apply(this, arguments);
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Processes a field node, in particular, put a flag on the field to give
|
||||||
|
* special directives to the BasicModel.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {string} viewType
|
||||||
|
* @param {Object} field - the field properties
|
||||||
|
* @param {Object} attrs - the field attributes (from the xml)
|
||||||
|
* @returns {Object} 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 '(column_)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 look inside the modifiers and consider the value only if
|
||||||
|
// it is static (=== true),
|
||||||
|
if (attrs.modifiers.invisible === true || attrs.modifiers.column_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;
|
||||||
|
attrs.views[viewType] = self._processFieldsView(innerFieldsView, viewType);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (mode in attrs.views) {
|
||||||
|
var view = attrs.views[mode];
|
||||||
|
var defaultOrder = view.arch.attrs.default_order;
|
||||||
|
if (defaultOrder) {
|
||||||
|
// process the default_order, which is like 'name,id desc'
|
||||||
|
// but we need it like [{name: 'name', asc: true}, {name: 'id', asc: false}]
|
||||||
|
attrs.orderedBy = _.map(defaultOrder.split(','), function (order) {
|
||||||
|
order = order.trim().split(' ');
|
||||||
|
return {name: order[0], asc: order[1] !== 'desc'};
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// if there is a field with widget `handle`, the x2many
|
||||||
|
// needs to be ordered by this field to correctly display
|
||||||
|
// the records
|
||||||
|
var handleField = _.find(view.arch.children, function (child) {
|
||||||
|
return child.attrs && child.attrs.widget === 'handle';
|
||||||
|
});
|
||||||
|
if (handleField) {
|
||||||
|
attrs.orderedBy = [{name: handleField.attrs.name, asc: true}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs.columnInvisibleFields = {};
|
||||||
|
_.each(view.arch.children, function (child) {
|
||||||
|
if (child.attrs && child.attrs.modifiers) {
|
||||||
|
attrs.columnInvisibleFields[child.attrs.name] =
|
||||||
|
child.attrs.modifiers.column_invisible || false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (attrs.Widget.prototype.fieldsToFetch) {
|
||||||
|
attrs.viewType = 'default';
|
||||||
|
attrs.relatedFields = _.extend({}, 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] = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attrs.Widget.prototype.fieldDependencies) {
|
||||||
|
attrs.fieldDependencies = attrs.Widget.prototype.fieldDependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
return attrs;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Visits all nodes in the arch and processes each fields.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {string} viewType
|
||||||
|
* @param {Object} arch
|
||||||
|
* @param {Object} fields
|
||||||
|
* @returns {Object} fieldsInfo
|
||||||
|
*/
|
||||||
|
_processFields: function (viewType, arch, fields) {
|
||||||
|
var self = this;
|
||||||
|
var fieldsInfo = Object.create(null);
|
||||||
|
utils.traverse(arch, function (node) {
|
||||||
|
if (typeof node === 'string') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!_.isObject(node.attrs.modifiers)) {
|
||||||
|
node.attrs.modifiers = node.attrs.modifiers ? JSON.parse(node.attrs.modifiers) : {};
|
||||||
|
}
|
||||||
|
if (!_.isObject(node.attrs.options) && node.tag === 'button') {
|
||||||
|
node.attrs.options = node.attrs.options ? JSON.parse(node.attrs.options) : {};
|
||||||
|
}
|
||||||
|
if (node.tag === 'field') {
|
||||||
|
fieldsInfo[node.attrs.name] = self._processField(viewType,
|
||||||
|
fields[node.attrs.name], node.attrs ? _.clone(node.attrs) : {});
|
||||||
|
|
||||||
|
if (fieldsInfo[node.attrs.name].fieldDependencies) {
|
||||||
|
var deps = fieldsInfo[node.attrs.name].fieldDependencies;
|
||||||
|
for (var dependency_name in deps) {
|
||||||
|
var dependency_dict = {name: dependency_name, type: deps[dependency_name].type};
|
||||||
|
if (!(dependency_name in fieldsInfo)) {
|
||||||
|
fieldsInfo[dependency_name] = _.extend({}, dependency_dict, {options: deps[dependency_name].options || {}});
|
||||||
|
}
|
||||||
|
if (!(dependency_name in fields)) {
|
||||||
|
fields[dependency_name] = dependency_dict;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return node.tag !== 'arch';
|
||||||
|
});
|
||||||
|
return fieldsInfo;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Overrides to process the fields, and generate fieldsInfo which contains
|
||||||
|
* the description of the fields in view, with their attrs in the arch.
|
||||||
|
*
|
||||||
|
* @override
|
||||||
|
* @private
|
||||||
|
* @param {Object} fieldsView
|
||||||
|
* @param {string} fieldsView.arch
|
||||||
|
* @param {Object} fieldsView.fields
|
||||||
|
* @param {string} [viewType] by default, this.viewType
|
||||||
|
* @returns {Object} the processed fieldsView with extra key 'fieldsInfo'
|
||||||
|
*/
|
||||||
|
_processFieldsView: function (fieldsView, viewType) {
|
||||||
|
var fv = this._super.apply(this, arguments);
|
||||||
|
|
||||||
|
viewType = viewType || this.viewType;
|
||||||
|
var viewFields = this._processFields(viewType, fv.arch, fv.viewFields);
|
||||||
|
fv.fieldsInfo = {};
|
||||||
|
fv.fieldsInfo[viewType] = viewFields;
|
||||||
|
fv.type = viewType;
|
||||||
|
|
||||||
|
return fv;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return BasicView;
|
return BasicView;
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ var CalendarView = AbstractView.extend({
|
|||||||
groupable: false,
|
groupable: false,
|
||||||
init: function (viewInfo, params) {
|
init: function (viewInfo, params) {
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
var arch = viewInfo.arch;
|
var arch = this.arch;
|
||||||
var fields = viewInfo.fields;
|
var fields = this.fields;
|
||||||
var attrs = arch.attrs;
|
var attrs = arch.attrs;
|
||||||
|
|
||||||
if (!attrs.date_start) {
|
if (!attrs.date_start) {
|
||||||
|
|||||||
@@ -90,7 +90,8 @@ var FormView = BasicView.extend({
|
|||||||
[[null, attrs.mode === 'tree' ? 'list' : attrs.mode]])
|
[[null, attrs.mode === 'tree' ? 'list' : attrs.mode]])
|
||||||
.then(function (views) {
|
.then(function (views) {
|
||||||
for (var viewName in views) {
|
for (var viewName in views) {
|
||||||
attrs.views[viewName] = views[viewName];
|
// clone to make runbot green?
|
||||||
|
attrs.views[viewName] = self._processFieldsView(views[viewName], viewName);
|
||||||
}
|
}
|
||||||
self._setSubViewLimit(attrs);
|
self._setSubViewLimit(attrs);
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -36,13 +36,13 @@ var GraphView = AbstractView.extend({
|
|||||||
/**
|
/**
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
init: function (viewInfo) {
|
init: function () {
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
|
|
||||||
var measure;
|
var measure;
|
||||||
var groupBys = [];
|
var groupBys = [];
|
||||||
viewInfo.fields = _.defaults({__count__: {string: _t("Count"), type: "integer"}}, viewInfo.fields);
|
this.fields.__count__ = {string: _t("Count"), type: "integer"};
|
||||||
viewInfo.arch.children.forEach(function (field) {
|
this.arch.children.forEach(function (field) {
|
||||||
var name = field.attrs.name;
|
var name = field.attrs.name;
|
||||||
if (field.attrs.interval) {
|
if (field.attrs.interval) {
|
||||||
name += ':' + field.attrs.interval;
|
name += ':' + field.attrs.interval;
|
||||||
@@ -55,7 +55,7 @@ var GraphView = AbstractView.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
var measures = {__count__: {string: _t("Count"), type: "integer"}};
|
var measures = {__count__: {string: _t("Count"), type: "integer"}};
|
||||||
_.each(viewInfo.fields, function (field, name) {
|
_.each(this.fields, function (field, name) {
|
||||||
if (name !== 'id' && field.store === true) {
|
if (name !== 'id' && field.store === true) {
|
||||||
if (field.type === 'integer' || field.type === 'float' || field.type === 'monetary') {
|
if (field.type === 'integer' || field.type === 'float' || field.type === 'monetary') {
|
||||||
measures[name] = field;
|
measures[name] = field;
|
||||||
@@ -64,12 +64,12 @@ var GraphView = AbstractView.extend({
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.controllerParams.measures = measures;
|
this.controllerParams.measures = measures;
|
||||||
this.rendererParams.stacked = viewInfo.arch.attrs.stacked !== "False";
|
this.rendererParams.stacked = this.arch.attrs.stacked !== "False";
|
||||||
|
|
||||||
this.loadParams.mode = viewInfo.arch.attrs.type || 'bar';
|
this.loadParams.mode = this.arch.attrs.type || 'bar';
|
||||||
this.loadParams.measure = measure || '__count__';
|
this.loadParams.measure = measure || '__count__';
|
||||||
this.loadParams.groupBys = groupBys || [];
|
this.loadParams.groupBys = groupBys || [];
|
||||||
this.loadParams.fields = viewInfo.fields;
|
this.loadParams.fields = this.fields;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -30,16 +30,14 @@ var KanbanView = BasicView.extend({
|
|||||||
init: function (viewInfo, params) {
|
init: function (viewInfo, params) {
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
|
|
||||||
var arch = viewInfo.arch;
|
|
||||||
|
|
||||||
this.loadParams.limit = this.loadParams.limit || 40;
|
this.loadParams.limit = this.loadParams.limit || 40;
|
||||||
// in mobile, columns are lazy-loaded, so set 'openGroupByDefault' to
|
// in mobile, columns are lazy-loaded, so set 'openGroupByDefault' to
|
||||||
// false so that they will won't be loaded by the initial load
|
// false so that they will won't be loaded by the initial load
|
||||||
this.loadParams.openGroupByDefault = config.device.isMobile ? false : true;
|
this.loadParams.openGroupByDefault = config.device.isMobile ? false : true;
|
||||||
this.loadParams.type = 'list';
|
this.loadParams.type = 'list';
|
||||||
this.loadParams.groupBy = arch.attrs.default_group_by ? [arch.attrs.default_group_by] : (params.groupBy || []);
|
this.loadParams.groupBy = this.arch.attrs.default_group_by ? [this.arch.attrs.default_group_by] : (params.groupBy || []);
|
||||||
var progressBar;
|
var progressBar;
|
||||||
utils.traverse(arch, function (n) {
|
utils.traverse(this.arch, function (n) {
|
||||||
var isProgressBar = (n.tag === 'progressbar');
|
var isProgressBar = (n.tag === 'progressbar');
|
||||||
if (isProgressBar) {
|
if (isProgressBar) {
|
||||||
progressBar = _.clone(n.attrs);
|
progressBar = _.clone(n.attrs);
|
||||||
@@ -54,16 +52,16 @@ var KanbanView = BasicView.extend({
|
|||||||
|
|
||||||
var activeActions = this.controllerParams.activeActions;
|
var activeActions = this.controllerParams.activeActions;
|
||||||
activeActions = _.extend(activeActions, {
|
activeActions = _.extend(activeActions, {
|
||||||
group_create: arch.attrs.group_create ? JSON.parse(arch.attrs.group_create) : true,
|
group_create: this.arch.attrs.group_create ? JSON.parse(this.arch.attrs.group_create) : true,
|
||||||
group_edit: arch.attrs.group_edit ? JSON.parse(arch.attrs.group_edit) : true,
|
group_edit: this.arch.attrs.group_edit ? JSON.parse(this.arch.attrs.group_edit) : true,
|
||||||
group_delete: arch.attrs.group_delete ? JSON.parse(arch.attrs.group_delete) : true,
|
group_delete: this.arch.attrs.group_delete ? JSON.parse(this.arch.attrs.group_delete) : true,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.rendererParams.column_options = {
|
this.rendererParams.column_options = {
|
||||||
editable: activeActions.group_edit,
|
editable: activeActions.group_edit,
|
||||||
deletable: activeActions.group_delete,
|
deletable: activeActions.group_delete,
|
||||||
group_creatable: activeActions.group_create && !config.device.isMobile,
|
group_creatable: activeActions.group_create && !config.device.isMobile,
|
||||||
quick_create: params.isQuickCreateEnabled || this._isQuickCreateEnabled(viewInfo),
|
quick_create: params.isQuickCreateEnabled || this._isQuickCreateEnabled(),
|
||||||
hasProgressBar: !!progressBar,
|
hasProgressBar: !!progressBar,
|
||||||
};
|
};
|
||||||
this.rendererParams.record_options = {
|
this.rendererParams.record_options = {
|
||||||
@@ -72,7 +70,7 @@ var KanbanView = BasicView.extend({
|
|||||||
read_only_mode: params.readOnlyMode,
|
read_only_mode: params.readOnlyMode,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.controllerParams.on_create = arch.attrs.on_create;
|
this.controllerParams.on_create = this.arch.attrs.on_create;
|
||||||
|
|
||||||
this.controllerParams.readOnlyMode = false;
|
this.controllerParams.readOnlyMode = false;
|
||||||
this.controllerParams.hasButtons = true;
|
this.controllerParams.hasButtons = true;
|
||||||
@@ -90,17 +88,17 @@ var KanbanView = BasicView.extend({
|
|||||||
* @private
|
* @private
|
||||||
* @param {Object} viewInfo
|
* @param {Object} viewInfo
|
||||||
*/
|
*/
|
||||||
_isQuickCreateEnabled: function (viewInfo) {
|
_isQuickCreateEnabled: function () {
|
||||||
var groupBy = this.loadParams.groupBy[0];
|
var groupBy = this.loadParams.groupBy[0];
|
||||||
groupBy = groupBy !== undefined ? groupBy.split(':')[0] : undefined;
|
groupBy = groupBy !== undefined ? groupBy.split(':')[0] : undefined;
|
||||||
if (groupBy !== undefined && !_.contains(['char', 'boolean', 'many2one'], viewInfo.fields[groupBy].type)) {
|
if (groupBy !== undefined && !_.contains(['char', 'boolean', 'many2one'], this.fields[groupBy].type)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!this.controllerParams.activeActions.create) {
|
if (!this.controllerParams.activeActions.create) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (viewInfo.arch.attrs.quick_create !== undefined) {
|
if (this.arch.attrs.quick_create !== undefined) {
|
||||||
return JSON.parse(viewInfo.arch.attrs.quick_create);
|
return JSON.parse(this.arch.attrs.quick_create);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -36,19 +36,18 @@ var ListView = BasicView.extend({
|
|||||||
init: function (viewInfo, params) {
|
init: function (viewInfo, params) {
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
|
|
||||||
var arch = viewInfo.arch;
|
var mode = this.arch.attrs.editable && !params.readonly ? "edit" : "readonly";
|
||||||
var mode = arch.attrs.editable && !params.readonly ? "edit" : "readonly";
|
|
||||||
|
|
||||||
this.controllerParams.editable = arch.attrs.editable;
|
this.controllerParams.editable = this.arch.attrs.editable;
|
||||||
this.controllerParams.hasSidebar = params.hasSidebar;
|
this.controllerParams.hasSidebar = params.hasSidebar;
|
||||||
this.controllerParams.toolbarActions = viewInfo.toolbar;
|
this.controllerParams.toolbarActions = viewInfo.toolbar;
|
||||||
this.controllerParams.noLeaf = !!this.loadParams.context.group_by_no_leaf;
|
this.controllerParams.noLeaf = !!this.loadParams.context.group_by_no_leaf;
|
||||||
this.controllerParams.mode = mode;
|
this.controllerParams.mode = mode;
|
||||||
|
|
||||||
this.rendererParams.arch = arch;
|
this.rendererParams.arch = this.arch;
|
||||||
this.rendererParams.hasSelectors =
|
this.rendererParams.hasSelectors =
|
||||||
'hasSelectors' in params ? params.hasSelectors : true;
|
'hasSelectors' in params ? params.hasSelectors : true;
|
||||||
this.rendererParams.editable = params.readonly ? false : arch.attrs.editable;
|
this.rendererParams.editable = params.readonly ? false : this.arch.attrs.editable;
|
||||||
|
|
||||||
this.loadParams.limit = this.loadParams.limit || 80;
|
this.loadParams.limit = this.loadParams.limit || 80;
|
||||||
this.loadParams.type = 'list';
|
this.loadParams.type = 'list';
|
||||||
|
|||||||
@@ -35,10 +35,6 @@ var PivotView = AbstractView.extend({
|
|||||||
init: function (viewInfo, params) {
|
init: function (viewInfo, params) {
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
|
|
||||||
var arch = viewInfo.arch;
|
|
||||||
var fields = _.extend({
|
|
||||||
__count: {string: _t("Count"), type: "integer"}
|
|
||||||
}, viewInfo.fields);
|
|
||||||
var activeMeasures = [];
|
var activeMeasures = [];
|
||||||
var colGroupBys = [];
|
var colGroupBys = [];
|
||||||
var rowGroupBys = [];
|
var rowGroupBys = [];
|
||||||
@@ -46,7 +42,8 @@ var PivotView = AbstractView.extend({
|
|||||||
var measures = {};
|
var measures = {};
|
||||||
var groupableFields = {};
|
var groupableFields = {};
|
||||||
|
|
||||||
_.each(fields, function (field, name) {
|
this.fields.__count = {string: _t("Count"), type: "integer"};
|
||||||
|
_.each(this.fields, function (field, name) {
|
||||||
if ((name !== 'id') && (field.store === true)) {
|
if ((name !== 'id') && (field.store === true)) {
|
||||||
if (_.contains(['integer', 'float', 'monetary'], field.type)) {
|
if (_.contains(['integer', 'float', 'monetary'], field.type)) {
|
||||||
measures[name] = field;
|
measures[name] = field;
|
||||||
@@ -58,7 +55,7 @@ var PivotView = AbstractView.extend({
|
|||||||
});
|
});
|
||||||
measures.__count = {string: _t("Count"), type: "integer"};
|
measures.__count = {string: _t("Count"), type: "integer"};
|
||||||
|
|
||||||
arch.children.forEach(function (field) {
|
this.arch.children.forEach(function (field) {
|
||||||
var name = field.attrs.name;
|
var name = field.attrs.name;
|
||||||
if (field.attrs.interval) {
|
if (field.attrs.interval) {
|
||||||
name += ':' + field.attrs.interval;
|
name += ':' + field.attrs.interval;
|
||||||
@@ -84,17 +81,17 @@ var PivotView = AbstractView.extend({
|
|||||||
rowGroupBys.push(name);
|
rowGroupBys.push(name);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if ((!activeMeasures.length) || arch.attrs.display_quantity) {
|
if ((!activeMeasures.length) || this.arch.attrs.display_quantity) {
|
||||||
activeMeasures.push('__count');
|
activeMeasures.push('__count');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loadParams.measures = activeMeasures;
|
this.loadParams.measures = activeMeasures;
|
||||||
this.loadParams.colGroupBys = colGroupBys;
|
this.loadParams.colGroupBys = colGroupBys;
|
||||||
this.loadParams.rowGroupBys = rowGroupBys;
|
this.loadParams.rowGroupBys = rowGroupBys;
|
||||||
this.loadParams.fields = fields;
|
this.loadParams.fields = this.fields;
|
||||||
|
|
||||||
this.controllerParams.title = params.title || arch.attrs.string || _t("Untitled");
|
this.controllerParams.title = params.title || this.arch.attrs.string || _t("Untitled");
|
||||||
this.controllerParams.enableLinking = !arch.attrs.disable_linking;
|
this.controllerParams.enableLinking = !this.arch.attrs.disable_linking;
|
||||||
this.controllerParams.measures = measures;
|
this.controllerParams.measures = measures;
|
||||||
this.controllerParams.groupableFields = groupableFields;
|
this.controllerParams.groupableFields = groupableFields;
|
||||||
// retrieve form and list view ids from the action to open those views
|
// retrieve form and list view ids from the action to open those views
|
||||||
|
|||||||
@@ -165,9 +165,6 @@ var FormViewDialog = ViewDialog.extend({
|
|||||||
}
|
}
|
||||||
|
|
||||||
fields_view_def.then(function (viewInfo) {
|
fields_view_def.then(function (viewInfo) {
|
||||||
if (self.recordID) {
|
|
||||||
self.model.addFieldsInfo(self.recordID, viewInfo);
|
|
||||||
}
|
|
||||||
var formview = new FormView(viewInfo, {
|
var formview = new FormView(viewInfo, {
|
||||||
modelName: self.res_model,
|
modelName: self.res_model,
|
||||||
context: self.context,
|
context: self.context,
|
||||||
|
|||||||
@@ -2,10 +2,8 @@ odoo.define('web.MockServer', function (require) {
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var Class = require('web.Class');
|
var Class = require('web.Class');
|
||||||
var data_manager = require('web.data_manager');
|
|
||||||
var Domain = require('web.Domain');
|
var Domain = require('web.Domain');
|
||||||
var pyeval = require('web.pyeval');
|
var pyeval = require('web.pyeval');
|
||||||
var utils = require('web.utils');
|
|
||||||
|
|
||||||
var MockServer = Class.extend({
|
var MockServer = Class.extend({
|
||||||
/**
|
/**
|
||||||
@@ -56,6 +54,15 @@ var MockServer = Class.extend({
|
|||||||
// Public
|
// Public
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mocks a fields_get RPC for a given model.
|
||||||
|
*
|
||||||
|
* @param {string} model
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
fieldsGet: function (model) {
|
||||||
|
return this.data[model].fields;
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* helper: read a string describing an arch, and returns a simulated
|
* helper: read a string describing an arch, and returns a simulated
|
||||||
* 'field_view_get' call to the server. Calls processViews() of data_manager
|
* 'field_view_get' call to the server. Calls processViews() of data_manager
|
||||||
@@ -77,13 +84,10 @@ var MockServer = Class.extend({
|
|||||||
}
|
}
|
||||||
var fields = $.extend(true, {}, this.data[model].fields);
|
var fields = $.extend(true, {}, this.data[model].fields);
|
||||||
var fvg = this._fieldsViewGet(params.arch, model, fields, viewOptions.context);
|
var fvg = this._fieldsViewGet(params.arch, model, fields, viewOptions.context);
|
||||||
var fields_views = {};
|
|
||||||
fields_views[fvg.type] = fvg;
|
|
||||||
data_manager.processViews(fields_views, fields);
|
|
||||||
if (toolbar) {
|
if (toolbar) {
|
||||||
fvg.toolbar = toolbar;
|
fvg.toolbar = toolbar;
|
||||||
}
|
}
|
||||||
return fields_views[fvg.type];
|
return fvg;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Simulate a complete RPC call. This is the main method for this class.
|
* Simulate a complete RPC call. This is the main method for this class.
|
||||||
@@ -166,7 +170,7 @@ var MockServer = Class.extend({
|
|||||||
* 'fields_view_get' call to the server.
|
* 'fields_view_get' call to the server.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {string|Object} arch a string OR a parsed xml document
|
* @param {string} arch a string OR a parsed xml document
|
||||||
* @param {string} model a model name (that should be in this.data)
|
* @param {string} model a model name (that should be in this.data)
|
||||||
* @param {Object} fields
|
* @param {Object} fields
|
||||||
* @param {Object} context
|
* @param {Object} context
|
||||||
@@ -179,29 +183,32 @@ var MockServer = Class.extend({
|
|||||||
var onchanges = this.data[model].onchanges || {};
|
var onchanges = this.data[model].onchanges || {};
|
||||||
var fieldNodes = {};
|
var fieldNodes = {};
|
||||||
|
|
||||||
|
var doc;
|
||||||
if (typeof arch === 'string') {
|
if (typeof arch === 'string') {
|
||||||
var doc = $.parseXML(arch).documentElement;
|
doc = $.parseXML(arch).documentElement;
|
||||||
arch = utils.xml_to_json(doc, true);
|
} else {
|
||||||
|
doc = arch;
|
||||||
}
|
}
|
||||||
|
|
||||||
var inTreeView = (arch.tag === 'tree');
|
var inTreeView = (doc.tagName === 'tree');
|
||||||
|
|
||||||
this._traverse(arch, function (node) {
|
this._traverse(doc, function (node) {
|
||||||
if (typeof node === "string") {
|
if (node.nodeType === Node.TEXT_NODE) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
var modifiers = {};
|
var modifiers = {};
|
||||||
|
|
||||||
var isField = (node.tag === 'field');
|
var isField = (node.tagName === 'field');
|
||||||
|
|
||||||
if (isField) {
|
if (isField) {
|
||||||
fieldNodes[node.attrs.name] = node;
|
var fieldName = node.getAttribute('name');
|
||||||
|
fieldNodes[fieldName] = node;
|
||||||
|
|
||||||
// 'transfer_field_to_modifiers' simulation
|
// 'transfer_field_to_modifiers' simulation
|
||||||
var field = fields[node.attrs.name];
|
var field = fields[fieldName];
|
||||||
|
|
||||||
if (!field) {
|
if (!field) {
|
||||||
throw new Error("Field " + node.attrs.name + " does not exist");
|
throw new Error("Field " + fieldName + " does not exist");
|
||||||
}
|
}
|
||||||
var defaultValues = {};
|
var defaultValues = {};
|
||||||
var stateExceptions = {};
|
var stateExceptions = {};
|
||||||
@@ -209,7 +216,7 @@ var MockServer = Class.extend({
|
|||||||
stateExceptions[attr] = [];
|
stateExceptions[attr] = [];
|
||||||
defaultValues[attr] = !!field[attr];
|
defaultValues[attr] = !!field[attr];
|
||||||
});
|
});
|
||||||
_.each(field['states'] || {}, function (modifs, state) {
|
_.each(field.states || {}, function (modifs, state) {
|
||||||
_.each(modifs, function (modif) {
|
_.each(modifs, function (modif) {
|
||||||
if (defaultValues[modif[0]] !== modif[1]) {
|
if (defaultValues[modif[0]] !== modif[1]) {
|
||||||
stateExceptions[modif[0]].append(state);
|
stateExceptions[modif[0]].append(state);
|
||||||
@@ -226,36 +233,42 @@ var MockServer = Class.extend({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 'transfer_node_to_modifiers' simulation
|
// 'transfer_node_to_modifiers' simulation
|
||||||
if (node.attrs.attrs) {
|
var attrs = node.getAttribute('attrs');
|
||||||
var attrs = pyeval.py_eval(node.attrs.attrs);
|
if (attrs) {
|
||||||
|
attrs = pyeval.py_eval(attrs);
|
||||||
_.extend(modifiers, attrs);
|
_.extend(modifiers, attrs);
|
||||||
delete node.attrs.attrs;
|
node.removeAttribute('attrs');
|
||||||
}
|
}
|
||||||
if (node.attrs.states) {
|
|
||||||
|
var states = node.getAttribute('states');
|
||||||
|
if (states) {
|
||||||
if (!modifiers.invisible) {
|
if (!modifiers.invisible) {
|
||||||
modifiers.invisible = [];
|
modifiers.invisible = [];
|
||||||
}
|
}
|
||||||
modifiers.invisible.push(["state", "not in", node.attrs.states.split(",")]);
|
modifiers.invisible.push(["state", "not in", states.split(",")]);
|
||||||
}
|
}
|
||||||
_.each(modifiersNames, function (a) {
|
_.each(modifiersNames, function (a) {
|
||||||
if (node.attrs[a]) {
|
var mod = node.getAttribute(a);
|
||||||
|
if (mod) {
|
||||||
var pyevalContext = window.py.dict.fromJSON(context || {});
|
var pyevalContext = window.py.dict.fromJSON(context || {});
|
||||||
var v = pyeval.py_eval(node.attrs[a], {context: pyevalContext}) ? true: false;
|
var v = pyeval.py_eval(mod, {context: pyevalContext}) ? true: false;
|
||||||
if (inTreeView && a === 'invisible') {
|
if (inTreeView && a === 'invisible') {
|
||||||
modifiers['column_invisible'] = v;
|
modifiers.column_invisible = v;
|
||||||
} else if (v || !(a in modifiers) || !_.isArray(modifiers[a])) {
|
} else if (v || !(a in modifiers) || !_.isArray(modifiers[a])) {
|
||||||
modifiers[a] = v;
|
modifiers[a] = v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 'transfer_modifiers_to_node' simulation
|
|
||||||
_.each(modifiersNames, function (a) {
|
_.each(modifiersNames, function (a) {
|
||||||
if (a in modifiers && (!!modifiers[a] === false || (_.isArray(modifiers[a]) && !modifiers[a].length))) {
|
if (a in modifiers && (!!modifiers[a] === false || (_.isArray(modifiers[a]) && !modifiers[a].length))) {
|
||||||
delete modifiers[a];
|
delete modifiers[a];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
node.attrs.modifiers = JSON.stringify(modifiers);
|
|
||||||
|
if (Object.keys(modifiers).length) {
|
||||||
|
node.setAttribute('modifiers', JSON.stringify(modifiers));
|
||||||
|
}
|
||||||
|
|
||||||
return !isField;
|
return !isField;
|
||||||
});
|
});
|
||||||
@@ -264,29 +277,34 @@ var MockServer = Class.extend({
|
|||||||
_.each(fieldNodes, function (node, name) {
|
_.each(fieldNodes, function (node, name) {
|
||||||
var field = fields[name];
|
var field = fields[name];
|
||||||
if (field.type === "many2one" || field.type === "many2many") {
|
if (field.type === "many2one" || field.type === "many2many") {
|
||||||
node.attrs.can_create = node.attrs.can_create || "true";
|
var canCreate = node.getAttribute('can_create');
|
||||||
node.attrs.can_write = node.attrs.can_write || "true";
|
node.setAttribute('can_create', canCreate || "true");
|
||||||
|
var canWrite = node.getAttribute('can_write');
|
||||||
|
node.setAttribute('can_write', canWrite || "true");
|
||||||
}
|
}
|
||||||
if (field.type === "one2many" || field.type === "many2many") {
|
if (field.type === "one2many" || field.type === "many2many") {
|
||||||
field.views = {};
|
field.views = {};
|
||||||
_.each(node.children, function (children) {
|
_.each(node.childNodes, function (children) {
|
||||||
relModel = field.relation;
|
relModel = field.relation;
|
||||||
relFields = $.extend(true, {}, self.data[relModel].fields);
|
relFields = $.extend(true, {}, self.data[relModel].fields);
|
||||||
field.views[children.tag] = self._fieldsViewGet(children, relModel,
|
field.views[children.tagName] = self._fieldsViewGet(children, relModel,
|
||||||
relFields, context);
|
relFields, context);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// add onchanges
|
// add onchanges
|
||||||
if (name in onchanges) {
|
if (name in onchanges) {
|
||||||
node.attrs.on_change="1";
|
node.setAttribute('on_change', "1");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var xmlSerializer = new XMLSerializer();
|
||||||
|
var processedArch = xmlSerializer.serializeToString(doc);
|
||||||
return {
|
return {
|
||||||
arch: arch,
|
arch: processedArch,
|
||||||
fields: _.pick(fields, _.keys(fieldNodes)),
|
fields: _.pick(fields, _.keys(fieldNodes)),
|
||||||
model: model,
|
model: model,
|
||||||
type: arch.tag === 'tree' ? 'list' : arch.tag,
|
type: doc.tagName === 'tree' ? 'list' : doc.tagName,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@@ -1040,7 +1058,7 @@ var MockServer = Class.extend({
|
|||||||
_traverse: function (tree, f) {
|
_traverse: function (tree, f) {
|
||||||
var self = this;
|
var self = this;
|
||||||
if (f(tree)) {
|
if (f(tree)) {
|
||||||
_.each(tree.children, function (c) { self._traverse(c, f); });
|
_.each(tree.childNodes, function (c) { self._traverse(c, f); });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -126,6 +126,23 @@ var createActionManager = function (params) {
|
|||||||
return actionManager;
|
return actionManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* performs a fields_view_get, and mocks the postprocessing done by the
|
||||||
|
* data_manager to return an equivalent structure.
|
||||||
|
*
|
||||||
|
* @param {MockServer} server
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {string} params.model
|
||||||
|
* @returns {Object} an object with 3 keys: arch, fields and viewFields
|
||||||
|
*/
|
||||||
|
function fieldsViewGet(server, params) {
|
||||||
|
var fieldsView = server.fieldsViewGet(params);
|
||||||
|
// mock the structure produced by the DataManager
|
||||||
|
fieldsView.viewFields = fieldsView.fields;
|
||||||
|
fieldsView.fields = server.fieldsGet(params.model);
|
||||||
|
return fieldsView;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create a view synchronously. This method uses the createAsyncView method.
|
* create a view synchronously. This method uses the createAsyncView method.
|
||||||
* Most views are synchronous, so the deferred can be resolved immediately and
|
* Most views are synchronous, so the deferred can be resolved immediately and
|
||||||
@@ -189,8 +206,7 @@ function createAsyncView(params) {
|
|||||||
|
|
||||||
// add mock environment: mock server, session, fieldviewget, ...
|
// add mock environment: mock server, session, fieldviewget, ...
|
||||||
var mockServer = addMockEnvironment(widget, params);
|
var mockServer = addMockEnvironment(widget, params);
|
||||||
var viewInfo = mockServer.fieldsViewGet(params);
|
var viewInfo = fieldsViewGet(mockServer, params);
|
||||||
|
|
||||||
// create the view
|
// create the view
|
||||||
var viewOptions = {
|
var viewOptions = {
|
||||||
modelName: params.model || 'foo',
|
modelName: params.model || 'foo',
|
||||||
@@ -419,7 +435,7 @@ function addMockEnvironment(widget, params) {
|
|||||||
model: event.data.modelName,
|
model: event.data.modelName,
|
||||||
}).then(function (views) {
|
}).then(function (views) {
|
||||||
views = _.mapObject(views, function (viewParams) {
|
views = _.mapObject(views, function (viewParams) {
|
||||||
return mockServer.fieldsViewGet(viewParams);
|
return fieldsViewGet(mockServer, viewParams);
|
||||||
});
|
});
|
||||||
event.data.on_success(views);
|
event.data.on_success(views);
|
||||||
});
|
});
|
||||||
@@ -694,6 +710,7 @@ return $.when(
|
|||||||
createParent: createParent,
|
createParent: createParent,
|
||||||
createView: createView,
|
createView: createView,
|
||||||
dragAndDrop: dragAndDrop,
|
dragAndDrop: dragAndDrop,
|
||||||
|
fieldsViewGet: fieldsViewGet,
|
||||||
intercept: intercept,
|
intercept: intercept,
|
||||||
observe: observe,
|
observe: observe,
|
||||||
patch: patch,
|
patch: patch,
|
||||||
|
|||||||
@@ -114,36 +114,12 @@ QUnit.module('Views', {
|
|||||||
'<field name="stop_date"/>'+
|
'<field name="stop_date"/>'+
|
||||||
'</group>'+
|
'</group>'+
|
||||||
'</form>',
|
'</form>',
|
||||||
"event,1,form": {
|
"event,1,form":
|
||||||
attrs: {},
|
'<form>' +
|
||||||
children: [
|
'<field name="allday" invisible="1"/>' +
|
||||||
{
|
'<field name="start" attrs=\'{"invisible": [["allday","=",false]]}\'/>' +
|
||||||
attrs: {
|
'<field name="stop" attrs=\'{"invisible": [["allday","=",true]]}\'/>' +
|
||||||
modifiers: '{"invisible": true}',
|
'</form>',
|
||||||
name: "allday"
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
tag: 'field'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
attrs: {
|
|
||||||
modifiers: '{"invisible": [["allday","=",false]]}',
|
|
||||||
name: "start"
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
tag: 'field'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
attrs: {
|
|
||||||
modifiers: '{"invisible": [["allday","=",true]]}',
|
|
||||||
name: "stop"
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
tag: 'field'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
tag: "form"
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
QUnit.test('simple calendar rendering', function (assert) {
|
QUnit.test('simple calendar rendering', function (assert) {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ var DiagramView = BasicView.extend({
|
|||||||
init: function (viewInfo, params) {
|
init: function (viewInfo, params) {
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
var self = this;
|
var self = this;
|
||||||
var arch = viewInfo.arch;
|
var arch = this.arch;
|
||||||
// Compute additional data for diagram model
|
// Compute additional data for diagram model
|
||||||
function toTitleCase(str) {
|
function toTitleCase(str) {
|
||||||
return str.replace(/\w\S*/g, function (txt) {
|
return str.replace(/\w\S*/g, function (txt) {
|
||||||
|
|||||||
Reference in New Issue
Block a user