#
# You can set these variables from the command line.
-SPHINXOPTS =
+SPHINXOPTS = -q
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
<h3>Other Docs</h3>
<ul>
<li><a href="http://doc.openerp.com/v6.1/developers">OpenERP Developers Documentation</a></li>
- <li><a href="http://doc.openerp.com/v6.1/developers/web">OpenERP Web Developers Documentation</a></li>
+ <li><a href="http://doc.openerp.com/v6.1/developers/server">OpenERP Server Developers Documentation</a></li>
<li><a href="http://doc.openerp.com/v6.1/users">OpenERP Users Documentation</a></li>
</ul>
--- /dev/null
+<addon name>
+ +-- __openerp__.py
+ +-- controllers/
+ +-- static/
+ +-- lib/
+ +-- src/
+ +-- css/
+ +-- img/
+ +-- js/
+ +-- xml/
+ +-- test/
+ +-- test/
--- /dev/null
+Developing OpenERP Web Addons
+=============================
+
+An OpenERP Web addon is simply a Python package with an openerp
+descriptor (a ``__openerp__.py`` file) which follows a few structural
+and namespacing rules.
+
+Structure
+---------
+
+.. literalinclude:: addon-structure.txt
+
+``__openerp__.py``
+ The addon's descriptor, contains the following information:
+
+ ``name: str``
+ The addon name, in plain, readable english
+ ``version: str``
+ The addon version, following `Semantic Versioning`_ rules
+ ``depends: [str]``
+ A list of addons this addon needs to work correctly. ``base`` is
+ an implied dependency if the list is empty.
+ ``css: [str]``
+ An ordered list of CSS files this addon provides and needs. The
+ file paths are relative to the addon's root. Because the Web
+ Client *may* perform concatenations and other various
+ optimizations on CSS files, the order is important.
+ ``js: [str]``
+ An ordered list of Javascript files this addon provides and needs
+ (including dependencies files). As with CSS files, the order is
+ important as the Web Client *may* perform contatenations and
+ minimizations of files.
+ ``active: bool``
+ Whether this addon should be enabled by default any time it is
+ found, or whether it will be enabled through other means (on a
+ by-need or by-installation basis for instance).
+
+``controllers/``
+ All of the Python controllers and JSON-RPC endpoints.
+
+``static/``
+ The static files directory, may be served via a separate web server.
+
+``static/lib/``
+ Third-party libraries used by the addon.
+
+``static/src/{css,js,img,xml}``
+ Location for (respectively) the addon's static CSS files, its JS
+ files, its various image resources as well as the template files
+
+``static/test``
+ Javascript tests files
+
+``test/``
+ The directories in which all tests for the addon are located.
+
+Some of these are guidelines (and not enforced by code), but it's
+suggested that these be followed. Code which does not fit into these
+categories can go wherever deemed suitable.
+
+Namespacing
+-----------
+
+Python
+++++++
+
+Because addons are also Python packages, they're inherently namespaced
+and nothing special needs to be done on that front.
+
+JavaScript
+++++++++++
+
+The JavaScript side of an addon has to live in the namespace
+``openerp.$addon_name``. For instance, everything created by the addon
+``base`` lives in ``openerp.base``.
+
+The root namespace of the addon is a function which takes a single
+parameter ``openerp``, which is an OpenERP client instance. Objects
+(as well as functions, registry instances, etc...) should be added on
+the correct namespace on that object.
+
+The root function will be called by the OpenERP Web client when
+initializing the addon.
+
+.. code-block:: javascript
+
+ // root namespace of the openerp.example addon
+ /** @namespace */
+ openerp.example = function (openerp) {
+ // basic initialization code (e.g. templates loading)
+ openerp.example.SomeClass = openerp.base.Class.extend(
+ /** @lends openerp.example.SomeClass# */{
+ /**
+ * Description for SomeClass's constructor here
+ *
+ * @constructs
+ */
+ init: function () {
+ // SomeClass initialization code
+ }
+ // rest of SomeClass
+ });
+
+ // access an object in an other addon namespace to replace it
+ openerp.base.SearchView = openerp.base.SearchView.extend({
+ init: function () {
+ this._super.apply(this, arguments);
+ console.log('Search view initialized');
+ }
+ });
+ }
+
+Creating new standard roles
+---------------------------
+
+Widget
+++++++
+
+This is the base class for all visual components. It provides a number of
+services for the management of a DOM subtree:
+
+* Rendering with QWeb
+
+* Parenting-child relations
+
+* Life-cycle management (including facilitating children destruction when a
+ parent object is removed)
+
+* DOM insertion, via jQuery-powered insertion methods. Insertion targets can
+ be anything the corresponding jQuery method accepts (generally selectors,
+ DOM nodes and jQuery objects):
+
+ :js:func:`~openerp.base.Widget.appendTo`
+ Renders the widget and inserts it as the last child of the target, uses
+ `.appendTo()`_
+
+ :js:func:`~openerp.base.Widget.prependTo`
+ Renders the widget and inserts it as the first child of the target, uses
+ `.prependTo()`_
+
+ :js:func:`~openerp.base.Widget.insertAfter`
+ Renders the widget and inserts it as the preceding sibling of the target,
+ uses `.insertAfter()`_
+
+ :js:func:`~openerp.base.Widget.insertBefore`
+ Renders the widget and inserts it as the following sibling of the target,
+ uses `.insertBefore()`_
+
+:js:class:`~openerp.base.Widget` inherits from
+:js:class:`~openerp.base.SessionAware`, so subclasses can easily access the
+RPC layers.
+
+Subclassing Widget
+~~~~~~~~~~~~~~~~~~
+
+:js:class:`~openerp.base.Widget` is subclassed in the standard manner (via the
+:js:func:`~openerp.base.Class.extend` method), and provides a number of
+abstract properties and concrete methods (which you may or may not want to
+override). Creating a subclass looks like this:
+
+.. code-block:: javascript
+
+ var MyWidget = openerp.base.Widget.extend({
+ // QWeb template to use when rendering the object
+ template: "MyQWebTemplate",
+
+ init: function(parent) {
+ this._super(parent);
+ // insert code to execute before rendering, for object
+ // initialization
+ },
+ start: function() {
+ this._super();
+ // post-rendering initialization code, at this point
+ // ``this.$element`` has been initialized
+ this.$element.find(".my_button").click(/* an example of event binding * /);
+
+ // if ``start`` is asynchronous, return a promise object so callers
+ // know when the object is done initializing
+ return this.rpc(/* … */)
+ }
+ });
+
+The new class can then be used in the following manner:
+
+.. code-block:: javascript
+
+ // Create the instance
+ var my_widget = new MyWidget(this);
+ // Render and insert into DOM
+ my_widget.appendTo(".some-div");
+
+After these two lines have executed (and any promise returned by ``appendTo``
+has been resolved if needed), the widget is ready to be used.
+
+.. note:: the insertion methods will start the widget themselves, and will
+ return the result of :js:func:`~openerp.base.Widget.start()`.
+
+ If for some reason you do not want to call these methods, you will
+ have to first call :js:func:`~openerp.base.Widget.render()` on the
+ widget, then insert it into your DOM and start it.
+
+If the widget is not needed anymore (because it's transient), simply terminate
+it:
+
+.. code-block:: javascript
+
+ my_widget.stop();
+
+will unbind all DOM events, remove the widget's content from the DOM and
+destroy all widget data.
+
+Views
++++++
+
+Views are the standard high-level component in OpenERP. A view type corresponds
+to a way to display a set of data (coming from an OpenERP model).
+
+In OpenERP Web, views are standard objects registered against a dedicated
+object registry, so the :js:class:`~openerp.base.ViewManager` knows where to
+find and how to call them.
+
+Although not mandatory, it is recommended that views inherit from
+:js:class:`openerp.base.View`, which provides a view useful services to its
+children.
+
+Registering a view
+~~~~~~~~~~~~~~~~~~
+
+This is the first task to perform when creating a view, and the simplest by
+far: simply call ``openerp.base.views.add(name, object_path)`` to register
+the object of path ``object_path`` as the view for the view name ``name``.
+
+The view name is the name you gave to your new view in the OpenERP server.
+
+From that point onwards, OpenERP Web will be able to find your object and
+instantiate it.
+
+Standard view behaviors
+~~~~~~~~~~~~~~~~~~~~~~~
+
+In the normal OpenERP Web flow, views have to implement a number of methods so
+view managers can correctly communicate with them:
+
+``start()``
+ This method will always be called after creating the view (via its
+ constructor), but not necessarily immediately.
+
+ It is called with no arguments and should handle the heavy setup work,
+ including remote call (to load the view's setup data from the server via
+ e.g. ``fields_view_get``, for instance).
+
+ ``start`` should return a `promise object`_ which *must* be resolved when
+ the view's setup is completed. This promise is used by view managers to
+ know when they can start interacting with the view.
+
+``do_hide()``
+ Called by the view manager when it wants to replace this view by an other
+ one, but wants to keep this view around to re-activate it later.
+
+ Should put the view in some sort of hibernation mode, and *must* hide its
+ DOM elements.
+
+``do_show()``
+ Called when the view manager wants to re-display the view after having
+ hidden it. The view should refresh its data display upon receiving this
+ notification
+
+``do_search(domain: Array, context: Object, group_by: Array)``
+ If the view is searchable, this method is called to notify it of a search
+ against it.
+
+ It should use the provided query data to perform a search and refresh its
+ internal content (and display).
+
+ All views are searchable by default, but they can be made non-searchable
+ by setting the property ``searchable`` to ``false``.
+
+ This can be done either on the view class itself (at the same level as
+ defining e.g. the ``start`` method) or at the instance level (in the
+ class's ``init``), though you should generally set it on the class.
+
+Frequent development tasks
+--------------------------
+
+There are a number of tasks which OpenERP Web developers do or will need to
+perform quite regularly. To make these easier, we have written a few guides
+to help you get started:
+
+.. toctree::
+ :maxdepth: 1
+
+ guides/client-action
+ guides/sidebar-protocol
+
+Translations
+------------
+
+OpenERP Web should provide most of the tools needed to correctly translate your
+addons via the tool of your choice (OpenERP itself uses `Launchpad's own
+translation tool`_.
+
+Making strings translatable
++++++++++++++++++++++++++++
+
+QWeb
+~~~~
+
+QWeb automatically marks all text nodes (any text which is not in an XML
+attribute and not part of an XML tag) as translatable, and handles the
+replacement for you. There is nothing special to do to mark template text as
+translatable
+
+JavaScript
+~~~~~~~~~~
+
+OpenERP Web provides two functions to translate human-readable strings in
+javascript code. These functions should be "imported" in your module by
+aliasing them to their bare name:
+
+.. code-block:: javascript
+
+ var _t = openerp.web._t,
+ _tl = openerp.web._tl;
+
+importing those functions under any other name is not guaranteed to work.
+
+.. note:: only import them if necessary, and only the necessary one(s), no need
+ to clutter your module's namespace for nothing
+
+.. js:function:: openerp.web._t(s)
+
+ Base translation function, eager, works much like :manpage:`gettext(3)`
+
+ :type s: String
+ :rtype: String
+
+.. js:function:: openerp.web._lt(s)
+
+ Lazy equivalent to :js:func:`~openerp.web._t`, this function will postpone
+ fetching the translation to its argument until the last possible moment.
+
+ To use in contexts evaluated before the translation database can be
+ fetched, usually your module's toplevel and the attributes of classes
+ defined in it (class attributes, not instance attributes set in the
+ constructor).
+
+ :type s: String
+ :rtype: LazyString
+
+Text formatting & translations
+""""""""""""""""""""""""""""""
+
+A difficulty when translating is integrating data (from the code) into the
+translated string. In OpenERP Web addons, this should be done by wrapping the
+text to translate in an :manpage:`sprintf(3)` call. For OpenERP Web,
+:manpage:`sprintf(3)` is provided by `underscore.string
+<http://epeli.github.com/underscore.string/>`_.
+
+As much as possible, you should use the "named argument" form of sprintf:
+
+.. code-block:: javascript
+
+ var translated_string = _.str.sprintf(
+ _t("[%(first_record)d to %(last_record)d] of %(records_count)d"), {
+ first_record: first + 1,
+ last_record: last,
+ records_count: total
+ }));
+
+named arguments make the string to translate much clearer for translators, and
+allows them to "move" sections around based on the requirements of their
+language (not all language order text like english).
+
+Named arguments are specified using the following pattern: ``%($name)$type``
+where
+
+``$name``
+ the name of the argument, this is the key in the object/dictionary provided
+ as second parameter to ``sprintf``
+``$type``
+ a type/format specifier, `see the list for all possible types
+ <http://www.diveintojavascript.com/projects/javascript-sprintf>`_.
+
+.. note:: positional arguments are acceptable if the translated string has
+ *a single* argument and its content is easy to guess from the text
+ around it. Named arguments should still be preferred.
+
+.. warning:: you should *never* use string concatenation as it robs the
+ translator of context and make result in a completely incorrect
+ translation
+
+Extracting strings
+~~~~~~~~~~~~~~~~~~
+
+.. program:: gen_translations.sh
+
+Once strings have been marked for translation, they need to be extracted into
+:abbr:`POT (Portable Object Template)` files, from which most translation tools
+can build a database.
+
+This can be done via the provided :program:`gen_translations.sh`.
+
+It can be called either as :option:`gen_translations.sh -a` or by providing
+two parameters, a path to the addons and the complete path in which to put the
+extracted POT file.
+
+.. option:: -a
+
+ Extracts translations from all standard OpenERP Web addons (addons bundled
+ with OpenERP Web itself) and puts the extracted templates into the right
+ directory for `Rosetta`_ to handle them
+
+Utility behaviors
+-----------------
+
+JavaScript
+++++++++++
+
+* All javascript objects inheriting from
+ :js:class:`openerp.base.BasicConroller` will have all methods
+ starting with ``on_`` or ``do_`` bound to their ``this``. This means
+ they don't have to be manually bound (via ``_.bind`` or ``$.proxy``)
+ in order to be useable as bound event handlers (event handlers
+ keeping their object as ``this`` rather than taking whatever
+ ``this`` object they were called with).
+
+ Beware that this is only valid for methods starting with ``do_`` and
+ ``on_``, any other method will have to be bound manually.
+
+.. _addons-testing:
+
+Testing
+-------
+
+Python
+++++++
+
+OpenERP Web uses unittest2_ for its testing needs. We selected
+unittest2 rather than unittest_ for the following reasons:
+
+* autodiscovery_ (similar to nose, via the ``unit2``
+ CLI utility) and `pluggable test discovery`_.
+
+* `new and improved assertions`_ (with improvements in type-specific
+ inequality reportings) including `pluggable custom types equality
+ assertions`_
+
+* neveral new APIs, most notably `assertRaises context manager`_,
+ `cleanup function registration`_, `test skipping`_ and `class- and
+ module-level setup and teardown`_
+
+* finally, unittest2 is a backport of Python 3's unittest. We might as
+ well get used to it.
+
+To run tests on addons (from the root directory of OpenERP Web) is as
+simple as typing ``PYTHONPATH=. unit2 discover -s addons`` [#]_. To
+test an addon which does not live in the ``addons`` directory, simply
+replace ``addons`` by the directory in which your own addon lives.
+
+.. note:: unittest2 is entirely compatible with nose_ (or the
+ other way around). If you want to use nose as your test
+ runner (due to its addons for instance) you can simply install it
+ and run ``nosetests addons`` instead of the ``unit2`` command,
+ the result should be exactly the same.
+
+Python
+++++++
+
+.. autoclass:: web.common.session.OpenERPSession
+ :members:
+
+.. autoclass:: web.common.openerplib.main.Model
+ :members:
+
+* Addons lifecycle (loading, execution, events, ...)
+
+ * Python-side
+ * JS-side
+
+* Handling static files
+* Overridding a Python controller (object?)
+* Overridding a Javascript controller (object?)
+* Extending templates
+ .. how do you handle deploying static files via e.g. a separate lighttpd?
+* Python public APIs
+* QWeb templates description?
+* OpenERP Web modules (from OpenERP modules)
+
+.. [#] the ``-s`` parameter tells ``unit2`` to start trying to
+ find tests in the provided directory (here we're testing
+ addons). However a side-effect of that is to set the
+ ``PYTHONPATH`` there as well, so it will fail to find (and
+ import) ``openerpweb``.
+
+ The ``-t`` parameter lets us set the ``PYTHONPATH``
+ independently, but it doesn't accept multiple values and here
+ we really want to have both ``.`` and ``addons`` on the
+ ``PYTHONPATH``.
+
+ The solution is to set the ``PYTHONPATH`` to ``.`` on start,
+ and the ``start-directory`` to ``addons``. This results in a
+ correct ``PYTHONPATH`` within ``unit2``.
+
+.. _unittest:
+ http://docs.python.org/library/unittest.html
+
+.. _unittest2:
+ http://www.voidspace.org.uk/python/articles/unittest2.shtml
+
+.. _autodiscovery:
+ http://www.voidspace.org.uk/python/articles/unittest2.shtml#test-discovery
+
+.. _pluggable test discovery:
+ http://www.voidspace.org.uk/python/articles/unittest2.shtml#load-tests
+
+.. _new and improved assertions:
+ http://www.voidspace.org.uk/python/articles/unittest2.shtml#new-assert-methods
+
+.. _pluggable custom types equality assertions:
+ http://www.voidspace.org.uk/python/articles/unittest2.shtml#add-new-type-specific-functions
+
+.. _assertRaises context manager:
+ http://www.voidspace.org.uk/python/articles/unittest2.shtml#assertraises
+
+.. _cleanup function registration:
+ http://www.voidspace.org.uk/python/articles/unittest2.shtml#cleanup-functions-with-addcleanup
+
+.. _test skipping:
+ http://www.voidspace.org.uk/python/articles/unittest2.shtml#test-skipping
+
+.. _class- and module-level setup and teardown:
+ http://www.voidspace.org.uk/python/articles/unittest2.shtml#class-and-module-level-fixtures
+
+.. _Semantic Versioning:
+ http://semver.org/
+
+.. _nose:
+ http://somethingaboutorange.com/mrl/projects/nose/1.0.0/
+
+.. _promise object:
+ http://api.jquery.com/deferred.promise/
+
+.. _.appendTo():
+ http://api.jquery.com/appendTo/
+
+.. _.prependTo():
+ http://api.jquery.com/prependTo/
+
+.. _.insertAfter():
+ http://api.jquery.com/insertAfter/
+
+.. _.insertBefore():
+ http://api.jquery.com/insertBefore/
+
+.. _Rosetta:
+.. _Launchpad's own translation tool:
+ https://help.launchpad.net/Translations
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
sys.path.append(os.path.abspath('_themes'))
-sys.path.append(os.path.abspath('..'))
+sys.path.insert(0, os.path.abspath('../addons'))
+sys.path.insert(0, os.path.abspath('..'))
# -- General configuration -----------------------------------------------------
master_doc = 'index'
# General information about the project.
-project = u'OpenERP Server Developers Documentation'
+project = u'OpenERP Web Developers Documentation'
copyright = u'2012, OpenERP s.a.'
# The version info for the project you're documenting, acts as replacement for
#html_file_suffix = None
# Output file base name for HTML help builder.
-htmlhelp_basename = 'openerp-server-doc'
+htmlhelp_basename = 'openerp-web-doc'
# -- Options for LaTeX output --------------------------------------------------
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
- ('index', 'openerp-server-doc.tex', u'OpenERP Server Developers Documentation',
+ ('index', 'openerp-web-doc.tex', u'OpenERP Web Developers Documentation',
u'OpenERP s.a.', 'manual'),
]
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
- ('index', 'openerp-server-doc', u'OpenERP Server Developers Documentation',
+ ('index', 'openerp-web-doc', u'OpenERP Web Developers Documentation',
[u'OpenERP s.a.'], 1)
]
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
- ('index', 'OpenERPServerDocumentation', u'OpenERP Server Developers Documentation',
- u'OpenERP s.a.', 'OpenERPServerDocumentation', 'Developers documentation for the openobject-server project.',
+ ('index', 'OpenERPWebDocumentation', u'OpenERP Web Developers Documentation',
+ u'OpenERP s.a.', 'OpenERPWebDocumentation', 'Developers documentation for the openerp-web project.',
'Miscellaneous'),
]
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {
- 'http://docs.python.org/': None,
- }
+ 'python': ('http://docs.python.org/', None),
+ 'openerpserver': ('http://doc.openerp.com/v6.1/developers/server', None),
+ 'openerpdev': ('http://doc.openerp.com/v6.1/developers', None),
+}
--- /dev/null
+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/
--- /dev/null
+Getting Started with OpenERP Web
+================================
+
+Installing
+----------
+
+.. per-distro packaging
+
+Launching
+---------
--- /dev/null
+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.
+
+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
+-----------------
+
+In the OpenERP Web code, a client action only requires two pieces of
+information:
+
+* Mapping the action's ``tag`` to an OpenERP Web object
+
+* The OpenERP Web object itself, which must inherit from
+ :js:class:`openerp.web.Widget`
+
+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):
+
+.. code-block:: javascript
+
+ // Registers the object 'openerp.web_dashboard.Widget' to the client
+ // action tag 'board.home.widgets'
+ openerp.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({
+ 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.
+
+If the client action takes parameters, these parameters are passed in as a
+second positional parameter to the constructor:
+
+.. code-block:: javascript
+
+ init: function (parent, params) {
+ // execute the Widget's init
+ this._super(parent);
+ // board.home.widgets only takes a single param, the identifier of the
+ // res.widget object it should display. Store it for later
+ this.widget_id = params.widget_id;
+ }
+
+More complex initialization (DOM manipulations, RPC requests, ...) should be
+performed in the ``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.
+
+.. code-block:: javascript
+
+ start: function () {
+ 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));
+ }
+
+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
+
+ 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', {
+ widget: widget,
+ url: url
+ }));
+ }
--- /dev/null
+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<Number>
+
+.. 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<Object>
+
+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
+
-:orphan:
+.. OpenERP Web documentation master file, created by
+ sphinx-quickstart on Fri Mar 18 16:31:55 2011.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
-OpenERP Server Developers Documentation
+Welcome to OpenERP Web's documentation!
=======================================
-.. include:: index.rst.inc
+Contents:
+
+.. toctree::
+ :maxdepth: 2
+
+ getting-started
+ production
+ widgets
+ addons
+ development
+ project
+ old-version
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
-set BUILDDIR=build
-set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
)
--- /dev/null
+Main differences with the 6.0 client
+====================================
+
+.. No more populate.sh, use virtualenvs
+
+.. Logic is mainly in Javascript (had to make a choice between JS and
+.. Python logic)
+
+.. Templating language changes
+
+.. How to port addons and modules?
--- /dev/null
+Deploying OpenERP Web
+=====================
+
+.. After release one, add upgrade instructions if any
+
+.. How about running the web client on alternative Python
+.. implementations e.g. pypy or Jython? Since the only lib with C
+.. accelerators we're using right now is SimpleJSON and it has a pure
+.. Python base component, we should be able to test and deploy on
+.. non-cpython no?
+
+In-depth configuration
+----------------------
+
+SSL, basic proxy (link to relevant section), links to sections and
+example files for various servers and proxies, WSGI
+integration/explanation (if any), ...
+
+Deployment Options
+------------------
+
+Serving via WSGI
+~~~~~~~~~~~~~~~~
+
+Apache mod_wsgi
++++++++++++++++
+
+NGinx mod_wsgi
+++++++++++++++
+
+uWSGI
++++++
+
+Gunicorn
+++++++++
+
+FastCGI, SCGI, or AJP
++++++++++++++++++++++
+
+Behind a proxy
+~~~~~~~~~~~~~~
+
+Apache mod_proxy
+++++++++++++++++
+
+NGinx HttpProxy
++++++++++++++++
--- /dev/null
+The OpenERP Web open-source project
+===================================
+
+Getting involved
+----------------
+
+Translations
+++++++++++++
+
+Bug reporting
++++++++++++++
+
+Source code repository
+++++++++++++++++++++++
+
+Merge proposals
++++++++++++++++
+
+Coding issues and coding conventions
+++++++++++++++++++++++++++++++++++++
+
+Javascript coding
+~~~~~~~~~~~~~~~~~
+
+These are a number of guidelines for javascript code. More than coding
+conventions, these are warnings against potentially harmful or sub-par
+constructs.
+
+Ideally, you should be able to configure your editor or IDE to warn you against
+these kinds of issues.
+
+Use ``var`` for *all* declarations
+**********************************
+
+In javascript (as opposed to Python), assigning to a variable which does not
+already exist and is not explicitly declared (via ``var``) will implicitly
+create a global variable. This is bad for a number of reasons:
+
+* It leaks information outside function scopes
+* It keeps memory of previous run, with potentially buggy behaviors
+* It may conflict with other functions with the same issue
+* It makes code harder to statically check (via e.g. IDE inspectors)
+
+.. note::
+ It is perfectly possible to use ``var`` in ``for`` loops:
+
+ .. code-block:: javascript
+
+ for (var i = 0; i < some_array.length; ++i) {
+ // code here
+ }
+
+ this is not an issue
+
+All local *and global* variables should be declared via ``var``.
+
+.. note:: generally speaking, you should not need globals in OpenERP Web: you
+ can just declare a variable local to your top-level function. This
+ way, if your widget/addon is instantiated several times on the same
+ page (because it's used in embedded mode) each instance will have its
+ own internal but global-to-its-objects data.
+
+Do not leave trailing commas in object literals
+***********************************************
+
+While it is legal to leave trailing commas in Python dictionaries, e.g.
+
+.. code-block:: python
+
+ foo = {
+ 'a': 1,
+ 'b': 2,
+ }
+
+and it's valid in ECMAScript 5 and most browsers support it in Javascript, you
+should *never* use trailing commas in Javascript object literals:
+
+* Internet Explorer does *not* support trailing commas (at least until and
+ including Internet Explorer 8), and trailing comma will cause hard-to-debug
+ errors in it
+
+* JSON does not accept trailing comma (it is a syntax error), and using them
+ in object literals puts you at risks of using them in literal JSON strings
+ as well (though there are few reasons to write JSON by hand)
+
+*Never* use ``for … in`` to iterate on arrays
+*********************************************
+
+:ref:`Iterating over an object with for…in is a bit tricky already
+<for-in-iteration>`, it is far more complex than in Python (where it Just
+Works™) due to the interaction of various Javascript features, but to iterate
+on arrays it becomes downright deadly and errorneous: ``for…in`` really
+iterates over an *object*'s *properties*.
+
+With an array, this has the following consequences:
+
+* It does not necessarily iterate in numerical order, nor does it iterate in
+ any kind of set order. The order is implementation-dependent and may vary
+ from one run to the next depending on a number of reasons and implementation
+ details.
+* If properties are added to an array, to ``Array.prototype`` or to
+ ``Object.prototype`` (the latter two should not happen in well-behaved
+ javascript code, but you never know...) those properties *will* be iterated
+ over by ``for…in``. While ``Object.hasOwnProperty`` will guard against
+ iterating prototype properties, they will not guard against properties set
+ on the array instance itself (as memoizers for instance).
+
+ Note that this includes setting negative keys on arrays.
+
+For this reason, ``for…in`` should **never** be used on array objects. Instead,
+you should use either a normal ``for`` or (even better, unless you have
+profiled the code and found a hotspot) one of Underscore's array iteration
+methods (`_.each`_, `_.map`_, `_.filter`_, etc...).
+
+Underscore is guaranteed to be bundled and available in OpenERP Web scopes.
+
+.. _for-in-iteration:
+
+Use ``hasOwnProperty`` when iterating on an object with ``for … in``
+********************************************************************
+
+``for…in`` is Javascript's built-in facility for iterating over and object's
+properties.
+
+`It is also fairly tricky to use`_: it iterates over *all* non-builtin
+properties of your objects [#]_, which includes methods of an object's class.
+
+As a result, when iterating over an object with ``for…in`` the first line of
+the body *should* generally be a call to `Object.hasOwnProperty`_. This call
+will check whether the property was set directly on the object or comes from
+the object's class:
+
+.. code-block:: javascript
+
+ for(var key in ob) {
+ if (!ob.hasOwnProperty(key)) {
+ // comes from ob's class
+ continue;
+ }
+ // do stuff with key
+ }
+
+Since properties can be added directly to e.g. ``Object.prototype`` (even
+though it's usually considered bad style), you should not assume you ever know
+which properties ``for…in`` is going to iterate over.
+
+An alternative is to use Underscore's iteration methods, which generally work
+over objects as well as arrays:
+
+Instead of
+
+.. code-block:: javascript
+
+ for (var key in ob) {
+ if (!ob.hasOwnProperty(key)) { continue; }
+ var value = ob[key];
+ // Do stuff with key and value
+ }
+
+you could write:
+
+.. code-block:: javascript
+
+ _.each(ob, function (value, key) {
+ // do stuff with key and value
+ });
+
+and not worry about the details of the iteration: underscore should do the
+right thing for you on its own [#]_.
+
+Writing documentation
++++++++++++++++++++++
+
+The OpenERP Web project documentation uses Sphinx_ for the literate
+documentation (this document for instance), the development guides
+(for Python and Javascript alike) and the Python API documentation
+(via autodoc_).
+
+For the Javascript API, documentation should be written using the
+`JsDoc Toolkit`_.
+
+Guides and main documentation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The meat and most important part of all documentation. Should be
+written in plain English, using reStructuredText_ and taking advantage
+of `Sphinx's extensions`_, especially `cross-references`_.
+
+Python API Documentation
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+All public objects in Python code should have a docstring written in
+RST, using Sphinx's `Python domain`_ [#]_:
+
+* Functions and methods documentation should be in their own
+ docstring, using Sphinx's `info fields`_
+
+ For parameters types, built-in and stdlib types should be using the
+ combined syntax:
+
+ .. code-block:: restructuredtext
+
+ :param dict foo: what the purpose of foo is
+
+ unless a more extensive explanation needs to be given (e.g. the
+ specification that the input should be a list of 3-tuple needs to
+ use ``:type:`` even though all types involved are built-ins). Any
+ other type should be specified in full using the ``:type:`` field
+
+ .. code-block:: restructuredtext
+
+ :param foo: what the purpose of foo is
+ :type foo: some.addon.Class
+
+ Mentions of other methods (including within the same class), modules
+ or types in descriptions (of anything, including parameters) should
+ be cross-referenced.
+
+* Classes should likewise be documented using their own docstring, and
+ should include the documentation of their construction (``__init__``
+ and ``__new__``), using the `info fields`_ as well.
+
+* Attributes (class and instance) should be documented in their
+ class's docstring via the ``.. attribute::`` directive, following
+ the class's own documentation.
+
+* The relation between modules and module-level attributes is similar:
+ modules should be documented in their own docstring, public module
+ attributes should be documented in the module's docstring using the
+ ``.. data::`` directive.
+
+Javascript API documentation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Javascript API documentation uses JsDoc_, a javascript documentation
+toolkit with a syntax similar to (and inspired by) JavaDoc's.
+
+Due to limitations of JsDoc, the coding patterns in OpenERP Web and
+the Sphinx integration, there are a few peculiarities to be aware of
+when writing javascript API documentation:
+
+* Namespaces and classes *must* be explicitly marked up even if they
+ are not documented, or JsDoc will not understand what they are and
+ will not generate documentation for their content.
+
+ As a result, the bare minimum for a namespace is:
+
+ .. code-block:: javascript
+
+ /** @namespace */
+ foo.bar.baz = {};
+
+ while for a class it is:
+
+ .. code-block:: javascript
+
+ /** @class */
+ foo.bar.baz.Qux = [...]
+
+* Because the OpenERP Web project uses `John Resig's Class
+ implementation`_ instead of direct prototypal inheritance [#]_,
+ JsDoc fails to infer class scopes (and constructors or super
+ classes, for that matter) and has to be told explicitly.
+
+ See :ref:`js-class-doc` for the complete rundown.
+
+* Much like the JavaDoc, JsDoc does not include a full markup
+ language. Instead, comments are simply marked up in HTML.
+
+ This has a number of inconvenients:
+
+ * Complex documentation comments become nigh-unreadable to read in
+ text editors (as opposed to IDEs, which may handle rendering
+ documentation comments on the fly)
+
+ * Though cross-references are supported by JsDoc (via ``@link`` and
+ ``@see``), they only work within the JsDoc
+
+ * More general impossibility to integrate correctly with Sphinx, and
+ e.g. reference JavaScript objects from a tutorial, or have all the
+ documentation live at the same place.
+
+ As a result, JsDoc comments should be marked up using RST, not
+ HTML. They may use Sphinx's cross-references as well.
+
+.. _js-class-doc:
+
+Documenting a Class
+*******************
+
+The first task when documenting a class using JsDoc is to *mark* that
+class, so JsDoc knows it can be used to instantiate objects (and, more
+importantly as far as it's concerned, should be documented with
+methods and attributes and stuff).
+
+This is generally done through the ``@class`` tag, but this tag has a
+significant limitation: it "believes" the constructor and the class
+are one and the same [#]_. This will work for constructor-less
+classes, but because OpenERP Web uses Resig's class the constructor is
+not the class itself but its ``init()`` method.
+
+Because this pattern is common in modern javascript code bases, JsDoc
+supports it: it is possible to mark an arbitrary instance method as
+the *class specification* by using the ``@constructs`` tag.
+
+.. warning:: ``@constructs`` is a class specification in and of
+ itself, it *completely replaces* the class documentation.
+
+ Using both a class documentation (even without ``@class`` itself)
+ and a constructor documentation is an *error* in JsDoc and will
+ result in incorrect behavior and broken documentation.
+
+The second issue is that Resig's class uses an object literal to
+specify instance methods, and because JsDoc does not know anything
+about Resig's class, it does not know about the role of the object
+literal.
+
+As with constructors, though, JsDoc provides a pluggable way to tell
+it about methods: the ``@lends`` tag. It specifies that the object
+literal "lends" its properties to the class being built.
+
+``@lends`` must be specified right before the opening brace of the
+object literal (between the opening paren of the ``#extend`` call and
+the brace), and takes the full qualified name of the class being
+created as a parameter, followed by the character ``#`` or by
+``.prototype``. This latter part tells JsDoc these are instance
+methods, not class (static) methods..
+
+Finally, specifying a class's superclass is done through the
+``@extends`` tag, which takes a fully qualified class name as a
+parameter.
+
+Here are a class without a constructor, and a class with one, so that
+everything is clear (these are straight from the OpenERP Web source,
+with the descriptions and irrelevant atttributes stripped):
+
+.. code-block:: javascript
+
+ /**
+ * <Insert description here, not below>
+ *
+ * @class
+ * @extends openerp.base.search.Field
+ */
+ openerp.base.search.CharField = openerp.base.search.Field.extend(
+ /** @lends openerp.base.search.CharField# */ {
+ // methods here
+ });
+
+.. code-block:: javascript
+
+ openerp.base.search.Widget = openerp.base.Controller.extend(
+ /** @lends openerp.base.search.Widget# */{
+ /**
+ * <Insert description here, not below>
+ *
+ * @constructs
+ * @extends openerp.base.Controller
+ *
+ * @param view the ancestor view of this widget
+ */
+ init: function (view) {
+ // construction of the instance
+ },
+ // bunch of other methods
+ });
+
+OpenERP Web over time
+---------------------
+
+Release process
++++++++++++++++
+
+OpenSUSE packaging: http://blog.lowkster.com/2011/04/packaging-python-packages-in-opensuse.html
+
+Roadmap
++++++++
+
+Release notes
++++++++++++++
+
+.. [#] More precisely, it iterates over all *enumerable* properties. It just
+ happens that built-in properties (such as ``String.indexOf`` or
+ ``Object.toString``) are set to non-enumerable.
+
+ The enumerability of a property can be checked using
+ `Object.propertyIsEnumeable`_.
+
+ Before ECMAScript 5, it was not possible for user-defined properties
+ to be non-enumerable in a portable manner. ECMAScript 5 introduced
+ `Object.defineProperty`_ which lets user code create non-enumerable
+ properties (and more, read-only properties for instance, or implicit
+ getters and setters). However, support for these is not fully complete
+ at this point, and they are not being used in OpenERP Web code anyway.
+
+.. [#] While using underscore is generally the preferred method (simpler,
+ more reliable and easier to write than a *correct* ``for…in``
+ iteration), it is also probably slower (due to the overhead of
+ calling a bunch of functions).
+
+ As a result, if you profile some code and find out that an underscore
+ method adds unacceptable overhead in a tight loop, you may want to
+ replace it with a ``for…in`` (or a regular ``for`` statement for
+ arrays).
+
+.. [#] Because Python is the default domain, the ``py:`` markup prefix
+ is optional and should be left out.
+
+.. [#] Resig's Class still uses prototypes under the hood, it doesn't
+ reimplement its own object system although it does add several
+ helpers such as the ``_super()`` instance method.
+
+.. [#] Which is the case in normal Javascript semantics. Likewise, the
+ ``.prototype`` / ``#`` pattern we will see later on is due to
+ JsDoc defaulting to the only behavior it can rely on: "normal"
+ Javascript prototype-based type creation.
+
+.. _reStructuredText:
+ http://docutils.sourceforge.net/rst.html
+.. _Sphinx:
+ http://sphinx.pocoo.org/index.html
+.. _Sphinx's extensions:
+ http://sphinx.pocoo.org/markup/index.html
+.. _Python domain:
+ http://sphinx.pocoo.org/domains.html#the-python-domain
+.. _info fields:
+ http://sphinx.pocoo.org/domains.html#info-field-lists
+.. _autodoc:
+ http://sphinx.pocoo.org/ext/autodoc.html
+ ?highlight=autodoc#sphinx.ext.autodoc
+.. _cross-references:
+ http://sphinx.pocoo.org/markup/inline.html#xref-syntax
+.. _JsDoc:
+.. _JsDoc Toolkit:
+ http://code.google.com/p/jsdoc-toolkit/
+.. _John Resig's Class implementation:
+ http://ejohn.org/blog/simple-javascript-inheritance/
+.. _\_.each:
+ http://documentcloud.github.com/underscore/#each
+.. _\_.map:
+ http://documentcloud.github.com/underscore/#map
+.. _\_.filter:
+ http://documentcloud.github.com/underscore/#select
+.. _It is also fairly tricky to use:
+ https://developer.mozilla.org/en/JavaScript/Reference/Statements/for...in#Description
+.. _Object.propertyIsEnumeable:
+ https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/propertyIsEnumerable
+.. _Object.defineProperty:
+ https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty
+.. _Object.hasOwnProperty:
+ https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/hasOwnProperty
+++ /dev/null
-# Makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line.
-SPHINXOPTS = -q
-SPHINXBUILD = sphinx-build
-PAPER =
-BUILDDIR = _build
-
-# Internal variables.
-PAPEROPT_a4 = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-# the i18n builder cannot share the environment and doctrees with the others
-I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-
-.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
-
-help:
- @echo "Please use \`make <target>' where <target> is one of"
- @echo " html to make standalone HTML files"
- @echo " dirhtml to make HTML files named index.html in directories"
- @echo " singlehtml to make a single large HTML file"
- @echo " pickle to make pickle files"
- @echo " json to make JSON files"
- @echo " htmlhelp to make HTML files and a HTML help project"
- @echo " qthelp to make HTML files and a qthelp project"
- @echo " devhelp to make HTML files and a Devhelp project"
- @echo " epub to make an epub"
- @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
- @echo " latexpdf to make LaTeX files and run them through pdflatex"
- @echo " text to make text files"
- @echo " man to make manual pages"
- @echo " texinfo to make Texinfo files"
- @echo " info to make Texinfo files and run them through makeinfo"
- @echo " gettext to make PO message catalogs"
- @echo " changes to make an overview of all changed/added/deprecated items"
- @echo " linkcheck to check all external links for integrity"
- @echo " doctest to run all doctests embedded in the documentation (if enabled)"
-
-clean:
- -rm -rf $(BUILDDIR)/*
-
-html:
- $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
-
-dirhtml:
- $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
- sed -i '/-99999/d' _build/dirhtml/_static/flasky.css
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
-
-singlehtml:
- $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
- @echo
- @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
-
-pickle:
- $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
- @echo
- @echo "Build finished; now you can process the pickle files."
-
-json:
- $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
- @echo
- @echo "Build finished; now you can process the JSON files."
-
-htmlhelp:
- $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
- @echo
- @echo "Build finished; now you can run HTML Help Workshop with the" \
- ".hhp project file in $(BUILDDIR)/htmlhelp."
-
-qthelp:
- $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
- @echo
- @echo "Build finished; now you can run "qcollectiongenerator" with the" \
- ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
- @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/OpenERPTechnicalDocumentation.qhcp"
- @echo "To view the help file:"
- @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/OpenERPTechnicalDocumentation.qhc"
-
-devhelp:
- $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
- @echo
- @echo "Build finished."
- @echo "To view the help file:"
- @echo "# mkdir -p $$HOME/.local/share/devhelp/OpenERPTechnicalDocumentation"
- @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/OpenERPTechnicalDocumentation"
- @echo "# devhelp"
-
-epub:
- $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
- @echo
- @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
-
-latex:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo
- @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
- @echo "Run \`make' in that directory to run these through (pdf)latex" \
- "(use \`make latexpdf' here to do that automatically)."
-
-latexpdf:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo "Running LaTeX files through pdflatex..."
- $(MAKE) -C $(BUILDDIR)/latex all-pdf
- @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-text:
- $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
- @echo
- @echo "Build finished. The text files are in $(BUILDDIR)/text."
-
-man:
- $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
- @echo
- @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
-
-texinfo:
- $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
- @echo
- @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
- @echo "Run \`make' in that directory to run these through makeinfo" \
- "(use \`make info' here to do that automatically)."
-
-info:
- $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
- @echo "Running Texinfo files through makeinfo..."
- make -C $(BUILDDIR)/texinfo info
- @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
-
-gettext:
- $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
- @echo
- @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
-
-changes:
- $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
- @echo
- @echo "The overview file is in $(BUILDDIR)/changes."
-
-linkcheck:
- $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
- @echo
- @echo "Link check complete; look for any errors in the above output " \
- "or in $(BUILDDIR)/linkcheck/output.txt."
-
-doctest:
- $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
- @echo "Testing of doctests in the sources finished, look at the " \
- "results in $(BUILDDIR)/doctest/output.txt."
+++ /dev/null
-<p class="logo"><a href="http://doc.openerp.com/">
- <img class="logo" src="{{ pathto('_static/openerp.png', 1) }}" alt="Logo"/>
-</a></p>
-
-<h3>Other Docs</h3>
-<ul>
- <li><a href="http://doc.openerp.com/v6.1/developers">OpenERP Developers Documentation</a></li>
- <li><a href="http://doc.openerp.com/v6.1/developers/server">OpenERP Server Developers Documentation</a></li>
- <li><a href="http://doc.openerp.com/v6.1/users">OpenERP Users Documentation</a></li>
-</ul>
-
-<h3>Useful Links</h3>
-<ul>
- <li><a href="http://www.openerp.com/">The OpenERP website</a></li>
- <li><a href="http://python.org/">The Python programming language</a></li>
-</ul>
+++ /dev/null
-<p class="logo"><a href="{{ pathto(master_doc) }}">
- <img class="logo" src="{{ pathto('_static/openerp.png', 1) }}" alt="Logo"/>
-</a></p>
+++ /dev/null
-Copyright (c) 2010 by Armin Ronacher.
-
-Some rights reserved.
-
-Redistribution and use in source and binary forms of the theme, with or
-without modification, are permitted provided that the following conditions
-are met:
-
-* Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
-* The names of the contributors may not be used to endorse or
- promote products derived from this software without specific
- prior written permission.
-
-We kindly ask you to only use these themes in an unmodified manner just
-for Flask and Flask-related products, not for unrelated projects. If you
-like the visual style and want to use it for your own projects, please
-consider making some larger changes to the themes (such as changing
-font faces, sizes, colors or margins).
-
-THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
+++ /dev/null
-Flask Sphinx Styles
-===================
-
-This repository contains sphinx styles for Flask and Flask related
-projects. To use this style in your Sphinx documentation, follow
-this guide:
-
-1. put this folder as _themes into your docs folder. Alternatively
- you can also use git submodules to check out the contents there.
-2. add this to your conf.py:
-
- sys.path.append(os.path.abspath('_themes'))
- html_theme_path = ['_themes']
- html_theme = 'flask'
-
-The following themes exist:
-
-- 'flask' - the standard flask documentation theme for large
- projects
-- 'flask_small' - small one-page theme. Intended to be used by
- very small addon libraries for flask.
-
-The following options exist for the flask_small theme:
-
- [options]
- index_logo = '' filename of a picture in _static
- to be used as replacement for the
- h1 in the index.rst file.
- index_logo_height = 120px height of the index logo
- github_fork = '' repository name on github for the
- "fork me" badge
+++ /dev/null
-{%- extends "basic/layout.html" %}
-{%- block extrahead %}
- {{ super() }}
- {% if theme_touch_icon %}
- <link rel="apple-touch-icon" href="{{ pathto('_static/' ~ theme_touch_icon, 1) }}" />
- {% endif %}
- <link media="only screen and (max-device-width: 480px)" href="{{
- pathto('_static/small_flask.css', 1) }}" type= "text/css" rel="stylesheet" />
-{% endblock %}
-{%- block relbar2 %}{% endblock %}
-{% block header %}
- {{ super() }}
- {% if pagename == 'index' %}
- <div class=indexwrapper>
- {% endif %}
-{% endblock %}
-{%- block footer %}
- <div class="footer">
- © Copyright {{ copyright }}
- Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> and a modified <a href="https://github.com/mitsuhiko/flask-sphinx-themes">Flask theme</a>.
- </div>
- {% if pagename == 'index' %}
- </div>
- {% endif %}
-{%- endblock %}
+++ /dev/null
-<h3>Related Topics</h3>
-<ul>
- <li><a href="{{ pathto(master_doc) }}">Documentation overview</a><ul>
- {%- for parent in parents %}
- <li><a href="{{ parent.link|e }}">{{ parent.title }}</a><ul>
- {%- endfor %}
- {%- if prev %}
- <li>Previous: <a href="{{ prev.link|e }}" title="{{ _('previous chapter')
- }}">{{ prev.title }}</a></li>
- {%- endif %}
- {%- if next %}
- <li>Next: <a href="{{ next.link|e }}" title="{{ _('next chapter')
- }}">{{ next.title }}</a></li>
- {%- endif %}
- {%- for parent in parents %}
- </ul></li>
- {%- endfor %}
- </ul></li>
-</ul>
+++ /dev/null
-/*
- * flasky.css_t
- * ~~~~~~~~~~~~
- *
- * :copyright: Copyright 2010 by Armin Ronacher.
- * :license: Flask Design License, see LICENSE for details.
- */
-
-{% set page_width = '80em' %}
-{% set sidebar_width = '16em' %}
-
-@import url("basic.css");
-
-/* -- page layout ----------------------------------------------------------- */
-
-body {
- font-family: 'Georgia', serif;
- font-size: 15px;
- background-color: white;
- color: #000;
- margin: 0;
- padding: 0;
-}
-
-div.document {
- width: {{ page_width }};
- margin: 30px auto 0 auto;
-}
-
-div.documentwrapper {
- float: left;
- width: 100%;
-}
-
-div.bodywrapper {
- margin: 0 0 0 {{ sidebar_width }};
-}
-
-div.sphinxsidebar {
- width: {{ sidebar_width }};
-}
-
-hr {
- border: 1px solid #B1B4B6;
-}
-
-div.body {
- background-color: #ffffff;
- color: #3E4349;
- padding: 0 0px 0 0px;
-}
-
-img.floatingflask {
- padding: 0 0 10px 10px;
- float: right;
-}
-
-div.footer {
- width: {{ page_width }};
- margin: 20px auto 30px auto;
- font-size: 12px;
- color: #888;
- text-align: right;
-}
-
-div.footer a {
- color: #888;
-}
-
-div.related {
- display: none;
-}
-
-div.sphinxsidebar a {
- color: #444;
- text-decoration: none;
- border-bottom: 1px dotted #999;
-}
-
-div.sphinxsidebar a:hover {
- border-bottom: 1px solid #999;
-}
-
-div.sphinxsidebar {
- font-size: 12px;
- line-height: 1.5;
-}
-
-div.sphinxsidebarwrapper {
- padding: 0px 10px;
-}
-
-div.sphinxsidebarwrapper p.logo {
- padding: 0 0 20px 0;
- margin: 0;
- text-align: center;
-}
-
-div.sphinxsidebar h3,
-div.sphinxsidebar h4 {
- font-family: 'Garamond', 'Georgia', serif;
- color: #444;
- font-size: 22px;
- font-weight: normal;
- margin: 0 0 5px 0;
- padding: 0;
-}
-
-div.sphinxsidebar h4 {
- font-size: 18px;
-}
-
-div.sphinxsidebar h3 a {
- color: #444;
-}
-
-div.sphinxsidebar p.logo a,
-div.sphinxsidebar h3 a,
-div.sphinxsidebar p.logo a:hover,
-div.sphinxsidebar h3 a:hover {
- border: none;
-}
-
-div.sphinxsidebar p {
- color: #555;
- margin: 10px 0;
-}
-
-div.sphinxsidebar ul {
- margin: 10px 0;
- padding: 0;
- color: #000;
-}
-
-div.sphinxsidebar input {
- border: 1px solid #ccc;
- font-family: 'Georgia', serif;
- font-size: 1em;
-}
-
-/* -- body styles ----------------------------------------------------------- */
-
-a {
- color: #004B6B;
- text-decoration: underline;
-}
-
-a:hover {
- color: #6D4100;
- text-decoration: underline;
-}
-
-div.body h1,
-div.body h2,
-div.body h3,
-div.body h4,
-div.body h5,
-div.body h6 {
- font-family: 'Garamond', 'Georgia', serif;
- font-weight: normal;
- margin: 30px 0px 10px 0px;
- padding: 0;
-}
-
-{% if theme_index_logo %}
-div.indexwrapper h1 {
- text-indent: -999999px;
- background: url({{ theme_index_logo }}) no-repeat center center;
- height: {{ theme_index_logo_height }};
-}
-{% endif %}
-
-div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
-div.body h2 { font-size: 180%; }
-div.body h3 { font-size: 150%; }
-div.body h4 { font-size: 130%; }
-div.body h5 { font-size: 100%; }
-div.body h6 { font-size: 100%; }
-
-a.headerlink {
- color: #ddd;
- padding: 0 4px;
- text-decoration: none;
-}
-
-a.headerlink:hover {
- color: #444;
- background: #eaeaea;
-}
-
-div.body p, div.body dd, div.body li {
- line-height: 1.4em;
-}
-
-div.admonition {
- background: #fafafa;
- margin: 20px -30px;
- padding: 10px 30px;
- border-top: 1px solid #ccc;
- border-bottom: 1px solid #ccc;
-}
-
-div.admonition tt.xref, div.admonition a tt {
- border-bottom: 1px solid #fafafa;
-}
-
-dd div.admonition {
- margin-left: -60px;
- padding-left: 60px;
-}
-
-div.admonition p.admonition-title {
- font-family: 'Garamond', 'Georgia', serif;
- font-weight: normal;
- font-size: 22px;
- margin: 0 0 10px 0;
- padding: 0;
- line-height: 1;
-}
-
-div.admonition p.last {
- margin-bottom: 0;
-}
-
-div.highlight {
- background-color: white;
-}
-
-dt:target, .highlight {
- background: #FAF3E8;
-}
-
-div.note {
- background-color: #eee;
- border: 1px solid #ccc;
-}
-
-div.seealso {
- background-color: #ffc;
- border: 1px solid #ff6;
-}
-
-div.topic {
- background-color: #eee;
-}
-
-p.admonition-title {
- display: inline;
-}
-
-p.admonition-title:after {
- content: ":";
-}
-
-pre, tt {
- font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
- font-size: 0.9em;
-}
-
-img.screenshot {
-}
-
-tt.descname, tt.descclassname {
- font-size: 0.95em;
-}
-
-tt.descname {
- padding-right: 0.08em;
-}
-
-img.screenshot {
- -moz-box-shadow: 2px 2px 4px #eee;
- -webkit-box-shadow: 2px 2px 4px #eee;
- box-shadow: 2px 2px 4px #eee;
-}
-
-table.docutils {
- border: 1px solid #888;
- -moz-box-shadow: 2px 2px 4px #eee;
- -webkit-box-shadow: 2px 2px 4px #eee;
- box-shadow: 2px 2px 4px #eee;
-}
-
-table.docutils td, table.docutils th {
- border: 1px solid #888;
- padding: 0.25em 0.7em;
-}
-
-table.field-list, table.footnote {
- border: none;
- -moz-box-shadow: none;
- -webkit-box-shadow: none;
- box-shadow: none;
-}
-
-table.footnote {
- margin: 15px 0;
- width: 100%;
- border: 1px solid #eee;
- background: #fdfdfd;
- font-size: 0.9em;
-}
-
-table.footnote + table.footnote {
- margin-top: -15px;
- border-top: none;
-}
-
-table.field-list th {
- padding: 0 0.8em 0 0;
-}
-
-table.field-list td {
- padding: 0;
-}
-
-table.footnote td.label {
- width: 0px;
- padding: 0.3em 0 0.3em 0.5em;
-}
-
-table.footnote td {
- padding: 0.3em 0.5em;
-}
-
-dl {
- margin: 0;
- padding: 0;
-}
-
-dl dd {
- margin-left: 30px;
-}
-
-blockquote {
- margin: 0 0 0 30px;
- padding: 0;
-}
-
-ul, ol {
- margin: 10px 0 10px 30px;
- padding: 0;
-}
-
-pre {
- background: #eee;
- padding: 7px 30px;
- margin: 15px -30px;
- line-height: 1.3em;
-}
-
-dl pre, blockquote pre, li pre {
- margin-left: -60px;
- padding-left: 60px;
-}
-
-dl dl pre {
- margin-left: -90px;
- padding-left: 90px;
-}
-
-tt {
- background-color: #ecf0f3;
- color: #222;
- /* padding: 1px 2px; */
-}
-
-tt.xref, a tt {
- background-color: #FBFBFB;
- border-bottom: 1px solid white;
-}
-
-a.reference {
- text-decoration: none;
- border-bottom: 1px dotted #004B6B;
-}
-
-a.reference:hover {
- border-bottom: 1px solid #6D4100;
-}
-
-a.footnote-reference {
- text-decoration: none;
- font-size: 0.7em;
- vertical-align: top;
- border-bottom: 1px dotted #004B6B;
-}
-
-a.footnote-reference:hover {
- border-bottom: 1px solid #6D4100;
-}
-
-a:hover tt {
- background: #EEE;
-}
+++ /dev/null
-/*
- * small_flask.css_t
- * ~~~~~~~~~~~~~~~~~
- *
- * :copyright: Copyright 2010 by Armin Ronacher.
- * :license: Flask Design License, see LICENSE for details.
- */
-
-body {
- margin: 0;
- padding: 20px 30px;
-}
-
-div.documentwrapper {
- float: none;
- background: white;
-}
-
-div.sphinxsidebar {
- display: block;
- float: none;
- width: 102.5%;
- margin: 50px -30px -20px -30px;
- padding: 10px 20px;
- background: #333;
- color: white;
-}
-
-div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
-div.sphinxsidebar h3 a {
- color: white;
-}
-
-div.sphinxsidebar a {
- color: #aaa;
-}
-
-div.sphinxsidebar p.logo {
- display: none;
-}
-
-div.document {
- width: 100%;
- margin: 0;
-}
-
-div.related {
- display: block;
- margin: 0;
- padding: 10px 0 20px 0;
-}
-
-div.related ul,
-div.related ul li {
- margin: 0;
- padding: 0;
-}
-
-div.footer {
- display: none;
-}
-
-div.bodywrapper {
- margin: 0;
-}
-
-div.body {
- min-height: 0;
- padding: 0;
-}
+++ /dev/null
-[theme]
-inherit = basic
-stylesheet = flasky.css
-pygments_style = flask_theme_support.FlaskyStyle
-
-[options]
-index_logo = ''
-index_logo_height = 120px
-touch_icon =
+++ /dev/null
-{% extends "basic/layout.html" %}
-{% block header %}
- {{ super() }}
- {% if pagename == 'index' %}
- <div class=indexwrapper>
- {% endif %}
-{% endblock %}
-{% block footer %}
- {% if pagename == 'index' %}
- </div>
- {% endif %}
-{% endblock %}
-{# do not display relbars #}
-{% block relbar1 %}{% endblock %}
-{% block relbar2 %}
- {% if theme_github_fork %}
- <a href="http://github.com/{{ theme_github_fork }}"><img style="position: fixed; top: 0; right: 0; border: 0;"
- src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" /></a>
- {% endif %}
-{% endblock %}
-{% block sidebar1 %}{% endblock %}
-{% block sidebar2 %}{% endblock %}
+++ /dev/null
-/*
- * flasky.css_t
- * ~~~~~~~~~~~~
- *
- * Sphinx stylesheet -- flasky theme based on nature theme.
- *
- * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
- * :license: BSD, see LICENSE for details.
- *
- */
-
-@import url("basic.css");
-
-/* -- page layout ----------------------------------------------------------- */
-
-body {
- font-family: 'Georgia', serif;
- font-size: 17px;
- color: #000;
- background: white;
- margin: 0;
- padding: 0;
-}
-
-div.documentwrapper {
- float: left;
- width: 100%;
-}
-
-div.bodywrapper {
- margin: 40px auto 0 auto;
- width: 700px;
-}
-
-hr {
- border: 1px solid #B1B4B6;
-}
-
-div.body {
- background-color: #ffffff;
- color: #3E4349;
- padding: 0 30px 30px 30px;
-}
-
-img.floatingflask {
- padding: 0 0 10px 10px;
- float: right;
-}
-
-div.footer {
- text-align: right;
- color: #888;
- padding: 10px;
- font-size: 14px;
- width: 650px;
- margin: 0 auto 40px auto;
-}
-
-div.footer a {
- color: #888;
- text-decoration: underline;
-}
-
-div.related {
- line-height: 32px;
- color: #888;
-}
-
-div.related ul {
- padding: 0 0 0 10px;
-}
-
-div.related a {
- color: #444;
-}
-
-/* -- body styles ----------------------------------------------------------- */
-
-a {
- color: #004B6B;
- text-decoration: underline;
-}
-
-a:hover {
- color: #6D4100;
- text-decoration: underline;
-}
-
-div.body {
- padding-bottom: 40px; /* saved for footer */
-}
-
-div.body h1,
-div.body h2,
-div.body h3,
-div.body h4,
-div.body h5,
-div.body h6 {
- font-family: 'Garamond', 'Georgia', serif;
- font-weight: normal;
- margin: 30px 0px 10px 0px;
- padding: 0;
-}
-
-{% if theme_index_logo %}
-div.indexwrapper h1 {
- text-indent: -999999px;
- background: url({{ theme_index_logo }}) no-repeat center center;
- height: {{ theme_index_logo_height }};
-}
-{% endif %}
-
-div.body h2 { font-size: 180%; }
-div.body h3 { font-size: 150%; }
-div.body h4 { font-size: 130%; }
-div.body h5 { font-size: 100%; }
-div.body h6 { font-size: 100%; }
-
-a.headerlink {
- color: white;
- padding: 0 4px;
- text-decoration: none;
-}
-
-a.headerlink:hover {
- color: #444;
- background: #eaeaea;
-}
-
-div.body p, div.body dd, div.body li {
- line-height: 1.4em;
-}
-
-div.admonition {
- background: #fafafa;
- margin: 20px -30px;
- padding: 10px 30px;
- border-top: 1px solid #ccc;
- border-bottom: 1px solid #ccc;
-}
-
-div.admonition p.admonition-title {
- font-family: 'Garamond', 'Georgia', serif;
- font-weight: normal;
- font-size: 24px;
- margin: 0 0 10px 0;
- padding: 0;
- line-height: 1;
-}
-
-div.admonition p.last {
- margin-bottom: 0;
-}
-
-div.highlight{
- background-color: white;
-}
-
-dt:target, .highlight {
- background: #FAF3E8;
-}
-
-div.note {
- background-color: #eee;
- border: 1px solid #ccc;
-}
-
-div.seealso {
- background-color: #ffc;
- border: 1px solid #ff6;
-}
-
-div.topic {
- background-color: #eee;
-}
-
-div.warning {
- background-color: #ffe4e4;
- border: 1px solid #f66;
-}
-
-p.admonition-title {
- display: inline;
-}
-
-p.admonition-title:after {
- content: ":";
-}
-
-pre, tt {
- font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
- font-size: 0.85em;
-}
-
-img.screenshot {
-}
-
-tt.descname, tt.descclassname {
- font-size: 0.95em;
-}
-
-tt.descname {
- padding-right: 0.08em;
-}
-
-img.screenshot {
- -moz-box-shadow: 2px 2px 4px #eee;
- -webkit-box-shadow: 2px 2px 4px #eee;
- box-shadow: 2px 2px 4px #eee;
-}
-
-table.docutils {
- border: 1px solid #888;
- -moz-box-shadow: 2px 2px 4px #eee;
- -webkit-box-shadow: 2px 2px 4px #eee;
- box-shadow: 2px 2px 4px #eee;
-}
-
-table.docutils td, table.docutils th {
- border: 1px solid #888;
- padding: 0.25em 0.7em;
-}
-
-table.field-list, table.footnote {
- border: none;
- -moz-box-shadow: none;
- -webkit-box-shadow: none;
- box-shadow: none;
-}
-
-table.footnote {
- margin: 15px 0;
- width: 100%;
- border: 1px solid #eee;
-}
-
-table.field-list th {
- padding: 0 0.8em 0 0;
-}
-
-table.field-list td {
- padding: 0;
-}
-
-table.footnote td {
- padding: 0.5em;
-}
-
-dl {
- margin: 0;
- padding: 0;
-}
-
-dl dd {
- margin-left: 30px;
-}
-
-pre {
- padding: 0;
- margin: 15px -30px;
- padding: 8px;
- line-height: 1.3em;
- padding: 7px 30px;
- background: #eee;
- border-radius: 2px;
- -moz-border-radius: 2px;
- -webkit-border-radius: 2px;
-}
-
-dl pre {
- margin-left: -60px;
- padding-left: 60px;
-}
-
-tt {
- background-color: #ecf0f3;
- color: #222;
- /* padding: 1px 2px; */
-}
-
-tt.xref, a tt {
- background-color: #FBFBFB;
-}
-
-a:hover tt {
- background: #EEE;
-}
+++ /dev/null
-[theme]
-inherit = basic
-stylesheet = flasky.css
-nosidebar = true
-pygments_style = flask_theme_support.FlaskyStyle
-
-[options]
-index_logo = ''
-index_logo_height = 120px
-github_fork = ''
+++ /dev/null
-# flasky extensions. flasky pygments style based on tango style
-from pygments.style import Style
-from pygments.token import Keyword, Name, Comment, String, Error, \
- Number, Operator, Generic, Whitespace, Punctuation, Other, Literal
-
-
-class FlaskyStyle(Style):
- background_color = "#f8f8f8"
- default_style = ""
-
- styles = {
- # No corresponding class for the following:
- #Text: "", # class: ''
- Whitespace: "underline #f8f8f8", # class: 'w'
- Error: "#a40000 border:#ef2929", # class: 'err'
- Other: "#000000", # class 'x'
-
- Comment: "italic #8f5902", # class: 'c'
- Comment.Preproc: "noitalic", # class: 'cp'
-
- Keyword: "bold #004461", # class: 'k'
- Keyword.Constant: "bold #004461", # class: 'kc'
- Keyword.Declaration: "bold #004461", # class: 'kd'
- Keyword.Namespace: "bold #004461", # class: 'kn'
- Keyword.Pseudo: "bold #004461", # class: 'kp'
- Keyword.Reserved: "bold #004461", # class: 'kr'
- Keyword.Type: "bold #004461", # class: 'kt'
-
- Operator: "#582800", # class: 'o'
- Operator.Word: "bold #004461", # class: 'ow' - like keywords
-
- Punctuation: "bold #000000", # class: 'p'
-
- # because special names such as Name.Class, Name.Function, etc.
- # are not recognized as such later in the parsing, we choose them
- # to look the same as ordinary variables.
- Name: "#000000", # class: 'n'
- Name.Attribute: "#c4a000", # class: 'na' - to be revised
- Name.Builtin: "#004461", # class: 'nb'
- Name.Builtin.Pseudo: "#3465a4", # class: 'bp'
- Name.Class: "#000000", # class: 'nc' - to be revised
- Name.Constant: "#000000", # class: 'no' - to be revised
- Name.Decorator: "#888", # class: 'nd' - to be revised
- Name.Entity: "#ce5c00", # class: 'ni'
- Name.Exception: "bold #cc0000", # class: 'ne'
- Name.Function: "#000000", # class: 'nf'
- Name.Property: "#000000", # class: 'py'
- Name.Label: "#f57900", # class: 'nl'
- Name.Namespace: "#000000", # class: 'nn' - to be revised
- Name.Other: "#000000", # class: 'nx'
- Name.Tag: "bold #004461", # class: 'nt' - like a keyword
- Name.Variable: "#000000", # class: 'nv' - to be revised
- Name.Variable.Class: "#000000", # class: 'vc' - to be revised
- Name.Variable.Global: "#000000", # class: 'vg' - to be revised
- Name.Variable.Instance: "#000000", # class: 'vi' - to be revised
-
- Number: "#990000", # class: 'm'
-
- Literal: "#000000", # class: 'l'
- Literal.Date: "#000000", # class: 'ld'
-
- String: "#4e9a06", # class: 's'
- String.Backtick: "#4e9a06", # class: 'sb'
- String.Char: "#4e9a06", # class: 'sc'
- String.Doc: "italic #8f5902", # class: 'sd' - like a comment
- String.Double: "#4e9a06", # class: 's2'
- String.Escape: "#4e9a06", # class: 'se'
- String.Heredoc: "#4e9a06", # class: 'sh'
- String.Interpol: "#4e9a06", # class: 'si'
- String.Other: "#4e9a06", # class: 'sx'
- String.Regex: "#4e9a06", # class: 'sr'
- String.Single: "#4e9a06", # class: 's1'
- String.Symbol: "#4e9a06", # class: 'ss'
-
- Generic: "#000000", # class: 'g'
- Generic.Deleted: "#a40000", # class: 'gd'
- Generic.Emph: "italic #000000", # class: 'ge'
- Generic.Error: "#ef2929", # class: 'gr'
- Generic.Heading: "bold #000080", # class: 'gh'
- Generic.Inserted: "#00A000", # class: 'gi'
- Generic.Output: "#888", # class: 'go'
- Generic.Prompt: "#745334", # class: 'gp'
- Generic.Strong: "bold #000000", # class: 'gs'
- Generic.Subheading: "bold #800080", # class: 'gu'
- Generic.Traceback: "bold #a40000", # class: 'gt'
- }
+++ /dev/null
-<addon name>
- +-- __openerp__.py
- +-- controllers/
- +-- static/
- +-- lib/
- +-- src/
- +-- css/
- +-- img/
- +-- js/
- +-- xml/
- +-- test/
- +-- test/
+++ /dev/null
-Developing OpenERP Web Addons
-=============================
-
-An OpenERP Web addon is simply a Python package with an openerp
-descriptor (a ``__openerp__.py`` file) which follows a few structural
-and namespacing rules.
-
-Structure
----------
-
-.. literalinclude:: addon-structure.txt
-
-``__openerp__.py``
- The addon's descriptor, contains the following information:
-
- ``name: str``
- The addon name, in plain, readable english
- ``version: str``
- The addon version, following `Semantic Versioning`_ rules
- ``depends: [str]``
- A list of addons this addon needs to work correctly. ``base`` is
- an implied dependency if the list is empty.
- ``css: [str]``
- An ordered list of CSS files this addon provides and needs. The
- file paths are relative to the addon's root. Because the Web
- Client *may* perform concatenations and other various
- optimizations on CSS files, the order is important.
- ``js: [str]``
- An ordered list of Javascript files this addon provides and needs
- (including dependencies files). As with CSS files, the order is
- important as the Web Client *may* perform contatenations and
- minimizations of files.
- ``active: bool``
- Whether this addon should be enabled by default any time it is
- found, or whether it will be enabled through other means (on a
- by-need or by-installation basis for instance).
-
-``controllers/``
- All of the Python controllers and JSON-RPC endpoints.
-
-``static/``
- The static files directory, may be served via a separate web server.
-
-``static/lib/``
- Third-party libraries used by the addon.
-
-``static/src/{css,js,img,xml}``
- Location for (respectively) the addon's static CSS files, its JS
- files, its various image resources as well as the template files
-
-``static/test``
- Javascript tests files
-
-``test/``
- The directories in which all tests for the addon are located.
-
-Some of these are guidelines (and not enforced by code), but it's
-suggested that these be followed. Code which does not fit into these
-categories can go wherever deemed suitable.
-
-Namespacing
------------
-
-Python
-++++++
-
-Because addons are also Python packages, they're inherently namespaced
-and nothing special needs to be done on that front.
-
-JavaScript
-++++++++++
-
-The JavaScript side of an addon has to live in the namespace
-``openerp.$addon_name``. For instance, everything created by the addon
-``base`` lives in ``openerp.base``.
-
-The root namespace of the addon is a function which takes a single
-parameter ``openerp``, which is an OpenERP client instance. Objects
-(as well as functions, registry instances, etc...) should be added on
-the correct namespace on that object.
-
-The root function will be called by the OpenERP Web client when
-initializing the addon.
-
-.. code-block:: javascript
-
- // root namespace of the openerp.example addon
- /** @namespace */
- openerp.example = function (openerp) {
- // basic initialization code (e.g. templates loading)
- openerp.example.SomeClass = openerp.base.Class.extend(
- /** @lends openerp.example.SomeClass# */{
- /**
- * Description for SomeClass's constructor here
- *
- * @constructs
- */
- init: function () {
- // SomeClass initialization code
- }
- // rest of SomeClass
- });
-
- // access an object in an other addon namespace to replace it
- openerp.base.SearchView = openerp.base.SearchView.extend({
- init: function () {
- this._super.apply(this, arguments);
- console.log('Search view initialized');
- }
- });
- }
-
-Creating new standard roles
----------------------------
-
-Widget
-++++++
-
-This is the base class for all visual components. It provides a number of
-services for the management of a DOM subtree:
-
-* Rendering with QWeb
-
-* Parenting-child relations
-
-* Life-cycle management (including facilitating children destruction when a
- parent object is removed)
-
-* DOM insertion, via jQuery-powered insertion methods. Insertion targets can
- be anything the corresponding jQuery method accepts (generally selectors,
- DOM nodes and jQuery objects):
-
- :js:func:`~openerp.base.Widget.appendTo`
- Renders the widget and inserts it as the last child of the target, uses
- `.appendTo()`_
-
- :js:func:`~openerp.base.Widget.prependTo`
- Renders the widget and inserts it as the first child of the target, uses
- `.prependTo()`_
-
- :js:func:`~openerp.base.Widget.insertAfter`
- Renders the widget and inserts it as the preceding sibling of the target,
- uses `.insertAfter()`_
-
- :js:func:`~openerp.base.Widget.insertBefore`
- Renders the widget and inserts it as the following sibling of the target,
- uses `.insertBefore()`_
-
-:js:class:`~openerp.base.Widget` inherits from
-:js:class:`~openerp.base.SessionAware`, so subclasses can easily access the
-RPC layers.
-
-Subclassing Widget
-~~~~~~~~~~~~~~~~~~
-
-:js:class:`~openerp.base.Widget` is subclassed in the standard manner (via the
-:js:func:`~openerp.base.Class.extend` method), and provides a number of
-abstract properties and concrete methods (which you may or may not want to
-override). Creating a subclass looks like this:
-
-.. code-block:: javascript
-
- var MyWidget = openerp.base.Widget.extend({
- // QWeb template to use when rendering the object
- template: "MyQWebTemplate",
-
- init: function(parent) {
- this._super(parent);
- // insert code to execute before rendering, for object
- // initialization
- },
- start: function() {
- this._super();
- // post-rendering initialization code, at this point
- // ``this.$element`` has been initialized
- this.$element.find(".my_button").click(/* an example of event binding * /);
-
- // if ``start`` is asynchronous, return a promise object so callers
- // know when the object is done initializing
- return this.rpc(/* … */)
- }
- });
-
-The new class can then be used in the following manner:
-
-.. code-block:: javascript
-
- // Create the instance
- var my_widget = new MyWidget(this);
- // Render and insert into DOM
- my_widget.appendTo(".some-div");
-
-After these two lines have executed (and any promise returned by ``appendTo``
-has been resolved if needed), the widget is ready to be used.
-
-.. note:: the insertion methods will start the widget themselves, and will
- return the result of :js:func:`~openerp.base.Widget.start()`.
-
- If for some reason you do not want to call these methods, you will
- have to first call :js:func:`~openerp.base.Widget.render()` on the
- widget, then insert it into your DOM and start it.
-
-If the widget is not needed anymore (because it's transient), simply terminate
-it:
-
-.. code-block:: javascript
-
- my_widget.stop();
-
-will unbind all DOM events, remove the widget's content from the DOM and
-destroy all widget data.
-
-Views
-+++++
-
-Views are the standard high-level component in OpenERP. A view type corresponds
-to a way to display a set of data (coming from an OpenERP model).
-
-In OpenERP Web, views are standard objects registered against a dedicated
-object registry, so the :js:class:`~openerp.base.ViewManager` knows where to
-find and how to call them.
-
-Although not mandatory, it is recommended that views inherit from
-:js:class:`openerp.base.View`, which provides a view useful services to its
-children.
-
-Registering a view
-~~~~~~~~~~~~~~~~~~
-
-This is the first task to perform when creating a view, and the simplest by
-far: simply call ``openerp.base.views.add(name, object_path)`` to register
-the object of path ``object_path`` as the view for the view name ``name``.
-
-The view name is the name you gave to your new view in the OpenERP server.
-
-From that point onwards, OpenERP Web will be able to find your object and
-instantiate it.
-
-Standard view behaviors
-~~~~~~~~~~~~~~~~~~~~~~~
-
-In the normal OpenERP Web flow, views have to implement a number of methods so
-view managers can correctly communicate with them:
-
-``start()``
- This method will always be called after creating the view (via its
- constructor), but not necessarily immediately.
-
- It is called with no arguments and should handle the heavy setup work,
- including remote call (to load the view's setup data from the server via
- e.g. ``fields_view_get``, for instance).
-
- ``start`` should return a `promise object`_ which *must* be resolved when
- the view's setup is completed. This promise is used by view managers to
- know when they can start interacting with the view.
-
-``do_hide()``
- Called by the view manager when it wants to replace this view by an other
- one, but wants to keep this view around to re-activate it later.
-
- Should put the view in some sort of hibernation mode, and *must* hide its
- DOM elements.
-
-``do_show()``
- Called when the view manager wants to re-display the view after having
- hidden it. The view should refresh its data display upon receiving this
- notification
-
-``do_search(domain: Array, context: Object, group_by: Array)``
- If the view is searchable, this method is called to notify it of a search
- against it.
-
- It should use the provided query data to perform a search and refresh its
- internal content (and display).
-
- All views are searchable by default, but they can be made non-searchable
- by setting the property ``searchable`` to ``false``.
-
- This can be done either on the view class itself (at the same level as
- defining e.g. the ``start`` method) or at the instance level (in the
- class's ``init``), though you should generally set it on the class.
-
-Frequent development tasks
---------------------------
-
-There are a number of tasks which OpenERP Web developers do or will need to
-perform quite regularly. To make these easier, we have written a few guides
-to help you get started:
-
-.. toctree::
- :maxdepth: 1
-
- guides/client-action
- guides/sidebar-protocol
-
-Translations
-------------
-
-OpenERP Web should provide most of the tools needed to correctly translate your
-addons via the tool of your choice (OpenERP itself uses `Launchpad's own
-translation tool`_.
-
-Making strings translatable
-+++++++++++++++++++++++++++
-
-QWeb
-~~~~
-
-QWeb automatically marks all text nodes (any text which is not in an XML
-attribute and not part of an XML tag) as translatable, and handles the
-replacement for you. There is nothing special to do to mark template text as
-translatable
-
-JavaScript
-~~~~~~~~~~
-
-OpenERP Web provides two functions to translate human-readable strings in
-javascript code. These functions should be "imported" in your module by
-aliasing them to their bare name:
-
-.. code-block:: javascript
-
- var _t = openerp.web._t,
- _tl = openerp.web._tl;
-
-importing those functions under any other name is not guaranteed to work.
-
-.. note:: only import them if necessary, and only the necessary one(s), no need
- to clutter your module's namespace for nothing
-
-.. js:function:: openerp.web._t(s)
-
- Base translation function, eager, works much like :manpage:`gettext(3)`
-
- :type s: String
- :rtype: String
-
-.. js:function:: openerp.web._lt(s)
-
- Lazy equivalent to :js:func:`~openerp.web._t`, this function will postpone
- fetching the translation to its argument until the last possible moment.
-
- To use in contexts evaluated before the translation database can be
- fetched, usually your module's toplevel and the attributes of classes
- defined in it (class attributes, not instance attributes set in the
- constructor).
-
- :type s: String
- :rtype: LazyString
-
-Text formatting & translations
-""""""""""""""""""""""""""""""
-
-A difficulty when translating is integrating data (from the code) into the
-translated string. In OpenERP Web addons, this should be done by wrapping the
-text to translate in an :manpage:`sprintf(3)` call. For OpenERP Web,
-:manpage:`sprintf(3)` is provided by `underscore.string
-<http://epeli.github.com/underscore.string/>`_.
-
-As much as possible, you should use the "named argument" form of sprintf:
-
-.. code-block:: javascript
-
- var translated_string = _.str.sprintf(
- _t("[%(first_record)d to %(last_record)d] of %(records_count)d"), {
- first_record: first + 1,
- last_record: last,
- records_count: total
- }));
-
-named arguments make the string to translate much clearer for translators, and
-allows them to "move" sections around based on the requirements of their
-language (not all language order text like english).
-
-Named arguments are specified using the following pattern: ``%($name)$type``
-where
-
-``$name``
- the name of the argument, this is the key in the object/dictionary provided
- as second parameter to ``sprintf``
-``$type``
- a type/format specifier, `see the list for all possible types
- <http://www.diveintojavascript.com/projects/javascript-sprintf>`_.
-
-.. note:: positional arguments are acceptable if the translated string has
- *a single* argument and its content is easy to guess from the text
- around it. Named arguments should still be preferred.
-
-.. warning:: you should *never* use string concatenation as it robs the
- translator of context and make result in a completely incorrect
- translation
-
-Extracting strings
-~~~~~~~~~~~~~~~~~~
-
-.. program:: gen_translations.sh
-
-Once strings have been marked for translation, they need to be extracted into
-:abbr:`POT (Portable Object Template)` files, from which most translation tools
-can build a database.
-
-This can be done via the provided :program:`gen_translations.sh`.
-
-It can be called either as :option:`gen_translations.sh -a` or by providing
-two parameters, a path to the addons and the complete path in which to put the
-extracted POT file.
-
-.. option:: -a
-
- Extracts translations from all standard OpenERP Web addons (addons bundled
- with OpenERP Web itself) and puts the extracted templates into the right
- directory for `Rosetta`_ to handle them
-
-Utility behaviors
------------------
-
-JavaScript
-++++++++++
-
-* All javascript objects inheriting from
- :js:class:`openerp.base.BasicConroller` will have all methods
- starting with ``on_`` or ``do_`` bound to their ``this``. This means
- they don't have to be manually bound (via ``_.bind`` or ``$.proxy``)
- in order to be useable as bound event handlers (event handlers
- keeping their object as ``this`` rather than taking whatever
- ``this`` object they were called with).
-
- Beware that this is only valid for methods starting with ``do_`` and
- ``on_``, any other method will have to be bound manually.
-
-.. _addons-testing:
-
-Testing
--------
-
-Python
-++++++
-
-OpenERP Web uses unittest2_ for its testing needs. We selected
-unittest2 rather than unittest_ for the following reasons:
-
-* autodiscovery_ (similar to nose, via the ``unit2``
- CLI utility) and `pluggable test discovery`_.
-
-* `new and improved assertions`_ (with improvements in type-specific
- inequality reportings) including `pluggable custom types equality
- assertions`_
-
-* neveral new APIs, most notably `assertRaises context manager`_,
- `cleanup function registration`_, `test skipping`_ and `class- and
- module-level setup and teardown`_
-
-* finally, unittest2 is a backport of Python 3's unittest. We might as
- well get used to it.
-
-To run tests on addons (from the root directory of OpenERP Web) is as
-simple as typing ``PYTHONPATH=. unit2 discover -s addons`` [#]_. To
-test an addon which does not live in the ``addons`` directory, simply
-replace ``addons`` by the directory in which your own addon lives.
-
-.. note:: unittest2 is entirely compatible with nose_ (or the
- other way around). If you want to use nose as your test
- runner (due to its addons for instance) you can simply install it
- and run ``nosetests addons`` instead of the ``unit2`` command,
- the result should be exactly the same.
-
-Python
-++++++
-
-.. autoclass:: web.common.session.OpenERPSession
- :members:
-
-.. autoclass:: web.common.openerplib.main.Model
- :members:
-
-* Addons lifecycle (loading, execution, events, ...)
-
- * Python-side
- * JS-side
-
-* Handling static files
-* Overridding a Python controller (object?)
-* Overridding a Javascript controller (object?)
-* Extending templates
- .. how do you handle deploying static files via e.g. a separate lighttpd?
-* Python public APIs
-* QWeb templates description?
-* OpenERP Web modules (from OpenERP modules)
-
-.. [#] the ``-s`` parameter tells ``unit2`` to start trying to
- find tests in the provided directory (here we're testing
- addons). However a side-effect of that is to set the
- ``PYTHONPATH`` there as well, so it will fail to find (and
- import) ``openerpweb``.
-
- The ``-t`` parameter lets us set the ``PYTHONPATH``
- independently, but it doesn't accept multiple values and here
- we really want to have both ``.`` and ``addons`` on the
- ``PYTHONPATH``.
-
- The solution is to set the ``PYTHONPATH`` to ``.`` on start,
- and the ``start-directory`` to ``addons``. This results in a
- correct ``PYTHONPATH`` within ``unit2``.
-
-.. _unittest:
- http://docs.python.org/library/unittest.html
-
-.. _unittest2:
- http://www.voidspace.org.uk/python/articles/unittest2.shtml
-
-.. _autodiscovery:
- http://www.voidspace.org.uk/python/articles/unittest2.shtml#test-discovery
-
-.. _pluggable test discovery:
- http://www.voidspace.org.uk/python/articles/unittest2.shtml#load-tests
-
-.. _new and improved assertions:
- http://www.voidspace.org.uk/python/articles/unittest2.shtml#new-assert-methods
-
-.. _pluggable custom types equality assertions:
- http://www.voidspace.org.uk/python/articles/unittest2.shtml#add-new-type-specific-functions
-
-.. _assertRaises context manager:
- http://www.voidspace.org.uk/python/articles/unittest2.shtml#assertraises
-
-.. _cleanup function registration:
- http://www.voidspace.org.uk/python/articles/unittest2.shtml#cleanup-functions-with-addcleanup
-
-.. _test skipping:
- http://www.voidspace.org.uk/python/articles/unittest2.shtml#test-skipping
-
-.. _class- and module-level setup and teardown:
- http://www.voidspace.org.uk/python/articles/unittest2.shtml#class-and-module-level-fixtures
-
-.. _Semantic Versioning:
- http://semver.org/
-
-.. _nose:
- http://somethingaboutorange.com/mrl/projects/nose/1.0.0/
-
-.. _promise object:
- http://api.jquery.com/deferred.promise/
-
-.. _.appendTo():
- http://api.jquery.com/appendTo/
-
-.. _.prependTo():
- http://api.jquery.com/prependTo/
-
-.. _.insertAfter():
- http://api.jquery.com/insertAfter/
-
-.. _.insertBefore():
- http://api.jquery.com/insertBefore/
-
-.. _Rosetta:
-.. _Launchpad's own translation tool:
- https://help.launchpad.net/Translations
+++ /dev/null
-# -*- coding: utf-8 -*-
-#
-# OpenERP Technical Documentation configuration file, created by
-# sphinx-quickstart on Fri Feb 17 16:14:06 2012.
-#
-# This file is execfile()d with the current directory set to its containing dir.
-#
-# Note that not all possible configuration values are present in this
-# autogenerated file.
-#
-# All configuration values have a default; values that are commented out
-# serve to show the default.
-
-import sys, os
-
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path here. If the directory is relative to the
-# documentation root, use os.path.abspath to make it absolute, like shown here.
-sys.path.append(os.path.abspath('_themes'))
-sys.path.insert(0, os.path.abspath('../../addons'))
-sys.path.insert(0, os.path.abspath('../..'))
-
-# -- General configuration -----------------------------------------------------
-
-# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
-
-# Add any Sphinx extension module names here, as strings. They can be extensions
-# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.viewcode']
-
-# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
-
-# The suffix of source filenames.
-source_suffix = '.rst'
-
-# The encoding of source files.
-#source_encoding = 'utf-8-sig'
-
-# The master toctree document.
-master_doc = 'index'
-
-# General information about the project.
-project = u'OpenERP Server Developers Documentation'
-copyright = u'2012, OpenERP s.a.'
-
-# The version info for the project you're documenting, acts as replacement for
-# |version| and |release|, also used in various other places throughout the
-# built documents.
-#
-# The short X.Y version.
-version = '6.1'
-# The full version, including alpha/beta/rc tags.
-release = '6.1'
-
-# The language for content autogenerated by Sphinx. Refer to documentation
-# for a list of supported languages.
-#language = None
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-#today = ''
-# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
-
-# List of patterns, relative to source directory, that match files and
-# directories to ignore when looking for source files.
-exclude_patterns = ['_build']
-
-# The reST default role (used for this markup: `text`) to use for all documents.
-#default_role = None
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-#add_module_names = True
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-#show_authors = False
-
-# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
-
-# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
-
-
-# -- Options for HTML output ---------------------------------------------------
-
-# The theme to use for HTML and HTML Help pages. See the documentation for
-# a list of builtin themes.
-html_theme = 'flask'
-
-# Theme options are theme-specific and customize the look and feel of a theme
-# further. For a list of options available for each theme, see the
-# documentation.
-#html_theme_options = {}
-
-# Add any paths that contain custom themes here, relative to this directory.
-html_theme_path = ['_themes']
-
-# The name for this set of Sphinx documents. If None, it defaults to
-# "<project> v<release> documentation".
-#html_title = None
-
-# A shorter title for the navigation bar. Default is the same as html_title.
-#html_short_title = None
-
-# The name of an image file (relative to this directory) to place at the top
-# of the sidebar.
-#html_logo = None
-
-# The name of an image file (within the static path) to use as favicon of the
-# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-#html_favicon = None
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-#html_use_smartypants = True
-
-# Custom sidebar templates, maps document names to template names.
-html_sidebars = {
- 'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'],
- '**': ['sidebarlogo.html', 'localtoc.html', 'relations.html',
- 'sourcelink.html', 'searchbox.html']
-}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-#html_additional_pages = {}
-
-# If false, no module index is generated.
-#html_domain_indices = True
-
-# If false, no index is generated.
-#html_use_index = True
-
-# If true, the index is split into individual pages for each letter.
-#html_split_index = False
-
-# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
-
-# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
-
-# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a <link> tag referring to it. The value of this option must be the
-# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
-
-# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'openerp-server-doc'
-
-
-# -- Options for LaTeX output --------------------------------------------------
-
-latex_elements = {
-# The paper size ('letterpaper' or 'a4paper').
-#'papersize': 'letterpaper',
-
-# The font size ('10pt', '11pt' or '12pt').
-#'pointsize': '10pt',
-
-# Additional stuff for the LaTeX preamble.
-#'preamble': '',
-}
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, documentclass [howto/manual]).
-latex_documents = [
- ('index', 'openerp-server-doc.tex', u'OpenERP Server Developers Documentation',
- u'OpenERP s.a.', 'manual'),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# If true, show page references after internal links.
-#latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-#latex_show_urls = False
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_domain_indices = True
-
-
-# -- Options for manual page output --------------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
- ('index', 'openerp-server-doc', u'OpenERP Server Developers Documentation',
- [u'OpenERP s.a.'], 1)
-]
-
-# If true, show URL addresses after external links.
-#man_show_urls = False
-
-
-# -- Options for Texinfo output ------------------------------------------------
-
-# Grouping the document tree into Texinfo files. List of tuples
-# (source start file, target name, title, author,
-# dir menu entry, description, category)
-texinfo_documents = [
- ('index', 'OpenERPServerDocumentation', u'OpenERP Server Developers Documentation',
- u'OpenERP s.a.', 'OpenERPServerDocumentation', 'Developers documentation for the openobject-server project.',
- 'Miscellaneous'),
-]
-
-# Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
-
-# If false, no module index is generated.
-#texinfo_domain_indices = True
-
-# How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
-
-
-# Example configuration for intersphinx: refer to the Python standard library.
-intersphinx_mapping = {
- 'python': ('http://docs.python.org/', None),
- 'openerpserver': ('http://doc.openerp.com/v6.1/developers/server', None),
- 'openerpdev': ('http://doc.openerp.com/v6.1/developers', None),
-}
+++ /dev/null
-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/
+++ /dev/null
-Getting Started with OpenERP Web
-================================
-
-Installing
-----------
-
-.. per-distro packaging
-
-Launching
----------
+++ /dev/null
-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.
-
-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
------------------
-
-In the OpenERP Web code, a client action only requires two pieces of
-information:
-
-* Mapping the action's ``tag`` to an OpenERP Web object
-
-* The OpenERP Web object itself, which must inherit from
- :js:class:`openerp.web.Widget`
-
-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):
-
-.. code-block:: javascript
-
- // Registers the object 'openerp.web_dashboard.Widget' to the client
- // action tag 'board.home.widgets'
- openerp.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({
- 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.
-
-If the client action takes parameters, these parameters are passed in as a
-second positional parameter to the constructor:
-
-.. code-block:: javascript
-
- init: function (parent, params) {
- // execute the Widget's init
- this._super(parent);
- // board.home.widgets only takes a single param, the identifier of the
- // res.widget object it should display. Store it for later
- this.widget_id = params.widget_id;
- }
-
-More complex initialization (DOM manipulations, RPC requests, ...) should be
-performed in the ``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.
-
-.. code-block:: javascript
-
- start: function () {
- 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));
- }
-
-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
-
- 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', {
- widget: widget,
- url: url
- }));
- }
+++ /dev/null
-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<Number>
-
-.. 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<Object>
-
-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
-
+++ /dev/null
-.. OpenERP Web documentation master file, created by
- sphinx-quickstart on Fri Mar 18 16:31:55 2011.
- You can adapt this file completely to your liking, but it should at least
- contain the root `toctree` directive.
-
-Welcome to OpenERP Web's documentation!
-=======================================
-
-Contents:
-
-.. toctree::
- :maxdepth: 2
-
- getting-started
- production
- widgets
- addons
- development
- project
- old-version
-
-Indices and tables
-==================
-
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
+++ /dev/null
-Main differences with the 6.0 client
-====================================
-
-.. No more populate.sh, use virtualenvs
-
-.. Logic is mainly in Javascript (had to make a choice between JS and
-.. Python logic)
-
-.. Templating language changes
-
-.. How to port addons and modules?
+++ /dev/null
-Deploying OpenERP Web
-=====================
-
-.. After release one, add upgrade instructions if any
-
-.. How about running the web client on alternative Python
-.. implementations e.g. pypy or Jython? Since the only lib with C
-.. accelerators we're using right now is SimpleJSON and it has a pure
-.. Python base component, we should be able to test and deploy on
-.. non-cpython no?
-
-In-depth configuration
-----------------------
-
-SSL, basic proxy (link to relevant section), links to sections and
-example files for various servers and proxies, WSGI
-integration/explanation (if any), ...
-
-Deployment Options
-------------------
-
-Serving via WSGI
-~~~~~~~~~~~~~~~~
-
-Apache mod_wsgi
-+++++++++++++++
-
-NGinx mod_wsgi
-++++++++++++++
-
-uWSGI
-+++++
-
-Gunicorn
-++++++++
-
-FastCGI, SCGI, or AJP
-+++++++++++++++++++++
-
-Behind a proxy
-~~~~~~~~~~~~~~
-
-Apache mod_proxy
-++++++++++++++++
-
-NGinx HttpProxy
-+++++++++++++++
+++ /dev/null
-The OpenERP Web open-source project
-===================================
-
-Getting involved
-----------------
-
-Translations
-++++++++++++
-
-Bug reporting
-+++++++++++++
-
-Source code repository
-++++++++++++++++++++++
-
-Merge proposals
-+++++++++++++++
-
-Coding issues and coding conventions
-++++++++++++++++++++++++++++++++++++
-
-Javascript coding
-~~~~~~~~~~~~~~~~~
-
-These are a number of guidelines for javascript code. More than coding
-conventions, these are warnings against potentially harmful or sub-par
-constructs.
-
-Ideally, you should be able to configure your editor or IDE to warn you against
-these kinds of issues.
-
-Use ``var`` for *all* declarations
-**********************************
-
-In javascript (as opposed to Python), assigning to a variable which does not
-already exist and is not explicitly declared (via ``var``) will implicitly
-create a global variable. This is bad for a number of reasons:
-
-* It leaks information outside function scopes
-* It keeps memory of previous run, with potentially buggy behaviors
-* It may conflict with other functions with the same issue
-* It makes code harder to statically check (via e.g. IDE inspectors)
-
-.. note::
- It is perfectly possible to use ``var`` in ``for`` loops:
-
- .. code-block:: javascript
-
- for (var i = 0; i < some_array.length; ++i) {
- // code here
- }
-
- this is not an issue
-
-All local *and global* variables should be declared via ``var``.
-
-.. note:: generally speaking, you should not need globals in OpenERP Web: you
- can just declare a variable local to your top-level function. This
- way, if your widget/addon is instantiated several times on the same
- page (because it's used in embedded mode) each instance will have its
- own internal but global-to-its-objects data.
-
-Do not leave trailing commas in object literals
-***********************************************
-
-While it is legal to leave trailing commas in Python dictionaries, e.g.
-
-.. code-block:: python
-
- foo = {
- 'a': 1,
- 'b': 2,
- }
-
-and it's valid in ECMAScript 5 and most browsers support it in Javascript, you
-should *never* use trailing commas in Javascript object literals:
-
-* Internet Explorer does *not* support trailing commas (at least until and
- including Internet Explorer 8), and trailing comma will cause hard-to-debug
- errors in it
-
-* JSON does not accept trailing comma (it is a syntax error), and using them
- in object literals puts you at risks of using them in literal JSON strings
- as well (though there are few reasons to write JSON by hand)
-
-*Never* use ``for … in`` to iterate on arrays
-*********************************************
-
-:ref:`Iterating over an object with for…in is a bit tricky already
-<for-in-iteration>`, it is far more complex than in Python (where it Just
-Works™) due to the interaction of various Javascript features, but to iterate
-on arrays it becomes downright deadly and errorneous: ``for…in`` really
-iterates over an *object*'s *properties*.
-
-With an array, this has the following consequences:
-
-* It does not necessarily iterate in numerical order, nor does it iterate in
- any kind of set order. The order is implementation-dependent and may vary
- from one run to the next depending on a number of reasons and implementation
- details.
-* If properties are added to an array, to ``Array.prototype`` or to
- ``Object.prototype`` (the latter two should not happen in well-behaved
- javascript code, but you never know...) those properties *will* be iterated
- over by ``for…in``. While ``Object.hasOwnProperty`` will guard against
- iterating prototype properties, they will not guard against properties set
- on the array instance itself (as memoizers for instance).
-
- Note that this includes setting negative keys on arrays.
-
-For this reason, ``for…in`` should **never** be used on array objects. Instead,
-you should use either a normal ``for`` or (even better, unless you have
-profiled the code and found a hotspot) one of Underscore's array iteration
-methods (`_.each`_, `_.map`_, `_.filter`_, etc...).
-
-Underscore is guaranteed to be bundled and available in OpenERP Web scopes.
-
-.. _for-in-iteration:
-
-Use ``hasOwnProperty`` when iterating on an object with ``for … in``
-********************************************************************
-
-``for…in`` is Javascript's built-in facility for iterating over and object's
-properties.
-
-`It is also fairly tricky to use`_: it iterates over *all* non-builtin
-properties of your objects [#]_, which includes methods of an object's class.
-
-As a result, when iterating over an object with ``for…in`` the first line of
-the body *should* generally be a call to `Object.hasOwnProperty`_. This call
-will check whether the property was set directly on the object or comes from
-the object's class:
-
-.. code-block:: javascript
-
- for(var key in ob) {
- if (!ob.hasOwnProperty(key)) {
- // comes from ob's class
- continue;
- }
- // do stuff with key
- }
-
-Since properties can be added directly to e.g. ``Object.prototype`` (even
-though it's usually considered bad style), you should not assume you ever know
-which properties ``for…in`` is going to iterate over.
-
-An alternative is to use Underscore's iteration methods, which generally work
-over objects as well as arrays:
-
-Instead of
-
-.. code-block:: javascript
-
- for (var key in ob) {
- if (!ob.hasOwnProperty(key)) { continue; }
- var value = ob[key];
- // Do stuff with key and value
- }
-
-you could write:
-
-.. code-block:: javascript
-
- _.each(ob, function (value, key) {
- // do stuff with key and value
- });
-
-and not worry about the details of the iteration: underscore should do the
-right thing for you on its own [#]_.
-
-Writing documentation
-+++++++++++++++++++++
-
-The OpenERP Web project documentation uses Sphinx_ for the literate
-documentation (this document for instance), the development guides
-(for Python and Javascript alike) and the Python API documentation
-(via autodoc_).
-
-For the Javascript API, documentation should be written using the
-`JsDoc Toolkit`_.
-
-Guides and main documentation
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The meat and most important part of all documentation. Should be
-written in plain English, using reStructuredText_ and taking advantage
-of `Sphinx's extensions`_, especially `cross-references`_.
-
-Python API Documentation
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-All public objects in Python code should have a docstring written in
-RST, using Sphinx's `Python domain`_ [#]_:
-
-* Functions and methods documentation should be in their own
- docstring, using Sphinx's `info fields`_
-
- For parameters types, built-in and stdlib types should be using the
- combined syntax:
-
- .. code-block:: restructuredtext
-
- :param dict foo: what the purpose of foo is
-
- unless a more extensive explanation needs to be given (e.g. the
- specification that the input should be a list of 3-tuple needs to
- use ``:type:`` even though all types involved are built-ins). Any
- other type should be specified in full using the ``:type:`` field
-
- .. code-block:: restructuredtext
-
- :param foo: what the purpose of foo is
- :type foo: some.addon.Class
-
- Mentions of other methods (including within the same class), modules
- or types in descriptions (of anything, including parameters) should
- be cross-referenced.
-
-* Classes should likewise be documented using their own docstring, and
- should include the documentation of their construction (``__init__``
- and ``__new__``), using the `info fields`_ as well.
-
-* Attributes (class and instance) should be documented in their
- class's docstring via the ``.. attribute::`` directive, following
- the class's own documentation.
-
-* The relation between modules and module-level attributes is similar:
- modules should be documented in their own docstring, public module
- attributes should be documented in the module's docstring using the
- ``.. data::`` directive.
-
-Javascript API documentation
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Javascript API documentation uses JsDoc_, a javascript documentation
-toolkit with a syntax similar to (and inspired by) JavaDoc's.
-
-Due to limitations of JsDoc, the coding patterns in OpenERP Web and
-the Sphinx integration, there are a few peculiarities to be aware of
-when writing javascript API documentation:
-
-* Namespaces and classes *must* be explicitly marked up even if they
- are not documented, or JsDoc will not understand what they are and
- will not generate documentation for their content.
-
- As a result, the bare minimum for a namespace is:
-
- .. code-block:: javascript
-
- /** @namespace */
- foo.bar.baz = {};
-
- while for a class it is:
-
- .. code-block:: javascript
-
- /** @class */
- foo.bar.baz.Qux = [...]
-
-* Because the OpenERP Web project uses `John Resig's Class
- implementation`_ instead of direct prototypal inheritance [#]_,
- JsDoc fails to infer class scopes (and constructors or super
- classes, for that matter) and has to be told explicitly.
-
- See :ref:`js-class-doc` for the complete rundown.
-
-* Much like the JavaDoc, JsDoc does not include a full markup
- language. Instead, comments are simply marked up in HTML.
-
- This has a number of inconvenients:
-
- * Complex documentation comments become nigh-unreadable to read in
- text editors (as opposed to IDEs, which may handle rendering
- documentation comments on the fly)
-
- * Though cross-references are supported by JsDoc (via ``@link`` and
- ``@see``), they only work within the JsDoc
-
- * More general impossibility to integrate correctly with Sphinx, and
- e.g. reference JavaScript objects from a tutorial, or have all the
- documentation live at the same place.
-
- As a result, JsDoc comments should be marked up using RST, not
- HTML. They may use Sphinx's cross-references as well.
-
-.. _js-class-doc:
-
-Documenting a Class
-*******************
-
-The first task when documenting a class using JsDoc is to *mark* that
-class, so JsDoc knows it can be used to instantiate objects (and, more
-importantly as far as it's concerned, should be documented with
-methods and attributes and stuff).
-
-This is generally done through the ``@class`` tag, but this tag has a
-significant limitation: it "believes" the constructor and the class
-are one and the same [#]_. This will work for constructor-less
-classes, but because OpenERP Web uses Resig's class the constructor is
-not the class itself but its ``init()`` method.
-
-Because this pattern is common in modern javascript code bases, JsDoc
-supports it: it is possible to mark an arbitrary instance method as
-the *class specification* by using the ``@constructs`` tag.
-
-.. warning:: ``@constructs`` is a class specification in and of
- itself, it *completely replaces* the class documentation.
-
- Using both a class documentation (even without ``@class`` itself)
- and a constructor documentation is an *error* in JsDoc and will
- result in incorrect behavior and broken documentation.
-
-The second issue is that Resig's class uses an object literal to
-specify instance methods, and because JsDoc does not know anything
-about Resig's class, it does not know about the role of the object
-literal.
-
-As with constructors, though, JsDoc provides a pluggable way to tell
-it about methods: the ``@lends`` tag. It specifies that the object
-literal "lends" its properties to the class being built.
-
-``@lends`` must be specified right before the opening brace of the
-object literal (between the opening paren of the ``#extend`` call and
-the brace), and takes the full qualified name of the class being
-created as a parameter, followed by the character ``#`` or by
-``.prototype``. This latter part tells JsDoc these are instance
-methods, not class (static) methods..
-
-Finally, specifying a class's superclass is done through the
-``@extends`` tag, which takes a fully qualified class name as a
-parameter.
-
-Here are a class without a constructor, and a class with one, so that
-everything is clear (these are straight from the OpenERP Web source,
-with the descriptions and irrelevant atttributes stripped):
-
-.. code-block:: javascript
-
- /**
- * <Insert description here, not below>
- *
- * @class
- * @extends openerp.base.search.Field
- */
- openerp.base.search.CharField = openerp.base.search.Field.extend(
- /** @lends openerp.base.search.CharField# */ {
- // methods here
- });
-
-.. code-block:: javascript
-
- openerp.base.search.Widget = openerp.base.Controller.extend(
- /** @lends openerp.base.search.Widget# */{
- /**
- * <Insert description here, not below>
- *
- * @constructs
- * @extends openerp.base.Controller
- *
- * @param view the ancestor view of this widget
- */
- init: function (view) {
- // construction of the instance
- },
- // bunch of other methods
- });
-
-OpenERP Web over time
----------------------
-
-Release process
-+++++++++++++++
-
-OpenSUSE packaging: http://blog.lowkster.com/2011/04/packaging-python-packages-in-opensuse.html
-
-Roadmap
-+++++++
-
-Release notes
-+++++++++++++
-
-.. [#] More precisely, it iterates over all *enumerable* properties. It just
- happens that built-in properties (such as ``String.indexOf`` or
- ``Object.toString``) are set to non-enumerable.
-
- The enumerability of a property can be checked using
- `Object.propertyIsEnumeable`_.
-
- Before ECMAScript 5, it was not possible for user-defined properties
- to be non-enumerable in a portable manner. ECMAScript 5 introduced
- `Object.defineProperty`_ which lets user code create non-enumerable
- properties (and more, read-only properties for instance, or implicit
- getters and setters). However, support for these is not fully complete
- at this point, and they are not being used in OpenERP Web code anyway.
-
-.. [#] While using underscore is generally the preferred method (simpler,
- more reliable and easier to write than a *correct* ``for…in``
- iteration), it is also probably slower (due to the overhead of
- calling a bunch of functions).
-
- As a result, if you profile some code and find out that an underscore
- method adds unacceptable overhead in a tight loop, you may want to
- replace it with a ``for…in`` (or a regular ``for`` statement for
- arrays).
-
-.. [#] Because Python is the default domain, the ``py:`` markup prefix
- is optional and should be left out.
-
-.. [#] Resig's Class still uses prototypes under the hood, it doesn't
- reimplement its own object system although it does add several
- helpers such as the ``_super()`` instance method.
-
-.. [#] Which is the case in normal Javascript semantics. Likewise, the
- ``.prototype`` / ``#`` pattern we will see later on is due to
- JsDoc defaulting to the only behavior it can rely on: "normal"
- Javascript prototype-based type creation.
-
-.. _reStructuredText:
- http://docutils.sourceforge.net/rst.html
-.. _Sphinx:
- http://sphinx.pocoo.org/index.html
-.. _Sphinx's extensions:
- http://sphinx.pocoo.org/markup/index.html
-.. _Python domain:
- http://sphinx.pocoo.org/domains.html#the-python-domain
-.. _info fields:
- http://sphinx.pocoo.org/domains.html#info-field-lists
-.. _autodoc:
- http://sphinx.pocoo.org/ext/autodoc.html
- ?highlight=autodoc#sphinx.ext.autodoc
-.. _cross-references:
- http://sphinx.pocoo.org/markup/inline.html#xref-syntax
-.. _JsDoc:
-.. _JsDoc Toolkit:
- http://code.google.com/p/jsdoc-toolkit/
-.. _John Resig's Class implementation:
- http://ejohn.org/blog/simple-javascript-inheritance/
-.. _\_.each:
- http://documentcloud.github.com/underscore/#each
-.. _\_.map:
- http://documentcloud.github.com/underscore/#map
-.. _\_.filter:
- http://documentcloud.github.com/underscore/#select
-.. _It is also fairly tricky to use:
- https://developer.mozilla.org/en/JavaScript/Reference/Statements/for...in#Description
-.. _Object.propertyIsEnumeable:
- https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/propertyIsEnumerable
-.. _Object.defineProperty:
- https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty
-.. _Object.hasOwnProperty:
- https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/hasOwnProperty
+++ /dev/null
-OpenERP Web as a widgets provider
-=================================
-
-* Using a readonly view as a widget
-
- * Site example
- * iGoogle example
- * social site example e.g. Facebook app?
-
-* Write-access widgets (e.g. contact form)
-* Multiple widgets on the same page
-* JSON-RPC2 API description for third-parties?
--- /dev/null
+OpenERP Web as a widgets provider
+=================================
+
+* Using a readonly view as a widget
+
+ * Site example
+ * iGoogle example
+ * social site example e.g. Facebook app?
+
+* Write-access widgets (e.g. contact form)
+* Multiple widgets on the same page
+* JSON-RPC2 API description for third-parties?