summaryrefslogtreecommitdiff
path: root/addons/web/static/src
diff options
context:
space:
mode:
authorfwi-odoo <fwi@odoo.com>2017-04-25 13:55:38 +0200
committerfwi-odoo <fwi@odoo.com>2017-05-03 13:41:07 +0200
commita53335fdb2fe6e9232bde66f06e9499bece452d4 (patch)
tree662cb9a587ed2b076f0ffe4896d2e75ce120ae34 /addons/web/static/src
parent30384249e11d1d74b19bf2b63d4fd9fe6a99bad1 (diff)
[FIX] web, document: adapt toolbar to the new views
- Fix the toolbar behaviour since there is no more dataset in the widget. - Improve the communication beetwen the controller and the toolbar. - Move the code linked to the document upload in the right file. - Adapt the tests to mock the search_read done by ´document´ addon - Adapt the mock server to be able to give a custom toolbar action
Diffstat (limited to 'addons/web/static/src')
-rw-r--r--addons/web/static/src/js/chrome/sidebar.js295
-rw-r--r--addons/web/static/src/js/views/basic/basic_controller.js39
-rw-r--r--addons/web/static/src/js/views/form/form_controller.js25
-rw-r--r--addons/web/static/src/js/views/form/form_view.js2
-rw-r--r--addons/web/static/src/js/views/list/list_controller.js41
-rw-r--r--addons/web/static/src/js/views/list/list_view.js1
-rw-r--r--addons/web/static/src/xml/base.xml8
7 files changed, 230 insertions, 181 deletions
diff --git a/addons/web/static/src/js/chrome/sidebar.js b/addons/web/static/src/js/chrome/sidebar.js
index 3cd17334e9e..bf9e3668c35 100644
--- a/addons/web/static/src/js/chrome/sidebar.js
+++ b/addons/web/static/src/js/chrome/sidebar.js
@@ -3,9 +3,6 @@ odoo.define('web.Sidebar', function (require) {
var Context = require('web.Context');
var core = require('web.core');
-var data = require('web.data');
-var Dialog = require('web.Dialog');
-var framework = require('web.framework');
var pyeval = require('web.pyeval');
var Widget = require('web.Widget');
@@ -13,12 +10,25 @@ var QWeb = core.qweb;
var _t = core._t;
var Sidebar = Widget.extend({
- init: function(parent, options) {
- var self = this;
- this._super(parent);
+ events: {
+ "click .dropdown-menu li a": "_onDropdownClicked"
+ },
+ /**
+ * @override
+ *
+ * @param {Object} options
+ * @param {Object} options.items
+ * @param {Object} options.sections
+ * @param {Object} options.env
+ * @param {Object} options.actions
+ *
+ */
+ init: function (parent, options) {
+ this._super.apply(this, arguments);
this.options = _.defaults(options || {}, {
'editable': true
});
+ this.env = options.env;
this.sections = options.sections || [
{name: 'print', label: _t('Print')},
{name: 'other', label: _t('Action')},
@@ -27,51 +37,37 @@ var Sidebar = Widget.extend({
print: [],
other: [],
};
- this.fileupload_id = _.uniqueId('oe_fileupload');
- $(window).on(this.fileupload_id, function() {
- var args = [].slice.call(arguments).slice(1);
- self.do_attachement_update(self.dataset, self.model_id,args);
- framework.unblockUI();
- });
+ if (options.actions) {
+ this._addToolbarActions(options.actions);
+ }
},
- start: function() {
- var self = this;
- this._super(this);
+ /**
+ * @override
+ */
+ start: function () {
+ this._super.apply(this, arguments);
this.$el.addClass('btn-group');
- this.redraw();
- this.$el.on('click','.dropdown-menu li a', function(event) {
- var section = $(this).data('section');
- var index = $(this).data('index');
- var item = self.items[section][index];
- if (item.callback) {
- item.callback.apply(self, [item]);
- } else if (item.action) {
- self.on_item_action_clicked(item);
- } else if (item.url) {
- return true;
- }
- event.preventDefault();
- });
+ this._redraw();
},
- destroy: function() {
- $(window).off(this.fileupload_id);
- return this._super.apply(this, arguments);
- },
- redraw: function() {
- this.$el.html(QWeb.render('Sidebar', {widget: this}));
- // Hides Sidebar sections when item list is empty
- this.$('.o_dropdown').each(function() {
- if (!$(this).find('li').length) {
- $(this).hide();
- }
- });
- this.$("[title]").tooltip({
- delay: { show: 500, hide: 0}
- });
- this.$('.o_sidebar_add_attachment .o_form_binary_form').change(this.on_attachment_changed);
- this.$('.o_sidebar_delete_attachment').click(this.on_attachment_delete);
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ /**
+ * Update the env for the sidebar then rerender it.
+ *
+ * @param {Object} env
+ */
+ updateEnv: function (env) {
+ this.env = env;
+ this._redraw();
},
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
/**
* For each item added to the section:
*
@@ -86,7 +82,7 @@ var Sidebar = Widget.extend({
* function to call when the item is clicked in the sidebar, called
* with the item descriptor as its first argument (so information
* can be stored as additional keys on the object passed to
- * ``add_items``)
+ * ``_addItems``)
*
* ``classname`` (optional)
* ``@class`` set on the sidebar serialization of the item
@@ -94,135 +90,126 @@ var Sidebar = Widget.extend({
* ``title`` (optional)
* will be set as the item's ``@title`` (tooltip)
*
- * @param {String} section_code
+ * @private
+ * @param {String} sectionCode
* @param {Array<{label, action | callback[, classname][, title]}>} items
*/
- add_items: function(section_code, items) {
+ _addItems: function (sectionCode, items) {
if (items) {
- this.items[section_code].unshift.apply(this.items[section_code],items);
- this.redraw();
+ this.items[sectionCode].unshift.apply(this.items[sectionCode], items);
}
},
- add_toolbar: function(toolbar) {
+ /**
+ * Method that will add the custom actions to the toolbar
+ *
+ * @private
+ * @param {Object} toolbarActions
+ */
+ _addToolbarActions: function (toolbarActions) {
var self = this;
- _.each(['print','action','relate'], function(type) {
- var items = toolbar[type];
- if (items) {
- var actions = _.map(items, function (item) {
- return {
- label: item.name,
- action: item,
- };
- });
- self.add_items(type === 'print' ? 'print' : 'other', actions);
+ _.each(['print','action','relate'], function (type) {
+ if (type in toolbarActions) {
+ var actions = toolbarActions[type];
+ if (actions && actions.length) {
+ var items = _.map(actions, function (action) {
+ return {
+ label: action.name,
+ action: action,
+ };
+ });
+ self._addItems(type === 'print' ? 'print' : 'other', items);
+ }
}
});
+ if ('other' in toolbarActions) {
+ this._addItems('other', toolbarActions.other);
+ }
},
- on_item_action_clicked: function(item) {
+ /**
+ * Performs the action for the item clicked after getting the data
+ * necessary with a trigger up
+ *
+ * @private
+ * @param {Object} item
+ */
+ _onItemActionClicked: function (item) {
var self = this;
- self.getParent().sidebar_eval_context().done(function (sidebar_eval_context) {
- var ids = self.getParent().getSelectedIds();
- var domain;
- if (self.getParent().getActiveDomain) {
- domain = self.getParent().getActiveDomain();
- }
- else {
- domain = $.Deferred().resolve(undefined);
- }
- if (ids.length === 0) {
- new Dialog(this, {title: _t("Warning"), size: 'medium', $content: $("<div/>").html(_t("You must choose at least one record."))}).open();
- return false;
- }
- var dataset = self.getParent().dataset;
- var active_ids_context = {
- active_id: ids[0],
- active_ids: ids,
- active_model: dataset.model,
- };
-
- $.when(domain).done(function (domain) {
- if (domain !== undefined) {
- active_ids_context.active_domain = domain;
+ this.trigger_up('sidebar_data_asked', {
+ callback: function (env) {
+ self.env = env;
+ var activeIdsContext = {
+ active_id: env.activeIds[0],
+ active_ids: env.activeIds,
+ active_model: env.model,
+ };
+ if (env.domain) {
+ activeIdsContext.active_domain = env.domain;
}
- var c = pyeval.eval('context',
- new Context(
- sidebar_eval_context, active_ids_context));
+ var context = pyeval.eval('context', new Context(env.context, activeIdsContext));
self._rpc({
- route: '/web/action/load',
- params: {
- action_id: item.action.id,
- context: new Context(
- dataset.get_context(), active_ids_context).eval()
+ route: '/web/action/load',
+ params: {
+ action_id: item.action.id,
+ context: context,
+ },
+ }).done(function (result) {
+ result.context = new Context(
+ result.context || {}, activeIdsContext)
+ .set_eval_context(context);
+ result.flags = result.flags || {};
+ result.flags.new_window = true;
+ self.do_action(result, {
+ on_close: function () {
+ self.trigger_up('reload');
},
- })
- .done(function(result) {
- result.context = new Context(
- result.context || {}, active_ids_context)
- .set_eval_context(c);
- result.flags = result.flags || {};
- result.flags.new_window = true;
- self.do_action(result, {
- on_close: function() {
- // reload view
- self.getParent().reload();
- },
- });
});
- });
+ });
+ }
});
},
- do_attachement_update: function(dataset, model_id, args) {
- this.dataset = dataset;
- this.model_id = model_id;
- var upload_error = _.filter(args, function(attachment) {return attachment.error;});
- if (upload_error.length) {
- this.do_warn(_t('Uploading Error'), upload_error[0].error);
- }
- if (!model_id) {
- this.on_attachments_loaded([]);
- } else {
- var dom = [ ['res_model', '=', dataset.model], ['res_id', '=', model_id], ['type', 'in', ['binary', 'url']] ];
- var ds = new data.DataSetSearch(this, 'ir.attachment', dataset.get_context(), dom);
- ds.read_slice(['name', 'url', 'type', 'create_uid', 'create_date', 'write_uid', 'write_date'], {}).done(this.on_attachments_loaded);
- }
- },
- on_attachments_loaded: function(attachments) {
- _.each(attachments,function(a) {
- a.label = a.name;
- if(a.type === "binary") {
- a.url = '/web/content/' + a.id + '?download=true';
+ /**
+ * Method that renders the sidebar when there is a data update
+ *
+ * @private
+ */
+ _redraw: function () {
+ this.$el.html(QWeb.render('Sidebar', {widget: this}));
+
+ // Hides Sidebar sections when item list is empty
+ this.$('.o_dropdown').each(function () {
+ if (!$(this).find('li').length) {
+ $(this).hide();
}
});
- this.items.files = attachments;
- this.redraw();
+ this.$("[title]").tooltip({
+ delay: { show: 500, hide: 0}
+ });
},
- on_attachment_changed: function(e) {
- var $e = $(e.target);
- if ($e.val() !== '') {
- this.$('form.o_form_binary_form').submit();
- $e.parent().find('input[type=file]').prop('disabled', true);
- $e.parent().find('button').prop('disabled', true).find('img, span').toggle();
- this.$('.o_sidebar_add_attachment a').text(_t('Uploading...'));
- framework.blockUI();
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Method triggered when the user clicks on a toolbar dropdown
+ *
+ * @private
+ * @param {MouseEvent} event
+ */
+ _onDropdownClicked: function (event) {
+ var section = $(event.currentTarget).data('section');
+ var index = $(event.currentTarget).data('index');
+ var item = this.items[section][index];
+ if (item.callback) {
+ item.callback.apply(this, [item]);
+ } else if (item.action) {
+ this._onItemActionClicked(item);
+ } else if (item.url) {
+ return true;
}
+ event.preventDefault();
},
- on_attachment_delete: function(e) {
- e.preventDefault();
- e.stopPropagation();
- var self = this;
- var $e = $(e.currentTarget);
- var options = {
- confirm_callback: function () {
- new data.DataSet(this, 'ir.attachment')
- .unlink([parseInt($e.attr('data-id'), 10)])
- .done(function() {
- self.do_attachement_update(self.dataset, self.model_id);
- });
- }
- };
- Dialog.confirm(this, _t("Do you really want to delete this attachment ?"), options);
- }
});
return Sidebar;
diff --git a/addons/web/static/src/js/views/basic/basic_controller.js b/addons/web/static/src/js/views/basic/basic_controller.js
index 33f6727987e..87e8d1a6664 100644
--- a/addons/web/static/src/js/views/basic/basic_controller.js
+++ b/addons/web/static/src/js/views/basic/basic_controller.js
@@ -19,6 +19,7 @@ var _t = core._t;
var BasicController = AbstractController.extend(FieldManagerMixin, {
custom_events: _.extend({}, AbstractController.prototype.custom_events, FieldManagerMixin.custom_events, {
reload: '_onReload',
+ sidebar_data_asked: '_onSidebarDataAsked'
}),
/**
* @override
@@ -127,6 +128,14 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
});
},
/**
+ * Method that will be overriden by the views with the ability to have selected ids
+ *
+ * @returns []
+ */
+ getSelectedIds: function () {
+ return [];
+ },
+ /**
* @override
*/
renderPager: function ($node, options) {
@@ -288,6 +297,19 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
}
},
/**
+ * Returns the new sidebar env
+ *
+ * @private
+ * @return {Object} the new sidebar env
+ */
+ _getSidebarEnv: function () {
+ return {
+ context: this.model.get(this.handle).getContext(),
+ activeIds: this.getSelectedIds(),
+ model: this.modelName,
+ };
+ },
+ /**
* Used by list and kanban views to determine whether or not to display
* the no content helper (if there is no data in the state to display)
*
@@ -389,9 +411,15 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
/**
* Helper method, to get the current environment variables from the model
* and notifies the component chain (by bubbling an event up)
+ *
+ * @private
*/
_updateEnv: function () {
var env = this.model.get(this.handle, {env: true});
+ if (this.sidebar) {
+ var sidebarEnv = this._getSidebarEnv();
+ this.sidebar.updateEnv(sidebarEnv);
+ }
this.trigger_up('env_updated', env);
},
/**
@@ -457,6 +485,17 @@ var BasicController = AbstractController.extend(FieldManagerMixin, {
this.reload({fieldNames: data.fieldNames});
}
},
+ /**
+ * Handler used to get all the data necessary when a custom action is
+ * performed through the sidebar.
+ *
+ * @private
+ * @param {OdooEvent} event
+ */
+ _onSidebarDataAsked: function (event) {
+ var sidebarEnv = this._getSidebarEnv();
+ event.data.callback(sidebarEnv);
+ },
});
return BasicController;
diff --git a/addons/web/static/src/js/views/form/form_controller.js b/addons/web/static/src/js/views/form/form_controller.js
index e3b129c7915..f15bdfa5382 100644
--- a/addons/web/static/src/js/views/form/form_controller.js
+++ b/addons/web/static/src/js/views/form/form_controller.js
@@ -21,6 +21,9 @@ var FormController = BasicController.extend({
}),
/**
* @override
+ *
+ * @param {boolean} params.hasSidebar
+ * @param {Object} params.toolbarActions
*/
init: function (parent, model, renderer, params) {
this._super.apply(this, arguments);
@@ -29,7 +32,7 @@ var FormController = BasicController.extend({
this.footerToButtons = params.footerToButtons;
this.defaultButtons = params.defaultButtons;
this.hasSidebar = params.hasSidebar;
- this.toolbar = params.toolbar;
+ this.toolbarActions = params.toolbarActions || {};
},
//--------------------------------------------------------------------------
@@ -71,13 +74,12 @@ var FormController = BasicController.extend({
* Returns the current res_id, wrapped in a list. This is only used by the
* sidebar (and the debugmanager)
*
- * @todo This should be private. Need to change sidebar code
+ * @override
*
* @returns {number[]} either [current res_id] or []
*/
getSelectedIds: function () {
var env = this.model.get(this.handle, {env: true});
- // FIX ME : fix sidebar widget
return env.currentId ? [env.currentId] : [];
},
/**
@@ -137,12 +139,6 @@ var FormController = BasicController.extend({
**/
renderSidebar: function ($node) {
if (!this.sidebar && this.hasSidebar) {
- this.sidebar = new Sidebar(this, {
- editable: this.is_action_enabled('edit')
- });
- if (this.toolbar) {
- this.sidebar.add_toolbar(this.toolbar);
- }
var otherItems = [];
if (this.is_action_enabled('delete')) {
otherItems.push({
@@ -156,7 +152,16 @@ var FormController = BasicController.extend({
callback: this._onDuplicateRecord.bind(this),
});
}
- this.sidebar.add_items('other', otherItems);
+ this.sidebar = new Sidebar(this, {
+ editable: this.is_action_enabled('edit'),
+ viewType: 'form',
+ env: {
+ context: this.model.get(this.handle).getContext(),
+ activeIds: this.getSelectedIds(),
+ model: this.modelName,
+ },
+ actions: _.extend(this.toolbarActions, {other: otherItems}),
+ });
this.sidebar.appendTo($node);
// Show or hide the sidebar according to the view mode
diff --git a/addons/web/static/src/js/views/form/form_view.js b/addons/web/static/src/js/views/form/form_view.js
index 4fff1a74ee4..aa6bdf69755 100644
--- a/addons/web/static/src/js/views/form/form_view.js
+++ b/addons/web/static/src/js/views/form/form_view.js
@@ -30,7 +30,7 @@ var FormView = BasicView.extend({
this.loadParams.parentID = params.parentID;
this.controllerParams.hasSidebar = params.sidebar;
- this.controllerParams.toolbar = false;
+ this.controllerParams.toolbarActions = viewInfo.toolbar;
this.controllerParams.footerToButtons = params.footer_to_buttons;
if ('action' in params && 'flags' in params.action) {
this.controllerParams.footerToButtons = params.action.flags.footer_to_buttons;
diff --git a/addons/web/static/src/js/views/list/list_controller.js b/addons/web/static/src/js/views/list/list_controller.js
index 50f47a73fc0..f2b15cca349 100644
--- a/addons/web/static/src/js/views/list/list_controller.js
+++ b/addons/web/static/src/js/views/list/list_controller.js
@@ -30,16 +30,15 @@ var ListController = BasicController.extend({
* @constructor
* @override
* @param {Object} params
- * @param {Object} params
* @param {boolean} params.editable
* @param {boolean} params.hasSidebar
- * @param {boolean} params.hasToolbar
+ * @param {Object} params.toolbarActions
* @param {boolean} params.noLeaf
*/
init: function (parent, model, renderer, params) {
this._super.apply(this, arguments);
this.hasSidebar = params.hasSidebar;
- this.hasToolbar = params.hasToolbar;
+ this.toolbarActions = params.toolbarActions || {};
this.editable = params.editable;
this.noLeaf = params.noLeaf;
this.selectedRecords = []; // there is no selected record by default
@@ -72,7 +71,7 @@ var ListController = BasicController.extend({
* if the header checkbox has been checked. This is done by evaluating the
* search results, and then adding the dataset domain (i.e. action domain).
*
- * @todo This is done only for the sidebar. The full mechanism is wrong,
+ * @todo This is done only for the data export. The full mechanism is wrong,
* this method should be private, most of the code in the sidebar should be
* moved to the controller, and we should not use the getParent method...
*
@@ -99,7 +98,7 @@ var ListController = BasicController.extend({
* Returns the list of currently selected res_ids (with the check boxes on
* the left)
*
- * @todo This should be private. Need to change sidebar code
+ * @override
*
* @returns {number[]} list of res_ids
*/
@@ -135,12 +134,6 @@ var ListController = BasicController.extend({
*/
renderSidebar: function ($node) {
if (this.hasSidebar && !this.sidebar) {
- this.sidebar = new Sidebar(this, {
- editable: this.is_action_enabled('edit')
- });
- if (this.hasToolbar) {
- this.sidebar.add_toolbar(this.fields_view.toolbar);
- }
var other = [{
label: _t("Export"),
callback: this._onExportData.bind(this)
@@ -160,8 +153,16 @@ var ListController = BasicController.extend({
label: _t('Delete'),
callback: this._onDeleteSelectedRecords.bind(this)
});
+ this.sidebar = new Sidebar(this, {
+ editable: this.is_action_enabled('edit'),
+ env: {
+ context: this.model.get(this.handle).getContext(),
+ activeIds: this.getSelectedIds(),
+ model: this.modelName,
+ },
+ actions: _.extend(this.toolbarActions, {other: other}),
+ });
}
- this.sidebar.add_items('other', other);
this.sidebar.appendTo($node);
this._toggleSidebar();
@@ -233,6 +234,15 @@ var ListController = BasicController.extend({
return this.renderer.confirmSave(state, id);
},
/**
+ * @override
+ * @private
+ */
+ _getSidebarEnv: function() {
+ var env = this._super.apply(this, arguments);
+ var record = this.model.get(this.handle);
+ return _.extend(env, {domain: record.getDomain()});
+ },
+ /**
* Allows to change the mode of a single row.
*
* @override
@@ -381,6 +391,13 @@ var ListController = BasicController.extend({
this._toggleSidebar();
},
/**
+ * @override
+ */
+ _onSidebarDataAsked: function (event) {
+ var env = this._getSidebarEnv();
+ event.data.callback(env);
+ },
+ /**
* Called when clicking on 'Archive' or 'Unarchive' in the sidebar.
*
* @private
diff --git a/addons/web/static/src/js/views/list/list_view.js b/addons/web/static/src/js/views/list/list_view.js
index 7b597c712bc..61612282bf5 100644
--- a/addons/web/static/src/js/views/list/list_view.js
+++ b/addons/web/static/src/js/views/list/list_view.js
@@ -41,6 +41,7 @@ var ListView = BasicView.extend({
this.controllerParams.editable = arch.attrs.editable;
this.controllerParams.hasSidebar = params.sidebar;
+ this.controllerParams.toolbarActions = viewInfo.toolbar;
this.controllerParams.activeActions.delete = true;
this.controllerParams.noLeaf = !!this.loadParams.context.group_by_no_leaf;
this.controllerParams.mode = mode;
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index fec8dd8c7ee..6ffe8fca2db 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -241,12 +241,12 @@
</li>
<li t-if="section.name == 'files' and widget.options.editable" class="o_sidebar_add_attachment">
<t t-call="HiddenInputFile">
- <t t-set="fileupload_id" t-value="widget.fileupload_id"/>
+ <t t-set="fileupload_id" t-value="widget.fileuploadId"/>
<t t-set="fileupload_action" t-translation="off">/web/binary/upload_attachment</t>
<t t-set="multi_upload" t-value="true"/>
- <input type="hidden" name="model" t-att-value="widget.dataset and widget.dataset.model"/>
- <input type="hidden" name="id" t-att-value="widget.model_id"/>
- <input type="hidden" name="session_id" t-att-value="widget.session.session_id" t-if="widget.session.override_session"/>
+ <input type="hidden" name="model" t-att-value="widget.env and widget.env.model"/>
+ <input type="hidden" name="id" t-att-value="widget.env.activeIds[0]"/>
+ <input type="hidden" name="session_id" t-att-value="widget.getSession().session_id" t-if="widget.getSession().override_session"/>
<span>Add...</span>
</t>
</li>