summaryrefslogtreecommitdiff
path: root/doc
diff options
context:
space:
mode:
Diffstat (limited to 'doc')
-rw-r--r--doc/_themes/odoodoc/layout.html6
-rw-r--r--doc/_themes/odoodoc/static/app.js5
-rw-r--r--doc/_themes/odoodoc/static/style.css431
-rw-r--r--doc/_themes/odoodoc/static/style.less158
-rw-r--r--doc/api_integration.rst750
-rw-r--r--doc/conf.py2
-rw-r--r--doc/howtos/backend.rst10
-rw-r--r--doc/howtos/themes.rst4
-rw-r--r--doc/howtos/web.rst6
-rw-r--r--doc/howtos/website.rst4
-rw-r--r--doc/index.rst4
-rw-r--r--doc/reference.rst1
-rw-r--r--doc/reference/cmdline.rst6
-rw-r--r--doc/reference/security.rst2
-rw-r--r--doc/reference/translations.rst96
-rw-r--r--doc/reference/translations/po-export.pngbin0 -> 6069 bytes
-rw-r--r--doc/reference/views.rst6
-rw-r--r--doc/reference/workflows.rst2
-rw-r--r--doc/tutorials.rst2
19 files changed, 1359 insertions, 136 deletions
diff --git a/doc/_themes/odoodoc/layout.html b/doc/_themes/odoodoc/layout.html
index 5235137eddd..b01eaf05216 100644
--- a/doc/_themes/odoodoc/layout.html
+++ b/doc/_themes/odoodoc/layout.html
@@ -14,6 +14,12 @@
<!doctype html>
{%- endblock -%}
+{%- block htmltitle -%}
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ {{ super() }}
+{%- endblock -%}
+
{%- block extrahead -%}
{%- if canonical -%}
<link rel="canonical" href="{{ canonical }}"/>
diff --git a/doc/_themes/odoodoc/static/app.js b/doc/_themes/odoodoc/static/app.js
index 47c33f41d23..a0d7376530a 100644
--- a/doc/_themes/odoodoc/static/app.js
+++ b/doc/_themes/odoodoc/static/app.js
@@ -77,7 +77,7 @@ $(function () {
var sheet = findSheet(/style\.css$/);
if (!sheet) { return; }
// build PL switcher UI and hook toggle event
- $(buildSwitcher(Object.keys(languages)))
+ var $switcher = $(buildSwitcher(Object.keys(languages)))
.prependTo('div.documentwrapper')
.on('click', 'li', function (e) {
$(e.target).addClass('active')
@@ -93,7 +93,8 @@ $(function () {
].join('');
sheet.deleteRule(lastIndex);
sheet.insertRule(sel + '{' + content + '}', lastIndex);
- });
+ });
+ $switcher.affix();
})(); }
diff --git a/doc/_themes/odoodoc/static/style.css b/doc/_themes/odoodoc/static/style.css
index 7c0b8e82959..12ffb135f6f 100644
--- a/doc/_themes/odoodoc/static/style.css
+++ b/doc/_themes/odoodoc/static/style.css
@@ -1,4 +1,4 @@
-@import url(http://fonts.googleapis.com/css?family=Lato);
+@import url(https://fonts.googleapis.com/css?family=Lato);
/*! normalize.css v3.0.1 | MIT License | git.io/normalize */
html {
font-family: sans-serif;
@@ -6808,11 +6808,405 @@ td.field-body > ul {
float: left;
clear: left;
}
+ .stripe .section > .force-right {
+ padding-left: 1em;
+ padding-right: 1em;
+ }
.stripe .section > .force-right,
.stripe .section > [class*=highlight] {
float: none;
clear: none;
+ margin-left: 50%;
+ width: 50%;
+ color: #eeeeee;
+ }
+ .stripe .section > .force-right a,
+ .stripe .section > [class*=highlight] a {
+ color: #d9a8cc;
+ }
+ .stripe .section > .force-right code,
+ .stripe .section > [class*=highlight] code,
+ .stripe .section > .force-right .literal,
+ .stripe .section > [class*=highlight] .literal {
+ color: #f9f2f4;
+ background-color: #555555;
+ }
+ .stripe .section > .force-right:not(.highlight-json) .highlight,
+ .stripe .section > [class*=highlight]:not(.highlight-json) .highlight {
+ border-bottom-color: #777777;
+ }
+ .stripe .section > .force-right.admonition,
+ .stripe .section > [class*=highlight].admonition {
margin-left: 51%;
+ width: 49%;
+ border-top-color: #777777;
+ border-bottom-color: #777777;
+ border-right-color: #777777;
+ }
+ .stripe .section > .force-right pre,
+ .stripe .section > [class*=highlight] pre {
+ padding: 0;
+ margin: 10px;
+ background: none;
+ border: none;
+ }
+ .stripe .section > .force-right .highlight,
+ .stripe .section > [class*=highlight] .highlight {
+ border-color: #555555;
+ border-style: solid;
+ border-width: 1px 0;
+ color: #cccccc;
+ background: none;
+ /* Line Numbers */
+ /* Comment */
+ /* Error */
+ /* Generic */
+ /* Keyword */
+ /* Literal */
+ /* Name */
+ /* Operator */
+ /* Other */
+ /* Punctuation */
+ /* Comment.Multiline */
+ /* Comment.Preproc */
+ /* Comment.Single */
+ /* Comment.Special */
+ /* Generic.Deleted */
+ /* Generic.Emph */
+ /* Generic.Error */
+ /* Generic.Heading */
+ /* Generic.Inserted */
+ /* Generic.Output */
+ /* Generic.Prompt */
+ /* Generic.Strong */
+ /* Generic.Subheading */
+ /* Generic.Traceback */
+ /* Keyword.Constant */
+ /* Keyword.Declaration */
+ /* Keyword.Namespace */
+ /* Keyword.Pseudo */
+ /* Keyword.Reserved */
+ /* Keyword.Type */
+ /* Literal.Date */
+ /* Literal.Number */
+ /* Literal.String */
+ /* Name.Attribute */
+ /* Name.Builtin */
+ /* Name.Class */
+ /* Name.Constant */
+ /* Name.Decorator */
+ /* Name.Entity */
+ /* Name.Exception */
+ /* Name.Function */
+ /* Name.Label */
+ /* Name.Namespace */
+ /* Name.Other */
+ /* Name.Property */
+ /* Name.Tag */
+ /* Name.Variable */
+ /* Operator.Word */
+ /* Text.Whitespace */
+ /* Literal.Number.Float */
+ /* Literal.Number.Hex */
+ /* Literal.Number.Integer */
+ /* Literal.Number.Oct */
+ /* Literal.String.Backtick */
+ /* Literal.String.Char */
+ /* Literal.String.Doc */
+ /* Literal.String.Double */
+ /* Literal.String.Escape */
+ /* Literal.String.Heredoc */
+ /* Literal.String.Interpol */
+ /* Literal.String.Other */
+ /* Literal.String.Regex */
+ /* Literal.String.Single */
+ /* Literal.String.Symbol */
+ /* Name.Builtin.Pseudo */
+ /* Name.Variable.Class */
+ /* Name.Variable.Global */
+ /* Name.Variable.Instance */
+ /* Literal.Number.Integer.Long */
+ }
+ .stripe .section > .force-right .highlight .lineno,
+ .stripe .section > [class*=highlight] .highlight .lineno {
+ color: #586e75;
+ }
+ .stripe .section > .force-right .highlight .c,
+ .stripe .section > [class*=highlight] .highlight .c {
+ color: #586e75;
+ }
+ .stripe .section > .force-right .highlight .err,
+ .stripe .section > [class*=highlight] .highlight .err {
+ color: #cccccc;
+ }
+ .stripe .section > .force-right .highlight .g,
+ .stripe .section > [class*=highlight] .highlight .g {
+ color: #cccccc;
+ }
+ .stripe .section > .force-right .highlight .k,
+ .stripe .section > [class*=highlight] .highlight .k {
+ color: #859900;
+ }
+ .stripe .section > .force-right .highlight .l,
+ .stripe .section > [class*=highlight] .highlight .l {
+ color: #cccccc;
+ }
+ .stripe .section > .force-right .highlight .n,
+ .stripe .section > [class*=highlight] .highlight .n {
+ color: #cccccc;
+ }
+ .stripe .section > .force-right .highlight .o,
+ .stripe .section > [class*=highlight] .highlight .o {
+ color: #859900;
+ }
+ .stripe .section > .force-right .highlight .x,
+ .stripe .section > [class*=highlight] .highlight .x {
+ color: #cb4b16;
+ }
+ .stripe .section > .force-right .highlight .p,
+ .stripe .section > [class*=highlight] .highlight .p {
+ color: #cccccc;
+ }
+ .stripe .section > .force-right .highlight .cm,
+ .stripe .section > [class*=highlight] .highlight .cm {
+ color: #586e75;
+ }
+ .stripe .section > .force-right .highlight .cp,
+ .stripe .section > [class*=highlight] .highlight .cp {
+ color: #859900;
+ }
+ .stripe .section > .force-right .highlight .c1,
+ .stripe .section > [class*=highlight] .highlight .c1 {
+ color: #586e75;
+ }
+ .stripe .section > .force-right .highlight .cs,
+ .stripe .section > [class*=highlight] .highlight .cs {
+ color: #859900;
+ }
+ .stripe .section > .force-right .highlight .gd,
+ .stripe .section > [class*=highlight] .highlight .gd {
+ color: #2aa198;
+ }
+ .stripe .section > .force-right .highlight .ge,
+ .stripe .section > [class*=highlight] .highlight .ge {
+ color: #cccccc;
+ font-style: italic;
+ }
+ .stripe .section > .force-right .highlight .gr,
+ .stripe .section > [class*=highlight] .highlight .gr {
+ color: #dc322f;
+ }
+ .stripe .section > .force-right .highlight .gh,
+ .stripe .section > [class*=highlight] .highlight .gh {
+ color: #cb4b16;
+ }
+ .stripe .section > .force-right .highlight .gi,
+ .stripe .section > [class*=highlight] .highlight .gi {
+ color: #859900;
+ }
+ .stripe .section > .force-right .highlight .go,
+ .stripe .section > [class*=highlight] .highlight .go {
+ color: #cccccc;
+ }
+ .stripe .section > .force-right .highlight .gp,
+ .stripe .section > [class*=highlight] .highlight .gp {
+ color: #cccccc;
+ }
+ .stripe .section > .force-right .highlight .gs,
+ .stripe .section > [class*=highlight] .highlight .gs {
+ color: #cccccc;
+ font-weight: bold;
+ }
+ .stripe .section > .force-right .highlight .gu,
+ .stripe .section > [class*=highlight] .highlight .gu {
+ color: #cb4b16;
+ }
+ .stripe .section > .force-right .highlight .gt,
+ .stripe .section > [class*=highlight] .highlight .gt {
+ color: #cccccc;
+ }
+ .stripe .section > .force-right .highlight .kc,
+ .stripe .section > [class*=highlight] .highlight .kc {
+ color: #cb4b16;
+ }
+ .stripe .section > .force-right .highlight .kd,
+ .stripe .section > [class*=highlight] .highlight .kd {
+ color: #268bd2;
+ }
+ .stripe .section > .force-right .highlight .kn,
+ .stripe .section > [class*=highlight] .highlight .kn {
+ color: #859900;
+ }
+ .stripe .section > .force-right .highlight .kp,
+ .stripe .section > [class*=highlight] .highlight .kp {
+ color: #859900;
+ }
+ .stripe .section > .force-right .highlight .kr,
+ .stripe .section > [class*=highlight] .highlight .kr {
+ color: #268bd2;
+ }
+ .stripe .section > .force-right .highlight .kt,
+ .stripe .section > [class*=highlight] .highlight .kt {
+ color: #dc322f;
+ }
+ .stripe .section > .force-right .highlight .ld,
+ .stripe .section > [class*=highlight] .highlight .ld {
+ color: #cccccc;
+ }
+ .stripe .section > .force-right .highlight .m,
+ .stripe .section > [class*=highlight] .highlight .m {
+ color: #2aa198;
+ }
+ .stripe .section > .force-right .highlight .s,
+ .stripe .section > [class*=highlight] .highlight .s {
+ color: #2aa198;
+ }
+ .stripe .section > .force-right .highlight .na,
+ .stripe .section > [class*=highlight] .highlight .na {
+ color: #cccccc;
+ }
+ .stripe .section > .force-right .highlight .nb,
+ .stripe .section > [class*=highlight] .highlight .nb {
+ color: #b58900;
+ }
+ .stripe .section > .force-right .highlight .nc,
+ .stripe .section > [class*=highlight] .highlight .nc {
+ color: #268bd2;
+ }
+ .stripe .section > .force-right .highlight .no,
+ .stripe .section > [class*=highlight] .highlight .no {
+ color: #cb4b16;
+ }
+ .stripe .section > .force-right .highlight .nd,
+ .stripe .section > [class*=highlight] .highlight .nd {
+ color: #268bd2;
+ }
+ .stripe .section > .force-right .highlight .ni,
+ .stripe .section > [class*=highlight] .highlight .ni {
+ color: #cb4b16;
+ }
+ .stripe .section > .force-right .highlight .ne,
+ .stripe .section > [class*=highlight] .highlight .ne {
+ color: #cb4b16;
+ }
+ .stripe .section > .force-right .highlight .nf,
+ .stripe .section > [class*=highlight] .highlight .nf {
+ color: #268bd2;
+ }
+ .stripe .section > .force-right .highlight .nl,
+ .stripe .section > [class*=highlight] .highlight .nl {
+ color: #cccccc;
+ }
+ .stripe .section > .force-right .highlight .nn,
+ .stripe .section > [class*=highlight] .highlight .nn {
+ color: #cccccc;
+ }
+ .stripe .section > .force-right .highlight .nx,
+ .stripe .section > [class*=highlight] .highlight .nx {
+ color: #cccccc;
+ }
+ .stripe .section > .force-right .highlight .py,
+ .stripe .section > [class*=highlight] .highlight .py {
+ color: #cccccc;
+ }
+ .stripe .section > .force-right .highlight .nt,
+ .stripe .section > [class*=highlight] .highlight .nt {
+ color: #268bd2;
+ }
+ .stripe .section > .force-right .highlight .nv,
+ .stripe .section > [class*=highlight] .highlight .nv {
+ color: #268bd2;
+ }
+ .stripe .section > .force-right .highlight .ow,
+ .stripe .section > [class*=highlight] .highlight .ow {
+ color: #859900;
+ }
+ .stripe .section > .force-right .highlight .w,
+ .stripe .section > [class*=highlight] .highlight .w {
+ color: #cccccc;
+ }
+ .stripe .section > .force-right .highlight .mf,
+ .stripe .section > [class*=highlight] .highlight .mf {
+ color: #2aa198;
+ }
+ .stripe .section > .force-right .highlight .mh,
+ .stripe .section > [class*=highlight] .highlight .mh {
+ color: #2aa198;
+ }
+ .stripe .section > .force-right .highlight .mi,
+ .stripe .section > [class*=highlight] .highlight .mi {
+ color: #2aa198;
+ }
+ .stripe .section > .force-right .highlight .mo,
+ .stripe .section > [class*=highlight] .highlight .mo {
+ color: #2aa198;
+ }
+ .stripe .section > .force-right .highlight .sb,
+ .stripe .section > [class*=highlight] .highlight .sb {
+ color: #586e75;
+ }
+ .stripe .section > .force-right .highlight .sc,
+ .stripe .section > [class*=highlight] .highlight .sc {
+ color: #2aa198;
+ }
+ .stripe .section > .force-right .highlight .sd,
+ .stripe .section > [class*=highlight] .highlight .sd {
+ color: #cccccc;
+ }
+ .stripe .section > .force-right .highlight .s2,
+ .stripe .section > [class*=highlight] .highlight .s2 {
+ color: #2aa198;
+ }
+ .stripe .section > .force-right .highlight .se,
+ .stripe .section > [class*=highlight] .highlight .se {
+ color: #cb4b16;
+ }
+ .stripe .section > .force-right .highlight .sh,
+ .stripe .section > [class*=highlight] .highlight .sh {
+ color: #cccccc;
+ }
+ .stripe .section > .force-right .highlight .si,
+ .stripe .section > [class*=highlight] .highlight .si {
+ color: #2aa198;
+ }
+ .stripe .section > .force-right .highlight .sx,
+ .stripe .section > [class*=highlight] .highlight .sx {
+ color: #2aa198;
+ }
+ .stripe .section > .force-right .highlight .sr,
+ .stripe .section > [class*=highlight] .highlight .sr {
+ color: #dc322f;
+ }
+ .stripe .section > .force-right .highlight .s1,
+ .stripe .section > [class*=highlight] .highlight .s1 {
+ color: #2aa198;
+ }
+ .stripe .section > .force-right .highlight .ss,
+ .stripe .section > [class*=highlight] .highlight .ss {
+ color: #2aa198;
+ }
+ .stripe .section > .force-right .highlight .bp,
+ .stripe .section > [class*=highlight] .highlight .bp {
+ color: #268bd2;
+ }
+ .stripe .section > .force-right .highlight .vc,
+ .stripe .section > [class*=highlight] .highlight .vc {
+ color: #268bd2;
+ }
+ .stripe .section > .force-right .highlight .vg,
+ .stripe .section > [class*=highlight] .highlight .vg {
+ color: #268bd2;
+ }
+ .stripe .section > .force-right .highlight .vi,
+ .stripe .section > [class*=highlight] .highlight .vi {
+ color: #268bd2;
+ }
+ .stripe .section > .force-right .highlight .il,
+ .stripe .section > [class*=highlight] .highlight .il {
+ color: #2aa198;
+ }
+ .stripe .body > .section > .section {
+ border-top: 1px solid #eeeeee;
}
.stripe .section > h1,
.stripe .section > h2,
@@ -6820,7 +7214,7 @@ td.field-body > ul {
.stripe .section > h4,
.stripe .section > h5,
.stripe .section > h6 {
- background-color: rgba(255, 255, 255, 0.7);
+ max-width: 50%;
}
.stripe .section > h1,
.stripe .section > h2,
@@ -6834,27 +7228,40 @@ td.field-body > ul {
float: none;
clear: both;
}
- .stripe .bodywrapper {
- position: relative;
+ .stripe .documentwrapper {
+ overflow: auto;
}
- .stripe .bodywrapper:before {
+ .stripe .documentwrapper:before {
position: absolute;
top: 0;
bottom: 0;
left: 50%;
content: "";
- width: 0;
- border-left: 1px solid #777777;
+ width: 50%;
+ background: #333333;
}
.stripe .highlight pre {
white-space: pre-wrap;
}
+ .stripe .btn-clipboard {
+ background-color: transparent;
+ color: #eeeeee;
+ border-color: #777777;
+ border-top: none;
+ border-top-right-radius: 0;
+ }
+ .stripe .btn-clipboard.active {
+ background-color: #555555;
+ -webkit-transition: background-color 0.5s linear;
+ -o-transition: background-color 0.5s linear;
+ transition: background-color 0.5s linear;
+ }
}
.stripe .switcher {
color: white;
width: auto !important;
float: none !important;
- position: fixed;
+ position: absolute;
display: -webkit-flex;
display: flex;
-webkit-justify-content: flex-end;
@@ -6865,6 +7272,10 @@ td.field-body > ul {
margin: 0;
z-index: 5;
}
+.stripe .switcher.affix {
+ position: fixed;
+ top: 0;
+}
.stripe .switcher li {
background-color: #0f131a;
padding: 0.4em 1em;
@@ -6874,10 +7285,10 @@ td.field-body > ul {
}
.stripe .switcher li:first-child {
border-left-width: 1px;
- border-radius: 5px 0 0 5px;
+ border-radius: 0 0 0 5px;
}
.stripe .switcher li:last-child {
- border-radius: 0 5px 5px 0;
+ border-radius: 0 0 5px 0;
}
.stripe .switcher li:hover {
background-color: #222;
diff --git a/doc/_themes/odoodoc/static/style.less b/doc/_themes/odoodoc/static/style.less
index ea53cd95e85..d3703999414 100644
--- a/doc/_themes/odoodoc/static/style.less
+++ b/doc/_themes/odoodoc/static/style.less
@@ -1,5 +1,5 @@
@import "bootstrap/less/bootstrap";
-@import url(http://fonts.googleapis.com/css?family=Lato);
+@import url(https://fonts.googleapis.com/css?family=Lato);
@icon-font-path: 'fonts/';
@brand-primary: #a24689;
@@ -472,7 +472,6 @@ div.section > h2 {
*/
.highlight pre {
padding: 4px;
-
font-size: 75%;
// don't break lines within words
word-break: normal;
@@ -590,6 +589,11 @@ td.field-body {
}
}
}
+
+@background-right: @gray-dark;
+@color-right: @gray-lighter;
+@color-right-code: lighten(#000, 80%);
+@separator-right: @gray-light;
// STRIPE-STYLE PAGES
.stripe {
.section {
@@ -606,16 +610,129 @@ td.field-body {
clear: left;
}
// column 2
+ .section > .force-right {
+ padding-left: 1em;
+ padding-right: 1em;
+ }
.section > .force-right,
.section > [class*=highlight] {
float: none;
clear: none;
- margin-left: 51%;
+ margin-left: 50%;
+ width: 50%;
+ color: @color-right;
+ a {
+ color: lighten(@link-color, 30%);
+ }
+ code, .literal {
+ color: @code-bg;
+ background-color: @gray;
+ }
+ &:not(.highlight-json) .highlight {
+ border-bottom-color: @separator-right;
+ }
+ &.admonition {
+ margin-left: 51%;
+ width: 49%;
+ border-top-color: @separator-right;
+ border-bottom-color: @separator-right;
+ border-right-color: @separator-right;
+ }
+ pre {
+ // padding leads to overflow scrollbar in safari for no reason I understand,
+ // margin does not have that issue
+ padding: 0;
+ margin: 10px;
+
+ background: none;
+ border: none;
+ }
+ .highlight {
+ border-color: @gray;
+ border-style: solid;
+ border-width: 1px 0;
+ // solarized-ish from https://gist.github.com/qguv/7936275
+ color: @color-right-code;
+ background: none;
+ .lineno { color: #586e75 } /* Line Numbers */
+ .c { color: #586e75 } /* Comment */
+ .err { color: @color-right-code } /* Error */
+ .g { color: @color-right-code } /* Generic */
+ .k { color: #859900 } /* Keyword */
+ .l { color: @color-right-code } /* Literal */
+ .n { color: @color-right-code } /* Name */
+ .o { color: #859900 } /* Operator */
+ .x { color: #cb4b16 } /* Other */
+ .p { color: @color-right-code } /* Punctuation */
+ .cm { color: #586e75 } /* Comment.Multiline */
+ .cp { color: #859900 } /* Comment.Preproc */
+ .c1 { color: #586e75 } /* Comment.Single */
+ .cs { color: #859900 } /* Comment.Special */
+ .gd { color: #2aa198 } /* Generic.Deleted */
+ .ge { color: @color-right-code; font-style: italic } /* Generic.Emph */
+ .gr { color: #dc322f } /* Generic.Error */
+ .gh { color: #cb4b16 } /* Generic.Heading */
+ .gi { color: #859900 } /* Generic.Inserted */
+ .go { color: @color-right-code } /* Generic.Output */
+ .gp { color: @color-right-code } /* Generic.Prompt */
+ .gs { color: @color-right-code; font-weight: bold } /* Generic.Strong */
+ .gu { color: #cb4b16 } /* Generic.Subheading */
+ .gt { color: @color-right-code } /* Generic.Traceback */
+ .kc { color: #cb4b16 } /* Keyword.Constant */
+ .kd { color: #268bd2 } /* Keyword.Declaration */
+ .kn { color: #859900 } /* Keyword.Namespace */
+ .kp { color: #859900 } /* Keyword.Pseudo */
+ .kr { color: #268bd2 } /* Keyword.Reserved */
+ .kt { color: #dc322f } /* Keyword.Type */
+ .ld { color: @color-right-code } /* Literal.Date */
+ .m { color: #2aa198 } /* Literal.Number */
+ .s { color: #2aa198 } /* Literal.String */
+ .na { color: @color-right-code } /* Name.Attribute */
+ .nb { color: #B58900 } /* Name.Builtin */
+ .nc { color: #268bd2 } /* Name.Class */
+ .no { color: #cb4b16 } /* Name.Constant */
+ .nd { color: #268bd2 } /* Name.Decorator */
+ .ni { color: #cb4b16 } /* Name.Entity */
+ .ne { color: #cb4b16 } /* Name.Exception */
+ .nf { color: #268bd2 } /* Name.Function */
+ .nl { color: @color-right-code } /* Name.Label */
+ .nn { color: @color-right-code } /* Name.Namespace */
+ .nx { color: @color-right-code } /* Name.Other */
+ .py { color: @color-right-code } /* Name.Property */
+ .nt { color: #268bd2 } /* Name.Tag */
+ .nv { color: #268bd2 } /* Name.Variable */
+ .ow { color: #859900 } /* Operator.Word */
+ .w { color: @color-right-code } /* Text.Whitespace */
+ .mf { color: #2aa198 } /* Literal.Number.Float */
+ .mh { color: #2aa198 } /* Literal.Number.Hex */
+ .mi { color: #2aa198 } /* Literal.Number.Integer */
+ .mo { color: #2aa198 } /* Literal.Number.Oct */
+ .sb { color: #586e75 } /* Literal.String.Backtick */
+ .sc { color: #2aa198 } /* Literal.String.Char */
+ .sd { color: @color-right-code } /* Literal.String.Doc */
+ .s2 { color: #2aa198 } /* Literal.String.Double */
+ .se { color: #cb4b16 } /* Literal.String.Escape */
+ .sh { color: @color-right-code } /* Literal.String.Heredoc */
+ .si { color: #2aa198 } /* Literal.String.Interpol */
+ .sx { color: #2aa198 } /* Literal.String.Other */
+ .sr { color: #dc322f } /* Literal.String.Regex */
+ .s1 { color: #2aa198 } /* Literal.String.Single */
+ .ss { color: #2aa198 } /* Literal.String.Symbol */
+ .bp { color: #268bd2 } /* Name.Builtin.Pseudo */
+ .vc { color: #268bd2 } /* Name.Variable.Class */
+ .vg { color: #268bd2 } /* Name.Variable.Global */
+ .vi { color: #268bd2 } /* Name.Variable.Instance */
+ .il { color: #2aa198 } /* Literal.Number.Integer.Long */
+ }
+ }
+ // separator above H2
+ .body > .section > .section {
+ border-top: 1px solid @color-right;
}
- // fullwidth elements
.section > h1, .section > h2, .section > h3, .section > h4, .section > h5,
.section > h6 {
- background-color: fadeout(@body-bg, 30%);
+ max-width: 50%;
+
}
.section > h1, .section > h2, .section > h3, .section > h4, .section > h5,
.section > h6, .section > .section {
@@ -625,8 +742,8 @@ td.field-body {
clear: both;
}
- .bodywrapper {
- position: relative;
+ .documentwrapper {
+ overflow: auto;
// middle separator
&:before {
position: absolute;
@@ -634,8 +751,8 @@ td.field-body {
bottom: 0;
left: 50%;
content: "";
- width: 0;
- border-left: 1px solid @gray-light;
+ width: 50%;
+ background: @background-right;
}
}
@@ -643,6 +760,19 @@ td.field-body {
// but allow additional line-breaks between "words" to avoid overflow
white-space: pre-wrap;
}
+
+ .btn-clipboard {
+ background-color: transparent;
+ color: @color-right;
+ border-color: @separator-right;
+ border-top: none;
+ border-top-right-radius: 0;
+
+ &.active {
+ background-color: @gray;
+ .transition(background-color .5s linear);
+ }
+ }
}
.switcher {
@@ -650,7 +780,7 @@ td.field-body {
width: auto !important;
float: none !important;
- position: fixed;
+ position: absolute;
display: -webkit-flex;
display: flex;
-webkit-justify-content: flex-end;
@@ -661,6 +791,10 @@ td.field-body {
padding: 0;
margin: 0;
z-index: 5;
+ &.affix {
+ position: fixed;
+ top: 0;
+ }
li {
background-color: #0f131a;
@@ -670,10 +804,10 @@ td.field-body {
cursor: pointer;
&:first-child {
border-left-width: 1px;
- border-radius: 5px 0 0 5px;
+ border-radius: 0 0 0 5px;
}
&:last-child {
- border-radius: 0 5px 5px 0;
+ border-radius: 0 0 5px 0;
}
&:hover {
background-color: #222;
diff --git a/doc/api_integration.rst b/doc/api_integration.rst
index 4a9835fa743..6ee6871ff9f 100644
--- a/doc/api_integration.rst
+++ b/doc/api_integration.rst
@@ -19,8 +19,8 @@ easily available over XML-RPC_ and accessible from a variety of languages.
into every call. Session would allow db to be stored as well
These issues are especially visible in Java, somewhat less so in PHP
-Connection and authentication
-=============================
+Connection
+==========
.. kinda gross because it duplicates existing bits
@@ -61,11 +61,10 @@ Connection and authentication
.. code-block:: java
final XmlRpcClient client = new XmlRpcClient();
-
final XmlRpcClientConfigImpl start_config = new XmlRpcClientConfigImpl();
start_config.setServerURL(new URL("https://demo.odoo.com/start"));
final Map<String, String> info = (Map<String, String>)client.execute(
- start_config, "start", Collections.emptyList());
+ start_config, "start", emptyList());
final String url = info.get("host"),
db = info.get("database"),
@@ -77,7 +76,7 @@ Connection and authentication
int uid = (int)client.execute(
common_config, "authenticate", Arrays.asList(
- db, username, password, Collections.emptyMap()));
+ db, username, password, emptyMap()));
final XmlRpcClient models = new XmlRpcClient() {{
setConfig(new XmlRpcClientConfigImpl() {{
@@ -154,7 +153,7 @@ database:
final XmlRpcClientConfigImpl start_config = new XmlRpcClientConfigImpl();
start_config.setServerURL(new URL("https://demo.odoo.com/start"));
final Map<String, String> info = (Map<String, String>)client.execute(
- start_config, "start", Collections.emptyList());
+ start_config, "start", emptyList());
final String url = info.get("host"),
db = info.get("database"),
@@ -183,6 +182,9 @@ database:
These examples use the `Apache XML-RPC library
<https://ws.apache.org/xmlrpc/>`_
+ The examples do not include imports as these imports couldn't be
+ pasted in the code.
+
Logging in
----------
@@ -217,8 +219,9 @@ the login.
.. code-block:: java
final XmlRpcClientConfigImpl common_config = new XmlRpcClientConfigImpl();
- common_config.setServerURL(new URL(String.format("%s/xmlrpc/2/common", url)));
- client.execute(common_config, "version", Collections.emptyList());
+ common_config.setServerURL(
+ new URL(String.format("%s/xmlrpc/2/common", url)));
+ client.execute(common_config, "version", emptyList());
.. code-block:: json
@@ -246,8 +249,8 @@ the login.
.. code-block:: java
int uid = (int)client.execute(
- common_config, "authenticate", Arrays.asList(
- db, username, password, Collections.emptyMap()));
+ common_config, "authenticate", asList(
+ db, username, password, emptyMap()));
Calling methods
===============
@@ -302,10 +305,10 @@ rather than true/error):
setServerURL(new URL(String.format("%s/xmlrpc/2/object", url)));
}});
}};
- models.execute("execute_kw", Arrays.asList(
+ models.execute("execute_kw", asList(
db, uid, password,
"res.partner", "check_access_rights",
- Arrays.asList("read"),
+ asList("read"),
new HashMap() {{ put("raise_exception", false); }}
));
@@ -341,20 +344,19 @@ companies for instance:
.. code-block:: php
- $domain = array(array('is_company', '=', true),
- array('customer', '=', true));
$models->execute_kw($db, $uid, $password,
- 'res.partner', 'search', array($domain));
+ 'res.partner', 'search', array(
+ array(array('is_company', '=', true),
+ array('customer', '=', true))));
.. code-block:: java
- final List domain = Arrays.asList(
- Arrays.asList("is_company", "=", true),
- Arrays.asList("customer", "=", true));
- Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
+ asList((Object[])models.execute("execute_kw", asList(
db, uid, password,
"res.partner", "search",
- Arrays.asList(domain)
+ asList(asList(
+ asList("is_company", "=", true),
+ asList("customer", "=", true)))
)));
.. code-block:: json
@@ -388,15 +390,18 @@ available to only retrieve a subset of all matched records.
$models->execute_kw($db, $uid, $password,
'res.partner', 'search',
- array($domain),
+ array(array(array('is_company', '=', true),
+ array('customer', '=', true))),
array('offset'=>10, 'limit'=>5));
.. code-block:: java
- Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
+ asList((Object[])models.execute("execute_kw", asList(
db, uid, password,
"res.partner", "search",
- Arrays.asList(domain),
+ asList(asList(
+ asList("is_company", "=", true),
+ asList("customer", "=", true))),
new HashMap() {{ put("offset", 10); put("limit", 5); }}
)));
@@ -431,14 +436,17 @@ only the number of records matching the query. It takes the same
$models->execute_kw($db, $uid, $password,
'res.partner', 'search_count',
- array($domain));
+ array(array(array('is_company', '=', true),
+ array('customer', '=', true))));
.. code-block:: java
- (Integer)models.execute("execute_kw", Arrays.asList(
+ (Integer)models.execute("execute_kw", asList(
db, uid, password,
"res.partner", "search_count",
- Arrays.asList(domain)
+ asList(asList(
+ asList("is_company", "=", true),
+ asList("customer", "=", true)))
));
.. code-block:: json
@@ -488,7 +496,8 @@ which tends to be a huge amount.
$ids = $models->execute_kw($db, $uid, $password,
'res.partner', 'search',
- array($domain),
+ array(array(array('is_company', '=', true),
+ array('customer', '=', true))),
array('limit'=>1));
$records = $models->execute_kw($db, $uid, $password,
'res.partner', 'read', array($ids));
@@ -497,17 +506,19 @@ which tends to be a huge amount.
.. code-block:: java
- final List ids = Arrays.asList((Object[])models.execute(
- "execute_kw", Arrays.asList(
+ final List ids = asList((Object[])models.execute(
+ "execute_kw", asList(
db, uid, password,
"res.partner", "search",
- Arrays.asList(domain),
+ asList(asList(
+ asList("is_company", "=", true),
+ asList("customer", "=", true))),
new HashMap() {{ put("limit", 1); }})));
final Map record = (Map)((Object[])models.execute(
- "execute_kw", Arrays.asList(
+ "execute_kw", asList(
db, uid, password,
"res.partner", "read",
- Arrays.asList(ids)
+ asList(ids)
)
))[0];
// count the number of fields fetched by default
@@ -542,12 +553,12 @@ Conversedly, picking only three fields deemed interesting.
.. code-block:: java
- Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
+ asList((Object[])models.execute("execute_kw", asList(
db, uid, password,
"res.partner", "read",
- Arrays.asList(ids),
+ asList(ids),
new HashMap() {{
- put("fields", Arrays.asList("name", "country_id", "comment"));
+ put("fields", asList("name", "country_id", "comment"));
}}
)));
@@ -574,53 +585,32 @@ updating a record):
.. code-block:: python
- fields = models.execute_kw(db, uid, password, 'res.partner', 'fields_get', [])
- # filter keys of field attributes for display
- {field: {
- k: v for k, v in attributes.iteritems()
- if k in ['string', 'help', 'type']
- }
- for field, attributes in fields.iteritems()}
+ models.execute_kw(
+ db, uid, password, 'res.partner', 'fields_get',
+ [], {'attributes': ['string', 'help', 'type']})
.. code-block:: ruby
- fields = models.execute_kw(db, uid, password, 'res.partner', 'fields_get', [])
- # filter keys of field attributes for display
- fields.each {|k, v|
- fields[k] = v.keep_if {|kk, vv| %w(string help type).include? kk}
- }
+ models.execute_kw(
+ db, uid, password, 'res.partner', 'fields_get',
+ [], {attributes: %w(string help type)})
.. code-block:: php
- $fields_full = $models->execute_kw($db, $uid, $password,
- 'res.partner', 'fields_get', array());
- // filter keys of field attributes for display
- $allowed = array_flip(array('string', 'help', 'type'));
- $fields = array();
- foreach($fields_full as $field => $attributes) {
- $fields[$field] = array_intersect_key($attributes, $allowed);
- }
+ $models->execute_kw($db, $uid, $password,
+ 'res.partner', 'fields_get',
+ array(), array('attributes' => array('string', 'help', 'type')));
.. code-block:: java
- final Map<String, Map<String, Object>> fields =
- (Map<String, Map<String, Object>>)models.execute("execute_kw", Arrays.asList(
- db, uid, password,
- "res.partner", "fields_get",
- Collections.emptyList()));
- // filter keys of field attributes for display
- final List<String> allowed = Arrays.asList("string", "help", "type");
- new HashMap<String, Map<String, Object>>() {{
- for(Entry<String, Map<String, Object>> item: fields.entrySet()) {
- put(item.getKey(), new HashMap<String, Object>() {{
- for(Entry<String, Object> it: item.getValue().entrySet()) {
- if (allowed.contains(it.getKey())) {
- put(it.getKey(), it.getValue());
- }
- }
- }});
- }
- }};
+ (Map<String, Map<String, Object>>)models.execute("execute_kw", asList(
+ db, uid, password,
+ "res.partner", "fields_get",
+ emptyList(),
+ new HashMap() {{
+ put("attributes", asList("string", "help", "type"));
+ }}
+ ));
.. code-block:: json
@@ -668,10 +658,11 @@ Because that is a very common task, Odoo provides a
:meth:`~openerp.models.Model.search_read` shortcut which as its name notes is
equivalent to a :meth:`~openerp.models.Model.search` followed by a
:meth:`~openerp.models.Model.read`, but avoids having to perform two requests
-and keep ids around. Its arguments are similar to
-:meth:`~openerp.models.Model.search`'s, but it can also take a list of
-``fields`` (like :meth:`~openerp.models.Model.read`, if that list is not
-provided it'll fetch all fields of matched records):
+and keep ids around.
+
+Its arguments are similar to :meth:`~openerp.models.Model.search`'s, but it
+can also take a list of ``fields`` (like :meth:`~openerp.models.Model.read`,
+if that list is not provided it'll fetch all fields of matched records):
.. rst-class:: switchable
@@ -693,17 +684,20 @@ provided it'll fetch all fields of matched records):
$models->execute_kw($db, $uid, $password,
'res.partner', 'search_read',
- array($domain),
+ array(array(array('is_company', '=', true),
+ array('customer', '=', true))),
array('fields'=>array('name', 'country_id', 'comment'), 'limit'=>5));
.. code-block:: java
- Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
+ asList((Object[])models.execute("execute_kw", asList(
db, uid, password,
"res.partner", "search_read",
- Arrays.asList(domain),
+ asList(asList(
+ asList("is_company", "=", true),
+ asList("customer", "=", true))),
new HashMap() {{
- put("fields", Arrays.asList("name", "country_id", "comment"));
+ put("fields", asList("name", "country_id", "comment"));
put("limit", 5);
}}
)));
@@ -747,6 +741,13 @@ provided it'll fetch all fields of matched records):
Create records
--------------
+Records of a model are created using :meth:`~openerp.models.Model.create`. The
+method will create a single record and return its database identifier.
+
+:meth:`~openerp.models.Model.create` takes a mapping of fields to values, used
+to initialize the record. For any field which has a default value and is not
+set through the mapping argument, the default value will be used.
+
.. rst-class:: switchable
.. code-block:: python
@@ -769,19 +770,40 @@ Create records
.. code-block:: java
- final Integer id = (Integer)models.execute("execute_kw", Arrays.asList(
+ final Integer id = (Integer)models.execute("execute_kw", asList(
db, uid, password,
"res.partner", "create",
- Arrays.asList(new HashMap() {{ put("name", "New Partner"); }})
+ asList(new HashMap() {{ put("name", "New Partner"); }})
));
.. code-block:: json
78
+.. warning::
+
+ while most value types are what would be expected (integer for
+ :class:`~openerp.fields.Integer`, string for :class:`~openerp.fields.Char`
+ or :class:`~openerp.fields.Text`),
+
+ * :class:`~openerp.fields.Date`, :class:`~openerp.fields.Datetime` and
+ :class:`~openerp.fields.Binary` fields use string values
+ * :class:`~openerp.fields.One2many` and :class:`~openerp.fields.Many2many`
+ use a special command protocol detailed in :meth:`the documentation to
+ the write method <openerp.models.Model.write>`.
+
Update records
--------------
+Reccords can be updated using :meth:`~openerp.models.Model.write`, it takes
+a list of records to update and a mapping of updated fields to values similar
+to :meth:`~openerp.models.Model.create`.
+
+Multiple records can be updated simultanously, but they will all get the same
+values for the fields being set. It is not currently possible to perform
+"computed" updates (where the value being set depends on an existing value of
+a record).
+
.. rst-class:: switchable
.. code-block:: python
@@ -810,19 +832,19 @@ Update records
.. code-block:: java
- models.execute("execute_kw", Arrays.asList(
+ models.execute("execute_kw", asList(
db, uid, password,
"res.partner", "write",
- Arrays.asList(
- Arrays.asList(id),
+ asList(
+ asList(id),
new HashMap() {{ put("name", "Newer Partner"); }}
)
));
// get record name after having changed it
- Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
+ asList((Object[])models.execute("execute_kw", asList(
db, uid, password,
"res.partner", "name_get",
- Arrays.asList(Arrays.asList(id))
+ asList(asList(id))
)));
.. code-block:: json
@@ -832,6 +854,9 @@ Update records
Delete records
--------------
+Records can be deleted in bulk by providing the ids of all records to remove
+to :meth:`~openerp.models.Model.unlink`.
+
.. rst-class:: switchable
.. code-block:: python
@@ -860,20 +885,561 @@ Delete records
.. code-block:: java
- models.execute("execute_kw", Arrays.asList(
+ models.execute("execute_kw", asList(
db, uid, password,
"res.partner", "unlink",
- Arrays.asList(Arrays.asList(id))));
+ asList(asList(id))));
// check if the deleted record is still in the database
- Arrays.asList((Object[])models.execute("execute_kw", Arrays.asList(
+ asList((Object[])models.execute("execute_kw", asList(
db, uid, password,
"res.partner", "search",
- Arrays.asList(Arrays.asList(Arrays.asList("id", "=", 78)))
+ asList(asList(asList("id", "=", 78)))
)));
.. code-block:: json
[]
+Inspection and introspection
+----------------------------
+
+.. todo:: ``get_external_id`` is kinda crap and may not return an id: it just
+ gets a random existing xid but won't generate one if there is no
+ xid currently associated with the record. And operating with xids
+ isn't exactly fun in RPC.
+
+While we previously used :meth:`~openerp.models.Model.fields_get` to query a
+model's and have been using an arbitrary model from the start, Odoo stores
+most model metadata inside a few meta-models which allow both querying the
+system and altering models and fields (with some limitations) on the fly over
+XML-RPC.
+
+.. _reference/webservice/inspection/models:
+
+``ir.model``
+''''''''''''
+
+Provides informations about Odoo models themselves via its various fields
+
+``name``
+ a human-readable description of the model
+``model``
+ the name of each model in the system
+``state``
+ whether the model was generated in Python code (``base``) or by creating
+ an ``ir.model`` record (``manual``)
+``field_id``
+ list of the model's fields through a :class:`~openerp.fields.One2many` to
+ :ref:`reference/webservice/inspection/fields`
+``view_ids``
+ :class:`~openerp.fields.One2many` to the :ref:`reference/views` defined
+ for the model
+``access_ids``
+ :class:`~openerp.fields.One2many` relation to the
+ :ref:`reference/security/acl` set on the model
+
+``ir.model`` can be used to
+
+* query the system for installed models (as a precondition to operations
+ on the model or to explore the system's content)
+* get information about a specific model (generally by listing the fields
+ associated with it)
+* create new models dynamically over RPC
+
+.. warning::
+
+ * "custom" model names must start with ``x_``
+ * the ``state`` must be provided and ``manual``, otherwise the model will
+ not be loaded
+ * it is not possible to add new *methods* to a custom model, only fields
+
+.. rst-class:: force-right
+
+ a custom model will initially contain only the "built-in" fields available
+ on all models:
+
+.. rst-class:: switchable
+
+ .. code-block:: python
+
+ models.execute_kw(db, uid, password, 'ir.model', 'create', [{
+ 'name': "Custom Model",
+ 'model': "x_custom_model",
+ 'state': 'manual',
+ }])
+ models.execute_kw(
+ db, uid, password, 'x_custom_model', 'fields_get',
+ [], {'attributes': ['string', 'help', 'type']})
+
+ .. code-block:: php
+
+ $models->execute_kw(
+ $db, $uid, $password,
+ 'ir.model', 'create', array(array(
+ 'name' => "Custom Model",
+ 'model' => 'x_custom_model',
+ 'state' => 'manual'
+ ))
+ );
+ $models->execute_kw(
+ $db, $uid, $password,
+ 'x_custom_model', 'fields_get',
+ array(),
+ array('attributes' => array('string', 'help', 'type'))
+ );
+
+ .. code-block:: ruby
+
+ models.execute_kw(
+ db, uid, password,
+ 'ir.model', 'create', [{
+ name: "Custom Model",
+ model: 'x_custom_model',
+ state: 'manual'
+ }])
+ fields = models.execute_kw(
+ db, uid, password, 'x_custom_model', 'fields_get',
+ [], {attributes: %w(string help type)})
+
+ .. code-block:: java
+
+ models.execute(
+ "execute_kw", asList(
+ db, uid, password,
+ "ir.model", "create",
+ asList(new HashMap<String, Object>() {{
+ put("name", "Custom Model");
+ put("model", "x_custom_model");
+ put("state", "manual");
+ }})
+ ));
+ final Object fields = models.execute(
+ "execute_kw", asList(
+ db, uid, password,
+ "x_custom_model", "fields_get",
+ emptyList(),
+ new HashMap<String, Object> () {{
+ put("attributes", asList(
+ "string",
+ "help",
+ "type"));
+ }}
+ ));
+
+.. code-block:: json
+
+ {
+ "create_uid": {
+ "type": "many2one",
+ "string": "Created by"
+ },
+ "create_date": {
+ "type": "datetime",
+ "string": "Created on"
+ },
+ "__last_update": {
+ "type": "datetime",
+ "string": "Last Modified on"
+ },
+ "write_uid": {
+ "type": "many2one",
+ "string": "Last Updated by"
+ },
+ "write_date": {
+ "type": "datetime",
+ "string": "Last Updated on"
+ },
+ "display_name": {
+ "type": "char",
+ "string": "Display Name"
+ },
+ "id": {
+ "type": "integer",
+ "string": "Id"
+ }
+ }
+
+.. _reference/webservice/inspection/fields:
+
+``ir.model.fields``
+'''''''''''''''''''
+
+Provides informations about the fields of Odoo models and allows adding
+custom fields without using Python code
+
+``model_id``
+ :class:`~openerp.fields.Many2one` to
+ :ref:`reference/webservice/inspection/models` to which the field belongs
+``name``
+ the field's technical name (used in ``read`` or ``write``)
+``field_description``
+ the field's user-readable label (e.g. ``string`` in ``fields_get``)
+``ttype``
+ the :ref:`type <reference/orm/fields>` of field to create
+``state``
+ whether the field was created via Python code (``base``) or via
+ ``ir.model.fields`` (``manual``)
+``required``, ``readonly``, ``translate``
+ enables the corresponding flag on the field
+``groups``
+ :ref:`field-level access control <reference/security/fields>`, a
+ :class:`~openerp.fields.Many2many` to ``res.groups``
+``selection``, ``size``, ``on_delete``, ``relation``, ``relation_field``, ``domain``
+ type-specific properties and customizations, see :ref:`the fields
+ documentation <reference/orm/fields>` for details
+
+Like custom models, only new fields created with ``state="manual"`` are
+activated as actual fields on the model.
+
+.. warning:: computed fields can not be added via ``ir.model.fields``, some
+ field meta-information (defaults, onchange) can not be set either
+
+.. todo:: maybe new-API fields could store constant ``default`` in a new
+ column, maybe JSON-encoded?
+
+.. rst-class:: switchable
+
+ .. code-block:: python
+
+ id = models.execute_kw(db, uid, password, 'ir.model', 'create', [{
+ 'name': "Custom Model",
+ 'model': "x_custom",
+ 'state': 'manual',
+ }])
+ models.execute_kw(
+ db, uid, password,
+ 'ir.model.fields', 'create', [{
+ 'model_id': id,
+ 'name': 'x_name',
+ 'ttype': 'char',
+ 'state': 'manual',
+ 'required': True,
+ }])
+ record_id = models.execute_kw(
+ db, uid, password,
+ 'x_custom', 'create', [{
+ 'x_name': "test record",
+ }])
+ models.execute_kw(db, uid, password, 'x_custom', 'read', [[record_id]])
+
+ .. code-block:: php
+
+ $id = $models->execute_kw(
+ $db, $uid, $password,
+ 'ir.model', 'create', array(array(
+ 'name' => "Custom Model",
+ 'model' => 'x_custom',
+ 'state' => 'manual'
+ ))
+ );
+ $models->execute_kw(
+ $db, $uid, $password,
+ 'ir.model.fields', 'create', array(array(
+ 'model_id' => $id,
+ 'name' => 'x_name',
+ 'ttype' => 'char',
+ 'state' => 'manual',
+ 'required' => true
+ ))
+ );
+ $record_id = $models->execute_kw(
+ $db, $uid, $password,
+ 'x_custom', 'create', array(array(
+ 'x_name' => "test record"
+ ))
+ );
+ $models->execute_kw(
+ $db, $uid, $password,
+ 'x_custom', 'read',
+ array(array($record_id)));
+
+ .. code-block:: ruby
+
+ id = models.execute_kw(
+ db, uid, password,
+ 'ir.model', 'create', [{
+ name: "Custom Model",
+ model: "x_custom",
+ state: 'manual'
+ }])
+ models.execute_kw(
+ db, uid, password,
+ 'ir.model.fields', 'create', [{
+ model_id: id,
+ name: "x_name",
+ ttype: "char",
+ state: "manual",
+ required: true
+ }])
+ record_id = models.execute_kw(
+ db, uid, password,
+ 'x_custom', 'create', [{
+ x_name: "test record"
+ }])
+ models.execute_kw(
+ db, uid, password,
+ 'x_custom', 'read', [[record_id]])
+
+ .. code-block:: java
+
+ final Integer id = (Integer)models.execute(
+ "execute_kw", asList(
+ db, uid, password,
+ "ir.model", "create",
+ asList(new HashMap<String, Object>() {{
+ put("name", "Custom Model");
+ put("model", "x_custom");
+ put("state", "manual");
+ }})
+ ));
+ models.execute(
+ "execute_kw", asList(
+ db, uid, password,
+ "ir.model.fields", "create",
+ asList(new HashMap<String, Object>() {{
+ put("model_id", id);
+ put("name", "x_name");
+ put("ttype", "char");
+ put("state", "manual");
+ put("required", true);
+ }})
+ ));
+ final Integer record_id = (Integer)models.execute(
+ "execute_kw", asList(
+ db, uid, password,
+ "x_custom", "create",
+ asList(new HashMap<String, Object>() {{
+ put("x_name", "test record");
+ }})
+ ));
+
+ client.execute(
+ "execute_kw", asList(
+ db, uid, password,
+ "x_custom", "read",
+ asList(asList(record_id))
+ ));
+
+.. code-block:: json
+
+ [
+ {
+ "create_uid": [1, "Administrator"],
+ "x_name": "test record",
+ "__last_update": "2014-11-12 16:32:13",
+ "write_uid": [1, "Administrator"],
+ "write_date": "2014-11-12 16:32:13",
+ "create_date": "2014-11-12 16:32:13",
+ "id": 1,
+ "display_name": "test record"
+ }
+ ]
+
+Workflow manipulations
+----------------------
+
+:ref:`reference/workflows` can be moved along by sending them *signals*.
+Instead of using the top-level ``execute_kw``, signals are sent using
+``exec_workflow``.
+
+Signals are sent to a specific record, and possibly trigger a transition on
+the workflow instance associated with the record.
+
+.. warning:: requires that the ``account`` module be installed
+ :class: force-right
+
+.. rst-class:: switchable
+
+ .. code-block:: python
+
+ client = models.execute_kw(
+ db, uid, password,
+ 'res.partner', 'search_read',
+ [[('customer', '=', True)]],
+ {'limit': 1, 'fields': [
+ 'property_account_receivable',
+ 'property_payment_term',
+ 'property_account_position']
+ })[0]
+ invoice_id = models.execute_kw(
+ db, uid, password,
+ 'account.invoice', 'create', [{
+ 'partner_id': client['id'],
+ 'account_id': client['property_account_receivable'][0],
+ 'invoice_line': [(0, False, {'name': "AAA"})]
+ }])
+
+ models.exec_workflow(
+ db, uid, password, 'account.invoice', 'invoice_open', invoice_id)
+
+ .. code-block:: php
+
+ $client = $models->execute_kw(
+ $db, $uid, $password,
+ 'res.partner', 'search_read',
+ array(array(array('customer', '=', true))),
+ array(
+ 'limit' => 1,
+ 'fields' => array(
+ 'property_account_receivable',
+ 'property_payment_term',
+ 'property_account_position'
+ )))[0];
+ $invoice_id = $models->execute_kw(
+ $db, $uid, $password,
+ 'account.invoice', 'create', array(array(
+ 'partner_id' => $client['id'],
+ 'account_id' => $client['property_account_receivable'][0],
+ 'invoice_line' => array(array(0, false, array('name' => "AAA")))
+ )));
+
+ $models->exec_workflow(
+ $db, $uid, $password,
+ 'account.invoice', 'invoice_open',
+ $invoice_id);
+
+ .. code-block:: ruby
+
+ client = models.execute_kw(
+ db, uid, password,
+ 'res.partner', 'search_read',
+ [[['customer', '=', true]]],
+ {limit: 1, fields: %w(property_account_receivable property_payment_term property_account_position)}
+ )[0]
+ invoice_id = models.execute_kw(
+ db, uid, password,
+ 'account.invoice', 'create', [{
+ partner_id: client['id'],
+ account_id: client['property_account_receivable'][0],
+ invoice_line: [[0, false, {name: "AAA"}]]
+ }])
+
+ models.exec_workflow(
+ db, uid, password,
+ 'account.invoice', 'invoice_open', invoice_id)
+
+ .. code-block:: java
+
+ final Map<String, Object> c = (Map<String, Object>)
+ ((Object[])models.execute("execute_kw", asList(
+ db, uid, password,
+ "res.partner", "search_read",
+ asList(
+ asList(
+ asList("customer", "=", true))),
+ new HashMap<String, Object>() {{
+ put("limit", 1);
+ put("fields", asList(
+ "property_account_receivable",
+ "property_payment_term",
+ "property_account_position"
+ ));
+ }}
+ )))[0];
+ final Integer invoice_id = (Integer)models.execute(
+ "execute_kw", asList(
+ db, uid, password,
+ "account.invoice", "create",
+ asList(new HashMap<String, Object>() {{
+ put("partner_id", c.get("id"));
+ put("account_id", ((Object[])c.get("property_account_receivable"))[0]);
+ put("invoice_line", asList(
+ asList(0, false, new HashMap<String, Object>() {{
+ put("name", "AAA");
+ }})
+ ));
+ }})
+ ));
+
+ models.execute(
+ "exec_workflow", asList(
+ db, uid, password,
+ "account.invoice", "invoice_open", invoice_id));
+
+Report printing
+---------------
+
+Available reports can be listed by searching the ``ir.actions.report.xml``
+model, fields of interest being
+
+``model``
+ the model on which the report applies, can be used to look for available
+ reports on a specific model
+``name``
+ human-readable report name
+``report_name``
+ the technical name of the report, used to print it
+
+Reports can be printed over RPC with the following information:
+
+* the name of the report (``report_name``)
+* the ids of the records to include in the report
+
+.. rst-class:: switchable
+
+ .. code-block:: python
+
+ invoice_ids = models.execute_kw(
+ db, uid, password, 'account.invoice', 'search',
+ [[('type', '=', 'out_invoice'), ('state', '=', 'open')]])
+ report = xmlrpclib.ServerProxy('{}/xmlrpc/2/report'.format(url))
+ result = report.render_report(
+ db, uid, password, 'account.report_invoice', invoice_ids)
+ report_data = result['result'].decode('base64')
+
+ .. code-block:: php
+
+ $invoice_ids = $models->execute_kw(
+ $db, $uid, $password,
+ 'account.invoice', 'search',
+ array(array(array('type', '=', 'out_invoice'),
+ array('state', '=', 'open'))));
+ $report = ripcord::client("$url/xmlrpc/2/report");
+ $result = $report->render_report(
+ $db, $uid, $password,
+ 'account.report_invoice', $invoice_ids);
+ $report_data = base64_decode($result['result']);
+
+ .. code-block:: ruby
+
+ require 'base64'
+ invoice_ids = models.execute_kw(
+ db, uid, password,
+ 'account.invoice', 'search',
+ [[['type', '=', 'out_invoice'], ['state', '=', 'open']]])
+ report = XMLRPC::Client.new2("#{url}/xmlrpc/2/report").proxy
+ result = report.render_report(
+ db, uid, password,
+ 'account.report_invoice', invoice_ids)
+ report_data = Base64.decode64(result['result'])
+
+ .. code-block:: java
+
+ final Object[] invoice_ids = (Object[])models.execute(
+ "execute_kw", asList(
+ db, uid, password,
+ "account.invoice", "search",
+ asList(asList(
+ asList("type", "=", "out_invoice"),
+ asList("state", "=", "open")))
+ ));
+ final XmlRpcClientConfigImpl report_config = new XmlRpcClientConfigImpl();
+ report_config.setServerURL(
+ new URL(String.format("%s/xmlrpc/2/report", url)));
+ final Map<String, Object> result = (Map<String, Object>)client.execute(
+ report_config, "render_report", asList(
+ db, uid, password,
+ "account.report_invoice",
+ invoice_ids));
+ final byte[] report_data = DatatypeConverter.parseBase64Binary(
+ (String)result.get("result"));
+
+.. note::
+ :class: force-right
+
+ the report is sent as PDF binary data encoded in base64_, it must be
+ decoded and may need to be saved to disk before use
+
.. _PostgreSQL: http://www.postgresql.org
.. _XML-RPC: http://en.wikipedia.org/wiki/XML-RPC
+.. _base64: http://en.wikipedia.org/wiki/Base64
diff --git a/doc/conf.py b/doc/conf.py
index 086e3ec4d95..b4751fb5db9 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -46,7 +46,7 @@ master_doc = 'index'
# General information about the project.
project = u'odoo'
-copyright = u'OpenERP S.A.'
+copyright = u'Odoo 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
diff --git a/doc/howtos/backend.rst b/doc/howtos/backend.rst
index c9715893614..b72540cf2d3 100644
--- a/doc/howtos/backend.rst
+++ b/doc/howtos/backend.rst
@@ -1,8 +1,8 @@
.. queue:: backend/series
-=======
-Backend
-=======
+=================
+Building a Module
+=================
Start/Stop the Odoo server
==========================
@@ -1725,12 +1725,12 @@ server with the library ``xmlrpclib``::
print "Logged in as %s (uid:%d)" % (USER,uid)
call = functools.partial(
- xmlcprlib.ServerProxy(ROOT + 'object').execute,
+ xmlrpclib.ServerProxy(ROOT + 'object').execute,
DB, uid, PASS)
# 2. Read the sessions
sessions = call('openacademy.session','search_read', [], ['name','seats'])
- for session in sessions :
+ for session in sessions:
print "Session %s (%s seats)" % (session['name'], session['seats'])
# 3.create a new session
session_id = call('openacademy.session', 'create', {
diff --git a/doc/howtos/themes.rst b/doc/howtos/themes.rst
index 9044c47587e..35cb43989aa 100644
--- a/doc/howtos/themes.rst
+++ b/doc/howtos/themes.rst
@@ -1,7 +1,7 @@
.. highlight:: xml
===============
-Creating themes
+Building Themes
===============
Basic set up
@@ -56,7 +56,7 @@ Refresh the page and feel the hit.
Editing content on a page
'''''''''''''''''''''''''
-You can now add you content! You should always use the Bootstrap structure as
+You can now add your content! You should always use the Bootstrap structure as
below::
<template id="website.hello" name="Homepage" page="True">
diff --git a/doc/howtos/web.rst b/doc/howtos/web.rst
index 98cec4a5d3f..384297dc6a9 100644
--- a/doc/howtos/web.rst
+++ b/doc/howtos/web.rst
@@ -1,6 +1,6 @@
-==========
-Web Client
-==========
+=============================
+Building Interface Extensions
+=============================
.. highlight:: javascript
diff --git a/doc/howtos/website.rst b/doc/howtos/website.rst
index 62c28f53de9..2187956abaf 100644
--- a/doc/howtos/website.rst
+++ b/doc/howtos/website.rst
@@ -1,7 +1,7 @@
.. queue:: website/series
==================
-Building a website
+Building a Website
==================
.. warning::
@@ -457,7 +457,7 @@ though they may have to be looked for.
* to extend a model in-place, it's :attr:`inherited
<openerp.models.Model._inherit>` without giving it a new
:attr:`~openerp.models.Model._name`
- * ``product.template`` already uses the discussions system, so we don't
+ * ``product.template`` already uses the discussions system, so we
can remove it from our extension model
* we're creating our courses as *published* by default so they can be
seen without having to log in
diff --git a/doc/index.rst b/doc/index.rst
index 56f58b6aa0d..45394cdfd58 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -1,9 +1,7 @@
============================
-odoo developer documentation
+Odoo Technical Documentation
============================
-Welcome to the Odoo developer documentation.
-
.. todo:: what's the documentation's license?
.. rst-class:: index-tree
diff --git a/doc/reference.rst b/doc/reference.rst
index 8a09e86cbf7..0d17f5df7c7 100644
--- a/doc/reference.rst
+++ b/doc/reference.rst
@@ -18,6 +18,7 @@ Reference
reference/qweb
reference/javascript
+ reference/translations
reference/reports
reference/workflows
reference/cdn
diff --git a/doc/reference/cmdline.rst b/doc/reference/cmdline.rst
index 1ec7d96ae9d..e0713410981 100644
--- a/doc/reference/cmdline.rst
+++ b/doc/reference/cmdline.rst
@@ -54,6 +54,12 @@ Running the server
runs tests after installing modules
+.. option:: --debug
+
+ when an unexpected error is raised (not a warning or an access error),
+ automatically starts :mod:`python:pdb` before logging and returning the
+ error
+
.. _reference/cmdline/scaffold:
Scaffolding
diff --git a/doc/reference/security.rst b/doc/reference/security.rst
index 72742f0639c..05ca9079fb7 100644
--- a/doc/reference/security.rst
+++ b/doc/reference/security.rst
@@ -71,6 +71,8 @@ This means the first *group rule* restricts access, but any further
although access rules do
+.. _reference/security/fields:
+
Field Access
============
diff --git a/doc/reference/translations.rst b/doc/reference/translations.rst
new file mode 100644
index 00000000000..45616189c0d
--- /dev/null
+++ b/doc/reference/translations.rst
@@ -0,0 +1,96 @@
+.. _reference/translations:
+
+===================
+Translating Modules
+===================
+
+Exporting translatable term
+===========================
+
+A number of terms in your modules are "implicitly translatable" as a result,
+even if you haven't done any specific work towards translation you can export
+your module's translatable terms and may find content to work with.
+
+.. todo:: needs technical features
+
+Translations export is done via the administration interface by logging into
+the backend interface and opening :menuselection:`Settings --> Translations
+--> Import / Export --> Export Translations`
+
+* leave the language to the default (new language/empty template)
+* select the `PO File`_ format
+* select your module
+* click :guilabel:`Export` and download the file
+
+.. image:: translations/po-export.*
+ :align: center
+ :width: 75%
+
+This gives you a file called :file:`{yourmodule}.pot` which should be moved to
+the :file:`{yourmodule}/i18n/` directory. The file is a *PO Template* which
+simply lists translatable strings and from which actual translations (PO files)
+can be created. PO files can be created using msginit_, with a dedicated
+translation tool like POEdit_ or by simply copying the template to a new file
+called :file:`{language}.po`. Translation files should be put in
+:file:`{yourmodule}/i18n/`, next to :file:`{yourmodule}.pot`, and will be
+automatically loaded by Odoo when the corresponding language is installed (via
+:menuselection:`Settings --> Translations --> Load a Translation`)
+
+.. note:: translations for all loaded languages are also installed or updated
+ when installing or updating a module
+
+Implicit exports
+================
+
+Odoo automatically exports translatable strings from "data"-type content:
+
+* in non-QWeb views, all text nodes are exported as well as the content of
+ the ``string``, ``help``, ``sum``, ``confirm`` and ``placeholder``
+ attributes
+* QWeb templates (both server-side and client-side), all text nodes are
+ exported except inside ``t-translation="off"`` blocks, the content of the
+ ``title``, ``alt``, ``label`` and ``placeholder`` attributes are also
+ exported
+* for :class:`~openerp.fields.Field`, unless their model is marked with
+ ``_translate = False``:
+
+ * their ``string`` and ``help`` attributes are exported
+ * if ``selection`` is present and a list (or tuple), it's exported
+ * if their ``translate`` attribute is set to ``True``, all of their existing
+ values (across all records) are exported
+* help/error messages of :attr:`~openerp.models.Model._constraints` and
+ :attr:`~openerp.models.Model._sql_constraints` are exported
+
+Explicit exports
+================
+
+When it comes to more "imperative" situations in Python code or Javascript
+code, Odoo is not able to automatically export translatable terms and they
+must be marked explicitly for export. This is done by wrapping a literal
+string in a function call.
+
+In Python, the wrapping function is :func:`openerp._`::
+
+ title = _("Bank Accounts")
+
+In JavaScript, the wrapping function is generally :js:func:`openerp.web._t`:
+
+.. code-block:: javascript
+
+ title = _t("Bank Accounts")
+
+.. warning::
+
+ Only literal strings can be marked for exports, not expressions and not
+ variables. For situations where strings are formatted, this means the
+ format string must be marked, not the formatted string::
+
+ # bad, the extract may work but it will not correctly translate the text
+ _("Scheduled meeting with %s" % invitee.name)
+
+ # good
+ _("Scheduled meeting with %s") % invitee.name
+
+.. _PO File: http://en.wikipedia.org/wiki/Gettext#Translating
+.. _msginit: http://www.gnu.org/software/gettext/manual/gettext.html#Creating
+.. _POEdit: http://poedit.net/
diff --git a/doc/reference/translations/po-export.png b/doc/reference/translations/po-export.png
new file mode 100644
index 00000000000..82f8d5ad011
--- /dev/null
+++ b/doc/reference/translations/po-export.png
Binary files differ
diff --git a/doc/reference/views.rst b/doc/reference/views.rst
index 9eaab61b324..3f5b2ea2043 100644
--- a/doc/reference/views.rst
+++ b/doc/reference/views.rst
@@ -85,8 +85,8 @@ There are three types of inheritance specs:
* any other element, the first element with the same name and identical
attributes (ignoring ``position``) is matched
-The inheritance spec can an optional ``position`` attribute specifhing how
-the matched node should be altered:
+The inheritance spec may have an optional ``position`` attribute specifying
+how the matched node should be altered:
``inside`` (default)
the content of the inheritance spec is appended to the matched node
@@ -98,7 +98,7 @@ the matched node should be altered:
``before``
the content of the inheritance spec is added to the matched node's
parent, before the matched node
-``attribute``
+``attributes``
the content of the inheritance spec should be ``attribute`` elements
with a ``name`` attribute and an optional body:
diff --git a/doc/reference/workflows.rst b/doc/reference/workflows.rst
index 210213e5f55..a6890f05ab6 100644
--- a/doc/reference/workflows.rst
+++ b/doc/reference/workflows.rst
@@ -284,6 +284,8 @@ defined (in addition to the Odoo ``safe_eval`` environment):
- all the model column names, and
- all the browse record's attributes.
+.. _reference/workflows/signals:
+
Signals
'''''''
diff --git a/doc/tutorials.rst b/doc/tutorials.rst
index a9df0517bc7..6ad06861ef1 100644
--- a/doc/tutorials.rst
+++ b/doc/tutorials.rst
@@ -7,5 +7,5 @@ Tutorials
howtos/website
howtos/backend
- howtos/web
howtos/themes
+ howtos/web