diff options
| author | fwi-odoo <fwi@odoo.com> | 2017-04-25 13:55:38 +0200 |
|---|---|---|
| committer | fwi-odoo <fwi@odoo.com> | 2017-05-03 13:41:07 +0200 |
| commit | a53335fdb2fe6e9232bde66f06e9499bece452d4 (patch) | |
| tree | 662cb9a587ed2b076f0ffe4896d2e75ce120ae34 /addons/web/static/src | |
| parent | 30384249e11d1d74b19bf2b63d4fd9fe6a99bad1 (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.js | 295 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/basic/basic_controller.js | 39 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/form/form_controller.js | 25 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/form/form_view.js | 2 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/list/list_controller.js | 41 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/list/list_view.js | 1 | ||||
| -rw-r--r-- | addons/web/static/src/xml/base.xml | 8 |
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> |
