Files
odoo_source/addons/board/static/src/js/board_view.js
T

472 lines
16 KiB
JavaScript

odoo.define('board.BoardView', function (require) {
"use strict";
var Context = require('web.Context');
var core = require('web.core');
var dataManager = require('web.data_manager');
var Dialog = require('web.Dialog');
var Domain = require('web.Domain');
var FormController = require('web.FormController');
var FormRenderer = require('web.FormRenderer');
var FormView = require('web.FormView');
var pyUtils = require('web.pyeval'); // do not forwardport this to 12.0
var session = require('web.session');
var viewRegistry = require('web.view_registry');
var _t = core._t;
var _lt = core._lt;
var QWeb = core.qweb;
var BoardController = FormController.extend({
custom_events: _.extend({}, FormController.prototype.custom_events, {
change_layout: '_onChangeLayout',
enable_dashboard: '_onEnableDashboard',
save_dashboard: '_saveDashboard',
switch_view: '_onSwitchView',
}),
/**
* @override
*/
init: function (parent, model, renderer, params) {
this._super.apply(this, arguments);
this.customViewID = params.customViewID;
},
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
/**
* @override
*/
getTitle: function () {
if (this.inDashboard) {
return _t("My Dashboard");
}
return this._super.apply(this, arguments);
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* Actually save a dashboard
*
* @returns {Deferred}
*/
_saveDashboard: function () {
var board = this.renderer.getBoard();
var arch = QWeb.render('DashBoard.xml', _.extend({}, board));
return this._rpc({
route: '/web/view/edit_custom',
params: {
custom_id: this.customViewID,
arch: arch,
}
}).then(dataManager.invalidate.bind(dataManager));
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
/**
* @private
* @param {OdooEvent} event
*/
_onChangeLayout: function (event) {
var self = this;
var dialog = new Dialog(this, {
title: _t("Edit Layout"),
$content: QWeb.render('DashBoard.layouts', _.clone(event.data))
});
dialog.opened().then(function () {
dialog.$('li').click(function () {
var layout = $(this).attr('data-layout');
self.renderer.changeLayout(layout);
self._saveDashboard();
dialog.close();
});
});
dialog.open();
},
/**
* @private
*/
_onEnableDashboard: function () {
this.inDashboard = true;
},
/**
* We need to intercept switch_view event coming from sub views, because we
* don't actually want to switch view in dashboard, we want to do a
* do_action (which will open the record in a different breadcrumb).
*
* @private
* @param {OdooEvent} event
*/
_onSwitchView: function (event) {
event.stopPropagation();
this.do_action({
type: 'ir.actions.act_window',
res_model: event.data.model,
views: [[event.data.formViewID || false, 'form']],
res_id: event.data.res_id,
});
},
});
var BoardRenderer = FormRenderer.extend({
custom_events: _.extend({}, FormRenderer.prototype.custom_events, {
do_action: '_onDoAction',
env_updated: '_onEnvUpdated',
update_filters: '_onUpdateFilters',
switch_view: '_onSwitchView',
}),
events: _.extend({}, FormRenderer.prototype.events, {
'click .oe_dashboard_column .oe_fold': '_onFoldClick',
'click .oe_dashboard_link_change_layout': '_onChangeLayout',
'click .oe_dashboard_column .oe_close': '_onCloseAction',
}),
/**
* @override
*/
init: function (parent, state, params) {
this._super.apply(this, arguments);
this.noContentHelp = params.noContentHelp;
this.actionsDescr = {};
this._boardSubcontrollers = []; // for board: controllers of subviews
this._boardFormViewIDs = {}; // for board: mapping subview controller to form view id
},
/**
* Call `on_attach_callback` for each subview
*
* @override
*/
on_attach_callback: function () {
_.each(this._boardSubcontrollers, function (controller) {
if ('on_attach_callback' in controller) {
controller.on_attach_callback();
}
});
},
/**
* Call `on_detach_callback` for each subview
*
* @override
*/
on_detach_callback: function () {
_.each(this._boardSubcontrollers, function (controller) {
if ('on_detach_callback' in controller) {
controller.on_detach_callback();
}
});
},
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
/**
* @param {string} layout
*/
changeLayout: function (layout) {
var $dashboard = this.$('.oe_dashboard');
var current_layout = $dashboard.attr('data-layout');
if (current_layout !== layout) {
var clayout = current_layout.split('-').length,
nlayout = layout.split('-').length,
column_diff = clayout - nlayout;
if (column_diff > 0) {
var $last_column = $();
$dashboard.find('.oe_dashboard_column').each(function (k, v) {
if (k >= nlayout) {
$(v).find('.oe_action').appendTo($last_column);
} else {
$last_column = $(v);
}
});
}
$dashboard.toggleClass('oe_dashboard_layout_' + current_layout + ' oe_dashboard_layout_' + layout);
$dashboard.attr('data-layout', layout);
}
},
/**
* Returns a representation of the current dashboard
*
* @returns {Object}
*/
getBoard: function () {
var self = this;
var board = {
form_title : this.arch.attrs.string,
style : this.$('.oe_dashboard').attr('data-layout'),
columns : [],
};
this.$('.oe_dashboard_column').each(function () {
var actions = [];
$(this).find('.oe_action').each(function () {
var actionID = $(this).attr('data-id');
var newAttrs = _.clone(self.actionsDescr[actionID]);
/* prepare attributes as they should be saved */
if (newAttrs.modifiers) {
newAttrs.modifiers = JSON.stringify(newAttrs.modifiers);
}
actions.push(newAttrs);
});
board.columns.push(actions);
});
return board;
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
/**
* @private
* @param {Object} params
* @param {jQueryElement} params.$node
* @param {integer} params.actionID
* @param {Object} params.context
* @param {any[]} params.domain
* @param {string} params.viewType
* @returns {Deferred}
*/
_createController: function (params) {
var self = this;
return this._rpc({
route: '/web/action/load',
params: {action_id: params.actionID}
})
.then(function (action) {
if (!action) {
// the action does not exist anymore
return $.when();
}
// tz and lang are saved in the custom view
// override the language to take the current one
var rawContext = new Context(params.context, action.context, {lang: session.user_context.lang});
var context = pyUtils.eval('context', rawContext);
var domain = params.domain || pyUtils.eval('domain', action.domain || '[]', action.context);
var viewType = params.viewType || action.views[0][1];
var view = _.find(action.views, function (descr) {
return descr[1] === viewType;
}) || [false, viewType];
return self.loadViews(action.res_model, context, [view])
.then(function (viewsInfo) {
var viewInfo = viewsInfo[viewType];
var View = viewRegistry.get(viewType);
var view = new View(viewInfo, {
action: action,
context: context,
domain: domain,
groupBy: context.group_by || [],
modelName: action.res_model,
hasSelectors: false,
});
return view.getController(self).then(function (controller) {
self._boardFormViewIDs[controller.handle] = _.first(
_.find(action.views, function (descr) {
return descr[1] === 'form';
})
);
self._boardSubcontrollers.push(controller);
return controller.appendTo(params.$node);
});
});
});
},
/**
* @private
* @param {Object} node
* @returns {jQueryElement}
*/
_renderTagBoard: function (node) {
var self = this;
// we add the o_dashboard class to the renderer's $el. This means that
// this function has a side effect. This is ok because we assume that
// once we have a '<board>' tag, we are in a special dashboard mode.
this.$el.addClass('o_dashboard');
this.trigger_up('enable_dashboard');
var hasAction = _.detect(node.children, function (column) {
return _.detect(column.children,function (element){
return element.tag === "action"? element: false;
});
});
if (!hasAction) {
return $(QWeb.render('DashBoard.NoContent'));
}
// We should start with three columns available
node = $.extend(true, {}, node);
// no idea why master works without this, but whatever
if (!('layout' in node.attrs)) {
node.attrs.layout = node.attrs.style;
}
for (var i = node.children.length; i < 3; i++) {
node.children.push({
tag: 'column',
attrs: {},
children: []
});
}
// register actions, alongside a generated unique ID
_.each(node.children, function (column, column_index) {
_.each(column.children, function (action, action_index) {
action.attrs.id = 'action_' + column_index + '_' + action_index;
self.actionsDescr[action.attrs.id] = action.attrs;
});
});
var $html = $('<div>').append($(QWeb.render('DashBoard', {node: node})));
// render each view
_.each(this.actionsDescr, function (action) {
self.defs.push(self._createController({
$node: $html.find('.oe_action[data-id=' + action.id + '] .oe_content'),
actionID: _.str.toNumber(action.name),
context: new Context(action.context),
domain: Domain.prototype.stringToArray(action.domain, {}),
viewType: action.view_mode,
}));
});
$html.find('.oe_dashboard_column').sortable({
connectWith: '.oe_dashboard_column',
handle: '.oe_header',
scroll: false
}).bind('sortstop', function () {
self.trigger_up('save_dashboard');
});
return $html;
},
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
/**
* @private
*/
_onChangeLayout: function () {
var currentLayout = this.$('.oe_dashboard').attr('data-layout');
this.trigger_up('change_layout', {currentLayout: currentLayout});
},
/**
* @private
* @param {MouseEvent} event
*/
_onCloseAction: function (event) {
var self = this;
var $container = $(event.currentTarget).parents('.oe_action:first');
Dialog.confirm(this, (_t("Are you sure you want to remove this item?")), {
confirm_callback: function () {
$container.remove();
self.trigger_up('save_dashboard');
},
});
},
/**
* Intercepts (without stopping) 'do_action' events to force the
* 'keepSearchView' option to false, as the dashboard action has no search
* view, and thus there is no search view that could be re-used for the
* action to execute (a new one will be created instead).
*
* @private
* @param {OdooEvent} event
*/
_onDoAction: function (event) {
if (event.data.options) {
event.data.options.keepSearchView = false;
}
},
/**
* Stops the propagation of 'env_updated' events triggered by the controllers
* instantiated by the dashboard.
*
* @private
*/
_onEnvUpdated: function (event) {
event.stopPropagation();
},
/**
* @private
* @param {MouseEvent} event
*/
_onFoldClick: function (event) {
var $e = $(event.currentTarget);
var $action = $e.closest('.oe_action');
var id = $action.data('id');
var actionAttrs = this.actionsDescr[id];
if ($e.is('.oe_minimize')) {
actionAttrs.fold = '1';
} else {
delete(actionAttrs.fold);
}
$e.toggleClass('oe_minimize oe_maximize');
$action.find('.oe_content').toggle();
this.trigger_up('save_dashboard');
},
/**
* Let FormController know which form view it should display based on the
* window action of the sub controller that is switching view
*
* @private
* @param {OdooEvent} event
*/
_onSwitchView: function (event) {
event.data.formViewID = this._boardFormViewIDs[event.target.handle];
},
/**
* Stops the propagation of 'update_filters' events triggered by the
* controllers instantiated by the dashboard to prevent them from
* interfering with the ActionManager.
*
* @private
* @param {OdooEvent} event
*/
_onUpdateFilters: function (event) {
event.stopPropagation();
},
});
var BoardView = FormView.extend({
config: _.extend({}, FormView.prototype.config, {
Controller: BoardController,
Renderer: BoardRenderer,
}),
display_name: _lt('Board'),
/**
* @override
*/
init: function (viewInfo) {
this._super.apply(this, arguments);
this.controllerParams.customViewID = viewInfo.custom_view_id;
},
});
return BoardView;
});
odoo.define('board.viewRegistry', function (require) {
"use strict";
var BoardView = require('board.BoardView');
var viewRegistry = require('web.view_registry');
viewRegistry.add('board', BoardView);
});