[MERGE] remove standalone web client

bzr revid: al@openerp.com-20121010204451-nq3eatsl1x9zi1cf
This commit is contained in:
Antony Lesuisse
2012-10-10 22:44:51 +02:00
47 changed files with 176 additions and 1671 deletions
-154
View File
@@ -1,154 +0,0 @@
# 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."
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

-16
View File
@@ -1,16 +0,0 @@
<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/trunk/developers">OpenERP Developers Documentation</a></li>
<li><a href="http://doc.openerp.com/trunk/developers/server">OpenERP Server Developers Documentation</a></li>
<li><a href="http://doc.openerp.com/trunk/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>
-3
View File
@@ -1,3 +0,0 @@
<p class="logo"><a href="{{ pathto(master_doc) }}">
<img class="logo" src="{{ pathto('_static/openerp.png', 1) }}" alt="Logo"/>
</a></p>
-37
View File
@@ -1,37 +0,0 @@
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.
-31
View File
@@ -1,31 +0,0 @@
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
-25
View File
@@ -1,25 +0,0 @@
{%- 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">
&copy; 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 %}
-19
View File
@@ -1,19 +0,0 @@
<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>
-395
View File
@@ -1,395 +0,0 @@
/*
* 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;
}
-70
View File
@@ -1,70 +0,0 @@
/*
* 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;
}
-9
View File
@@ -1,9 +0,0 @@
[theme]
inherit = basic
stylesheet = flasky.css
pygments_style = flask_theme_support.FlaskyStyle
[options]
index_logo = ''
index_logo_height = 120px
touch_icon =
-22
View File
@@ -1,22 +0,0 @@
{% 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 %}
-287
View File
@@ -1,287 +0,0 @@
/*
* 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;
}
-10
View File
@@ -1,10 +0,0 @@
[theme]
inherit = basic
stylesheet = flasky.css
nosidebar = true
pygments_style = flask_theme_support.FlaskyStyle
[options]
index_logo = ''
index_logo_height = 120px
github_fork = ''
-86
View File
@@ -1,86 +0,0 @@
# 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'
}
-12
View File
@@ -1,12 +0,0 @@
<addon name>
+-- __openerp__.py
+-- controllers/
+-- static/
+-- lib/
+-- src/
+-- css/
+-- img/
+-- js/
+-- xml/
+-- test/
+-- test/
-353
View File
@@ -1,353 +0,0 @@
Don't stop the world now: asynchronous development and Javascript
=================================================================
As a language (and runtime), javascript is fundamentally
single-threaded. This means any blocking request or computation will
blocks the whole page (and, in older browsers, the software itself
even preventing users from switching to an other tab): a javascript
environment can be seen as an event-based runloop where application
developers have no control over the runloop itself.
As a result, performing long-running synchronous network requests or
other types of complex and expensive accesses is frowned upon and
asynchronous APIs are used instead.
Asynchronous code rarely comes naturally, especially for developers
used to synchronous server-side code (in Python, Java or C#) where the
code will just block until the deed is gone. This is increased further
when asynchronous programming is not a first-class concept and is
instead implemented on top of callbacks-based programming, which is
the case in javascript.
The goal of this guide is to provide some tools to deal with
asynchronous systems, and warn against systematic issues or dangers.
Deferreds
---------
Deferreds are a form of `promises`_. OpenERP Web currently uses
`jQuery's deferred`_, but any `CommonJS Promises/A`_ implementation
should work.
The core idea of deferreds is that potentially asynchronous methods
will return a :js:class:`Deferred` object instead of an arbitrary
value or (most commonly) nothing.
This object can then be used to track the end of the asynchronous
operation by adding callbacks onto it, either success callbacks or
error callbacks.
A great advantage of deferreds over simply passing callback functions
directly to asynchronous methods is the ability to :ref:`compose them
<deferred-composition>`.
Using deferreds
~~~~~~~~~~~~~~~
`CommonJS Promises/A`_ deferreds have only one method of importance:
:js:func:`Deferred.then`. This method is used to attach new callbacks
to the deferred object.
* the first parameter attaches a success callback, called when the
deferred object is successfully resolved and provided with the
resolved value(s) for the asynchronous operation.
* the second parameter attaches a failure callback, called when the
deferred object is rejected and provided with rejection values
(often some sort of error message).
Callbacks attached to deferreds are never "lost": if a callback is
attached to an already resolved or rejected deferred, the callback
will be called (or ignored) immediately. A deferred is also only ever
resolved or rejected once, and is either resolved or rejected: a given
deferred can not call a single success callback twice, or call both a
success and a failure callbacks.
:js:func:`~Deferred.then` should be the method you'll use most often
when interacting with deferred objects (and thus asynchronous APIs).
Building deferreds
~~~~~~~~~~~~~~~~~~
After using asynchronous APIs may come the time to build them: for
`mocks`_, to compose deferreds from multiple source in a complex
manner, in order to let the current operations repaint the screen or
give other events the time to unfold, ...
This is easy using jQuery's deferred objects.
.. note:: this section is an implementation detail of jQuery Deferred
objects, the creation of promises is not part of any
standard (even tentative) that I know of. If you are using
deferred objects which are not jQuery's, their API may (and
often will) be completely different.
Deferreds are created by invoking their constructor [#]_ without any
argument. This creates a :js:class:`Deferred` instance object with the
following methods:
:js:func:`Deferred.resolve`
As its name indicates, this method moves the deferred to the
"Resolved" state. It can be provided as many arguments as
necessary, these arguments will be provided to any pending success
callback.
:js:func:`Deferred.reject`
Similar to :js:func:`~Deferred.resolve`, but moves the deferred to
the "Rejected" state and calls pending failure handlers.
:js:func:`Deferred.promise`
Creates a readonly view of the deferred object. It is generally a
good idea to return a promise view of the deferred to prevent
callers from resolving or rejecting the deferred in your stead.
:js:func:`~Deferred.reject` and :js:func:`~Deferred.resolve` are used
to inform callers that the asynchronous operation has failed (or
succeeded). These methods should simply be called when the
asynchronous operation has ended, to notify anybody interested in its
result(s).
.. _deferred-composition:
Composing deferreds
~~~~~~~~~~~~~~~~~~~
What we've seen so far is pretty nice, but mostly doable by passing
functions to other functions (well adding functions post-facto would
probably be a chore... still, doable).
Deferreds truly shine when code needs to compose asynchronous
operations in some way or other, as they can be used as a basis for
such composition.
There are two main forms of compositions over deferred: multiplexing
and piping/cascading.
Deferred multiplexing
`````````````````````
The most common reason for multiplexing deferred is simply performing
2+ asynchronous operations and wanting to wait until all of them are
done before moving on (and executing more stuff).
The jQuery multiplexing function for promises is :js:func:`when`.
.. note:: the multiplexing behavior of jQuery's :js:func:`when` is an
(incompatible, mostly) extension of the behavior defined in
`CommonJS Promises/B`_.
This function can take any number of promises [#]_ and will return a
promise.
This returned promise will be resolved when *all* multiplexed promises
are resolved, and will be rejected as soon as one of the multiplexed
promises is rejected (it behaves like Python's ``all()``, but with
promise objects instead of boolean-ish).
The resolved values of the various promises multiplexed via
:js:func:`when` are mapped to the arguments of :js:func:`when`'s
success callback, if they are needed. The resolved values of a promise
are at the same index in the callback's arguments as the promise in
the :js:func:`when` call so you will have:
.. code-block:: javascript
$.when(p0, p1, p2, p3).then(
function (results0, results1, results2, results3) {
// code
});
.. warning::
in a normal mapping, each parameter to the callback would be an
array: each promise is conceptually resolved with an array of 0..n
values and these values are passed to :js:func:`when`'s
callback. But jQuery treats deferreds resolving a single value
specially, and "unwraps" that value.
For instance, in the code block above if the index of each promise
is the number of values it resolves (0 to 3), ``results0`` is an
empty array, ``results2`` is an array of 2 elements (a pair) but
``results1`` is the actual value resolved by ``p1``, not an array.
Deferred chaining
`````````````````
A second useful composition is starting an asynchronous operation as
the result of an other asynchronous operation, and wanting the result
of both: :js:func:`Deferred.then` returns the deferred on which it was
called, so handle e.g. OpenERP's search/read sequence with this would
require something along the lines of:
.. code-block:: javascript
var result = $.Deferred();
Model.search(condition).then(function (ids) {
Model.read(ids, fields).then(function (records) {
result.resolve(records);
});
});
return result.promise();
While it doesn't look too bad for trivial code, this quickly gets
unwieldy.
Instead, jQuery provides a tool to handle this kind of chains:
:js:func:`Deferred.pipe`.
:js:func:`~Deferred.pipe` has the same signature as
:js:func:`~Deferred.then` and could be used in the same manner
provided its return value was not used.
It differs from :js:func:`~Deferred.then` in two ways: it returns a
new promise object, not the one it was called with, and the return
values of the callbacks is actually important to it: whichever
callback is called,
* If the callback is not set (not provided or left to null), the
resolution or rejection value(s) is simply forwarded to
:js:func:`~Deferred.pipe`'s promise (it's essentially a noop)
* If the callback is set and does not return an observable object (a
deferred or a promise), the value it returns (``undefined`` if it
does not return anything) will replace the value it was given, e.g.
.. code-block:: javascript
promise.pipe(function () {
console.log('called');
});
will resolve with the sole value ``undefined``.
* If the callback is set and returns an observable object, that object
will be the actual resolution (and result) of the pipe. This means a
resolved promise from the failure callback will resolve the pipe,
and a failure promise from the success callback will reject the
pipe.
This provides an easy way to chain operation successes, and the
previous piece of code can now be rewritten:
.. code-block:: javascript
return Model.search(condition).pipe(function (ids) {
return Model.read(ids, fields);
});
the result of the whole expression will encode failure if either
``search`` or ``read`` fails (with the right rejection values), and
will be resolved with ``read``'s resolution values if the chain
executes correctly.
:js:func:`~Deferred.pipe` is also useful to adapt third-party
promise-based APIs, in order to filter their resolution value counts
for instance (to take advantage of :js:func:`when` 's special treatment
of single-value promises).
jQuery.Deferred API
~~~~~~~~~~~~~~~~~~~
.. js:function:: when(deferreds…)
:param deferreds: deferred objects to multiplex
:returns: a multiplexed deferred
:rtype: :js:class:`Deferred`
.. js:class:: Deferred
.. js:function:: Deferred.then(doneCallback[, failCallback])
Attaches new callbacks to the resolution or rejection of the
deferred object. Callbacks are executed in the order they are
attached to the deferred.
To provide only a failure callback, pass ``null`` as the
``doneCallback``, to provide only a success callback the
second argument can just be ignored (and not passed at all).
:param doneCallback: function called when the deferred is resolved
:type doneCallback: Function
:param failCallback: function called when the deferred is rejected
:type failCallback: Function
:returns: the deferred object on which it was called
:rtype: :js:class:`Deferred`
.. js:function:: Deferred.done(doneCallback)
Attaches a new success callback to the deferred, shortcut for
``deferred.then(doneCallback)``.
This is a jQuery extension to `CommonJS Promises/A`_ providing
little value over calling :js:func:`~Deferred.then` directly,
it should be avoided.
:param doneCallback: function called when the deferred is resolved
:type doneCallback: Function
:returns: the deferred object on which it was called
:rtype: :js:class:`Deferred`
.. js:function:: Deferred.fail(failCallback)
Attaches a new failure callback to the deferred, shortcut for
``deferred.then(null, failCallback)``.
A second jQuery extension to `Promises/A <CommonJS
Promises/A>`_. Although it provides more value than
:js:func:`~Deferred.done`, it still is not much and should be
avoided as well.
:param failCallback: function called when the deferred is rejected
:type failCallback: Function
:returns: the deferred object on which it was called
:rtype: :js:class:`Deferred`
.. js:function:: Deferred.promise()
Returns a read-only view of the deferred object, with all
mutators (resolve and reject) methods removed.
.. js:function:: Deferred.resolve(value…)
Called to resolve a deferred, any value provided will be
passed onto the success handlers of the deferred object.
Resolving a deferred which has already been resolved or
rejected has no effect.
.. js:function:: Deferred.reject(value…)
Called to reject (fail) a deferred, any value provided will be
passed onto the failure handler of the deferred object.
Rejecting a deferred which has already been resolved or
rejected has no effect.
.. js:function:: Deferred.pipe(doneFilter[, failFilter])
Filters the result of a deferred, able to transform a success
into failure and a failure into success, or to delay
resolution further.
.. [#] or simply calling :js:class:`Deferred` as a function, the
result is the same
.. [#] or not-promises, the `CommonJS Promises/B`_ role of
:js:func:`when` is to be able to treat values and promises
uniformly: :js:func:`when` will pass promises through directly,
but non-promise values and objects will be transformed into a
resolved promise (resolving themselves with the value itself).
jQuery's :js:func:`when` keeps this behavior making deferreds
easy to build from "static" values, or allowing defensive code
where expected promises are wrapped in :js:func:`when` just in
case.
.. _promises: http://en.wikipedia.org/wiki/Promise_(programming)
.. _jQuery's deferred: http://api.jquery.com/category/deferred-object/
.. _CommonJS Promises/A: http://wiki.commonjs.org/wiki/Promises/A
.. _CommonJS Promises/B: http://wiki.commonjs.org/wiki/Promises/B
.. _mocks: http://en.wikipedia.org/wiki/Mock_object
-108
View File
@@ -1,108 +0,0 @@
API changes from OpenERP Web 6.1 to 7.0
=======================================
DataSet -> Model
----------------
The 6.1 ``DataSet`` API has been deprecated in favor of the smaller
and more orthogonal :doc:`Model </rpc>` API, which more closely
matches the API in OpenERP Web's Python side and in OpenObject addons
and removes most stateful behavior of DataSet.
Migration guide
~~~~~~~~~~~~~~~
* Actual arbitrary RPC calls can just be remapped on a
:js:class:`~openerp.web.Model` instance:
.. code-block:: javascript
dataset.call(method, args)
or
.. code-block:: javascript
dataset.call_and_eval(method, args)
can be replaced by calls to :js:func:`openerp.web.Model.call`:
.. code-block:: javascript
model.call(method, args)
If callbacks are passed directly to the older methods, they need to
be added to the new one via ``.then()``.
.. note::
The ``context_index`` and ``domain_index`` features were not
ported, context and domain now need to be passed in "in full",
they won't be automatically filled with the user's current
context.
* Shorcut methods (``name_get``, ``name_search``, ``unlink``,
``write``, ...) should be ported to
:js:func:`openerp.web.Model.call`, using the server's original
signature. On the other hand, the non-shortcut equivalents can now
use keyword arguments (see :js:func:`~openerp.web.Model.call`'s
signature for details)
* ``read_slice``, which allowed a single round-trip to perform a
search and a read, should be reimplemented via
:js:class:`~openerp.web.Query` objects (see:
:js:func:`~openerp.web.Model.query`) for clearer and simpler
code. ``read_index`` should be replaced by a
:js:class:`~openerp.web.Query` as well, combining
:js:func:`~openerp.web.Query.offset` and
:js:func:`~openerp.web.Query.first`.
Rationale
~~~~~~~~~
Renaming
The name *DataSet* exists in the CS community consciousness, and
(as its name implies) it's a set of data (often fetched from a
database, maybe lazily). OpenERP Web's dataset behaves very
differently as it does not store (much) data (only a bunch of ids
and just enough state to break things). The name "Model" matches
the one used on the Python side for the task of building an RPC
proxy to OpenERP objects.
API simplification
``DataSet`` has a number of methods which serve as little more
than shortcuts, or are there due to domain and context evaluation
issues in 6.1.
The shortcuts really add little value, and OpenERP Web 6.2 embeds
a restricted Python evaluator (in javascript) meaning most of the
context and domain parsing & evaluation can be moved to the
javascript code and does not require cooperative RPC bridging.
DataGroup -> also Model
-----------------------
Alongside the deprecation of ``DataSet`` for
:js:class:`~openerp.web.Model`, OpenERP Web 7.0 also deprecates
``DataGroup`` and its subtypes in favor of a single method on
:js:class:`~openerp.web.Query`:
:js:func:`~openerp.web.Query.group_by`.
Migration guide
~~~~~~~~~~~~~~~
Rationale
~~~~~~~~~
While the ``DataGroup`` API worked (mostly), it is quite odd and
alien-looking, a bit too Smalltalk-inspired (behaves like a
self-contained flow-control structure for reasons which may or may not
have been good).
Because it is heavily related to ``DataSet`` (as it *yields*
``DataSet`` objects), deprecating ``DataSet`` automatically deprecates
``DataGroup`` (if we want to stay consistent), which is a good time to
make the API more imperative and look more like what most developers
are used to.
-257
View File
@@ -1,257 +0,0 @@
# -*- 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.insert(0, os.path.abspath('.'))
sys.path.append(os.path.abspath('_themes'))
sys.path.append(os.path.abspath('..'))
sys.path.append(os.path.abspath('../openerp'))
# -- 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 = '7.0'
# The full version, including alpha/beta/rc tags.
release = '7.0b'
# 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),
'openerpweb': ('http://doc.openerp.com/trunk/developers/web', None),
'openerpdev': ('http://doc.openerp.com/trunk/developers', None),
}
-55
View File
@@ -1,55 +0,0 @@
Notes on the usage of the Form View as a sub-widget
===================================================
Undocumented stuff
------------------
* ``initial_mode`` *option* defines the starting mode of the form
view, one of ``view`` and ``edit`` (?). Default value is ``view``
(non-editable form).
* ``embedded_view`` *attribute* has to be set separately when
providing a view directly, no option available for that usage.
* View arch **must** contain node with
``@class="oe_form_container"``, otherwise everything will break
without any info
* Root element of view arch not being ``form`` may or may not work
correctly, no idea.
* Freeform views => ``@version="7.0"``
* Form is not entirely loaded (some widgets may not appear) unless
``on_record_loaded`` is called (or ``do_show``, which itself calls
``on_record_loaded``).
* "Empty" form => ``on_button_new`` (...), or manually call
``default_get`` + ``on_record_loaded``
* Form fields default to width: 100%, padding, !important margin, can
be reached via ``.oe_form_field``
* Form *will* render buttons and a pager, offers options to locate
both outside of form itself (``$buttons`` and ``$pager``), providing
empty jquery objects (``$()``) seems to stop displaying both but not
sure if there are deleterious side-effects.
Other options:
* Pass in ``$(document.createDocumentFragment)`` to ensure it's a
DOM-compatible tree completely outside of the actual DOM.
* ???
* readonly fields probably don't have a background, beware if need of
overlay
* What is the difference between ``readonly`` and
``effective_readonly``?
* No facilities for DOM events handling/delegations e.g. handling
keyup/keydown/keypress from a form fields into the form's user.
* Also no way to reverse from a DOM node (e.g. DOMEvent#target) back to a
form view field easily
-112
View File
@@ -1,112 +0,0 @@
.. highlight:: javascript
Creating a new client action
============================
Client actions are the client-side version of OpenERP's "Server
Actions": instead of allowing for semi-arbitrary code to be executed
in the server, they allow for execution of client-customized code.
On the server side, a client action is an action of type
``ir.actions.client``, which has (at most) two properties: a mandatory
``tag``, which is an arbitrary string by which the client will
identify the action, and an optional ``params`` which is simply a map
of keys and values sent to the client as-is (this way, client actions
can be made generic and reused in multiple contexts).
General Structure
-----------------
In the OpenERP Web code, a client action only requires two pieces of
information:
* Mapping the action's ``tag`` to an object
* Providing said object. Two different types of objects can be mapped
to a client action:
* An OpenERP Web widget, which must inherit from
:js:class:`openerp.web.Widget`
* A regular javascript function
The major difference is in the lifecycle of these:
* if the client action maps to a function, the function will simply be
called when executing the action. The function can have no further
interaction with the Web Client itself, although it can return an
action which will be executed after it.
* if, on the other hand, the client action maps to a
:js:class:`~openerp.web.Widget`, that
:js:class:`~openerp.web.Widget` will be instantiated and added to
the web client's canvas, with the usual
:js:class:`~openerp.web.Widget` lifecycle (essentially, it will
either take over the content area of the client or it will be
integrated within a dialog).
For example, to create a client action displaying a ``res.widget``
object::
// Registers the object 'openerp.web_dashboard.Widget' to the client
// action tag 'board.home.widgets'
instance.web.client_actions.add(
'board.home.widgets', 'openerp.web_dashboard.Widget');
instance.web_dashboard.Widget = instance.web.Widget.extend({
template: 'HomeWidget'
});
At this point, the generic :js:class:`~openerp.web.Widget` lifecycle
takes over, the template is rendered, inserted in the client DOM,
bound on the object's ``$el`` property and the object is started.
If the client action takes parameters, these parameters are passed in as a
second positional parameter to the constructor::
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 :js:func:`~openerp.web.Widget.start()`
method.
.. note::
As required by :js:class:`~openerp.web.Widget`'s contract, if
:js:func:`~openerp.web.Widget.start()` executes any asynchronous
code it should return a ``$.Deferred`` so callers know when it's
ready for interaction.
Although generally speaking client actions are not really
interacted with.
.. code-block:: javascript
start: function () {
return $.when(
this._super(),
// Simply read the res.widget object this action should display
new instance.web.Model('res.widget').call(
'read', [[this.widget_id], ['title']])
.then(this.proxy('on_widget_loaded'));
}
The client action can then behave exactly as it wishes to within its
root (``this.$el``). In this case, it performs further renderings once
its widget's content is retrieved::
on_widget_loaded: function (widgets) {
var widget = widgets[0];
var url = _.sprintf(
'/web_dashboard/widgets/content?session_id=%s&widget_id=%d',
this.session.session_id, widget.id);
this.$el.html(QWeb.render('HomeWidget.content', {
widget: widget,
url: url
}));
}
-32
View File
@@ -1,32 +0,0 @@
.. 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: 1
changelog-7.0
async
rpc
widget
search-view
list-view
form-notes
guides/client-action
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
-531
View File
@@ -1,531 +0,0 @@
List View
=========
Style Hooks
-----------
The list view provides a few style hook classes for re-styling of list views in
various situations:
``.oe_list``
The root element of the list view, styling rules should be rooted
on that class.
``table.oe_list_content``
The root table for the listview, accessory components may be
generated or added outside this section, this is the list view
"proper".
``.oe_list_buttons``
The action buttons array for the list view, with its sub-elements
``.oe_list_add``
The default "Create"/"Add" button of the list view
``.oe_alternative``
The "alternative choice" for the list view, by default text
along the lines of "or import" with a link.
``.oe_list_field_cell``
The cell (``td``) for a given field of the list view, cells which
are *not* fields (e.g. name of a group, or number of items in a
group) will not have this class. The field cell can be further
specified:
``.oe_number``
Numeric cell types (integer and float)
``.oe_button``
Action button (``button`` tag in the view) inside the cell
``.oe_readonly``
Readonly field cell
``.oe_list_field_$type``
Additional class for the precise type of the cell, ``$type``
is the field's @widget if there is one, otherwise it's the
field's type.
``.oe_list_record_selector``
Selector cells
Editable list view
++++++++++++++++++
The editable list view module adds a few supplementary style hook
classes, for edition situations:
``.oe_list_editable``
Added to the ``.oe_list`` when the list is editable (however that
was done). The class may be removed on-the-fly if the list becomes
non-editable.
``.oe_editing``
Added to both ``.oe_list`` and ``.oe_list_button`` (as the
buttons may be outside of the list view) when a row of the list is
currently being edited.
``tr.oe_edition``
Class set on the row being edited itself. Note that the edition
form is *not* contained within the row, this allows for styling or
modifying the row while it's being edited separately. Mostly for
fields which can not be edited (e.g. read-only fields).
Columns display customization
-----------------------------
The list view provides a registry to
:js:class:`openerp.web.list.Column` objects allowing for the
customization of a column's display (e.g. so that a binary field is
rendered as a link to the binary file directly in the list view).
The registry is ``instance.web.list.columns``, the keys are of the
form ``tag.type`` where ``tag`` can be ``field`` or ``button``, and
``type`` can be either the field's type or the field's ``@widget`` (in
the view).
Most of the time, you'll want to define a ``tag.widget`` key
(e.g. ``field.progressbar``).
.. js:class:: openerp.web.list.Column(id, tag, attrs)
.. js:function:: openerp.web.list.Column.format(record_data, options)
Top-level formatting method, returns an empty string if the
column is invisible (unless the ``process_modifiers=false``
option is provided); returns ``options.value_if_empty`` or an
empty string if there is no value in the record for the
column.
Otherwise calls :js:func:`~openerp.web.list.Column._format`
and returns its result.
This method only needs to be overridden if the column has no
concept of values (and needs to bypass that check), for a
button for instance.
Otherwise, custom columns should generally override
:js:func:`~openerp.web.list.Column._format` instead.
:returns: String
.. js:function:: openerp.web.list.Column._format(record_data, options)
Never called directly, called if the column is visible and has
a value.
The default implementation calls
:js:func:`~openerp.web.format_value` and htmlescapes the
result (via ``_.escape``).
Note that the implementation of
:js:func:`~openerp.web.list.Column._format` *must* escape the
data provided to it, its output will *not* be escaped by
:js:func:`~openerp.web.list.Column.format`.
:returns: String
Editable list view
------------------
List view edition is an extension to the base listview providing the
capability of inline record edition by delegating to an embedded form
view.
Editability status
++++++++++++++++++
The editability status of a list view can be queried through the
:js:func:`~openerp.web.ListView.editable` method, will return a falsy
value if the listview is not currently editable.
The editability status is based on three flags:
``tree/@editable``
If present, can be either ``"top"`` or ``"bottom"``. Either will
make the list view editable, with new records being respectively
created at the top or at the bottom of the view.
``context.set_editable``
Boolean flag extracted from a search context (during the
:js:func:`~openerp.web.ListView.do_search`` handler), ``true``
will make the view editable (from the top), ``false`` or the
absence of the flag is a noop.
``defaults.editable``
Like ``tree/@editable``, one of absent (``null``)), ``"top"`` or
``"bottom"``, fallback for the list view if none of the previous
two flags are set.
These three flags can only *make* a listview editable, they can *not*
override a previously set flag. To do that, a listview user should
instead cancel :ref:`the edit:before event <listview-edit-before>`.
The editable list view module adds a number of methods to the list
view, on top of implementing the :js:class:`EditorDelegate` protocol:
Interaction Methods
+++++++++++++++++++
.. js:function:: openerp.web.ListView.ensure_saved
Attempts to resolve the pending edition, if any, by saving the
edited row's current state.
:returns: delegate resolving to all editions having been saved, or
rejected if a pending edition could not be saved
(e.g. validation failure)
.. js:function:: openerp.web.ListView.start_edition([record][, options])
Starts editing the provided record inline, through an overlay form
view of editable fields in the record.
If no record is provided, creates a new one according to the
editability configuration of the list view.
This method resolves any pending edition when invoked, before
starting a new edition.
:param record: record to edit, or null to create a new record
:type record: :js:class:`~openerp.web.list.Record`
:param EditOptions options:
:returns: delegate to the form used for the edition
.. js:function:: openerp.web.ListView.save_edition
Resolves the pending edition.
:returns: delegate to the save being completed, resolves to an
object with two attributes ``created`` (flag indicating
whether the saved record was just created or was
updated) and ``record`` the reloaded record having been
edited.
.. js:function:: openerp.web.ListView.cancel_edition([force=false])
Cancels pending edition, cleans up the list view in case of
creation (removes the empty record being created).
:param Boolean force: doesn't check if the user has added any
data, discards the edition unconditionally
Utility Methods
+++++++++++++++
.. js:function:: openerp.web.ListView.get_cells_for(row)
Extracts the cells from a listview row, and puts them in a
{fieldname: cell} mapping for analysis and manipulation.
:param jQuery row:
:rtype: Object
.. js:function:: openerp.web.ListView.with_event(event_name, event, action[, args][, trigger_params])
Executes ``action`` in the context of the view's editor,
bracketing it with cancellable event signals.
:param String event_name: base name for the bracketing event, will
be postfixed by ``:before`` and
``:after`` before being called
(respectively before and after
``action`` is executed)
:param Object event: object passed to the ``:before`` event
handlers.
:param Function action: function called with the view's editor as
its ``this``. May return a deferred.
:param Array args: arguments passed to ``action``
:param Array trigger_params: arguments passed to the ``:after``
event handler alongside the results
of ``action``
Behavioral Customizations
+++++++++++++++++++++++++
.. js:function:: openerp.web.ListView.handle_onwrite(record)
Implements the handling of the ``onwrite`` listview attribute:
calls the RPC methods specified by ``@onwrite``, and if that
method returns an array of ids loads or reloads the records
corresponding to those ids.
:param record: record being written having triggered the
``onwrite`` callback
:type record: openerp.web.list.Record
:returns: deferred to all reloadings being done
Events
++++++
For simpler interactions by/with external users of the listview, the
view provides a number of dedicated events to its lifecycle.
.. note:: if an event is defined as *cancellable*, it means its first
parameter is an object on which the ``cancel`` attribute can
be set. If the ``cancel`` attribute is set, the view will
abort its current behavior as soon as possible, and rollback
any state modification.
Generally speaking, an event should only be cancelled (by
setting the ``cancel`` flag to ``true``), uncancelling an
event is undefined as event handlers are executed on a
first-come-first-serve basis and later handlers may
re-cancel an uncancelled event.
.. _listview-edit-before:
``edit:before`` *cancellable*
Invoked before the list view starts editing a record.
Provided with an event object with a single property ``record``,
holding the attributes of the record being edited (``record`` is
empty *but not null* for a new record)
``edit:after``
Invoked after the list view has gone into an edition state,
provided with the attributes of the record being edited (see
``edit:before``) as first parameter and the form used for the
edition as second parameter.
``save:before`` *cancellable*
Invoked right before saving a pending edition, provided with an
event object holding the listview's editor (``editor``) and the
edition form (``form``)
``save:after``
Invoked after a save has been completed
``cancel:before`` *cancellable*
Invoked before cancelling a pending edition, provided with the
same information as ``save:before``.
``cancel:after``
Invoked after a pending edition has been cancelled.
DOM events
++++++++++
The list view has grown hooks for the ``keyup`` event on its edition
form (during edition): any such event bubbling out of the edition form
will be forwarded to a method ``keyup_EVENTNAME``, where ``EVENTNAME``
is the name of the key in ``$.ui.keyCode``.
The method will also get the event object (originally passed to the
``keyup`` handler) as its sole parameter.
The base editable list view has handlers for the ``ENTER`` and
``ESCAPE`` keys.
Editor
------
The list-edition modules does not generally interact with the embedded
formview, delegating instead to its
:js:class:`~openerp.web.list.Editor`.
.. js:class:: openerp.web.list.Editor(parent[, options])
The editor object provides a more convenient interface to form
views, and simplifies the usage of form views for semi-arbitrary
edition of stuff.
However, the editor does *not* task itself with being internally
consistent at this point: calling
e.g. :js:func:`~openerp.web.list.Editor.edit` multiple times in a
row without saving or cancelling each edit is undefined.
:param parent:
:type parent: :js:class:`~openerp.web.Widget`
:param EditorOptions options:
.. js:function:: openerp.web.list.Editor.is_editing([record_state])
Indicates whether the editor is currently in the process of
providing edition for a record.
Can be filtered by the state of the record being edited
(whether it's a record being *created* or a record being
*altered*), in which case it asserts both that an edition is
underway and that the record being edited respectively does
not yet exist in the database or already exists there.
:param record_state: state of the record being edited.
Either ``"new"`` or ``"edit"``.
:type record_state: String
:rtype: Boolean
.. js:function:: openerp.web.list.Editor.edit(record, configureField[, options])
Loads the provided record into the internal form view and
displays the form view.
Will also attempt to focus the first visible field of the form
view.
:param Object record: record to load into the form view
(key:value mapping similar to the result
of a ``read``)
:param configureField: function called with each field of the
form view right after the form is
displayed, lets whoever called this
method do some last-minute
configuration of form fields.
:type configureField: Function<String, openerp.web.form.Field>
:param EditOptions options:
:returns: jQuery delegate to the form object
.. js:function:: openerp.web.list.Editor.save
Attempts to save the internal form, then hide it
:returns: delegate to the record under edition (with ``id``
added for a creation). The record is not updated
from when it was passed in, aside from the ``id``
attribute.
.. js:function:: openerp.web.list.Editor.cancel([force=false])
Attemps to cancel the edition of the internal form, then hide
the form
:param Boolean force: unconditionally cancels the edition of
the internal form, even if the user has
already entered data in it.
:returns: delegate to the record under edition
.. js:class:: EditorOptions
.. js:attribute:: EditorOptions.formView
Form view (sub)-class to instantiate and delegate edition to.
By default, :js:class:`~openerp.web.FormView`
.. js:attribute:: EditorOptions.delegate
Object used to get various bits of information about how to
display stuff.
By default, uses the editor's parent widget. See
:js:class:`EditorDelegate` for the methods and attributes to
provide.
.. js:class:: EditorDelegate
Informal protocol defining the methods and attributes expected of
the :js:class:`~openerp.web.list.Editor`'s delegate.
.. js:attribute:: EditorDelegate.dataset
The dataset passed to the form view to synchronize the form
view and the outer widget.
.. js:function:: EditorDelegate.edition_view(editor)
Called by the :js:class:`~openerp.web.list.Editor` object to
get a form view (JSON) to pass along to the form view it
created.
The result should be a valid form view, see :doc:`Form Notes
<form-notes>` for various peculiarities of the form view
format.
:param editor: editor object asking for the view
:type editor: :js:class:`~openerp.web.list.Editor`
:returns: form view
:rtype: Object
.. js:function:: EditorDelegate.prepends_on_create
By default, the :js:class:`~openerp.web.list.Editor` will
append the ids of newly created records to the
:js:attr:`EditorDelegate.dataset`. If this method returns
``true``, it will prepend these ids instead.
:returns: whether new records should be prepended to the
dataset (instead of appended)
:rtype: Boolean
.. js:class:: EditOptions
Options object optionally passed into a method starting an edition
to configure its setup and behavior
.. js:attribute:: focus_field
Name of the field to set focus on after setting up the edition
of the record.
If this option is not provided, or the requested field can not
be focused (invisible, readonly or not in the view), the first
visible non-readonly field is focused.
Changes from 6.1
----------------
* The editable listview behavior has been rewritten pretty much from
scratch, any code touching on editability will have to be modified
* The overloading of :js:class:`~openerp.web.ListView.Groups` and
:js:class:`~openerp.web.ListView.List` for editability has been
drastically simplified, and most of the behavior has been moved to
the list view itself. Only
:js:func:`~openerp.web.ListView.List.row_clicked` is still
overridden.
* A new method ``get_row_for(record) -> jQuery(tr) | null`` has been
added to both ListView.List and ListView.Group, it can be called
from the list view to get the table row matching a record (if such
a row exists).
* :js:func:`~openerp.web.ListView.do_button_action`'s core behavior
has been split away to
:js:func:`~openerp.web.ListView.handle_button`. This allows bypassing
overrides of :js:func:`~openerp.web.ListView.do_button_action` in a
parent class.
Ideally, :js:func:`~openerp.web.ListView.handle_button` should not be
overridden.
* Modifiers handling has been improved (all modifiers information
should now be available through :js:func:`~Column.modifiers_for`,
not just ``invisible``)
* Changed some handling of the list view's record: a record may now
have no id, and the listview will handle that correctly (for new
records being created) as well as correctly handle the ``id`` being
set.
* Extended the internal collections structure of the list view with
`#find`_, `#succ`_ and `#pred`_.
.. _#find: http://underscorejs.org/#find
.. _#succ: http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.html#v:succ
.. _#pred: http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.html#v:pred
-170
View File
@@ -1,170 +0,0 @@
@ECHO OFF
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set BUILDDIR=_build
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
)
if "%1" == "" goto help
if "%1" == "help" (
: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. text to make text files
echo. man to make manual pages
echo. changes to make an overview over 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
goto end
)
if "%1" == "clean" (
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
del /q /s %BUILDDIR%\*
goto end
)
if "%1" == "html" (
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
goto end
)
if "%1" == "dirhtml" (
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
goto end
)
if "%1" == "singlehtml" (
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
goto end
)
if "%1" == "pickle" (
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the pickle files.
goto end
)
if "%1" == "json" (
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the JSON files.
goto end
)
if "%1" == "htmlhelp" (
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run HTML Help Workshop with the ^
.hhp project file in %BUILDDIR%/htmlhelp.
goto end
)
if "%1" == "qthelp" (
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %BUILDDIR%/qthelp, like this:
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\OpenERPWeb.qhcp
echo.To view the help file:
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\OpenERPWeb.ghc
goto end
)
if "%1" == "devhelp" (
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished.
goto end
)
if "%1" == "epub" (
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The epub file is in %BUILDDIR%/epub.
goto end
)
if "%1" == "latex" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
if errorlevel 1 exit /b 1
echo.
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "text" (
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The text files are in %BUILDDIR%/text.
goto end
)
if "%1" == "man" (
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The manual pages are in %BUILDDIR%/man.
goto end
)
if "%1" == "changes" (
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
if errorlevel 1 exit /b 1
echo.
echo.The overview file is in %BUILDDIR%/changes.
goto end
)
if "%1" == "linkcheck" (
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
if errorlevel 1 exit /b 1
echo.
echo.Link check complete; look for any errors in the above output ^
or in %BUILDDIR%/linkcheck/output.txt.
goto end
)
if "%1" == "doctest" (
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
if errorlevel 1 exit /b 1
echo.
echo.Testing of doctests in the sources finished, look at the ^
results in %BUILDDIR%/doctest/output.txt.
goto end
)
:end
-276
View File
@@ -1,276 +0,0 @@
Outside the box: network interactions
=====================================
Building static displays is all nice and good and allows for neat
effects (and sometimes you're given data to display from third parties
so you don't have to make any effort), but a point generally comes
where you'll want to talk to the world and make some network requests.
OpenERP Web provides two primary APIs to handle this, a low-level
JSON-RPC based API communicating with the Python section of OpenERP
Web (and of your addon, if you have a Python part) and a high-level
API above that allowing your code to talk directly to the OpenERP
server, using familiar-looking calls.
All networking APIs are :doc:`asynchronous </async>`. As a result, all
of them will return :js:class:`Deferred` objects (whether they resolve
those with values or not). Understanding how those work before before
moving on is probably necessary.
High-level API: calling into OpenERP models
-------------------------------------------
Access to OpenERP object methods (made available through XML-RPC from
the server) is done via the :js:class:`openerp.web.Model` class. This
class maps onto the OpenERP server objects via two primary methods,
:js:func:`~openerp.web.Model.call` and
:js:func:`~openerp.web.Model.query`.
:js:func:`~openerp.web.Model.call` is a direct mapping to the
corresponding method of the OpenERP server object. Its usage is
similar to that of the OpenERP Model API, with three differences:
* The interface is :doc:`asynchronous </async>`, so instead of
returning results directly RPC method calls will return
:js:class:`Deferred` instances, which will themselves resolve to the
result of the matching RPC call.
* Because ECMAScript 3/Javascript 1.5 doesnt feature any equivalent to
``__getattr__`` or ``method_missing``, there needs to be an explicit
method to dispatch RPC methods.
* No notion of pooler, the model proxy is instantiated where needed,
not fetched from an other (somewhat global) object
.. code-block:: javascript
var Users = new Model('res.users');
Users.call('change_password', ['oldpassword', 'newpassword'],
{context: some_context}).then(function (result) {
// do something with change_password result
});
:js:func:`~openerp.web.Model.query` is a shortcut for a builder-style
interface to searches (``search`` + ``read`` in OpenERP RPC terms). It
returns a :js:class:`~openerp.web.Query` object which is immutable but
allows building new :js:class:`~openerp.web.Query` instances from the
first one, adding new properties or modifiying the parent object's:
.. code-block:: javascript
Users.query(['name', 'login', 'user_email', 'signature'])
.filter([['active', '=', true], ['company_id', '=', main_company]])
.limit(15)
.all().then(function (users) {
// do work with users records
});
The query is only actually performed when calling one of the query
serialization methods, :js:func:`~openerp.web.Query.all` and
:js:func:`~openerp.web.Query.first`. These methods will perform a new
RPC call every time they are called.
For that reason, it's actually possible to keep "intermediate" queries
around and use them differently/add new specifications on them.
.. js:class:: openerp.web.Model(name)
.. js:attribute:: openerp.web.Model.name
name of the OpenERP model this object is bound to
.. js:function:: openerp.web.Model.call(method[, args][, kwargs])
Calls the ``method`` method of the current model, with the
provided positional and keyword arguments.
:param String method: method to call over rpc on the
:js:attr:`~openerp.web.Model.name`
:param Array<> args: positional arguments to pass to the
method, optional
:param Object<> kwargs: keyword arguments to pass to the
method, optional
:rtype: Deferred<>
.. js:function:: openerp.web.Model.query(fields)
:param Array<String> fields: list of fields to fetch during
the search
:returns: a :js:class:`~openerp.web.Query` object
representing the search to perform
.. js:class:: openerp.web.Query(fields)
The first set of methods is the "fetching" methods. They perform
RPC queries using the internal data of the object they're called
on.
.. js:function:: openerp.web.Query.all()
Fetches the result of the current
:js:class:`~openerp.web.Query` object's search.
:rtype: Deferred<Array<>>
.. js:function:: openerp.web.Query.first()
Fetches the **first** result of the current
:js:class:`~openerp.web.Query`, or ``null`` if the current
:js:class:`~openerp.web.Query` does have any result.
:rtype: Deferred<Object | null>
.. js:function:: openerp.web.Query.count()
Fetches the number of records the current
:js:class:`~openerp.web.Query` would retrieve.
:rtype: Deferred<Number>
.. js:function:: openerp.web.Query.group_by(grouping...)
Fetches the groups for the query, using the first specified
grouping parameter
:param Array<String> grouping: Lists the levels of grouping
asked of the server. Grouping
can actually be an array or
varargs.
:rtype: Deferred<Array<openerp.web.QueryGroup>> | null
The second set of methods is the "mutator" methods, they create a
**new** :js:class:`~openerp.web.Query` object with the relevant
(internal) attribute either augmented or replaced.
.. js:function:: openerp.web.Query.context(ctx)
Adds the provided ``ctx`` to the query, on top of any existing
context
.. js:function:: openerp.web.Query.filter(domain)
Adds the provided domain to the query, this domain is
``AND``-ed to the existing query domain.
.. js:function:: opeenrp.web.Query.offset(offset)
Sets the provided offset on the query. The new offset
*replaces* the old one.
.. js:function:: openerp.web.Query.limit(limit)
Sets the provided limit on the query. The new limit *replaces*
the old one.
.. js:function:: openerp.web.Query.order_by(fields…)
Overrides the model's natural order with the provided field
specifications. Behaves much like Django's `QuerySet.order_by
<https://docs.djangoproject.com/en/dev/ref/models/querysets/#order-by>`_:
* Takes 1..n field names, in order of most to least importance
(the first field is the first sorting key). Fields are
provided as strings.
* A field specifies an ascending order, unless it is prefixed
with the minus sign "``-``" in which case the field is used
in the descending order
Divergences from Django's sorting include a lack of random sort
(``?`` field) and the inability to "drill down" into relations
for sorting.
Aggregation (grouping)
~~~~~~~~~~~~~~~~~~~~~~
OpenERP has powerful grouping capacities, but they are kind-of strange
in that they're recursive, and level n+1 relies on data provided
directly by the grouping at level n. As a result, while ``read_group``
works it's not a very intuitive API.
OpenERP Web 7.0 eschews direct calls to ``read_group`` in favor of
calling a method of :js:class:`~openerp.web.Query`, `much in the way
it is one in SQLAlchemy
<http://docs.sqlalchemy.org/en/latest/orm/query.html#sqlalchemy.orm.query.Query.group_by>`_ [#]_:
.. code-block:: javascript
some_query.group_by(['field1', 'field2']).then(function (groups) {
// do things with the fetched groups
});
This method is asynchronous when provided with 1..n fields (to group
on) as argument, but it can also be called without any field (empty
fields collection or nothing at all). In this case, instead of
returning a Deferred object it will return ``null``.
When grouping criterion come from a third-party and may or may not
list fields (e.g. could be an empty list), this provides two ways to
test the presence of actual subgroups (versus the need to perform a
regular query for records):
* A check on ``group_by``'s result and two completely separate code
paths
.. code-block:: javascript
var groups;
if (groups = some_query.group_by(gby)) {
groups.then(function (gs) {
// groups
});
}
// no groups
* Or a more coherent code path using :js:func:`when`'s ability to
coerce values into deferreds:
.. code-block:: javascript
$.when(some_query.group_by(gby)).then(function (groups) {
if (!groups) {
// No grouping
} else {
// grouping, even if there are no groups (groups
// itself could be an empty array)
}
});
The result of a (successful) :js:func:`~openerp.web.Query.group_by` is
an array of :js:class:`~openerp.web.QueryGroup`.
Low-level API: RPC calls to Python side
---------------------------------------
While the previous section is great for calling core OpenERP code
(models code), it does not work if you want to call the Python side of
OpenERP Web.
For this, a lower-level API exists on on
:js:class:`~openerp.web.Connection` objects (usually available through
``openerp.connection``): the ``rpc`` method.
This method simply takes an absolute path (which is the combination of
the Python controller's ``_cp_path`` attribute and the name of the
method you want to call) and a mapping of attributes to values (applied
as keyword arguments on the Python method [#]_). This function fetches
the return value of the Python methods, converted to JSON.
For instance, to call the ``eval_domain_and_context`` of the
:class:`~web.controllers.main.Session` controller:
.. code-block:: javascript
openerp.connection.rpc('/web/session/eval_domain_and_context', {
domains: ds,
contexts: cs
}).then(function (result) {
// handle result
});
.. [#] with a small twist: SQLAlchemy's ``orm.query.Query.group_by``
is not terminal, it returns a query which can still be altered.
.. [#] except for ``context``, which is extracted and stored in the
request object itself.
-549
View File
@@ -1,549 +0,0 @@
Search View
===========
OpenERP Web 7.0 implements a unified facets-based search view instead
of the previous form-like search view (composed of buttons and
multiple fields). The goal for this change is twofold:
* Avoid the common issue of users confusing the search view with a
form view and trying to create their records through it (or entering
all their data, hitting the ``Create`` button expecting their record
to be created and losing everything).
* Improve the looks and behaviors of the view, and the fit within
OpenERP Web's new design.
The internal structure of the faceted search is inspired by
`VisualSearch <http://documentcloud.github.com/visualsearch/>`_
[#previous]_.
As does VisualSearch, the new search view is based on `Backbone`_ and
makes significant use of Backbone's models and collections (OpenERP
Web's widgets make a good replacement for Backbone's own views). As a
result, understanding the implementation details of the OpenERP Web 7
search view also requires a basic understanding of Backbone's models,
collections and events.
.. note::
This document may mention *fetching* data. This is a shortcut for
"returning a :js:class:`Deferred` to [whatever is being
fetched]". Unless further noted, the function or method may opt to
return nothing by fetching ``null`` (which can easily be done by
returning ``$.when(null)``, which simply wraps the ``null`` in a
Deferred).
Working with the search view: creating new inputs
-------------------------------------------------
The primary component of search views, as with all other OpenERP
views, are inputs. The search view has two types of inputs — filters
and fields — but only one is easly customizable: fields.
The mapping from OpenERP field types (and widgets) to search view
objects is stored in the ``openerp.web.search.fields``
:js:class:`~openerp.web.Registry` where new field types and widgets
can be added.
Search view inputs have four main roles:
Loading defaults
++++++++++++++++
Once the search view has initialized all its inputs, it will call
:js:func:`~openerp.web.search.Input.facet_for_defaults` on each input,
passing it a mapping (a javascript object) of ``name:value`` extracted
from the action's context.
This method should fetch a :js:class:`~openerp.web.search.Facet` (or
an equivalent object) for the field's default value if applicable (if
a default value for the field is found in the ``defaults`` mapping).
A default implementation is provided which checks if ``defaults``
contains a non-falsy value for the field's ``@name`` and calls
:js:func:`openerp.web.search.Input.facet_for` with that value.
There is no default implementation of
:js:func:`openerp.web.search.Input.facet_for` [#no_impl]_, but
:js:class:`openerp.web.search.Field` provides one, which uses the
value as-is to fetch a :js:class:`~openerp.web.search.Facet`.
Providing completions
+++++++++++++++++++++
An important component of the new search view is the auto-completion
pane, and the task of providing completion items is delegated to
inputs through the :js:func:`~openerp.web.search.Input.complete`
method.
This method should take a single argument (the string being typed by
the user) and should fetch an ``Array`` of possible completions
[#completion]_.
A default implementation is provided which fetches nothing.
A completion item is a javascript object with two keys (technically it
can have any number of keys, but only these two will be used by the
search view):
``label``
The string which will be displayed in the completion pane. It may
be formatted using HTML (inline only), as a result if ``value`` is
interpolated into it it *must* be escaped. ``_.escape`` can be
used for this.
``facet``
Either a :js:class:`~openerp.web.search.Facet` object or (more
commonly) the corresponding attributes object. This is the facet
which will be inserted into the search query if the completion
item is selected by the user.
If the ``facet`` is not provided (not present, ``null``, ``undefined``
or any other falsy value), the completion item will not be selectable
and will act as a section title of sort (the ``label`` will be
formatted differently). If an input *may* fetch multiple completion
items, it *should* prefix those with a section title using its own
name. This has no technical consequence but is clearer for users.
Providing drawer/supplementary UI
+++++++++++++++++++++++++++++++++
For some inputs (fields or not), interaction via autocompletion may be
awkward or even impossible.
These may opt to being rendered in a "drawer" as well or instead. In
that case, they will undergo the normal widget lifecycle and be
rendered inside the drawer.
.. Found no good type-based way to handle this, since there is no MI
(so no type-tagging) and it's possible for both Field and non-Field
input to be put into the drawer, for whatever reason (e.g. some
sort of auto-detector completion item for date widgets, but a
second more usual calendar widget in the drawer for more
obvious/precise interactions)
Any input can note its desire to be rendered in the drawer by
returning a truthy value from
:js:func:`~openerp.web.search.Input.in_drawer`.
By default, :js:func:`~openerp.web.search.Input.in_drawer` returns the
value of :js:attr:`~openerp.web.search.Input._in_drawer`, which is
``false``. The behavior can be toggled either by redefining the
attribute to ``true`` (either on the class or on the input), or by
overriding :js:func:`~openerp.web.search.Input.in_drawer` itself.
The input will be rendered in the full width of the drawer, it will be
started only once (per view).
.. todo:: drawer API (if a widget wants to close the drawer in some
way), part of the low-level SearchView API/interactions?
.. todo:: handle filters and filter groups via a "driver" input which
dynamically collects, lays out and renders filters? =>
exercises drawer thingies
Converting from facet objects
+++++++++++++++++++++++++++++
Ultimately, the point of the search view is to allow searching. In
OpenERP this is done via :ref:`domains <openerpserver:domains>`. On
the other hand, the OpenERP Web 7 search view's state is modelled
after a collection of :js:class:`~openerp.web.search.Facet`, and each
field of a search view may have special requirements when it comes to
the domains it produces [#special]_.
So there needs to be some way of mapping
:js:class:`~openerp.web.search.Facet` objects to OpenERP search data.
This is done via an input's
:js:func:`~openerp.web.search.Input.get_domain` and
:js:func:`~openerp.web.search.Input.get_context`. Each takes a
:js:class:`~openerp.web.search.Facet` and returns whatever it's
supposed to generate (a domain or a context, respectively). Either can
return ``null`` if the current value does not map to a domain or
context, and can throw an :js:class:`~openerp.web.search.Invalid`
exception if the value is not valid at all for the field.
.. note::
The :js:class:`~openerp.web.search.Facet` object can have any
number of values (from 1 upwards)
.. note::
There is a third conversion method,
:js:func:`~openerp.web.search.Input.get_groupby`, which returns an
``Array`` of groupby domains rather than a single context. At this
point, it is only implemented on (and used by) filters.
Programmatic interactions: internal model
-----------------------------------------
This new searchview is built around an instance of
:js:class:`~openerp.web.search.SearchQuery` available as
:js:attr:`openerp.web.SearchView.query`.
The query is a `backbone collection`_ of
:js:class:`~openerp.web.search.Facet` objects, which can be interacted
with directly by external objects or search view controls
(e.g. widgets displayed in the drawer).
.. js:class:: openerp.web.search.SearchQuery
The current search query of the search view, provides convenience
behaviors for manipulating :js:class:`~openerp.web.search.Facet`
on top of the usual `backbone collection`_ methods.
The query ensures all of its facets contain at least one
:js:class:`~openerp.web.search.FacetValue` instance. Otherwise,
the facet is automatically removed from the query.
.. js:function:: openerp.web.search.SearchQuery.add(values, options)
Overridden from the base ``add`` method so that adding a facet
which is *already* in the collection will merge the value of
the new facet into the old one rather than add a second facet
with different values.
:param values: facet, facet attributes or array thereof
:returns: the collection itself
.. js:function:: openerp.web.search.SearchQuery.toggle(value, options)
Convenience method for toggling facet values in a query:
removes the values (through the facet itself) if they are
present, adds them if they are not. If the facet itself is not
in the collection, adds it automatically.
A toggling is atomic: only one change event will be triggered
on the facet regardless of the number of values added to or
removed from the facet (if the facet already exists), and the
facet is only removed from the query if it has no value *at
the end* of the toggling.
:param value: facet or facet attributes
:returns: the collection
.. js:class:: openerp.web.search.Facet
A `backbone model`_ representing a single facet of the current
research. May map to a search field, or to a more complex or
fuzzier input (e.g. a custom filter or an advanced search).
.. js:attribute:: category
The displayed name of the facet, as a ``String``. This is a
backbone model attribute.
.. js:attribute:: field
The :js:class:`~openerp.web.search.Input` instance which
originally created the facet [#facet-field]_, used to delegate
some operations (such as serializing the facet's values to
domains and contexts). This is a backbone model attribute.
.. js:attribute:: values
:js:class:`~openerp.web.search.FacetValues` as a javascript
attribute, stores all the values for the facet and helps
propagate their events to the facet. Is also available as a
backbone attribute (via ``#get`` and ``#set``) in which cases
it serializes to and deserializes from javascript arrays (via
``Collection#toJSON`` and ``Collection#reset``).
.. js:attribute:: [icon]
optional, a single ASCII letter (a-z or A-Z) mapping to the
bundled mnmliconsRegular icon font.
When a facet with an ``icon`` attribute is rendered, the icon
is displayed (in the icon font) in the first section of the
facet instead of the ``category``.
By default, only filters make use of this facility.
.. js:class:: openerp.web.search.FacetValues
`Backbone collection`_ of
:js:class:`~openerp.web.search.FacetValue` instances.
.. js:class:: openerp.web.search.FacetValue
`Backbone model`_ representing a single value within a facet,
represents a pair of (displayed name, logical value).
.. js:attribute:: label
Backbone model attribute storing the "displayable"
representation of the value, visually output to the
user. Must be a string.
.. js:attribute:: value
Backbone model attribute storing the logical/internal value
(of itself), will be used by
:js:class:`~openerp.web.search.Input` to serialize to domains
and contexts.
Can be of any type.
Field services
--------------
:js:class:`~openerp.web.search.Field` provides a default
implementation of :js:func:`~openerp.web.search.Input.get_domain` and
:js:func:`~openerp.web.search.Input.get_context` taking care of most
of the peculiarities pertaining to OpenERP's handling of fields in
search views. It also provides finer hooks to let developers of new
fields and widgets customize the behavior they want without
necessarily having to reimplement all of
:js:func:`~openerp.web.search.Input.get_domain` or
:js:func:`~openerp.web.search.Input.get_context`:
.. js:function:: openerp.web.search.Field.get_context(facet)
If the field has no ``@context``, simply returns
``null``. Otherwise, calls
:js:func:`~openerp.web.search.Field.value_from` once for each
:js:class:`~openerp.web.search.FacetValue` of the current
:js:class:`~openerp.web.search.Facet` (in order to extract the
basic javascript object from the
:js:class:`~openerp.web.search.FacetValue` then evaluates
``@context`` with each of these values set as ``self``, and
returns the union of all these contexts.
:param facet:
:type facet: openerp.web.search.Facet
:returns: a context (literal or compound)
.. js:function:: openerp.web.search.Field.get_domain(facet)
If the field has no ``@filter_domain``, calls
:js:func:`~openerp.web.search.Field.make_domain` once with each
:js:class:`~openerp.web.search.FacetValue` of the current
:js:class:`~openerp.web.search.Facet` as well as the field's
``@name`` and either its ``@operator`` or
:js:attr:`~openerp.web.search.Field.default_operator`.
If the field has an ``@filter_value``, calls
:js:func:`~openerp.web.search.Field.value_from` once per
:js:class:`~openerp.web.search.FacetValue` and evaluates
``@filter_value`` with each of these values set as ``self``.
In either case, "ors" all of the resulting domains (using ``|``)
if there is more than one
:js:class:`~openerp.web.search.FacetValue` and returns the union
of the result.
:param facet:
:type facet: openerp.web.search.Facet
:returns: a domain (literal or compound)
.. js:function:: openerp.web.search.Field.make_domain(name, operator, facetValue)
Builds a literal domain from the provided data. Calls
:js:func:`~openerp.web.search.Field.value_from` on the
:js:class:`~openerp.web.search.FacetValue` and evaluates and sets
it as the domain's third value, uses the other two parameters as
the first two values.
Can be overridden to build more complex default domains.
:param String name: the field's name
:param String operator: the operator to use in the field's domain
:param facetValue:
:type facetValue: openerp.web.search.FacetValue
:returns: Array<(String, String, Object)>
.. js:function:: openerp.web.search.Field.value_from(facetValue)
Extracts a "bare" javascript value from the provided
:js:class:`~openerp.web.search.FacetValue`, and returns it.
The default implementation will simply return the ``value``
backbone property of the argument.
:param facetValue:
:type facetValue: openerp.web.search.FacetValue
:returns: Object
.. js:attribute:: openerp.web.search.Field.default_operator
Operator used to build a domain when a field has no ``@operator``
or ``@filter_domain``. ``"="`` for
:js:class:`~openerp.web.search.Field`
Arbitrary data storage
----------------------
:js:class:`~openerp.web.search.Facet` and
:js:class:`~openerp.web.search.FacetValue` objects (and structures)
provided by your widgets should never be altered by the search view
(or an other widget). This means you are free to add arbitrary fields
in these structures if you need to (because you have more complex
needs than the attributes described in this document).
Ideally this should be avoided, but the possibility remains.
Changes
-------
.. todo:: merge in changelog instead?
The displaying of the search view was significantly altered from
OpenERP Web 6.1 to OpenERP Web 7.
As a result, while the external API used to interact with the search
view does not change many internal details — including the interaction
between the search view and its widgets — were significantly altered:
Internal operations
+++++++++++++++++++
* :js:func:`openerp.web.SearchView.do_clear` has been removed
* :js:func:`openerp.web.SearchView.do_toggle_filter` has been removed
Widgets API
+++++++++++
* :js:func:`openerp.web.search.Widget.render` has been removed
* :js:func:`openerp.web.search.Widget.make_id` has been removed
* Search field objects are not openerp widgets anymore, their
``start`` is not generally called
* :js:func:`~openerp.web.search.Input.clear` has been removed since
clearing the search view now simply consists of removing all search
facets
* :js:func:`~openerp.web.search.Input.get_domain` and
:js:func:`~openerp.web.search.Input.get_context` now take a
:js:class:`~openerp.web.search.Facet` as parameter, from which it's
their job to get whatever value they want
* :js:func:`~openerp.web.search.Input.get_groupby` has been added. It returns
an :js:class:`Array` of context-like constructs. By default, it does not do
anything in :js:class:`~openerp.web.search.Field` and it returns the various
contexts of its enabled filters in
:js:class:`~openerp.web.search.FilterGroup`.
Filters
+++++++
* :js:func:`openerp.web.search.Filter.is_enabled` has been removed
* :js:class:`~openerp.web.search.FilterGroup` instances are still
rendered (and started) in the "advanced search" drawer.
Fields
++++++
* ``get_value`` has been replaced by
:js:func:`~openerp.web.search.Field.value_from` as it now takes a
:js:class:`~openerp.web.search.FacetValue` argument (instead of no
argument). It provides a default implementation returning the
``value`` property of its argument.
* The third argument to
:js:func:`~openerp.web.search.Field.make_domain` is now a
:js:class:`~openerp.web.search.FacetValue` so child classes have all
the information they need to derive the "right" resulting domain.
Custom filters
++++++++++++++
Instead of being an intrinsic part of the search view, custom filters
are now a special case of filter groups. They are treated specially
still, but much less so than they used to be.
Many To One
+++++++++++
* Because the autocompletion service is now provided by the search
view itself,
:js:func:`openerp.web.search.ManyToOneField.setup_autocomplete` has
been removed.
Advanced Search
+++++++++++++++
* The advanced search is now a more standard
:js:class:`~openerp.web.search.Input` configured to be rendered in
the drawer.
* :js:class:`~openerp.web.search.ExtendedSearchProposition.Field` are
now standard widgets, with the "right" behaviors (they don't rebind
their ``$element`` in ``start()``)
* The ad-hoc optional setting of the openerp field descriptor on a
:js:class:`~openerp.web.search.ExtendedSearchProposition.Field` has
been removed, the field descriptor is now passed as second argument
to the
:js:class:`~openerp.web.search.ExtendedSearchProposition.Field`'s
constructor, and bound to its
:js:attr:`~openerp.web.search.ExtendedSearchProposition.Field.field`.
* Instead of its former domain triplet ``(field, operator, value)``,
:js:func:`~openerp.web.search.ExtendedSearchProposition.get_proposition`
now returns an object with two fields ``label`` and ``value``,
respectively a human-readable version of the proposition and the
corresponding domain triplet for the proposition.
.. [#previous]
the original view was implemented on top of a monkey-patched
VisualSearch, but as our needs diverged from VisualSearch's goal
this made less and less sense ultimately leading to a clean-room
reimplementation
.. [#no_impl]
In case you are extending the search view with a brand new type of
input
.. [#completion]
Ideally this array should not hold more than about 10 items, but
the search view does not put any constraint on this at the
moment. Note that this may change.
.. [#facet-field]
``field`` does not actually need to be an instance of
:js:class:`~openerp.web.search.Input`, nor does it need to be what
created the facet, it just needs to provide the three
facet-serialization methods
:js:func:`~openerp.web.search.Input.get_domain`,
:js:func:`~openerp.web.search.Input.get_context` and
:js:func:`~openerp.web.search.Input.get_gropuby`, existing
:js:class:`~openerp.web.search.Input` subtypes merely provide
convenient base implementation for those methods.
Complex search view inputs (especially those living in the drawer)
may prefer using object literals with the right slots returning
closed-over values or some other scheme un-bound to an actual
:js:class:`~openerp.web.search.Input`, as
:js:class:`~openerp.web.search.CustomFilters` and
:js:class:`~openerp.web.search.Advanced` do.
.. [#special]
search view fields may also bundle context data to add to the
search context
.. _Backbone:
http://documentcloud.github.com/backbone/
.. _Backbone.Collection:
.. _Backbone collection:
http://documentcloud.github.com/backbone/#Collection
.. _Backbone model:
http://documentcloud.github.com/backbone/#Model
.. _commit 3fca87101d:
https://github.com/documentcloud/visualsearch/commit/3fca87101d
-274
View File
@@ -1,274 +0,0 @@
User Interaction: Widget
========================
This is the base class for all visual components. It corresponds to an MVC
view. It provides a number of services to handle a section of a page:
* 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()`_
* Backbone-compatible shortcuts
DOM Root
--------
A :js:class:`~openerp.web.Widget` is responsible for a section of the
page materialized by the DOM root of the widget. The DOM root is
available via the :js:attr:`~openerp.web.Widget.el` and
:js:attr:`~openerp.web.Widget.$element` attributes, which are
respectively the raw DOM Element and the jQuery wrapper around the DOM
element.
There are two main ways to define and generate this DOM root:
.. js:attribute:: openerp.web.Widget.template
Should be set to the name of a QWeb template (a
:js:class:`String`). If set, the template will be rendered after
the widget has been initialized but before it has been
started. The root element generated by the template will be set as
the DOM root of the widget.
.. js:attribute:: openerp.web.Widget.tagName
Used if the widget has no template defined. Defaults to ``div``,
will be used as the tag name to create the DOM element to set as
the widget's DOM root. It is possible to further customize this
generated DOM root with the following attributes:
.. js:attribute:: openerp.web.Widget.id
Used to generate an ``id`` attribute on the generated DOM
root.
.. js:attribute:: openerp.web.Widget.className
Used to generate a ``class`` attribute on the generated DOM root.
.. js:attribute:: openerp.web.Widget.attributes
Mapping (object literal) of attribute names to attribute
values. Each of these k:v pairs will be set as a DOM attribute
on the generated DOM root.
None of these is used in case a template is specified on the widget.
The DOM root can also be defined programmatically by overridding
.. js:function:: openerp.web.Widget.renderElement
Renders the widget's DOM root and sets it. The default
implementation will render a set template or generate an element
as described above, and will call
:js:func:`~openerp.web.Widget.setElement` on the result.
Any override to :js:func:`~openerp.web.Widget.renderElement` which
does not call its ``_super`` **must** call
:js:func:`~openerp.web.Widget.setElement` with whatever it
generated or the widget's behavior is undefined.r
.. note::
The default :js:func:`~openerp.web.Widget.renderElement` can
be called repeatedly, it will *replace* the previous DOM root
(using ``replaceWith``). However, this requires that the
widget correctly sets and unsets its events (and children
widgets). Generally,
:js:func:`~openerp.web.Widget.renderElement` should not be
called repeatedly unless the widget advertizes this feature.
Accessing DOM content
~~~~~~~~~~~~~~~~~~~~~
Because a widget is only responsible for the content below its DOM
root, there is a shortcut for selecting sub-sections of a widget's
DOM:
.. js:function:: openerp.web.Widget.$(selector)
Applies the CSS selector specified as parameter to the widget's
DOM root.
.. code-block:: javascript
this.$(selector);
is functionally identical to:
.. code-block:: javascript
this.$element.find(selector);
:param String selector: CSS selector
:returns: jQuery object
.. note:: this helper method is compatible with
``Backbone.View.$``
Resetting the DOM root
~~~~~~~~~~~~~~~~~~~~~~
.. js:function:: openerp.web.Widget.setElement(element)
Re-sets the widget's DOM root to the provided element, also
handles re-setting the various aliases of the DOM root as well as
unsetting and re-setting delegated events.
:param Element element: a DOM element or jQuery object to set as
the widget's DOM root
.. note:: should be mostly compatible with `Backbone's
setElement`_
DOM events handling
-------------------
A widget will generally need to respond to user action within its
section of the page. This entails binding events to DOM elements.
To this end, :js:class:`~openerp.web.Widget` provides an shortcut:
.. js:attribute:: openerp.web.Widget.events
Events are a mapping of ``event selector`` (an event name and a
CSS selector separated by a space) to a callback. The callback can
be either a method name in the widget or a function. In either
case, the ``this`` will be set to the widget.
The selector is used for jQuery's `event delegation`_, the
callback will only be triggered for descendants of the DOM root
matching the selector [0]_. If the selector is left out (only an
event name is specified), the event will be set directly on the
widget's DOM root.
.. js:function:: openerp.web.Widget.delegateEvents
This method is in charge of binding
:js:attr:`~openerp.web.Widget.events` to the DOM. It is
automatically called after setting the widget's DOM root.
It can be overridden to set up more complex events than the
:js:attr:`~openerp.web.Widget.events` map allows, but the parent
should always be called (or :js:attr:`~openerp.web.Widget.events`
won't be handled correctly).
.. js:function:: openerp.web.Widget.undelegateEvents
This method is in charge of unbinding
:js:attr:`~openerp.web.Widget.events` from the DOM root when the
widget is destroyed or the DOM root is reset, in order to avoid
leaving "phantom" events.
It should be overridden to un-set any event set in an override of
:js:func:`~openerp.web.Widget.delegateEvents`.
.. note:: this behavior should be compatible with `Backbone's
delegateEvents`_, apart from not accepting any argument.
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.destroy();
will unbind all DOM events, remove the widget's content from the DOM and
destroy all widget data.
.. [0] not all DOM events are compatible with events delegation
.. _.appendTo():
http://api.jquery.com/appendTo/
.. _.prependTo():
http://api.jquery.com/prependTo/
.. _.insertAfter():
http://api.jquery.com/insertAfter/
.. _.insertBefore():
http://api.jquery.com/insertBefore/
.. _event delegation:
http://api.jquery.com/delegate/
.. _Backbone's setElement:
http://backbonejs.org/#View-setElement
.. _Backbone's delegateEvents:
http://backbonejs.org/#View-delegateEvents