From 10513dc524cb46489fe3ed890359712e8a087611 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 3 Oct 2012 13:42:58 +0200 Subject: [PATCH] [IMP] client actions: allow functions, not just widgets bzr revid: xmo@openerp.com-20121003114258-vp3mg1yqps4qfkyp --- addons/web/static/src/js/chrome.js | 39 +++++------- addons/web/static/src/js/views.js | 9 +++ doc/guides/client-action.rst | 96 ++++++++++++++++++------------ doc/guides/sidebar-protocol.rst | 78 ------------------------ doc/index.rst | 2 + 5 files changed, 85 insertions(+), 139 deletions(-) delete mode 100644 doc/guides/sidebar-protocol.rst diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index 3fe5e9c952c..bed4a159535 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -614,39 +614,32 @@ instance.web.client_actions.add("login", "instance.web.Login"); * Client action to reload the whole interface. * If params has an entry 'menu_id', it opens the given menu entry. */ -instance.web.Reload = instance.web.Widget.extend({ - init: function(parent, params) { - this._super(parent); - this.menu_id = (params && params.menu_id) || false; - }, - start: function() { - var l = window.location; +instance.web.Reload = function(parent, params) { + var menu_id = (params && params.menu_id) || false; + var l = window.location; - var sobj = $.deparam(l.search.substr(1)); - sobj.ts = new Date().getTime(); - var search = '?' + $.param(sobj); + var sobj = $.deparam(l.search.substr(1)); + sobj.ts = new Date().getTime(); + var search = '?' + $.param(sobj); - var hash = l.hash; - if (this.menu_id) { - hash = "#menu_id=" + this.menu_id; - } - var url = l.protocol + "//" + l.host + l.pathname + search + hash; - window.location = url; + var hash = l.hash; + if (menu_id) { + hash = "#menu_id=" + menu_id; } -}); + var url = l.protocol + "//" + l.host + l.pathname + search + hash; + window.location = url; +}; instance.web.client_actions.add("reload", "instance.web.Reload"); /** * Client action to go back in breadcrumb history. * If can't go back in history stack, will go back to home. */ -instance.web.HistoryBack = instance.web.Widget.extend({ - init: function(parent, params) { - if (!parent.history_back()) { - window.location = '/' + (window.location.search || ''); - } +instance.web.HistoryBack = function(parent, params) { + if (!parent.history_back()) { + window.location = '/' + (window.location.search || ''); } -}); +}; instance.web.client_actions.add("history_back", "instance.web.HistoryBack"); /** diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index 715da126f0d..2fff0e7b683 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -313,6 +313,15 @@ instance.web.ActionManager = instance.web.Widget.extend({ ir_actions_client: function (action, on_close, clear_breadcrumbs) { var self = this; var ClientWidget = instance.web.client_actions.get_object(action.tag); + + if (!(ClientWidget.prototype instanceof instance.web.Widget)) { + var next; + if (next = ClientWidget(this, action.params)) { + return this.do_action(next, on_close, clear_breadcrumbs); + } + return $.when(); + } + return this.ir_actions_common({ widget: function () { return new ClientWidget(self, action.params); }, action: action, diff --git a/doc/guides/client-action.rst b/doc/guides/client-action.rst index 2e1177371a2..30f0c7eeb27 100644 --- a/doc/guides/client-action.rst +++ b/doc/guides/client-action.rst @@ -1,15 +1,18 @@ +.. highlight:: javascript + Creating a new client action ============================ -Client actions are the client-side of OpenERP's "Server Actions": instead of -allowing for semi-arbitrary code to be executed in the server, they allow -for execution of client-customized code. +Client actions are the client-side version of OpenERP's "Server +Actions": instead of allowing for semi-arbitrary code to be executed +in the server, they allow for execution of client-customized code. -On the server side, a client action is an action of type ``ir.actions.client``, -which has (at most) two properties: a mandatory ``tag``, which is an arbitrary -string by which the client will identify the action, and an optional ``params`` -which is simply a map of keys and values sent to the client as-is (this way, -client actions can be made generic and reused in multiple contexts). +On the server side, a client action is an action of type +``ir.actions.client``, which has (at most) two properties: a mandatory +``tag``, which is an arbitrary string by which the client will +identify the action, and an optional ``params`` which is simply a map +of keys and values sent to the client as-is (this way, client actions +can be made generic and reused in multiple contexts). General Structure ----------------- @@ -17,34 +20,48 @@ General Structure In the OpenERP Web code, a client action only requires two pieces of information: -* Mapping the action's ``tag`` to an OpenERP Web object +* Mapping the action's ``tag`` to an object -* The OpenERP Web object itself, which must inherit from - :js:class:`openerp.web.Widget` +* Providing said object. Two different types of objects can be mapped + to a client action: -Our example will be the actual code for the widgets client action (a client -action displaying a ``res.widget`` object, used in the homepage dashboard of -the web client): + * An OpenERP Web widget, which must inherit from + :js:class:`openerp.web.Widget` -.. code-block:: javascript + * A regular javascript function + +The major difference is in the lifecycle of these: + +* if the client action maps to a function, the function will simply be + called when executing the action. The function can have no further + interaction with the Web Client itself, although it can return an + action which will be executed after it. + +* if, on the other hand, the client action maps to a + :js:class:`~openerp.web.Widget`, that + :js:class:`~openerp.web.Widget` will be instantiated and added to + the web client's canvas, with the usual + :js:class:`~openerp.web.Widget` lifecycle (essentially, it will + either take over the content area of the client or it will be + integrated within a dialog). + +For example, to create a client action displaying a ``res.widget`` +object:: // Registers the object 'openerp.web_dashboard.Widget' to the client // action tag 'board.home.widgets' - openerp.web.client_actions.add( + instance.web.client_actions.add( 'board.home.widgets', 'openerp.web_dashboard.Widget'); - // This object inherits from View, but only Widget is required - openerp.web_dashboard.Widget = openerp.web.View.extend({ + instance.web_dashboard.Widget = instance.web.Widget.extend({ template: 'HomeWidget' }); -At this point, the generic ``Widget`` lifecycle takes over, the template is -rendered, inserted in the client DOM, bound on the object's ``$element`` -property and the object is started. +At this point, the generic :js:class:`~openerp.web.Widget` lifecycle +takes over, the template is rendered, inserted in the client DOM, +bound on the object's ``$el`` property and the object is started. If the client action takes parameters, these parameters are passed in as a -second positional parameter to the constructor: - -.. code-block:: javascript +second positional parameter to the constructor:: init: function (parent, params) { // execute the Widget's init @@ -54,15 +71,19 @@ second positional parameter to the constructor: this.widget_id = params.widget_id; } -More complex initialization (DOM manipulations, RPC requests, ...) should be -performed in the ``start()`` method. +More complex initialization (DOM manipulations, RPC requests, ...) +should be performed in the :js:func:`~openerp.web.Widget.start()` +method. .. note:: - As required by ``Widget``'s contract, if ``start`` executes any - asynchronous code it should return a ``$.Deferred`` so callers know when - it's ready for interaction. - Although generally speaking client actions are not really interacted with. + As required by :js:class:`~openerp.web.Widget`'s contract, if + :js:func:`~openerp.web.Widget.start()` executes any asynchronous + code it should return a ``$.Deferred`` so callers know when it's + ready for interaction. + + Although generally speaking client actions are not really + interacted with. .. code-block:: javascript @@ -70,22 +91,21 @@ performed in the ``start()`` method. return $.when( this._super(), // Simply read the res.widget object this action should display - new openerp.web.DataSet(this, 'res.widget').read_ids( - [this.widget_id], ['title'], this.on_widget_loaded)); + new instance.web.Model('res.widget').call( + 'read', [[this.widget_id], ['title']]) + .then(this.proxy('on_widget_loaded')); } -The client action can then behave exactly as it wishes to within its root -(``this.$element``). In this case, it performs further renderings once its -widget's content is retrieved: - -.. code-block:: javascript +The client action can then behave exactly as it wishes to within its +root (``this.$el``). In this case, it performs further renderings once +its widget's content is retrieved:: on_widget_loaded: function (widgets) { var widget = widgets[0]; var url = _.sprintf( '/web_dashboard/widgets/content?session_id=%s&widget_id=%d', this.session.session_id, widget.id); - this.$element.html(QWeb.render('HomeWidget.content', { + this.$el.html(QWeb.render('HomeWidget.content', { widget: widget, url: url })); diff --git a/doc/guides/sidebar-protocol.rst b/doc/guides/sidebar-protocol.rst deleted file mode 100644 index 83626c32fc3..00000000000 --- a/doc/guides/sidebar-protocol.rst +++ /dev/null @@ -1,78 +0,0 @@ -Adding a sidebar to a view -========================== - -Initialization --------------- - -Each view has the responsibility to create its sidebar (or not) if and only if -the ``sidebar`` flag is set in its options. - -In that case, it should use the ``sidebar_id`` value (from its options) to -initialize the sidebar at the right position in the DOM: - -.. code-block:: javascript - - if (this.options.sidebar && this.options.sidebar_id) { - this.sidebar = new openerp.web.Sidebar(this, this.options.sidebar_id); - this.sidebar.start(); - } - -Because the sidebar is an old-style widget, it must be started after being -initialized. - -Sidebar communication protocol ------------------------------- - -In order to behave correctly, a sidebar needs informations from its parent -view. - -This information is extracted via a very basic protocol consisting of a -property and two methods: - -.. js:attribute:: dataset - - the view's dataset, used to fetch the currently active model and provide it - to remote action handlers as part of the basic context - -.. js:function:: get_selected_ids() - - Used to query the parent view for the set of currently available record - identifiers. Used to setup the basic context's ``active_id`` and - ``active_ids`` keys. - - .. warning:: - - :js:func:`get_selected_ids` must return at least one id - - :returns: an array of at least one id - :rtype: Array - -.. js:function:: sidebar_context() - - Queries the view for additional context data to provide to the sidebar. - - :js:class:`~openerp.base.View` provides a default NOOP implementation, - which simply resolves to an empty object. - - :returns: a promise yielding an object on success, this object is mergeed - into the sidebar's own context - :rtype: $.Deferred - -Programmatic folding and unfolding ----------------------------------- - -The sidebar object starts folded. It provides three methods to handle its -folding status: - -.. js:function:: do_toggle - - Toggles the status of the sidebar - -.. js:function:: do_fold - - Forces the sidebar closed if it's currently open - -.. js:function:: do_unfold - - Forces the sidebar open if it's currently closed - diff --git a/doc/index.rst b/doc/index.rst index 0f0c4bf87f5..a6d2248cedf 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -24,6 +24,8 @@ Contents: templates + guides/client-action + Indices and tables ==================