1 OpenERP Web Core and standard addons
2 ====================================
4 * General organization and core ideas (design philosophies)
5 * Internal documentation, autodoc, Python and JS domains
6 * QWeb code documentation/description
7 * Documentation of the OpenERP APIs and choices taken based on that?
8 * Style guide and coding conventions (PEP8? More)
9 * Test frameworks in JS?
17 The OpenERP search view really is a sub-view, used in support of views
18 acting on collections of records (list view or graph view, for
21 Its main goal is to collect information from its widgets (themselves
22 collecting information from the users) and make those available to the
25 The search view's root is :js:class:`~openerp.base.SearchView`. This
26 object should never need to be created or managed directly, its
27 lifecycle should be driven by the
28 :js:class:`~openerp.base.ViewManager`.
30 .. TODO: insert SearchView constructor here
32 The search view defines a number of internal and external protocols to
33 communicate with the objects around and within it. Most of these
34 protocols are informal, and types available for inheritance are more
35 mixins than mandatory.
42 .. TODO: method openerp.base.SearchView.on_loaded
44 Fires when the search view receives its view data (the result of
45 ``fields_view_get``). Hooking up before the event allows for
46 altering view data before it can be used.
48 By the time ``on_loaded`` is done, the search view is guaranteed to
49 be fully set up and ready to use.
53 .. TODO: method openerp.base.SearchView.on_search
55 Event triggered after a user asked for a search. The search view
56 fires this event after collecting all input data (contexts, domains
57 and group_by contexts). Note that the search view does *not* merge
58 those (or otherwise evaluate them), they are returned as provided by
59 the various inputs within the view.
63 .. TODO: method openerp.base.SearchView.on_clear
65 Triggered after a user asked for a form clearing.
70 An important concept in the search view is that of input. It is both
71 an informal protocol and an abstract type that can be inherited from.
73 Inputs are widgets which can contain user data (a char widget for
74 instance, or a selection box). They are capable of action and of
77 .. _views-search-registration:
81 This is an input action. Inputs have to register themselves to the
82 main view (which they receive as a constructor argument). This is
83 performed by pushing themselves on the
84 :js:attr:`openerp.base.SearchView.inputs` array.
88 An input reaction. When it needs to collect contexts, the view calls
89 ``get_context()`` on all its inputs.
91 Inputs can react in the following manners:
93 * Return a context (an object), this is the "normal" response if the
96 * Return a value that evaluates as false (generally ``null``). This
97 value indicates the input does not contain any value and will not
98 affect the results of the search.
100 * Raise :js:class:`openerp.base.search.Invalid` to indicate that it
101 holds a value but this value can not be used in the search
102 (because it is incorrectly formatted or nonsensical). Raising
103 :js:class:`~openerp.base.search.Invalid` is guaranteed to cancel
106 :js:class:`~openerp.base.search.Invalid` takes three mandatory
107 arguments: an identifier (a name for instance), the invalid value,
108 and a validation message indicating the issue.
112 The second input reaction, the possible behaviors of inputs are the
113 same as for ``get_context``.
115 The :js:class:`openerp.base.search.Input` type implements registration
116 on its own, but its implementations of ``get_context`` and
117 ``get_domain`` simply raise errors and *must* be overridden.
119 One last action is for filters, as an activation order has to be kept
120 on them for some controls (to establish the correct grouping sequence,
123 To that end, filters can call
124 :js:func:`openerp.base.Search.do_toggle_filter`, providing themselves
127 Filters calling :js:func:`~openerp.base.Search.do_toggle_filter` also
128 need to implement a method called
129 :js:func:`~openerp.base.search.Filter.is_enabled`, which the search
130 view will use to know the current status of the filter.
132 The search view automatically triggers a search after calls to
133 :js:func:`~openerp.base.Search.do_toggle_filter`.
138 The search view has a pretty simple and linear life cycle, in three main steps:
140 :js:class:`~openerp.base.SearchView.init`
142 Nothing interesting happens here
144 :js:func:`~openerp.base.SearchView.start`
146 Called by the main view's creator, this is the main initialization
147 step for the list view.
149 It begins with a remote call to fetch the view's descriptors
150 (``fields_view_get``).
152 Once the remote call is complete, the ``on_loaded`` even happens,
153 holding three main operations:
155 :js:func:`~openerp.base.SearchView.make_widgets`
157 Builds and returns the top-level widgets of the search
158 view. Because it returns an array of widget lines (a 2-dimensional
159 matrix of widgets) it should be called recursively by container
160 widgets (:js:class:`openerp.base.search.Group` for instance).
162 :js:func:`~openerp.base.search.Widget.render`
164 Called by the search view on all top-level widgets. Container
165 widgets should recursively call this method on their own children
168 Widgets are provided with a mapping of ``{name: value}`` holding
169 default values for the search view. They can freely pick their
170 initial values from there, but must pass the mapping to their
171 children widgets if they have any.
173 :js:func:`~openerp.base.search.Widget.start`
175 The last operation of the search view startup is to initialize all
176 its widgets in order. This is again done recursively (the search
177 view starts its children, which have to start their own children).
179 :js:func:`~openerp.base.SearchView.stop`
181 Used before discarding a search view, allows the search view to
182 disable its events and pass the message to its own widgets,
183 gracefully shutting down the whole view.
188 In a search view, the widget is simply a unit of display.
190 All widgets must be able to react to three events, which will be
191 called in this order:
193 :js:func:`~openerp.base.search.Widget.render`
195 Called with a map of default values. The widget must return a
196 ``String``, which is its HTML representation. That string can be
197 empty (if the widget should not be represented).
199 Widgets are responsible for asking their children for rendering, and
200 for passing along the default values.
202 :js:func:`~openerp.base.search.Widget.start`
204 Called without arguments. At this point, the widget has been fully
205 rendered and can set its events up, if any.
207 The widget is responsible for starting its children, if it has any.
209 :js:func:`~openerp.base.search.Widget.stop`
211 Gives the widget the opportunity to unbind its events, remove itself
212 from the DOM and perform any other cleanup task it may have.
214 Even if the widget does not do anything itself, it is responsible
215 for shutting down its children.
217 An abstract type is available and can be inherited from, to simplify
218 the implementation of those tasks:
220 .. TODO: insert Widget here
222 .. remember to document all methods
227 The search namespace (``openerp.base.search``) provides two more
228 abstract types, used to implement input widgets:
230 * :js:class:`openerp.base.search.Input` is the most basic input type,
231 it only implements :ref:`input registration
232 <views-search-registration>`.
234 If inherited from, descendant classes should not call its
235 implementations of :js:func:`~openerp.base.search.Input.get_context`
236 and :js:func:`~openerp.base.search.Input.get_domain`.
238 * :js:class:`openerp.base.search.Field` is used to implement more
239 "field" widgets (which allow the user to input potentially complex
242 It provides various services for its subclasses:
244 * Sets up the field attributes, using attributes from the field and
247 * It fills the widget with :js:class:`~openerp.base.search.Filter`
248 if the field has any child filter.
250 * It automatically generates an identifier based on the field type
251 and the field name, using
252 :js:func:`~openerp.base.search.Widget.make_id`.
254 * It sets up a basic (overridable)
255 :js:attr:`~openerp.base.search.Field.template` attribute, combined
256 with the previous tasks, this makes subclasses of
257 :js:class:`~openerp.base.search.Field` render themselves "for
260 * It provides basic implementations of ``get_context`` and
261 ``get_domain``, both hinging on the subclasses implementing
262 ``get_value()`` (which should return a correct, converted
265 :js:func:`~openerp.base.search.Field.get_context`
267 Checks if the field has a non-``null`` and non-empty
268 (``String``) value, and that the field has a ``context`` attr.
270 If both conditions are fullfilled, returns the context.
272 :js:func:`~openerp.base.search.Field.get_domain`
274 Only requires that the field has a non-``null`` and non-empty
277 If the field has a ``filter_domain``, returns it
278 immediately. Otherwise, builds a context using the field's
279 name, the field :js:attr:`~openerp.base.search.Field.operator`
280 and the field value, and returns it.
282 .. TODO: insert Input, Field, Filter, and just about every Field subclass
287 OpenERP Web's list views don't actually exist as such in OpenERP itself: a
288 list view is an OpenERP tree view in the ``view_mode`` form.
290 The overall purpose of a list view is to display collections of objects in two
291 main forms: per-object, where each object is a row in and of itself, and
292 grouped, where multiple objects are represented with a single row providing
293 an aggregated view of all grouped objects.
295 These two forms can be mixed within a single list view, if needed.
297 The root of a list view is :js:class:`openerp.base.ListView`, which may need
298 to be overridden (partially or fully) to control list behavior in non-view
299 cases (when using a list view as sub-component of a form widget for instance).
301 Creation and Initialization
302 """""""""""""""""""""""""""
304 As with most OpenERP Web views, the list view's
305 :js:func:`~openerp.base.ListView.init` takes quite a number of arguments.
307 While most of them are the standard view constructor arguments
308 (``view_manager``, ``session``, ``element_id``, ``dataset`` and an
309 optional ``view_id``), the list view adds a number of options for basic
310 customization (without having to override methods or templates):
312 ``selectable`` (default: ``true``)
313 Indicates that the list view should allow records to be selected
314 individually. Displays selection check boxes to the left of all record rows,
315 and allows for the triggering of the
316 :ref:`selection event <listview-events-selection>`.
317 ``deletable`` (default: ``true``)
318 Indicates that the list view should allow records to be removed
319 individually. Displays a deletion button to the right of all record rows,
320 and allows for the triggering of the
321 :ref:`deletion event <listview-events-deletion>`.
322 ``header`` (default: ``true``)
323 Indicates that list columns should bear a header sporting their name (for
325 ``addable`` (default: ``"New"``)
326 Indicates that a record addition/creation button should be displayed in
327 the list's header, along with its label. Also allows for the triggering of
328 the :ref:`record addition event <listview-events-addition>`.
329 ``sortable`` (default: ``true``)
330 Indicates that the list view can be sorted per-column (by clicking on its
334 ``reorderable`` (default: ``true``)
335 Indicates that the list view records can be reordered (and re-sequenced)
342 .. _listview-events-addition:
346 The addition event is used to add a record to an existing list view. The
347 default behavior is to switch to the form view, on a new record.
349 Addition behavior can be overridden by replacing the
350 :js:func:`~openerp.base.ListView.do_add_record` method.
352 .. _listview-events-selection:
356 The selection event is triggered when a given record is selected in the list
359 It can be overridden by replacing the
360 :js:func:`~openerp.base.ListView.do_select` method.
362 The default behavior is simply to hide or display the list-wise deletion button
363 depending on whether there are selected records or not.
365 .. _listview-events-deletion:
369 The deletion event is triggered when the user tries to remove 1..n records from
370 the list view, either individually or globally (via the header button).
372 Deletion can be overridden by replacing the
373 :js:func:`~openerp.base.ListView.do_delete` method. By default, this method
374 calls :js:func:`~openerp.base.DataSet.unlink` in order to remove the records
379 the list-wise deletion button (next to the record addition button)
380 simply proxies to :js:func:`~openerp.base.ListView.do_delete` after
381 obtaining all selected record ids, but it is possible to override it
383 :js:func:`~openerp.base.ListView.do_delete_selected`.
391 These classes should be moved to other sections of the doc as needed,
394 .. automodule:: web.common.http
398 See also: :class:`~web.common.session.OpenERPSession`,
399 :class:`~web.common.openerplib.main.OpenERPModel`
401 .. automodule:: web.controllers.main
411 Testing for the OpenERP Web core is similar to :ref:`testing addons
412 <addons-testing>`: the tests live in ``openerpweb.tests``, unittest2_
413 is the testing framework and tests can be run via either unittest2
414 (``unit2 discover``) or via nose_ (``nosetests``).
416 Tests for the OpenERP Web core can also be run using ``setup.py
421 http://www.voidspace.org.uk/python/articles/unittest2.shtml
424 http://somethingaboutorange.com/mrl/projects/nose/1.0.0/