summaryrefslogtreecommitdiff
path: root/doc/development.rst
diff options
context:
space:
mode:
authorXavier Morel <xmo@openerp.com>2012-03-22 15:33:31 +0100
committerXavier Morel <xmo@openerp.com>2012-03-22 15:33:31 +0100
commit7353e02e2ef4a403d4b79c1b157f2ca00b77d2fc (patch)
treea319552e3664ea272fdfb8abf31c961bcd5e1e6e /doc/development.rst
parent514c63288c7d5d88d764b44696c5e3a5fc148dfb (diff)
parent1da49a44a57aa413e5eb96afe9c6d294e09848fd (diff)
[MERGE] from trunk
bzr revid: xmo@openerp.com-20120322143331-iajlifomw1hohbht
Diffstat (limited to 'doc/development.rst')
-rw-r--r--doc/development.rst424
1 files changed, 424 insertions, 0 deletions
diff --git a/doc/development.rst b/doc/development.rst
new file mode 100644
index 00000000000..7431a3e16de
--- /dev/null
+++ b/doc/development.rst
@@ -0,0 +1,424 @@
+OpenERP Web Core and standard addons
+====================================
+
+* General organization and core ideas (design philosophies)
+* Internal documentation, autodoc, Python and JS domains
+* QWeb code documentation/description
+* Documentation of the OpenERP APIs and choices taken based on that?
+* 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 otherwise 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 capable 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 value that evaluates as false (generally ``null``). This
+ value indicates the input does not contain any value and will not
+ affect the results of the search.
+
+ * 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 identifier (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 *must* be overridden.
+
+One last action is for filters, as an activation order has to be kept
+on them for some controls (to 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:`~openerp.base.SearchView.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.
+
+ Even 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
+ <views-search-registration>`.
+
+ If inherited from, descendant classes should not call its
+ implementations of :js:func:`~openerp.base.search.Input.get_context`
+ and :js:func:`~openerp.base.search.Input.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:`~openerp.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
+
+List View
++++++++++
+
+OpenERP Web's list views don't actually exist as such in OpenERP itself: a
+list view is an OpenERP tree view in the ``view_mode`` form.
+
+The overall purpose of a list view is to display collections of objects in two
+main forms: per-object, where each object is a row in and of itself, and
+grouped, where multiple objects are represented with a single row providing
+an aggregated view of all grouped objects.
+
+These two forms can be mixed within a single list view, if needed.
+
+The root of a list view is :js:class:`openerp.base.ListView`, which may need
+to be overridden (partially or fully) to control list behavior in non-view
+cases (when using a list view as sub-component of a form widget for instance).
+
+Creation and Initialization
+"""""""""""""""""""""""""""
+
+As with most OpenERP Web views, the list view's
+:js:func:`~openerp.base.ListView.init` takes quite a number of arguments.
+
+While most of them are the standard view constructor arguments
+(``view_manager``, ``session``, ``element_id``, ``dataset`` and an
+optional ``view_id``), the list view adds a number of options for basic
+customization (without having to override methods or templates):
+
+``selectable`` (default: ``true``)
+ Indicates that the list view should allow records to be selected
+ individually. Displays selection check boxes to the left of all record rows,
+ and allows for the triggering of the
+ :ref:`selection event <listview-events-selection>`.
+``deletable`` (default: ``true``)
+ Indicates that the list view should allow records to be removed
+ individually. Displays a deletion button to the right of all record rows,
+ and allows for the triggering of the
+ :ref:`deletion event <listview-events-deletion>`.
+``header`` (default: ``true``)
+ Indicates that list columns should bear a header sporting their name (for
+ non-action columns).
+``addable`` (default: ``"New"``)
+ Indicates that a record addition/creation button should be displayed in
+ the list's header, along with its label. Also allows for the triggering of
+ the :ref:`record addition event <listview-events-addition>`.
+``sortable`` (default: ``true``)
+ Indicates that the list view can be sorted per-column (by clicking on its
+ column headers).
+
+ .. TODO: event?
+``reorderable`` (default: ``true``)
+ Indicates that the list view records can be reordered (and re-sequenced)
+ by drag and drop.
+
+ .. TODO: event?
+
+Events
+""""""
+.. _listview-events-addition:
+
+Addition
+''''''''
+The addition event is used to add a record to an existing list view. The
+default behavior is to switch to the form view, on a new record.
+
+Addition behavior can be overridden by replacing the
+:js:func:`~openerp.base.ListView.do_add_record` method.
+
+.. _listview-events-selection:
+
+Selection
+'''''''''
+The selection event is triggered when a given record is selected in the list
+view.
+
+It can be overridden by replacing the
+:js:func:`~openerp.base.ListView.do_select` method.
+
+The default behavior is simply to hide or display the list-wise deletion button
+depending on whether there are selected records or not.
+
+.. _listview-events-deletion:
+
+Deletion
+''''''''
+The deletion event is triggered when the user tries to remove 1..n records from
+the list view, either individually or globally (via the header button).
+
+Deletion can be overridden by replacing the
+:js:func:`~openerp.base.ListView.do_delete` method. By default, this method
+calls :js:func:`~openerp.base.DataSet.unlink` in order to remove the records
+entirely.
+
+.. note::
+
+ the list-wise deletion button (next to the record addition button)
+ simply proxies to :js:func:`~openerp.base.ListView.do_delete` after
+ obtaining all selected record ids, but it is possible to override it
+ alone by replacing
+ :js:func:`~openerp.base.ListView.do_delete_selected`.
+
+Internal API Doc
+----------------
+
+Python
+++++++
+
+These classes should be moved to other sections of the doc as needed,
+probably.
+
+.. automodule:: web.common.http
+ :members:
+ :undoc-members:
+
+ See also: :class:`~web.common.session.OpenERPSession`,
+ :class:`~web.common.openerplib.main.OpenERPModel`
+
+.. automodule:: web.controllers.main
+ :members:
+ :undoc-members:
+
+Testing
+-------
+
+Python
+++++++
+
+Testing for the OpenERP Web core is similar to :ref:`testing addons
+<addons-testing>`: the tests live in ``openerpweb.tests``, unittest2_
+is the testing framework and tests can be run via either unittest2
+(``unit2 discover``) or via nose_ (``nosetests``).
+
+Tests for the OpenERP Web core can also be run using ``setup.py
+test``.
+
+
+.. _unittest2:
+ http://www.voidspace.org.uk/python/articles/unittest2.shtml
+
+.. _nose:
+ http://somethingaboutorange.com/mrl/projects/nose/1.0.0/