diff --git a/addons/base/static/openerp/js/base_views.js b/addons/base/static/openerp/js/base_views.js index 591b6857806..ec06f446280 100644 --- a/addons/base/static/openerp/js/base_views.js +++ b/addons/base/static/openerp/js/base_views.js @@ -614,9 +614,9 @@ openerp.base.search.Widget = openerp.base.Controller.extend({ /** * Sets and returns a globally unique identifier for the widget. * - * If a prefix is appended, the identifier will be appended to it. + * If a prefix is specified, the identifier will be appended to it. * - * @params sections prefix sections, empty/falsy sections will be removed + * @params prefix prefix sections, empty/falsy sections will be removed */ make_id: function () { this.element_id = _.uniqueId( diff --git a/doc/source/development.rst b/doc/source/development.rst index e6f31b21e83..a1d989b46ac 100644 --- a/doc/source/development.rst +++ b/doc/source/development.rst @@ -1,5 +1,5 @@ -Contributing to OpenERP Web -=========================== +OpenERP Web Core and standard addons +==================================== * General organization and core ideas (design philosophies) * Internal documentation, autodoc, Python and JS domains @@ -8,6 +8,278 @@ Contributing to OpenERP Web * Style guide and coding conventions (PEP8? More) * Test frameworks in JS? +Standard Views +-------------- + +Search View ++++++++++++ + +The OpenERP search view really is a sub-view, used in support of views +acting on collections of records (list view or graph view, for +instance). + +Its main goal is to collect information from its widgets (themselves +collecting information from the users) and make those available to the +rest of the client. + +The search view's root is :js:class:`~openerp.base.SearchView`. This +object should never need to be created or managed directly, its +lifecycle should be driven by the +:js:class:`~openerp.base.ViewManager`. + +.. TODO: insert SearchView constructor here + +The search view defines a number of internal and external protocols to +communicate with the objects around and within it. Most of these +protocols are informal, and types available for inheritance are more +mixins than mandatory. + +Events +"""""" + +``on_loaded`` + + .. TODO: method openerp.base.SearchView.on_loaded + + Fires when the search view receives its view data (the result of + ``fields_view_get``). Hooking up before the event allows for + altering view data before it can be used. + + By the time ``on_loaded`` is done, the search view is guaranteed to + be fully set up and ready to use. + +``on_search`` + + .. TODO: method openerp.base.SearchView.on_search + + Event triggered after a user asked for a search. The search view + fires this event after collecting all input data (contexts, domains + and group_by contexts). Note that the search view does *not* merge + those (or therwise evaluate them), they are returned as provided by + the various inputs within the view. + +``on_clear`` + + .. TODO: method openerp.base.SearchView.on_clear + + Triggered after a user asked for a form clearing. + +Input management +"""""""""""""""" + +An important concept in the search view is that of input. It is both +an informal protocol and an abstract type that can be inherited from. + +Inputs are widgets which can contain user data (a char widget for +instance, or a selection box). They are able of action and of +reaction: + +.. _views-search-registration: + +``registration`` + + This is an input action. Inputs have to register themselves to the + main view (which they receive as a constructor argument). This is + performed by pushing themselves on the + :js:attr:`openerp.base.SearchView.inputs` array. + +``get_context`` + + An input reaction. When it needs to collect contexts, the view calls + ``get_context()`` on all its inputs. + + Inputs can react in the following manners: + + * Return a context (an object), this is the "normal" response if the + input holds a value. + + * Return a falsy value (generally ``null``). This value indicates + the input does not contain any value and will not take part in the + research. + + * Raise :js:class:`openerp.base.search.Invalid` to indicate that it + holds a value but this value can not be used in the search + (because it is incorrectly formatted or nonsensical). Raising + :js:class:`~openerp.base.search.Invalid` is guaranteed to cancel + the search process. + + :js:class:`~openerp.base.search.Invalid` takes three mandatory + arguments: an indentifier (a name for instance), the invalid value + and a validation message indicating the issue. + +``get_domain`` + + The second input reaction, the possible behaviors of inputs are the + same as for ``get_context``. + +The :js:class:`openerp.base.search.Input` type implements registration +on its own, but its implementations of ``get_context`` and +``get_domain`` simply raise errors and *have* to be overridden. + +One last action is for filters, as an activation order has to be kept +on them for some controls (establish the correct grouping sequence for +instance). + +To that end, filters can call +:js:func:`openerp.base.Search.do_toggle_filter`, providing themselves +as first argument. + +Filters calling :js:func:`~openerp.base.Search.do_toggle_filter` also +need to implement a method called +:js:func:`~openerp.base.search.Filter.is_enabled`, which the search +view will use to know the current status of the filter. + +The search view automatically triggers a search after calls to +:js:func:`~openerp.base.Search.do_toggle_filter`. + +Life cycle +"""""""""" + +The search view has a pretty simple and linear life cycle, in three main steps: + +:js:class:`init ` + + Nothing interesting happens here + +:js:func:`~openerp.base.SearchView.start` + + Called by the main view's creator, this is the main initialization + step for the list view. + + It begins with a remote call to fetch the view's descriptors + (``fields_view_get``). + + Once the remote call is complete, the ``on_loaded`` even happens, + holding three main operations: + + :js:func:`~openerp.base.SearchView.make_widgets` + + Builds and returns the top-level widgets of the search + view. Because it returns an array of widget lines (a 2-dimensional + matrix of widgets) it should be called recursively by container + widgets (:js:class:`openerp.base.search.Group` for instance). + + :js:func:`~openerp.base.search.Widget.render` + + Called by the search view on all top-level widgets. Container + widgets should recursively call this method on their own children + widgets. + + Widgets are provided with a mapping of ``{name: value}`` holding + default values for the search view. They can freely pick their + initial values from there, but must pass the mapping to their + children widgets if they have any. + + :js:func:`~openerp.base.search.Widget.start` + + The last operation of the search view startup is to initialize all + its widgets in order. This is again done recursively (the search + view starts its children, which have to start their own children). + +:js:func:`~openerp.base.SearchView.stop` + + Used before discarding a search view, allows the search view to + disable its events and pass the message to its own widgets, + gracefully shutting down the whole view. + +Widgets +""""""" + +In a search view, the widget is simply a unit of display. + +All widgets must be able to react to three events, which will be +called in this order: + +:js:func:`~openerp.base.search.Widget.render` + + Called with a map of default values. The widget must return a + ``String``, which is its HTML representation. That string can be + empty (if the widget should not be represented). + + Widgets are responsible for asking their children for rendering, and + for passing along the default values. + +:js:func:`~openerp.base.search.Widget.start` + + Called without arguments. At this point, the widget has been fully + rendered and can set its events up, if any. + + The widget is responsible for starting its children, if it has any. + +:js:func:`~openerp.base.search.Widget.stop` + + Gives the widget the opportunity to unbind its events, remove itself + from the DOM and perform any other cleanup task it may have. + + Event if the widget does not do anything itself, it is responsible + for shutting down its children. + +An abstract type is available and can be inherited from, to simplify +the implementation of those tasks: + +.. TODO: insert Widget here + +.. remember to document all methods + +Inputs +"""""" + +The search namespace (``openerp.base.search``) provides two more +abstract types, used to implement input widgets: + +* :js:class:`openerp.base.search.Input` is the most basic input type, + it only implements :ref:`input registration + `. + + If inherited from, descendant classes should not call its + implementations of ``get_context`` and ``get_domain``. + +* :js:class:`openerp.base.search.Field` is used to implement more + "field" widgets (which allow the user to input potentially complex + values). + + It provides various services for its subclasses: + + * Sets up the field attributes, using attributes from the field and + the view node. + + * It fills the widget with :js:class:`~openerp.base.search.Filter` + if the field has any child filter. + + * It automatically generates an identifier based on the field type + and the field name, using + :js:func:`~openerp.base.search.Widget.make_id`. + + * It sets up a basic (overridable) + :js:attr:`~opererp.base.search.Field.template` attribute, combined + with the previous tasks, this makes subclasses of + :js:class:`~openerp.base.search.Field` render themselves "for + free". + + * It provides basic implementations of ``get_context`` and + ``get_domain``, both hinging on the subclasses implementing + ``get_value()`` (which should return a correct, converted + Javascript value): + + :js:func:`~openerp.base.search.Field.get_context` + + Checks if the field has a non-``null`` and non-empty + (``String``) value, and that the field has a ``context`` attr. + + If both conditions are fullfilled, returns the context. + + :js:func:`~openerp.base.search.Field.get_domain` + + Only requires that the field has a non-``null`` and non-empty + value. + + If the field has a ``filter_domain``, returns it + immediately. Otherwise, builds a context using the field's + name, the field :js:attr:`~openerp.base.search.Field.operator` + and the field value, and returns it. + +.. TODO: insert Input, Field, Filter, and just about every Field subclass + Internal API Doc ----------------