`内部的内容。 模板的 `id` 必须为在报表声明中指定的名称;例如对上面的报表为 `account.report_invoice` 。因为这是一个QWeb模板,你可以访问由模板接收的`docs`对象的所有字段。
88 |
89 | 有一些可在报表中访问的具体变量,主要有:
90 |
91 | - `docs`
92 |
93 | 针对当前报表的记录
94 |
95 | - `doc_ids`
96 |
97 | 针对 `docs`记录的id列表
98 |
99 | - `doc_model`
100 |
101 | 针对`docs` 记录的模型
102 |
103 | - `time`
104 |
105 | 对Python标准库 [`time`](https://docs.python.org/3/library/time.html#module-time)的引用
106 |
107 | - `user`
108 |
109 | 打印报表的用户的`res.user` 记录
110 |
111 | - `res_company`
112 |
113 | 当前 `user`的公司的记录
114 |
115 | 如果你希望访问模板中的记录/模型的其它记录,会需要用到[自定义报表](#reference-reports-custom-reports)。
116 |
117 | ### 可翻译模板
118 |
119 | 如果要翻译报表 (如翻译到伙伴的语言),需要定义两个模板:
120 |
121 | - 主报表模板
122 | - 可翻译的文档
123 |
124 | 然后可以调用通过将属性设置 `t-lang` 为语言代码(例如`fr` 或 `en_US`)或是记录字段的主模板中的可翻译文档。如果使用可翻译的字段(如国家名、销售条件等)还需要通过相应的上下文重新浏览相关记录。
125 |
126 | ### ⚠️警告
127 |
128 | 如果报表模板不使用可翻译记录字段,不需要在另一种语言中重新浏览记录且这样会影响性能。
129 |
130 | 例如,我们来看一下Sale模块中的销售订单报表:
131 |
132 | ```
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
Invoice and shipping address:
152 |
Invoice address:
153 |
154 | <...>
155 |
156 |
157 |
158 |
159 | ```
160 |
161 | 主模板调用可翻译模板,将 `doc.partner_id.lang` 作为 `t-lang` 参数,因此它会以伙伴的语言进行渲染。这样,每个订单会以对应客户的语言进行打印。如果希望仅翻译文档内容体,但保留头部和底部为默认语言,需要像下面这样调用报表的外部布局:
162 |
163 | ```
164 |
165 | ```
166 |
167 | 注意这样仅在调用外部模板时起作用, 不能通过对`t-call`以外的xml节点设置 `t-lang`属性来翻译文档的部分内容。如果要翻译模板的部分内容,可以使用这一部分模板来创建外部模板并通过带`t-lang`属性的主模板调用它。
168 |
169 | ### 条形码
170 |
171 | 条形码为由控制器返回的图像,可轻易地借助QWeb语法来在报表中内嵌(例如参见[属性](#reference-qweb-attributes)):
172 |
173 | ```
174 |
175 | ```
176 |
177 | 可以查询字符串传递更多的参数:
178 |
179 | ```
180 |
182 | ```
183 |
184 | ### 实用备注
185 |
186 | - 可在报表中使用Twitter Bootstrap和FontAwesome类
187 |
188 | - 可在模板中直接放置本地CSS
189 |
190 | - 可通过继承模板来在主报表中插入全局CSS并插入你自己的CSS:
191 |
192 | ```
193 |
194 |
195 |
196 | .example-css-class {
197 | background-color: red;
198 | }
199 |
200 |
201 |
202 | ```
203 |
204 | - 如果在 PDF报表中缺失了样式,请查看[这些指南](https://alanhou.org/odoo-13-building-module/#reference-backend-reporting-printed-reports-pdf-without-styles)。
205 |
206 |
207 |
208 | ## 纸张格式
209 |
210 | 纸张格式是`report.paperformat` 记录并可包含如下属性:
211 |
212 | - `name` (必传)
213 |
214 | 仅用于在某种列表中查看报表的助记内容/描述
215 |
216 | - `description`
217 |
218 | 格式的简短描述
219 |
220 | - `format`
221 |
222 | 是预定义格式(A0到A9, B0到 B10, 法律文书, 信件, 公报,…) 或 `自定义`;默认为 A4。你果定义页面大小时不能使用非自定义格式。
223 |
224 | - `dpi`
225 |
226 | 输出DPI;默认为90
227 |
228 | - `margin_top`, `margin_bottom`, `margin_left`, `margin_right`
229 |
230 | 单位为mm的边框大小
231 |
232 | - `page_height`, `page_width`
233 |
234 | 单位为mm的页面大小
235 |
236 | - `orientation`
237 |
238 | 横向或纵向
239 |
240 | - `header_line`
241 |
242 | 显示头部线的布尔值
243 |
244 | - `header_spacing`
245 |
246 | 单位为mm的头部间距
247 |
248 | 例:
249 |
250 | ```
251 |
252 | French Bank Check
253 |
254 | custom
255 | 80
256 | 175
257 | Portrait
258 | 3
259 | 3
260 | 3
261 | 3
262 |
263 | 3
264 | 80
265 |
266 | ```
267 |
268 |
269 |
270 | ## 自定义报表
271 |
272 | 报表模型有默认的`get_html`函数,会查找名为`report.*module.report_name*`的模型。如若存在,会使用它来调用QWeb引擎;否则会使用一个通用函数。如果希望通过在模板中包含更多内容来自定义报表 (例如像其它模型中记录),可以定义这个模型,重写函数 `_get_report_values` 并在 `docargs` 字典中传递对象:
273 |
274 | ```
275 | from odoo import api, models
276 |
277 | class ParticularReport(models.AbstractModel):
278 | _name = 'report.module.report_name'
279 |
280 | @api.model
281 | def _get_report_values(self, docids, data=None):
282 | report_obj = self.env['ir.actions.report']
283 | report = report_obj._get_report_from_name('module.report_name')
284 | docargs = {
285 | 'doc_ids': docids,
286 | 'doc_model': report.model,
287 | 'docs': self,
288 | }
289 | return docargs
290 | ```
291 |
292 |
293 |
294 | ## 自定义字体
295 |
296 | If you want to use如果希望使用自定义字段则需要向`web.reports_assets_common`资源包添加自定义字体相相关联的 less/CSS。向`web.assets_common` 或 `web.assets_backend`添加自定义字体不会让该字体在QWeb可用。
297 |
298 | 例:
299 |
300 | ```
301 |
302 |
303 |
304 |
305 |
306 | ```
307 |
308 | 即使在(除`web.reports_assets_common`以外的)其它资源包中使用过,也需要在这个less文件中定义 `@font-face`。
309 |
310 | 例:
311 |
312 | ```
313 | @font-face {
314 | font-family: 'MonixBold';
315 | src: local('MonixBold'), local('MonixBold'), url(/your_module/static/src/fonts/MonixBold-Regular.otf) format('opentype');
316 | }
317 |
318 | .h1-title-big {
319 | font-family: MonixBold;
320 | font-size: 60px;
321 | color: #3399cc;
322 | }
323 | ```
324 |
325 | 在将这一 less添加到资源包中以后就可以在自定义QWeb报表中使用该类了- 配合中为 `h1-title-big`。
326 |
327 | ## 报表是网页
328 |
329 | 报表通过report模块动态生成并可直接通过URL进行访问:
330 |
331 | 例如,可以通过http:///report/html/sale.report_saleorder/38来以html模式访问销售订单报表
332 |
333 | 或者通过http:///report/pdf/sale.report_saleorder/38访问pdf版本
--------------------------------------------------------------------------------
/reference/qweb.md:
--------------------------------------------------------------------------------
1 | # QWeb
2 |
3 | - 本文来自[Odoo 13官方文档之开发者文档](../README.md)系列文章
4 |
5 | QWeb是由Odoo[2](#othertemplates)所使用的一种主要[模板](https://en.wikipedia.org/wiki/Template_processor)引擎。它是一种模板引擎[1](#genshif) 并多用于生成 [HTML](https://en.wikipedia.org/wiki/HTML)片断和页面。指定为XML属性的模板指令以 `t-`为前缀,例如 `t-if` 用于[条件判断](#reference-qweb-conditionals),其元素和其它属性会直接进行渲染。为避免元素渲染,还有 ``占位符元素,它执行其指令但不会在自身及其中生成输出:
6 |
7 | ```
8 |
9 | Test
10 |
11 | ```
12 |
13 | 以上会产生的结果:
14 |
15 | ```
16 | Test
17 | ```
18 |
19 | 如 `condition`为 true, 但使用:
20 |
21 | ```
22 |
25 | ```
26 |
27 | 会产生如下结果:
28 |
29 | ```
30 |
33 | ```
34 |
35 |
36 |
37 | ## 数据输出
38 |
39 | QWeb有一个主输出指令,它自动对其内容进行HTML转义,来在显示用户所提供的内容时降低 [XSS](https://en.wikipedia.org/wiki/Cross-site_scripting) 风险: `esc`.
40 |
41 | `esc` 接收一个表达式,运行它并打印出内容:
42 |
43 | ```
44 |
45 | ```
46 |
47 | 通过将`value` 设置为 `42` 来进行渲染并产生结果:
48 |
49 | ```
50 | 42
51 | ```
52 |
53 | 还有另一个输出指令 `raw` ,它的行为和 `esc` 相同,但*不对输出进行HTML转义。*它可用于显示单独的构建标记 (e.g. 通过函数) 或已清洗的用户提供标记。
54 |
55 |
56 |
57 | ## 条件判断
58 |
59 | QWeb拥有一个条件指令 `if`,它运行给定为属性值的表达式:
60 |
61 | ```
62 |
67 | ```
68 |
69 | 如果条件为true的话会渲染元素:
70 |
71 | ```
72 |
75 | ```
76 |
77 | 但如果条件为false,它会从结果中删除:
78 |
79 | ```
80 |
81 |
82 | ```
83 |
84 | 条件渲染应用于指令的所有者,不必一定为 ``:
85 |
86 | ```
87 |
90 | ```
91 |
92 | 将给出与前例相同的结果。
93 |
94 | 还存在额外的分支指令 `t-elif` 和 `t-else` :
95 |
96 | ```
97 |
98 |
Happy birthday!
99 |
Welcome master!
100 |
Welcome!
101 |
102 | ```
103 |
104 |
105 |
106 | ## 循环
107 |
108 | QWeb拥有遍历指令`foreach`,它接收一个表达式,返回待遍历的集合以及第二个用于对遍历的“当前项”提供名称的参数 `t-as` :
109 |
110 | ```
111 |
112 |
113 |
114 | ```
115 |
116 | 将会渲染为:
117 |
118 | ```
119 | 1
120 | 2
121 | 3
122 | ```
123 |
124 | 类似条件,`foreach` 应用于带有指令属性的元素,并且
125 |
126 | ```
127 |
128 |
129 |
130 | ```
131 |
132 | 等价于前例。
133 |
134 | `foreach` 可对数组 (当前项为当前值)或映射(当前项为当前键)进行遍历。.仍支持对整数的遍历(等价于遍历一个包含0到所提供整数(不含)的数组)但已淘汰。
135 |
136 | 除通过`t-as`传递名称乐, `foreach`还对不同数据点提供一些其它变量:
137 |
138 | ### ⚠️警告
139 |
140 | `$as`会替换为传递给 `t-as`的名称
141 |
142 | - `*$as*_all` (淘汰)
143 |
144 | 所进行遍历的对象这个变量仅在JavaScript QWeb中可用,在 Python中不可用。
145 |
146 | - `*$as*_value`
147 |
148 | 当前遍历值,和列表与整型的 `$as` 相同,但对于映射它提供了值 (其中 `$as`提供了键)
149 |
150 | - `*$as*_index`
151 |
152 | 当前迭代索引 (遍历中索引为0的第一项)
153 |
154 | - `*$as*_size`
155 |
156 | 如果可用时为集合的大小
157 |
158 | - `*$as*_first`
159 |
160 | 当前项是否为遍历的第一项 (等价于 `*$as*_index == 0`)
161 |
162 | - `*$as*_last`
163 |
164 | 当前项是否为遍历的最后一项(等价于 `*$as*_index + 1 == *$as*_size`),要求遍历的大小可用
165 |
166 | - `*$as*_parity` (淘汰)
167 |
168 | 为`"even"` 或 `"odd"`,当前遍历回合索引的奇偶性
169 |
170 | - `*$as*_even` (淘汰)
171 |
172 | 一个用于表明当前遍历回合为偶数索引的布尔标记
173 |
174 | - `*$as*_odd` (淘汰)
175 |
176 | 一个用于表明当前遍历回合为奇数索引的布尔标记
177 |
178 | 这些提供的额外变量及 `foreach`中创建的新变量仅在``foreach``的作用域中可用。若变量存在于`foreach`的上下文之外, foreach末尾的值会拷贝到全局上下文中
179 |
180 | ```
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 | ```
193 |
194 |
195 |
196 | ## 属性
197 |
198 | QWeb可补时计算属性并对输出节点设置计算的结果。这通过 `t-att` (属性)指令完成,存在3种不同形式:
199 |
200 | - `t-att-*$name*`
201 |
202 | 创建名为 `$name` 的属性,运行属性值并将结果设置为属性的值:`
`将会被渲染为:`
`
203 |
204 | - `t-attf-*$name*`
205 |
206 | 与前述相同,但参数是 [格式字符串](https://www.odoo.com/documentation/13.0/glossary.html#term-format-string)而不仅仅是一个表达式,通常用于混合字面量及非字面量字符串 (e.g. 类):` `将会被渲染为:`1 2 3 `
207 |
208 | - `t-att=mapping`
209 |
210 | 如果参数是一个映射,第个 (key, value) 对生成一个新属性及其值:`
`会被渲染为:`
`
211 |
212 | - `t-att=pair`
213 |
214 | 如果参数是一个值对 (两个元素的元组或数组),值对的第一项是属性的名称,第二项是其值:`
`会被渲染为:`
`
215 |
216 | ## 设置变量
217 |
218 | QWeb允许在模板内创建变量,来存储(memoize)一个运算 (多次使用它),提供一个更清晰名称的数据, …
219 |
220 | 这通过`set`指令完成,它接收待创建变量的名称。设置的值通过两种方式提供:
221 |
222 | - 一个包含表达式的
223 |
224 | ```
225 | t-value
226 | ```
227 |
228 | 属性,会设置其运行的结果:
229 |
230 | ```
231 |
232 |
233 | ```
234 |
235 | 以上将打印 `3`
236 |
237 | - 如果没有
238 |
239 | ```
240 | t-value
241 | ```
242 |
243 | 属性,节点的内容体会进行渲染并设置为变量的值:
244 |
245 | ```
246 |
247 | ok
248 |
249 |
250 | ```
251 |
252 | 将会生成 `ok ` (因我们使用了 `esc` 指令内容会进行转义)
253 |
254 | 使用这一运算的结果是`raw` 指令的一个显著用例。
255 |
256 | ## 调用子模板
257 |
258 | QWeb模板可用于顶层渲染,但它们也可在其它模板之内 (来避免复制或对部分模板给定) 使用 `t-call` 指令:
259 |
260 | ```
261 |
262 | ```
263 |
264 | 若定义`other_template`为下面的内容,通过父级执行上下文调用命名模板:
265 |
266 | ```
267 |
268 | ```
269 |
270 | 上面的调用会被渲染为 `
` (无内容),但:
271 |
272 | ```
273 |
274 |
275 | ```
276 |
277 | 会被渲染为 `1
`.
278 |
279 | 但这在 `t-call`之外的可见性存在问题。此外,`call` 指令体内的内容会在运行子模板之前调用,并可更改本地内容:
280 |
281 | ```
282 |
283 |
284 |
285 |
286 | ```
287 |
288 | `call` 指令体可任意复杂 (不仅仅是 `set` 指令),并且其渲染形式将在所调用模板为魔法 `0` 变量之内可用:
289 |
290 | ```
291 |
292 | This template was called with content:
293 |
294 |
295 | ```
296 |
297 | 被调用为:
298 |
299 | ```
300 |
301 | content
302 |
303 | ```
304 |
305 | 会产生结果:
306 |
307 | ```
308 |
309 | This template was called with content:
310 | content
311 |
312 | ```
313 |
314 | ## Python
315 |
316 | ### 独家指令
317 |
318 | #### 资源包
319 |
320 | #### “智能记录”字段模式化
321 |
322 | `t-field` 指令仅在对“智能”记录(`browse`方法的结果)执行字段访问(`a.b`) 时才可使用。它可以自动根据字段类型格式化,并且在网站的富文本版本中集成。
323 |
324 | `t-options` 可用于自定义字段,最常见的选项是 `widget`,其它选项独立于字段或组件。
325 |
326 | ### 调试
327 |
328 | - `t-debug`
329 |
330 | 使用PDB的`set_trace` API调用调试器。参数应为模块名,对其调用`set_trace` 方法:` `等价于 `importlib.import_module("pdb").set_trace()`
331 |
332 | ### 帮助函数
333 |
334 | #### 基于请求
335 |
336 | QWeb的大部分Python端使用在控制器(HTTP请求时)中进行,其中存储在数据库中的模板 (作为 [视图](https://alanhou.org/odoo-13-views/#reference-views-qweb)) 可通过调用 [`odoo.http.HttpRequest.render()`](https://alanhou.org/odoo-13-web-controllers/#odoo.http.HttpRequest.render)来进行渲染:
337 |
338 | ```
339 | response = http.request.render('my-template', {
340 | 'context_value': 42
341 | })
342 | ```
343 |
344 | 这会自动创建一个[`Response`](https://alanhou.org/odoo-13-web-controllers/#odoo.http.Response) 对象,它可在控制器中返回(或进行一步自定义在适配)。
345 |
346 | #### 基于视图
347 |
348 | 比上述帮助函数更深层次的是`ir.ui.view`中的`render`方法:
349 |
350 | ###### `render(*cr, uid, id[, values][, engine='ir.qweb][, context]*)`
351 |
352 | 通过数据库 id 或[外部id](https://www.odoo.com/documentation/13.0/glossary.html#term-external-id)渲染QWeb 视图/模板。模板会自动从`ir.ui.view` 记录中进行加载。
353 |
354 | 在渲染上下文中设置了很多地默认值:
355 |
356 | - `request`
357 |
358 | 若存在,为当前 [`WebRequest`](https://alanhou.org/odoo-13-web-controllers/#odoo.http.WebRequest) 对象
359 |
360 | - `debug`
361 |
362 | 当前请求(若有)是否为 `debug` 模式
363 |
364 | - [`quote_plus`](https://werkzeug.palletsprojects.com/en/0.16.x/urls/#werkzeug.urls.url_quote_plus)
365 |
366 | url编码工具函数
367 |
368 | - [`json`](https://docs.python.org/3/library/json.html#module-json)
369 |
370 | 相应的标准库模块
371 |
372 | - [`time`](https://docs.python.org/3/library/time.html#module-time)
373 |
374 | 相应的标准库模块
375 |
376 | - [`datetime`](https://docs.python.org/3/library/datetime.html#module-datetime)
377 |
378 | 相应的标准库模块
379 |
380 | - [relativedelta](https://labix.org/python-dateutil#head-ba5ffd4df8111d1b83fc194b97ebecf837add454)
381 |
382 | 参见模块
383 |
384 | - `keep_query`
385 |
386 | `keep_query` 帮助函数
387 |
388 | 参数
389 |
390 | - **values** – 传递给QWeb供渲染的上下文值
391 | - **engine** ([`str`](https://docs.python.org/3/library/stdtypes.html#str)) – 用于渲染的Odoo模型名称,可用于扩展或在本地自定义 QWeb (基于 `ir.qweb` 的修改创建“新”qweb)
392 |
393 | ## Javascript
394 |
395 | ### 独家指令
396 |
397 | #### 定义模板
398 |
399 | `t-name` 指令仅可放在模板文件(文档根目标的直接后代)的顶层:
400 |
401 | ```
402 |
403 |
404 |
405 |
406 |
407 | ```
408 |
409 | 它不接收其它参数,便可与``元素或任意其它元素共同使用。 通过 `` 元素时, `` 应包含一个子元素。
410 |
411 | 模板名是一个任意字符串,虽然关联了多个模板 (e.g. 调用了子模板),可自定义使用点击分隔名称来表明层级关系。
412 |
413 | #### 模板继承
414 |
415 | 模板继承用于在原处更改已有模板,e.g. 向由其它模块创建的模板添加信息。
416 |
417 | 模板继承通过`t-extend` 指令执行,它接收模板名称来修改为参数。
418 |
419 | 在 `t-extend` 与 `t-name` 进行合并时,会创建带有给定名称的新模板。在这种情况下所扩展的模板未进行修改,而是由指令定义了如何新建模板。
420 |
421 | 在这两种情况下修改由任意数量的 `t-jquery` 子指令执行:
422 |
423 | ```
424 |
425 |
426 | new element
427 |
428 |
429 | ```
430 |
431 | `t-jquery`指令接收一个 [CSS选择器](https://api.jquery.com/category/selectors/)。该选择器用于所继承的模板来选择*上下文节点*来应用所指定的 `t-operation` :
432 |
433 | - `append`
434 |
435 | 节点的内容体添加到上下文节点的结束处 (在上下文节点最一个子元素之后)
436 |
437 | - `prepend`
438 |
439 | 节点的内容体前置到上下文节点 (在上下文节点的第一个子元素之前插入)
440 |
441 | - `before`
442 |
443 | 节点的内容体插入到上下文节点为之前
444 |
445 | - `after`
446 |
447 | 节点的内容体插入到上下文节点为之后
448 |
449 | - `inner`
450 |
451 | 节点的内容体替换上下文节点的子元素
452 |
453 | - `replace`
454 |
455 | 节点的内容体用于替换上下文节点本身
456 |
457 | - `attributes`
458 |
459 | 节点的内容体可为任意数量的`attribute` 元素,每个都带有`name` 属性和一些文本内容,上下文节点的命令属性将设置为指定值(如已存在则替换否则进行添加)
460 |
461 | - No operation
462 |
463 | 如未指定`t-operation` ,模板体会解析为 javascript代码并以 `this`通过上下文节点执行⚠️警告虽然比其它操作强大得多,但这一模式更难调试、更难维护,推荐避免使用它。
464 |
465 | ### 调试
466 |
467 | javascript QWeb实现提供了一些调试钩子:
468 |
469 | - `t-log`
470 |
471 | 接收表达式参数,在渲染期间运行表达式并通过`console.log`在结果中进行记录:` `将在控制台中打印出print `42`
472 |
473 | - `t-debug`
474 |
475 | 在模板渲染时触发调试器断点:` `如调试为活跃状态则停止执行 (具体的情况取决于浏览器及其开发工具)
476 |
477 | - `t-js`
478 |
479 | 节点内容体为在模板渲染时执行的javascript代码。接收一个 `context` 参数,渲染上下文所使用的名称在 `t-js`的内容体中可以获取到:` console.log("Foo is", ctx.foo); `
480 |
481 | ### 帮助对象
482 |
483 | ###### `core.qweb`
484 |
485 | (core是 `web.core` 模块) 是加载了所有模块定义的模板的[`QWeb2.Engine()`](#QWeb2.Engine) 实例,并引用标准帮助对象 `_` (下划线), `_t` (翻译函数) 和 [JSON](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON).
486 |
487 | `core.qweb.render` can be used to easily render basic module templates
488 |
489 |
490 |
491 | ### API
492 |
493 | ###### `*class* QWeb2.Engine()`
494 |
495 | QWeb “渲染器”,处理大部分QWeb逻辑(加载、解析、编译及渲染模板)。
496 |
497 | Odoo Web在core模板为用户进行实例化,并将其导出到 `core.qweb`中。它还加载各种模板中的所有模板文件到该QWeb实例中。
498 |
499 | [`QWeb2.Engine()`](#QWeb2.Engine) 还作为“模板命名空间”提供服务。
500 |
501 | ###### `QWeb2.Engine.QWeb2.Engine.render(*template*[, *context*])`
502 |
503 | 渲染此前加载的模板为字符串,使用 `context` (若提供)来查找在模板渲染期间访问的变量(e.g. 要显示的字符串)。
504 |
505 | 参数
506 |
507 | - **template** (`String`) – 待渲染的模板名
508 | - **context** (`Object`) – 模板渲染要使用的基本命名空间
509 |
510 | 返回
511 |
512 | 字符串
513 |
514 | 该引擎暴露另一个方法,可在一些用例下使用 (e.g. 如需在Odoo Web,中通过看板视图分离模板命名空间来获取它们自己的[`QWeb2.Engine()`](#QWeb2.Engine) 实例,因此它们的模板不与更通用的“模块”模板相冲突):
515 |
516 | ###### `QWeb2.Engine.QWeb2.Engine.add_template(*templates*)`
517 |
518 | 在QWeb实例中加载一个模板文件 (一个模板集合) 。模板可指定为:
519 |
520 | - 一个XML字符串
521 |
522 | QWeb会尝试解析其为一个XML文档然后加载它。
523 |
524 | - 一个URL
525 |
526 | QWeb会尝试下载 URL内容,然后加载结果XML字符串。
527 |
528 | - 一个`Document` 或 `Node`
529 |
530 | QWeb会遍历文档的第一级 (所提供根的子节点)并加载任意命名模板或模板重载。
531 |
532 |
533 |
534 | [`QWeb2.Engine()`](#QWeb2.Engine) 还针对自定义行为暴露不同的属性:
535 |
536 | ###### `QWeb2.Engine.QWeb2.Engine.prefix`
537 |
538 | 用于在解析期间识别指令的前缀。一个字符串。默认为 `t`。
539 |
540 | ###### `QWeb2.Engine.QWeb2.Engine.debug`
541 |
542 | 将引擎置为“调试模式”的布尔标记。通常,QWeb拦截在模板执行期间抛出的任意错误。在调试模式下,它让所有的异常通过不进行拦截。
543 |
544 | ###### `QWeb2.Engine.QWeb2.Engine.jQuery`
545 |
546 | 这个 jQuery实例在模板继承处理过程中进行使用。默认为`window.jQuery`。
547 |
548 | ###### `QWeb2.Engine.QWeb2.Engine.preprocess_node`
549 |
550 | 一个`函数`。如存在,在编译每个DOM 节点到模板节点之前进行调用。在 Web中,它用于在模板中自动转译文本内容和一些属性。默认为 `null`。
551 |
552 | [[1\]](#id2) 类似于[Genshi](https://genshi.edgewall.org/)之中的, 但它不使用 (且不支持) [XML命名空间](https://en.wikipedia.org/wiki/XML_namespace)
553 |
554 | [[2\]](#id1) 但出于历史原因或因适合用例还保留一些其它模板。 Odoo 9.0仍依赖于 [Jinja](http://jinja.pocoo.org/) 和 [Mako](https://www.makotemplates.org/)。
--------------------------------------------------------------------------------
/reference/security-odoo.md:
--------------------------------------------------------------------------------
1 | # Odoo中的安全
2 |
3 | - 本文来自[Odoo 13官方文档之开发者文档](../README.md)系列文章
4 |
5 | 除手动通过自定义模式管理权限外,Odoo还提供了两种数据驱动的机制来管理或限制对数据的访问。
6 |
7 | 这两种机制都通过**组**来与具体用户相关联:一个用户可属于多个组,安全机制与组进行关联,进而应用对用户应用安全机制。
8 |
9 |
10 |
11 | ## 访问控制
12 |
13 | 由`ir.model.access`记录进行管理, 定义对整个模型的访问。
14 |
15 | 每个访问控制有其授予权限的模型,授予的权限及可选用户组。
16 |
17 | 访问控制是附加的,对于一个给定模型用户拥有所有授予其所属组的权限,如用记属于一个允许写入的组及一个允许删除的组,那么用记就同时拥有写入和删除的权限。
18 |
19 | 如未指定组,访问控制应用于所有用户,否则它仅应用于给定组的成员。
20 |
21 | 可用的权限有创建(`perm_create`)、搜索和读取 (`perm_read`)、更新已有记录s (`perm_write`) 及删除已有记录(`perm_unlink`)
22 |
23 |
24 |
25 | ## 记录规则
26 |
27 | 记录规则是必须满足所允许操作(创建、读取、更新或删除)记录的条件。它在应用了访问控制后逐记录应用。
28 |
29 | 记录规则有:
30 |
31 | - 其所应用的模型
32 | - 一组它所应用的权限 (例如,如果设置了 `perm_read`,规则仅在读取记录时进行检查)
33 | - 一组规则所应用的用户组,如未指定组则是**全局**规则
34 | - 用于检查给定记录是否匹配规则(并可访问)或不匹配(不可访问)的[作用域](https://alanhou.org/odoo-13-orm-api/l#reference-orm-domains) 。作用域通过两个上下文变量进行运行: `user` 是当前用户的记录,`time` 是 [time模块](https://docs.python.org/2/library/time.html)
35 |
36 | 全局赍是和组规则 (限制给定用记的规则 vs.应用所有用户的组) 的使用差别很大:
37 |
38 | - 使用局规则做减法,它们必须匹配可访问的记录
39 | - 组规则做加法,如匹配其中任一规则 (且匹配所有全局规则)则记录可进行访问
40 |
41 | 这表示第一个**组规则**限制访问,但其它的**组规则**对进行扩展,而**全局规则**仅限制访问 (或不起任何作用)。
42 |
43 | ### ⚠️警告
44 |
45 | 记录规则对管理员用户不起作用
46 |
47 |
48 |
49 | ## 字段权限
50 |
51 | 7.0版本中新增。
52 |
53 | ORM [`字段`](https://alanhou.org/odoo-13-orm-api/#odoo.fields.Field)可带有 `groups`属性来提供一个组列表 (作为一个[外部标识符](https://www.odoo.com/documentation/13.0/glossary.html#term-external-identifiers)的逗号分隔字符串)。
54 |
55 | 如当前用记不在所列举的组中,将无法访问该字段:
56 |
57 | - 受限的字段自动从所请求的视图中删除
58 | - 受限的字段从 [`fields_get()`](https://alanhou.org/odoo-13-orm-api/#odoo.models.Model.fields_get) 响应中进行删除
59 | - 尝试 (显式地) 从受限的字段中读取或进行写入会导致访问错误
--------------------------------------------------------------------------------
/reference/testing-odoo.md:
--------------------------------------------------------------------------------
1 | # 测试Odoo
2 |
3 | 本文来自[Odoo 13官方文档之开发者文档](https://alanhou.org/odoo-13-developer-documentation/)系列文章
4 |
5 | 有很多种测试应用的方法。在Odoo中,我们有三种测试
6 |
7 | * Python 单元测试 (参见[测试Python代码](#testing-python-code)):用于测试模型业务逻辑
8 | * JS单元测试(参见[测试JS代码](#testing-js-code)):用于分离测试javascript代码
9 | * 导览(参见[集成测试](#integration-testing)):模拟真实场景的导览。它们确保python及 javascript部分正常进行对话。
10 |
11 | ## 测试Python代码
12 |
13 | Odoo提供使用单元测试来测试模块的支持。
14 |
15 | 编写测试只需在模块中定义一个 `tests` 子包,它会自动查看测试模块。测试模块应有一个以`test_`开头的名称并且会从 `tests/__init__.py`, 进行导入,如:
16 |
17 | ```
18 | your_module
19 | |-- ...
20 | `-- tests
21 | |-- __init__.py
22 | |-- test_bar.py
23 | `-- test_foo.py
24 | ```
25 |
26 | 且 `__init__.py` 包含:
27 |
28 | `from . import test_foo, test_bar`
29 |
30 | ### ⚠️警告
31 |
32 | 不是通过 `tests/__init__.py`导入的测试模块将不会运行
33 |
34 | 测试运行器只是会运行测试类,正如[unittest 官方文档](https://docs.python.org/3/library/unittest.html)所描述的那样, 但 Odoo提供了一些与测试Odoo内容(主要是模块)相关的工具和帮助类:
35 |
36 | ###### `*class *odoo.tests.common.TransactionCase(*methodName='runTest'*)`
37 |
38 | 每个TestCase的测试方法以自己的游标在自身的事务中运行。在每次测试完成后会回滚事务并关闭游标。
39 |
40 | ###### `browse_ref(*xid*)`
41 |
42 | 为所提供的[外部标识符](https://www.odoo.com/documentation/13.0/glossary.html#term-external-identifier)返回记录对象
43 |
44 | 参数
45 |
46 | **xid** – 完全限定[外部标识符](https://www.odoo.com/documentation/13.0/glossary.html#term-external-identifier),形式为 `*module*.*identifier*`
47 |
48 | 在未找到时抛出
49 |
50 | ValueError
51 |
52 | 返回
53 |
54 | [`BaseModel`](https://alanhou.org/odoo-13-orm-api/#odoo.models.BaseModel "odoo.models.BaseModel")
55 |
56 | ###### `ref(*xid*)`
57 |
58 | 对所提供的[外部标识符](https://www.odoo.com/documentation/13.0/glossary.html#term-external-identifier)返回数据库ID,`get_object_reference`的快捷方式
59 |
60 | 参数
61 |
62 | **xid** – 完全限定 [外部标识符](https://www.odoo.com/documentation/13.0/glossary.html#term-external-identifier),形式为`*module*.*identifier*`
63 |
64 | 在未找到时抛出
65 |
66 | ValueError
67 |
68 | 返回
69 |
70 | 注册的 id
71 |
72 | ###### `*class *odoo.tests.common.SingleTransactionCase(*methodName='runTest'*)`
73 |
74 | 所有测试方法的TestCase在同一个事务中运行,事务通过第一个测试方法启动并在最后一个测试方法完成后进行回滚。
75 |
76 | ###### `browse_ref(*xid*)`
77 |
78 | 对所提供的[外部标识符](https://www.odoo.com/documentation/13.0/glossary.html#term-external-identifier)返回记录对象
79 |
80 | 参数
81 |
82 | **xid** – 完全限定 [外部标识符](https://www.odoo.com/documentation/13.0/glossary.html#term-external-identifier),形式为 `*module*.*identifier*`
83 |
84 | 在未找到时抛出
85 |
86 | ValueError
87 |
88 | 返回
89 |
90 | [`BaseModel`](https://alanhou.org/odoo-13-orm-api/#odoo.models.BaseModel "odoo.models.BaseModel")
91 |
92 | ###### `ref(*xid*)`
93 |
94 | 对所提供的[外部标识符](https://www.odoo.com/documentation/13.0/glossary.html#term-external-identifier)返回数据库ID,快捷方式为 `get_object_reference`
95 |
96 | 参数
97 |
98 | **xid** – 完全限定 [外部标识符](https://www.odoo.com/documentation/13.0/glossary.html#term-external-identifier),形式为 `*module*.*identifier*`
99 |
100 | 在未找到时抛出
101 |
102 | ValueError
103 |
104 | 返回
105 |
106 | 注册的id
107 |
108 | ###### `*class *odoo.tests.common.SavepointCase(*methodName='runTest'*)`
109 |
110 | 类似于 [`SingleTransactionCase`](#odoo.tests.common.SingleTransactionCase "odoo.tests.common.SingleTransactionCase") ,其中所有测试方法都在单个事务中运行,但每个测试用例在回滚的保存点(子事务)内部运行。
111 |
112 | 用于包含快速测试的测试用例,但具有所有测试用例的常见数据库配置(复杂in-db测试数据): `setUpClass()` 可用于生成一次数据库测试数据,然后所有测试用例使用不影响其它测试的相同数据, 但也无需重建测试数据。
113 |
114 | ###### `*class *odoo.tests.common.HttpCase(*methodName='runTest'*)`
115 |
116 | 带有url_open和Chrome headless帮助方法的事务型HTTP TestCase。
117 |
118 | ###### `browse_ref(*xid*)`
119 |
120 | 为所提供的[外部标识符](https://www.odoo.com/documentation/13.0/glossary.html#term-external-identifier)返回记录对象
121 |
122 | 参数
123 |
124 | **xid** – 完全限定[外部标识符](https://www.odoo.com/documentation/13.0/glossary.html#term-external-identifier),形式为`*module*.*identifier*`
125 |
126 | 在未找到时返回
127 |
128 | ValueError
129 |
130 | 返回
131 |
132 | [`BaseModel`](https://alanhou.org/odoo-13-orm-api/#odoo.models.BaseModel "odoo.models.BaseModel")
133 |
134 | ###### `phantom_js(*url_path*, *code*, *ready=''*, *login=None*, *timeout=60*, ***kw*)`
135 |
136 | 在浏览器中运行的Test js代码 - 可日志为‘login’ - 加载url_path给定的页面 - 等待对象就绪 - 页面内部的eval(code)
137 |
138 | 标识成功测试执行: console.log(‘test successful’)
139 |
140 | 标识失败时执行: console.error(‘test failed’)
141 |
142 | 在超时前两者都未执行则测试失败。
143 |
144 | ###### `ref(*xid*)`
145 |
146 | 对所提供的[外部标识符](https://www.odoo.com/documentation/13.0/glossary.html#term-external-identifier)返回数据库ID,是 `get_object_reference`的快捷方式
147 |
148 | 参数
149 |
150 | **xid** – 完全限定[外部标识符](https://www.odoo.com/documentation/13.0/glossary.html#term-external-identifier),形式为 `*module*.*identifier*`
151 |
152 | 在未找到时抛出
153 |
154 | ValueError
155 |
156 | 返回
157 |
158 | 注册的id
159 |
160 | ###### `odoo.tests.common.tagged(**tags*)`
161 |
162 | 标记BaseCase对象标签的装饰器存储于一个可由‘test_tags’属性访问的集合中。以 ‘-‘ 为标签的前缀会删除该标签,如要删除‘standard’标签。默认所有odoo.tests.common 中的测试类有一个默认为‘standard’及模块技术名称。在使用类继承时,标签未被继承。
163 |
164 | 默认,测试在相应模块安装后运行一次。测试用例也可配置为所有模块安装后运行,而不是紧接着模块安装后运行:
165 |
166 | ###### `odoo.tests.common.at_install(*flag*)`
167 |
168 | 设置测试的在安装状态,该标记是一个指定测试应(`True`)或不应 (`False`) 在模块安装时运行的布尔值。
169 |
170 | 默认,测试在模块安装之后、开始下一个模块安装之前运行。
171 |
172 | 自从版本12.0开始淘汰: `at_install` 当前是一个标记,虽然`tagged` 仅在测试类中生效,但你可以使用 [`tagged()`](https://alanhou.org/odoo-13-testing-odoo/#odoo.tests.common.tagged "odoo.tests.common.tagged") 来添加/删除它
173 |
174 | ###### `odoo.tests.common.post_install(*flag*)`
175 |
176 | 设置测试的安装后状态。该标记是一个指定测试应或不应在模块安装集合之后运行的布尔值。
177 |
178 | 默认,在当前安装集合中所有模块的安装测试不运行测试。
179 |
180 | 从版本12.0开始淘汰: `post_install`现在是一个标记, 虽然`tagged` 仅在测试类中生效,但你可以使用 [`tagged()`](https://alanhou.org/odoo-13-testing-odoo/#odoo.tests.common.tagged "odoo.tests.common.tagged") 来添加/删除它
181 |
182 | 最常见的情况是使用 [`TransactionCase`](https://alanhou.org/odoo-13-testing-odoo/#odoo.tests.common.TransactionCase "odoo.tests.common.TransactionCase") 并在每个方法中测试模型的属性:
183 |
184 | ```
185 | class TestModelA(common.TransactionCase):
186 | def test_some_action(self):
187 | record = self.env['model.a'].create({'field': 'value'})
188 | record.some_action()
189 | self.assertEqual(
190 | record.field,
191 | expected_field_value)
192 |
193 | # other tests...
194 | ```
195 |
196 | 测试方法必须以 `test_`开头
197 |
198 | ###### `*class *odoo.tests.common.Form(*recordp*, *view=None*)`
199 |
200 | 服务端 (部分)表单视图实现
201 |
202 | 实现很多的“表单视图”操作流,如服务端测试可更好的反映行为,它将在操作界面时观察到:
203 |
204 | * 对“创建”调用相关的 onchange
205 | * 对设置字段调用相关的onchange
206 | * 对在 x2many字段相应地处理默认值 & onchange
207 |
208 | 保存创建模式下返回的创建记录表单。
209 |
210 | 常用字段可仅直接分配给表单,对[`Many2one`](https://alanhou.org/odoo-13-orm-api/#odoo.fields.Many2one "odoo.fields.Many2one") 字段分配一个singleton记录集:
211 |
212 | ```
213 | # empty recordset => creation mode
214 | f = Form(self.env['sale.order'])
215 | f.partner_id = a_partner
216 | so = f.save()
217 | ```
218 |
219 | 在编辑记录时,使用表单作为上下文管理器来自动在作用域结束处保存它:
220 |
221 | ```
222 | with Form(so) as f2:
223 | f2.payment_term_id = env.ref('account.account_payment_term_15days')
224 | # f2 is saved here
225 | ```
226 |
227 | 对于 [`Many2many`](https://alanhou.org/odoo-13-orm-api/#odoo.fields.Many2many "odoo.fields.Many2many") 字段,字段本身是一个 [`M2MProxy`](#odoo.tests.common.M2MProxy "odoo.tests.common.M2MProxy") 并可通过添加或删除记录来进行修改:
228 |
229 | ```
230 | with Form(user) as u:
231 | u.groups_id.add(env.ref('account.group_account_manager'))
232 | u.groups_id.remove(id=env.ref('base.group_portal').id)
233 | ```
234 |
235 | 最后 [`One2many`](https://alanhou.org/odoo-13-orm-api/#odoo.fields.One2many "odoo.fields.One2many") 具体化为[`O2MProxy`](https://alanhou.org/odoo-13-testing-odoo/#odoo.tests.common.O2MProxy "odoo.tests.common.O2MProxy")。
236 |
237 | 因 [`One2many`](https://alanhou.org/odoo-13-orm-api/#odoo.fields.One2many "odoo.fields.One2many") 仅通过其父级存在,它更直接地通过[`new()`](#odoo.tests.common.O2MProxy.new "odoo.tests.common.O2MProxy.new") 和 [`edit()`](#odoo.tests.common.O2MProxy.edit "odoo.tests.common.O2MProxy.edit") 方法创建“子表单” 。这些通常将用作上下文管理器,因为它们在父级记录中进行保存:
238 |
239 | ```
240 | with Form(so) as f3:
241 | # add support
242 | with f3.order_line.new() as line:
243 | line.product_id = env.ref('product.product_product_2')
244 | # add a computer
245 | with f3.order_line.new() as line:
246 | line.product_id = env.ref('product.product_product_3')
247 | # we actually want 5 computers
248 | with f3.order_line.edit(1) as line:
249 | line.product_uom_qty = 5
250 | # remove support
251 | f3.order_line.remove(index=0)
252 | # SO is saved here
253 | ```
254 |
255 | 参数
256 |
257 | * **recordp** ([`odoo.models.Model`](https://alanhou.org/odoo-13-orm-api/#odoo.models.Model "odoo.models.Model")) – 对于singleton记录为空。空记录集会将视图置为“创建”模式并触发对default_get 和 on-load onchange的调用,一个 singleton会将其设置为“编辑”模式并仅加载视图的数据。
258 | * **view** (`int | str | odoo.model.Model`) – onchange和视图约束所使用的id, xmlid 或实际视图对象。如未提供,仅加载模型的默认视图。
259 |
260 | 版本12.0中新增。
261 |
262 | ###### `save()`
263 |
264 | 保存表单,在可用时返回所创建的记录
265 |
266 | * 不保存 `只读` 字段
267 | * (在编辑时)不保存未修改字段 — 任何赋值或 onchange返回标记字段为已修改,设置为当前值也是如此
268 |
269 | 抛出
270 |
271 | [**AssertionError**](https://docs.python.org/3/library/exceptions.html#AssertionError "(in Python v3.8)") – 若表单没有任何未填写的必填字段
272 |
273 | ###### `*class *odoo.tests.common.M2MProxy`
274 |
275 | 作为记录集的`序列`,可进行索引或切片来获取实际底层记录集。
276 |
277 | ###### `add(*record*)`
278 |
279 | 向字段添加`记录`,记录必须是已存在的。
280 |
281 | 所添加的内容仅在保存父级记录时最终生效。
282 |
283 | ###### `clear()`
284 |
285 | 删除m2m中的所有已有记录
286 |
287 | ###### `remove(*id=None*, *index=None*)`
288 |
289 | 删除指定索引或字段中具有所提供 id 的记录。
290 |
291 | ###### `*class *odoo.tests.common.O2MProxy`
292 |
293 | ###### `edit(*index*)`
294 |
295 | 返回一个 [`表单`](#odoo.tests.common.Form "odoo.tests.common.Form") 来编辑已存在的 [`One2many`](https://alanhou.org/odoo-13-orm-api/#odoo.fields.One2many "odoo.fields.One2many") 记录。
296 |
297 | 表单通过可编辑的列表视图或通过字段的表单视图进行创建。
298 |
299 | 抛出
300 |
301 | [**AssertionError**](https://docs.python.org/3/library/exceptions.html#AssertionError "(in Python v3.8)") – 若字段不可编辑
302 |
303 | ###### `new()`
304 |
305 | 返回一个相应初始化的新 [`One2many`](https://alanhou.org/odoo-13-orm-api/#odoo.fields.One2many "odoo.fields.One2many") 记录的[`表单`](#odoo.tests.common.Form "odoo.tests.common.Form")。
306 |
307 | 表单通过可编辑的列表视图或通过字段的表单视图进行创建。
308 |
309 | 抛出
310 |
311 | [**AssertionError**](https://docs.python.org/3/library/exceptions.html#AssertionError "(in Python v3.8)") – 若字段不可编辑
312 |
313 | ###### `remove(*index*)`
314 |
315 | 删除父级表单中位于 `index`处的记录。
316 |
317 | 抛出
318 |
319 | [**AssertionError**](https://docs.python.org/3/library/exceptions.html#AssertionError "(in Python v3.8)") – 若字段不可编辑
320 |
321 | ### 运行测试
322 |
323 | 如果在启动Odoo服务启用了[`--test-enable`](https://alanhou.org/odoo-13-command-line-interface-odoo-bin/#cmdoption-odoo-bin-test-enable),测试在安装或升级模块时自动运行。
324 |
325 | ### 测试选择
326 |
327 | 在Odoo中, Python测试可打标签,来便于在运行测试时选择测试。
328 |
329 | `odoo.tests.common.BaseCase` (通过通过 [`TransactionCase`](https://alanhou.org/odoo-13-testing-odoo/#odoo.tests.common.TransactionCase "odoo.tests.common.TransactionCase"), [`SavepointCase`](https://alanhou.org/odoo-13-testing-odoo/#odoo.tests.common.SavepointCase "odoo.tests.common.SavepointCase") 或 [`HttpCase`](https://alanhou.org/odoo-13-testing-odoo/#odoo.tests.common.HttpCase "odoo.tests.common.HttpCase")) 的子类自动使用 `standard`, `at_install` 及默认用它们的源模块的免进行打标签。
330 |
331 | #### 调用
332 |
333 | [`--test-tags`](https://alanhou.org/odoo-13-command-line-interface-odoo-bin/#cmdoption-odoo-bin-test-tags) 可用于选择/过滤在命令行中运行的测试。
334 |
335 | 这个选项默认为 `+standard` ,表示测试(显式或隐式地)使用 `standard` 打标签,在使用 [`--test-enable`](https://alanhou.org/odoo-13-command-line-interface-odoo-bin/#cmdoption-odoo-bin-test-enable)启动Odoo时默认运行。
336 |
337 | 在编写测试时,[`tagged()`](https://alanhou.org/odoo-13-testing-odoo/#odoo.tests.common.tagged "odoo.tests.common.tagged") 装饰器可用于**测试类**来添加或删除标签。
338 |
339 | 装饰器的参数为标签名,为字符串。
340 |
341 | ### 🚫危险
342 |
343 | [`tagged()`](#odoo.tests.common.tagged "odoo.tests.common.tagged")是一个类装饰器,对于函数或方法没有效果
344 |
345 | 标签可使用减号 (`-`) 作为前缀,来删除而非添加或选择它们,例:如果你不想要测试默认执行,可以删除 `standard` 标签:
346 |
347 | ```
348 | from odoo.tests import TransactionCase, tagged
349 |
350 | @tagged('-standard', 'nice')
351 | class NiceTest(TransactionCase):
352 | ...
353 | ```
354 |
355 | 这个测试默认不会选中,要运行它必须显式地选中相关标签:
356 |
357 | `$ odoo-bin --test-enable --test-tags nice`
358 |
359 | 注意只有添加了`nice` 标签的测试会进行执行。要同时运行 `nice` 和 `standard` 测试,为[`--test-tags`](#cmdoption-odoo-bin-test-tags)提供多个值:在命令行中,值是累加的 (你选择带有指定标签中的任意一个的所有测试)
360 |
361 | `$ odoo-bin --test-enable --test-tags nice,standard`
362 |
363 | 配置切换参数还接收 `+` 和 `-` 前缀。 `+` 前缀是暗含的,因此完全可选。即使由其它标签选中, `-` (减号) 前缀用于取消选择具有该前缀标签的测试, 例:如有 `standard` 的测试,还使用 `slow` 添加了标签,那么会运行除 slow 以外的所有标准测试:
364 |
365 | `$ odoo-bin --test-enable --test-tags 'standard,-slow'`
366 |
367 | 在编写不继承 `BaseCase`的测试时, 这个测试不会有默认标签,你需要显式地添加它们来让测试包含在默认测试套件中。这在使用简单的`unittest.TestCase` 时是一个普遍问题,因为它们不会进行运行:
368 |
369 | ```
370 | import unittest
371 | from odoo.tests import tagged
372 |
373 | @tagged('standard', 'at_install')
374 | class SmallTest(unittest.TestCase):
375 | ...
376 | ```
377 |
378 | #### 特殊标记
379 |
380 | * `standard`: 所有继承自`BaseCase` 的Odoo测试都隐式地打了standard标签。 [`--test-tags`](https://alanhou.org/odoo-13-command-line-interface-odoo-bin/#cmdoption-odoo-bin-test-tags)默认也为`standard`。这表示未打标签的测试在启用测试时会默认执行。
381 | * `at_install`: 表示测试会在该模块安装后及其它模块安装前执行。这是默认的隐式标签。
382 | * `post_install`: 表示测试会在所有模块安装完之后执行。对于大部分时候的HttpCase测试会希望这么做。注意这与`at_install`并不互斥,但通常不会需要两者同时使用,`post_install`通常在对测试类打标签时与 `-at_install` 配对。
383 | * *module_name*: Odoo测试类继承`BaseCase`,隐式地使用模块的技术名称打标签。这允许在测试时轻易地选择或排除具体模块,例:如果希望仅通过`stock_account`来运行测试:
384 |
385 | `$ odoo-bin --test-enable --test-tags stock_account`
386 |
387 | #### 示例
388 |
389 | ### ⚠️重要
390 |
391 | 测试仅在安装或升级模块时执行。因此模块需要通过 [`-u`](https://alanhou.org/odoo-13-command-line-interface-odoo-bin/#cmdoption-odoo-bin-u) 或 [`-i`](https://alanhou.org/odoo-13-command-line-interface-odoo-bin/#cmdoption-odoo-bin-i) 选项进行选中。为保持简洁,下面的示例中没有指定这些选项。
392 |
393 | 仅通过sale模块运行测试:
394 |
395 | `$ odoo-bin --test-enable --test-tags sale`
396 |
397 | 通过sale模块但不包含打了 slow 标签的内容运行测试:
398 |
399 | `$ odoo-bin --test-enable --test-tags 'sale,-slow'`
400 |
401 | 仅对stock或打了 slow 标签的运行测试:
402 |
403 | `$ odoo-bin --test-enable --test-tags '-standard, slow, stock'`
404 |
405 | `-standard` 是隐式的(非必须的),添加它是为了保持清晰
406 |
407 | ## 测试JS代码
408 |
409 | 测试复杂系统是防止退化的重要保障并可确保一些基本功能仍正常运行。因Odoo有大量的Javascript代码,很有必要对其进行测试。在这一小节中,我们将讨论在隔离的状态下测试 JS 代码的实践,这些测试只保留在浏览器层面,而不触达服务端。
410 |
411 | ### Qunit 测试套件
412 |
413 | Odoo框架使用[QUnit](https://qunitjs.com/) 库测试框架来作为测试运行器。QUnit定义了**测试**和**模组**(一组相关测试)的概念,并为我们提供了执行测试的web 界面。
414 |
415 | 例如,以下是 pyUtils测试运行的状况:
416 |
417 | ```
418 | QUnit.module('py_utils');
419 |
420 | QUnit.test('simple arithmetic', function (assert) {
421 | assert.expect(2);
422 |
423 | var result = pyUtils.py_eval("1 + 2");
424 | assert.strictEqual(result, 3, "should properly evaluate sum");
425 | result = pyUtils.py_eval("42 % 5");
426 | assert.strictEqual(result, 2, "should properly evaluate modulo operator");
427 | });
428 | ```
429 |
430 | 运行测试套件的主要方式是要有运行中的Odoo服务,然后在浏览器中导航至 `/web/tests`。然后测试套件会由浏览器的.Javascript引擎进行运行。
431 |
432 | 
433 |
434 | 网页 UI有很多有用的功能:它可以仅运行一些子模块,或过滤匹配某字符串的测试。它可以显示每个断言,不管成功或失败,重新运行具体测试, …
435 |
436 | ### ⚠️警告
437 |
438 | 在运行测试套件时,要确保:
439 |
440 | * 焦点在浏览器窗口中,
441 | * 未进行放大/缩放。需要是100%的精确原大小比例。
442 |
443 | 如果不是如此,有些测试会失败,不返回相当的说明。
444 |
445 | ### 测试基础框架
446 |
447 | 以下是对测试框架重要部分的高级别概览:
448 |
449 | * 有一个名为[web.js_tests_assets](https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/views/webclient_templates.xml#L427)的资源包。 这个包中包含主要代码 (通用资源+ 后台资源), 一些库, QUnit测试运行器, 及一些其它帮助方法代码
450 | * 另一个资源包[web.qunit_suite](https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/views/webclient_templates.xml#L509), 包含所有测试(及 js_tests_assets 代码)。几乎所有测试文都应添加到这个包里
451 | * 在网页中有一个[控制器](https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/controllers/main.py#L637),映射到路由 */web/tests*。这个控制器仅渲染 *web.qunit_suite* 模板。
452 | * 要执行测试,我们可以仅将浏览器指向路由 */web/tests*。这种情况下,浏览器会下载所有资源,而 QUnit会接管。
453 | * 在[qunit_config.js](https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/static/tests/helpers/qunit_config.js#L49)中有一些代码, 在测试成功或失败时会在控制台中记录一些信息。
454 | * 我们希望runbot也能运行这些测试, 因此(在 [test_js.py](https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/tests/test_js.py#L13)中)有一个测试,它只是产生一个浏览器并指向*web/tests* 链接。注意phantom_js 方法并不产生phantom_js,而是用的Chrome headless 。
455 |
456 | ### 模块化及测试
457 |
458 | 通过Odoo设计的方式,任何插件可修改系统中其它部分的行为。例如,*voip* 插件可修改 *FieldPhone* 张爱玲的来使用额外的功能。从测试系统的角度来看这并不是好事,因为这表示在插件网页中安装voip插件时测试会失败(注意runbot运行测试时安装了所有的插件)。
459 |
460 | 同时,我们的测试系统很棒,因为它会监测到其它影响核心功能的模块。对这一问题没有完整的方案。当前我们按具体情况逐一进行解决。
461 |
462 | 通常修改其它行为不是个好想法。就我们的voip示例来说,添加新的*FieldVOIPPhone组件*及修改需要它的一些视图显然更为清晰。通过这种方式,*FieldPhone* 组件不会受影响,两者均可进行测试。
463 |
464 | ### 新增测试用例
465 |
466 | 假设我们在维护一个插件*my_addon*,交且我们希望对一些javascript代码添加测试 (例如,位于*my_addon.utils*中的工具函数 myFunction)。添加u 新测试用例的流程如下:
467 |
468 | 1. 新建文件 *my_addon/static/tests/utils_tests.js*。该文件包含添加 QUnit 模块 *my_addon > utils*的基本代码。
469 | ```
470 | odoo.define('my_addon.utils_tests', function (require) {
471 | "use strict";
472 |
473 | var utils = require('my_addon.utils');
474 |
475 | QUnit.module('my_addon', {}, function () {
476 |
477 | QUnit.module('utils');
478 |
479 | });
480 | });
481 | ```
482 |
483 | 2. 在*my_addon/assets.xml*中,将该文件添加到主测试资源中:
484 | ```
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 | ```
494 |
495 | 3. 重启服务并更新*my_addon*,或通过界面进行 (来确保加载了新测试文件)
496 | 4. 在*utils* 子测试套件的定义之后添加一个测试用例:
497 |
498 | ```
499 | QUnit.test("some test case that we want to test", function (assert) {
500 | assert.expect(1);
501 |
502 | var result = utils.myFunction(someArgument);
503 | assert.strictEqual(result, expectedResult);
504 | > });
505 | ```
506 |
507 | 5. 访问*/web/tests/* 来确保执行了测试
508 |
509 | ### 帮助函数和特殊化断言
510 |
511 | 不借助帮助,很难测试到Odoo的某些部分。具体的说,视图很复杂,因其与服务端进行通讯并可能会执行一些rpc调用,这需要mock数据。这正是我们开发了一些特定帮助函数的原因,位于 [test_utils.js](https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/static/tests/helpers/test_utils.js)中。
512 |
513 | * Mock测试函数:这些函数帮助设置测试环境。最重要的用例是mock由Odoo服务所给定的结果。这些函数使用一个[mock服务](https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/static/tests/helpers/mock_server.js)。 这是一个模拟常用模型方法如read, search_read, nameget…的返回结果的 javascript类
514 | * DOM帮助函数:用于对具体目标模拟事件/动作。例如, testUtils.dom.click 对目标执行点击。注意这样比手动更为安全,因为它还检查目标是否存在及可见。
515 | * 创建帮助函数:它们可能是由[test_utils.js](https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/static/tests/helpers/test_utils.js)导出的最为重要的函数。 这些函数用于通过 mock 环境创建组件,以大量尽可能模拟真实条件的小细节。最重要的肯定是 [createView](https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/static/tests/helpers/test_utils_create.js#L267)。
516 | * [qunit断言](https://github.com/odoo/odoo/blob/51ee0c3cb59810449a60dae0b086b49b1ed6f946/addons/web/static/tests/helpers/qunit_asserts.js): QUnit可通过特定断言进行扩展。对于Odoo,我们经常测试一些DOM 属性。这是我们进行一些断言来进行协助的原因。例如,*containsOnce* 断言接收一个widget/jQuery/HtmlElement 和一个选择器,然后查看目标是否刚好匹配该 css 选择器。
517 |
518 | 例如,借助这些帮助函数,以下是简单的表单测试的示例:
519 |
520 | ```
521 | QUnit.test('simple group rendering', function (assert) {
522 | assert.expect(1);
523 |
524 | var form = testUtils.createView({
525 | View: FormView,
526 | model: 'partner',
527 | data: this.data,
528 | arch: '',
533 | res_id: 1,
534 | });
535 |
536 | assert.containsOnce(form, 'table.o_inner_group');
537 |
538 | form.destroy();
539 | });
540 | ```
541 |
542 | 注意使用testUtils.createView帮助函数及containsOnce断言。同时,表单控制在测试结束会进行相应的销毁。
543 |
544 | ### 最佳实践
545 |
546 | 以下并未进行排顺序:
547 |
548 | * 所有测试文件应在*some_addon/static/tests/*中进行添加
549 | * 对于漏洞修复,确保不进行漏洞修复时测试失败,修复时测试成功。这会确保其真实有效。
550 | * 尝试使用所需的最小化测试代码来让测试有效。
551 | * 通常,两个小测试胜于一个大测试。小测试更易于理解及修复。
552 | * 在测试后保持进行清理。例如,如果测试实例化了一个组件,应在结束时销毁它。
553 | * 无需要进行完整的代码覆盖。但添加一些测试会有所帮助:这让你的代码不会完全崩溃,并在测试修复时,它更易于向已有测试套件添加测试。
554 | * 如果想要查看一些负向断言(例如,HtmlElement 没有具体的 css 类), 那么尝试在相同测试中添加正向断言 (例如,通过执行一个修改该状态的动作)。这会帮助避免测试在未来无用 (例如在修改了css类时)。
555 |
556 | ### ℹ️小贴士
557 |
558 | * 仅运行一个测试:你可以 (临时!) 修改*QUnit.test(…)* 的定义到 *QUnit.only(…)*。这有助于确保QUnit 仅运行具体的测试。
559 | * debug 标记:大多数工具函数都有测试模式 (通过 debug: true 参数进行启用)。在这种情况下,目标组件会被放到DOM中而非隐藏的 qunit 具体夹具中,并且会记录更多信息。例如,所有的 mock网络通讯在控制台中都可用。
560 | * 在处理失败的测试时,通常会添加 debug 标记,然后注释测试的结尾 (具体地说是销毁的调用)。通过它,可以直接查看组件的状态,甚至能通过点击/与其交互来操作组件。
561 |
562 | ## 集成测试
563 |
564 | 分别Python代码和 JS代码非常有益,但无法证明网页客户端和服务端共同有效。为进行验证,我们可以编写另一种测试:导览。导览是一种有意义的业务流的小型场景。它说明了一系列应该遵循的步骤。然后测试运行器会创建一个phantom_js浏览器,指向相应的url并根据场景模拟点击和输入。
565 |
566 | ### 在browser_js测试时截屏和录屏
567 |
568 | 在通过命令行运行使用HttpCase.browser_js的测试时,Chrome浏览器在headless模式下运行。默认在测试失败时,会在失败的瞬间截一张PNGu 并保存在:
569 |
570 | ```
571 | '/tmp/odoo_tests/{db_name}/screenshots/'
572 | ```
573 |
574 | 自Odoo 13.0开始添加了两个控制这一行为命令行参数: [`--screenshots`](https://alanhou.org/odoo-13-command-line-interface-odoo-bin/#cmdoption-odoo-bin-screenshots) and [`--screencasts`](https://alanhou.org/odoo-13-command-line-interface-odoo-bin/#cmdoption-odoo-bin-screencasts)
--------------------------------------------------------------------------------
/reference/translating-modules.md:
--------------------------------------------------------------------------------
1 | # 模块翻译
2 |
3 | - 本文来自[Odoo 13官方文档之开发者文档](../README.md)系列文章
4 |
5 | 这部分讲解如何对自己的模块进行翻译。
6 |
7 | 如果想对Odoo本身进行翻译的藏南,请参见 [Odoo维基页面](https://github.com/odoo/odoo/wiki/Translations)。
8 |
9 | ## 导出可翻译词汇
10 |
11 | 你的模块中的词汇数结果是“隐式可翻译”, 即使你没有对翻译进行具体的操作也可以导出模块的可翻译词汇并能查找可操作的内容。
12 |
13 | 翻译通过登录后台界面并访问Settings ‣ Translations ‣ Import / Export ‣ Export Translations来导出
14 |
15 | - 将语言保持为默认 (新语言/空模板)
16 | - 选择[PO File](https://en.wikipedia.org/wiki/Gettext#Translating)格式
17 | - 选择模块
18 | - 点击Export下载文件
19 |
20 | [](https://www.odoo.com/documentation/13.0/_images/po-export.png)这会给出一个名为`*yourmodule*.pot` 的文件,应移动到 `*yourmodule*/i18n/` 目录。该文件是一个*PO 模板*,仅列出可翻译字段及通过什么创建实际翻译 (PO 文件)。PO文件可使用[msginit](https://www.gnu.org/software/gettext/manual/gettext.html#Creating)进行创建,有一个类似[POEdit](https://poedit.net/) 专门的翻译工具或者只是将模板拷贝到名为`*language*.po`的新文件。翻译文件应放到`*yourmodule*/i18n/`中,紧邻`*yourmodule*.pot`,并将在安装相应语言时自动(通过Settings ‣ Translations ‣ Languages)由Odoo加载
21 |
22 | 在安装或更新模块时所有已加载语言的翻译也会进行安装或更新
23 |
24 | ## 隐式导出
25 |
26 | Odoo自动从“data”类型内容导出可翻译字符串:
27 |
28 | - 在非QWeb视图中,会导出所有文本节点及`string`, `help`, `sum`, `confirm` 和 `placeholder` 属性的内容
29 |
30 | - QWeb模板(服务端及客户端),导出除 `t-translation="off"`代码块内的所有文本节点, `title`, `alt`, `label` 和 `placeholder`属性的内容也会进行导出
31 |
32 | - 对于
33 |
34 | `Field`
35 |
36 | , 除非它们的模型通过
37 |
38 | ```
39 | _translate = False
40 | ```
41 |
42 | 进行标记:
43 |
44 | - 其 `string` 和 `help` 属性会进行导出
45 | - 如出现 `selection` 及列表(或元组),会导出
46 | - 如若其`translate` 属性设置为 `True`,它们的所有(跨越所有记录的)已存在值会导出
47 |
48 | - `_constraints` 和 `_sql_constraints` 的帮助/错误信息会导出
49 |
50 | ## 显式导出
51 |
52 | 在Python或Javascript代码中更“迫切”的场景中, Odoo不能自动导出词,因此它们必须进行显式的标记来导出。这通过在函数调用中封装字面量字符串来实现。
53 |
54 | 在Python中,封装的函数是`odoo._()`:
55 |
56 | ```
57 | title = _("Bank Accounts")
58 | ```
59 |
60 | 在JavaScript中,封装的函数通常是 `odoo.web._t()`:
61 |
62 | ```
63 | title = _t("Bank Accounts");
64 | ```
65 |
66 | ### ⚠️警告
67 |
68 | 仅有字面量字符串可标记为导出,表达式或变量不行。对于字符串需格式化的场景,这意味着格式字符串而非格式化后的字符串必须进行标记
69 |
70 | `_` 和 `_t` 懒翻译版本为python中为 `odoo._lt()` ,在javascript中为 `odoo.web._lt()` 。翻译查找仅在渲染时查找并可用于声明全局变量类方法的可翻译属性。
71 |
72 | ### 变量
73 |
74 | **不要** 提取可能起作用但不会正确地翻译文本:
75 |
76 | ```
77 | _("Scheduled meeting with %s" % invitee.name)
78 | ```
79 |
80 | **要** 在翻译查找之外设置动态变量:
81 |
82 | ```
83 | _("Scheduled meeting with %s") % invitee.name
84 | ```
85 |
86 | ### 代码块
87 |
88 | **不要** 在一些代码块或多行中分隔翻译:
89 |
90 | ```
91 | # 不妥,后缀空格,上下文外的代码块
92 | _("You have ") + len(invoices) + _(" invoices waiting")
93 | _t("You have ") + invoices.length + _t(" invoices waiting");
94 |
95 | # 不妥, 多个碎片翻译
96 | _("Reference of the document that generated ") + \
97 | _("this sales order request.")
98 | ```
99 |
100 | **要** 保持在一个代码块中,将全文本给到翻译器:
101 |
102 | ```
103 | # 妥, 允许修改在翻译中数量的位置
104 | _("You have %s invoices wainting") % len(invoices)
105 | _.str.sprintf(_t("You have %s invoices wainting"), invoices.length);
106 |
107 | # 妥, 全句可理解
108 | _("Reference of the document that generated " + \
109 | "this sales order request.")
110 | ```
111 |
112 | ### 复数
113 |
114 | **不要** 按英文的方式设置词汇的复数:
115 |
116 | ```
117 | msg = _("You have %s invoice") % invoice_count
118 | if invoice_count > 1:
119 | msg += _("s")
120 | ```
121 |
122 | **要** 记住各种语言有不同的复数形式:
123 |
124 | ```
125 | if invoice_count > 1:
126 | msg = _("You have %s invoices") % invoice_count
127 | else:
128 | msg = _("You have %s invoice") % invoice_count
129 | ```
130 |
131 | ### 读取vs运行时间
132 |
133 | **不要** 在服务启动时调用翻译查询:
134 |
135 | ```
136 | ERROR_MESSAGE = {
137 | # 不妥, 没有用户语言时在服务端启动时运行
138 | 'access_error': _('Access Error'),
139 | 'missing_error': _('Missing Record'),
140 | }
141 |
142 | class Record(models.Model):
143 |
144 | def _raise_error(self, code):
145 | raise UserError(ERROR_MESSAGE[code])
146 | ```
147 |
148 | **不要** 在读取javascript文件时调用翻译查询:
149 |
150 | ```
151 | # 不妥, js _t 运行的过早
152 | var core = require('web.core');
153 | var _t = core._t;
154 | var map_title = {
155 | access_error: _t('Access Error'),
156 | missing_error: _t('Missing Record'),
157 | };
158 | ```
159 |
160 | **要** 使用懒翻译查找方法:
161 |
162 | ```
163 | ERROR_MESSAGE = {
164 | 'access_error': _lt('Access Error'),
165 | 'missing_error': _lt('Missing Record'),
166 | }
167 |
168 | class Record(models.Model):
169 |
170 | def _raise_error(self, code):
171 | # 在错误渲染时执行翻译查找
172 | raise UserError(ERROR_MESSAGE[code])
173 | ```
174 |
175 | 或 **要** 动态运行可翻译内容:
176 |
177 | ```
178 | # 妥,在运行时运行
179 | def _get_error_message(self):
180 | return {
181 | access_error: _('Access Error'),
182 | missing_error: _('Missing Record'),
183 | }
184 | ```
185 |
186 | **要** 在读取JS文件时翻译查找完成,在使用时用 `_lt` 而非 `_t`来翻译启发式规则:
187 |
188 | ```
189 | # 妥, js _lt 进行懒运行
190 | var core = require('web.core');
191 | var _lt = core._lt;
192 | var map_title = {
193 | access_error: _lt('Access Error'),
194 | missing_error: _lt('Missing Record'),
195 | };
196 | ```
--------------------------------------------------------------------------------
/reference/web-controllers.md:
--------------------------------------------------------------------------------
1 | # 网页控制器
2 |
3 | 本文来自[Odoo 13官方文档之开发者文档](../README.md)系列文章
4 |
5 | ## 控制器
6 |
7 | 控制器需要提供扩展性,很像 [`Model`](https://alanhou.org/odoo-13-orm-api/#odoo.models.Model),但无法使用相同的机制作为预设条件 (带有已加载模块的数据库) 尚不可使用(如未创建或未选择数据库)。
8 |
9 | 因此控制器具有其自己的扩展机制,与模型的机制相分离:
10 |
11 | 控制器通过[继承](https://docs.python.org/3/tutorial/classes.html#tut-inheritance) `Controller`来进行创建。 路由通过由 [`route()`](https://alanhou.org/odoo-13-web-controllers/#odoo.http.route)装饰的方法定义:
12 |
13 | ```
14 | class MyController(odoo.http.Controller):
15 | @route('/some_url', auth='public')
16 | def handler(self):
17 | return stuff()
18 | ```
19 |
20 | 要*重载*控制器,[继承](https://docs.python.org/3/tutorial/classes.html#tut-inheritance)其类并重载相关方法,如果需要重新暴露它们:
21 |
22 | ```
23 | class Extension(MyController):
24 | @route()
25 | def handler(self):
26 | do_before()
27 | return super(Extension, self).handler()
28 | ```
29 |
30 | - 有必要使用 [`route()`](https://alanhou.org/odoo-13-web-controllers/#odoo.http.route) 进行装饰来保持方法(及路由)可见:如方法未进行装饰就重新定义,会变成“未发布”
31 |
32 | - 合并所有方法的装饰器,如重载方法的装饰器没有参数则会保留此前的所有参数,所提供的任意参数会覆盖此前所定义参数,例:
33 |
34 | ```
35 | class Restrict(MyController):
36 | @route(auth='user')
37 | def handler(self):
38 | return super(Restrict, self).handler()
39 | ```
40 |
41 | 会修改对用户公共认证的`/some_url` (要求登录)
42 |
43 | ## API
44 |
45 |
46 |
47 | ### 路由
48 |
49 | ###### `odoo.http.route(*route=None*, ***kw*)`
50 |
51 | 装饰器让所装饰的方法成为请求的处理器。该方法必须要是`Controller`子类的一部分。
52 |
53 | 参数
54 |
55 | - **route** – 字符串或数组。决定哪种http请求匹配所装饰方法的路由部分。可以是单个字符串或字符串数组。参见 werkzeug的路由文档了解路由表达式的格式( http://werkzeug.pocoo.org/docs/routing/ )。
56 |
57 | - **type** – 请求的类型,可为`'http'` 或 `'json'`.
58 |
59 | - auth
60 |
61 | – 认证方法的类型,可为以下类型:
62 |
63 | - `user`: 用户必须认证且当前请求将使用用户的权限进行执行。
64 | - `public`: 用户可认证也可不认证。如未认证,当前请求会使用共享Public用户进行执行。
65 | - `none`: 即使用没有数据库,方法也一直是活跃的。主要由框架和认证模块使用。它们的请求代码对访问数据库没有任何作用,也没有表明当前数据库或当前用户的配置。
66 |
67 | - **methods** – 一个这个路由所应用的http方法的序列。如未指定,允许使用所有方法。
68 |
69 | - **cors** – Access-Control-Allow-Origin cors 指令值。
70 |
71 | - **csrf** ([`bool`](https://docs.python.org/3/library/functions.html#bool)) – 是否应为路由启用CSRF保护。默认为 `True`。参见[CSRF保护](https://alanhou.org/odoo-13-web-controllers/#csrf)了解更多内容。
72 |
73 | ### CSRF保护
74 |
75 | 版本9.0中新增。
76 |
77 | Odoo实现基于令牌的[CSRF保护](https://en.wikipedia.org/wiki/CSRF)。
78 |
79 | CSRF保护默认启用并应用于[**RFC 7231**](https://tools.ietf.org/html/rfc7231.html)中所定义的*不安全* HTTP 方法 ( `GET`, `HEAD`, `TRACE` 和 `OPTIONS`之外的所有方法)。
80 |
81 | CSRF保护通过名为`csrf_token`值作为请求表单数据的一部分以检查请求来实现。 该值作为验证的一部分从表单中删除并在你自己的表单处理过程中不进行考虑。
82 |
83 | 在为不安全方法(大多为表单等的POST)新增控制器时:
84 |
85 | - 如果表单在Python中生成,csrf令牌借由`request.csrf_token() {"jsonrpc": "2.0",
230 | "method": "call",
231 | "params": {"context": {},
232 | "arg1": "val1" },
233 | "id": null}
234 |
235 | <-- {"jsonrpc": "2.0",
236 | "result": { "res1": "val1" },
237 | "id": null}
238 | ```
239 |
240 | 产生错误的请求:
241 |
242 | ```
243 | --> {"jsonrpc": "2.0",
244 | "method": "call",
245 | "params": {"context": {},
246 | "arg1": "val1" },
247 | "id": null}
248 |
249 | <-- {"jsonrpc": "2.0",
250 | "error": {"code": 1,
251 | "message": "End user error message.",
252 | "data": {"code": "codestring",
253 | "debug": "traceback" } },
254 | "id": null}
255 | ```
256 |
257 | ### 响应
258 |
259 | ###### `*class* odoo.http.Response(**args*, ***kw*)`
260 |
261 | 通过控制器路由链传递的Response对象。
262 |
263 | [`werkzeug.wrappers.Response`](https://werkzeug.palletsprojects.com/en/0.16.x/wrappers/#werkzeug.wrappers.Response) 参数之外,这个类的构造方法可针对QWeb懒渲染接收如下的额外参数。
264 |
265 | 参数
266 |
267 | - **template** (`basestring`) – 等渲染的模板
268 | - **qcontext** ([`dict`](https://docs.python.org/3/library/stdtypes.html#dict)) – 要使用的渲染上下文
269 | - **uid** ([`int`](https://docs.python.org/3/library/functions.html#int)) – 针对ir.ui.view渲染调用所使用的用户 id,`None` 时使用请求的用户 (默认)
270 |
271 | 这些属性在Response对象中作为参数使用且可在渲染之前的任意时刻进行更改
272 |
273 | 还暴露[`werkzeug.wrappers.Response`](https://werkzeug.palletsprojects.com/en/0.16.x/wrappers/#werkzeug.wrappers.Response)的所有属性和方法。
274 |
275 | ###### `render()`
276 |
277 | 渲染Response的模板,返回结果
278 |
279 | ###### `flatten()`
280 |
281 | 强制渲染响应模板,设置结果为响应体并清除`template`
--------------------------------------------------------------------------------
/setting-up/community-enterprise.md:
--------------------------------------------------------------------------------
1 | # 社区版转企业版
2 |
3 | 本文来自[Odoo 13官方文档之开发者文档](../README.md)系列文章
4 |
5 | 根据当前的安装方式,有多种升级社区版的方法。各种情况下的基本指导思想是:
6 |
7 | - 备份社区版数据库
8 | - 关闭服务
9 | - 安装web_enterprise模块
10 | - 重启服务
11 | - 输入Odoo企业版订阅码
12 |
13 | 
14 |
15 | ## 在Linux中使用安装包
16 |
17 | - 备份社区版数据库
18 |
19 | - 停止odoo服务
20 |
21 | ```
22 | $ sudo service odoo stop
23 | ```
24 |
25 | - 安装企业版 .deb (应该在社区安装包之上安装)
26 |
27 | ```
28 | $ sudo dpkg -i
29 | ```
30 |
31 | - 使用如下命令更新数据库为企业版安装包
32 |
33 | ```
34 | $ python3 /usr/bin/odoo-bin -d -i web_enterprise --stop-after-init
35 | ```
36 |
37 | - 应当能够使用常规的认证方式连接到 Odoo企业版实例。然后你可以通过在表单输入框中输入邮件中所收到的订阅码将数据库与你的Odoo企业版订阅进行关联。
38 |
39 | ## 在Linux中使用源代码
40 |
41 | 有很多种使用源码启动服务器的方式,你可能已有自己的偏好。可以将下面部分调整为你自己的常规工作流。
42 |
43 | - 关闭你的服务
44 |
45 | - 备份社区版数据库
46 |
47 | - 更新启动命令中的 `--addons-path` 参数 (参见 [源码安装](https://alanhou.org/odoo-13-installing-odoo/#setup-install-source))
48 |
49 | - 通过使用如下命令安装 web_enterprise 模块
50 |
51 | ```
52 | $ -d -i web_enterprise --stop-after-init
53 | ```
54 |
55 | 根据数据库的大小不同,可能会花费一些时间。
56 |
57 | - 使用第3点中更新的addons路径重新启动服务。 此时应该可以连接到实例。可以通过在表单输入框中输入邮件中所收到的订阅码将数据库与你的Odoo企业版订阅进行关联。
58 |
59 | ## 在Windows上
60 |
61 | - 备份社区版数据库
62 |
63 | - 卸载Odoo 社区版 (使用安装文件夹中的 Uninstall可执行文件) - PostgreSQL将保持已安装状态
64 |
65 | - 启动 Odoo企业版安装器并按照常规步骤执行。在选择安装路径时,可以设置社区版软件的文件夹(这个文件夹中还包含PostgreSQL软件 )。 在安装的最后取消勾选 `Start Odoo` 
66 |
67 | - 使用命令行窗口,使用下面的命令(通过服务的子文件夹中的Odoo 安装路径)更新Odoo数据库
68 |
69 | ```
70 | $ odoo.exe -d -i web_enterprise --stop-after-init
71 | ```
72 |
73 | - 无需手动启动服务,服务已运行。这时应该可以使用常规的认证方式连接到Odoo企业版实例了。然后可以通过在表单输入框中输入邮件中所收到的订阅码将数据库与你的Odoo企业版订阅进行关联。
--------------------------------------------------------------------------------
/setting-up/deploying-content-delivery-networks.md:
--------------------------------------------------------------------------------
1 | # 通过 CDN 进行部署
2 |
3 | 本文来自[Odoo 13官方文档之开发者文档](https://alanhou.org/odoo-13-developer-documentation/)系列文章
4 |
5 | ## 通过[KeyCDN](https://www.keycdn.com/)进行部署
6 |
7 | 本文档将指导你为Odoo网站配置一个 [KeyCDN](https://www.keycdn.com/) 账户。
8 |
9 | ### 第1步:在KeyCDN仪表盘中创建一个pull 区
10 |
11 | 创建该区时,在高级功能子菜单中启用CORS选择。 (更多参见后述内容)
12 |
13 | 一旦完成后,需要等待片刻,此时[KeyCDN](https://www.keycdn.com/)会爬取你的网站。
14 |
15 | 
16 |
17 | 会为你的Zone生成一个新的 URL,本例中链接为 `http://pulltest-b49.kxcdn.com`
18 |
19 | ### 第2步:使用你的zone 配置odoo实例
20 |
21 | 在Odoo后台中,进入到网站设置(Website Settings)菜单,然后启用CDN并复制/拷贝你的zone URL到 CDN Base URL字段中。该字段仅在启用了开发者模式之后才可见并可配置。
22 |
23 | 现在你的网站会对匹配CDN过滤器正则表达式的资源使用 CDN。
24 |
25 | 可以查看网站的HTML源码来确定是否正确地进行了CDN集成。
26 |
27 | 
28 |
29 | ### 为什么要激活CORS?
30 |
31 | 在一些浏览器(编写本文档时有Firefox 和 Chrome )的安全限制中会防止远程链接的CSS文件获取该外部服务器上的相对资源。
32 |
33 | 如未在CDN区中启用 CORS选项,默认Odoo网站上最明显的问题是缺少 font-awesome图标,因为在 font-awesome的CSS中声明字体文件无法通过远程服务器进行加载。
34 |
35 | 以下是在这种情况下首页的样子:
36 |
37 | 一条安全错误信息会出现在浏览器的控制台中:
38 |
39 | 在 CDN 中启用CORS选择可解决这一问题。
--------------------------------------------------------------------------------
/setting-up/deploying-odoo.md:
--------------------------------------------------------------------------------
1 | # 部署Odoo
2 |
3 | 本文来自[Odoo 13官方文档之开发者文档](../README.md)系列文章
4 |
5 | 本文档描述在生产或面向因特网的环境下配置Odoo的步骤。它紧承[安装](https://alanhou.org/odoo-13-installing-odoo/)一节,对于不暴露到互联网的开发系统并不是很有必要。
6 |
7 | ### ⚠️警告
8 |
9 | 如果在建立对外服务,确保查看 [安全权限](#security)中的建议!
10 |
11 |
12 |
13 | ## dbfilter
14 |
15 | Odoo是一套多租户系统:单个 Odoo系统可在多个服务库实际上运行并提供服务。它也是高度可定制的,自定义内容(通过所加载的模块开始)可在“当前数据库”中实现。
16 |
17 | 这对于以公司用户在后台(客户端)登录时并不会成为问题:数据库可在登录时进行选择,并在之后加载自定义内容。
18 |
19 | 但对于未登录的用户(门户、网站)则会形成问题,他们未与数据库进行绑定:Odoo需要知道应当用于加载网页或执行操作的数据库。未使用多租户不会构成问题,这时仅有一个数据库,但如果存在多个数据库时, Odoo需要知道该使用哪个数据库的规则。
20 |
21 | 这也是使用 [`--db-filter`](https://alanhou.org/odoo-13-command-line-interface-odoo-bin/#cmdoption-odoo-bin-db-filter)的目的之一:它指定应如何根据所请求的主机名(域名)来选择数据。其值是一个 [正则表达式](https://docs.python.org/3/library/re.html),可能包含动态注入的用于访问系统的主机名 (`%h`) 或首个子域名 (`%d`) 。
22 |
23 | 对于在生产环境托管多个数据库的服务器,尤其是那些使用了 `website` 模块的,**必须**要设置dbfilter,否则一些功能将无法正确运行。
24 |
25 | ### 配置样例
26 |
27 | - 显示仅以‘mycompany’名称开头的数据库
28 |
29 | 在 `/etc/odoo.conf` 中设置:
30 |
31 | ```
32 | [options]
33 | dbfilter = ^mycompany.*$
34 | ```
35 |
36 | - 仅显示匹配 `www`之后首个子域名的数据库:例如如果请求是发送到 `www.mycompany.com` 或 `mycompany.co.uk`而非`www2.mycompany.com` 或 `helpdesk.mycompany.com`的话则会显示“mycompany”数据库
37 |
38 | 在 `/etc/odoo.conf` 中设置:
39 |
40 | ```
41 | [options]
42 | dbfilter = ^%d$
43 | ```
44 |
45 | 设置适当的 [`--db-filter`](https://alanhou.org/odoo-13-command-line-interface-odoo-bin/#cmdoption-odoo-bin-db-filter) 是确保部署正常的一个重要部分。一旦正确运行且每个主机名仅匹配一个数据库的话,强烈推荐限制对数据库管理器界面的访问,可以使用 `--no-database-list` 启动参数在防止列出数据库,并阻止对数据库管理界面的访问,参见 [权限安全](#security)。
46 |
47 | ## PostgreSQL
48 |
49 | 默认,PostgreSQL仅允许通过UNIX套接字及回环地址(PostgreSQL服务所安装的同一台机器,使用localhost)连接来进行连接。
50 |
51 | 如果Odoo和PostgreSQL在同一台机器上执行的话UNIX套接字是没有问题的,并且也是在未提供主机参数时的默认值,但如果希望Odoo 和 PostgreSQL在不同机器上进行执行 [1](#different-machines),它就需要通过之一[监听网卡](https://www.postgresql.org/docs/9.6/static/runtime-config-connection.html) [2](#remote-socket):
52 |
53 | - 仅接受回环连接并在Odoo和PostgreSQL所运行的机器之间[使用一条SSH通道](https://www.postgresql.org/docs/9.6/static/ssh-tunnels.html),然后配置Odoo来连接通道的另一端
54 | - 接受来自Odoo所安装的机器的连接,可能是通过ssl (参见 [PostgreSQL连接设置](https://www.postgresql.org/docs/9.6/static/runtime-config-connection.html)了解详情),然后配置Odoo来在网络上进行连接
55 |
56 | ### 配置样例
57 |
58 | - 允许在localhost上的tcp连接
59 | - 允许来自网络上192.168.1.x 的tcp连接
60 |
61 | 在`/etc/postgresql/9.5/main/pg_hba.conf` 中设置:
62 |
63 | ```
64 | # IPv4 local connections:
65 | host all all 127.0.0.1/32 md5
66 | host all all 192.168.1.0/24 md5
67 | ```
68 |
69 | 在 `/etc/postgresql/9.5/main/postgresql.conf` 中设置:
70 |
71 | ```
72 | listen_addresses = 'localhost,192.168.1.2'
73 | port = 5432
74 | max_connections = 80
75 | ```
76 |
77 |
78 |
79 | ### 配置Odoo
80 |
81 | 默认,Odoo在5432端口上通过UNIX套接字连接本地postgres。这对Postgres不在本地部署Postgres或软件不使用默认配置时可以使用[数据库选项](https://alanhou.org/odoo-13-command-line-interface-odoo-bin/#reference-cmdline-server-database)进行重置。[安装包安装](https://alanhou.org/odoo-13-installing-odoo/#setup-install-packaged)时会自动新建用户 (`odoo`) 并将其设置为数据库用户。
82 |
83 | - 数据库管理界面由 `admin_passwd` 设置进行保护。这一设置仅能通过配置文件进行设置,并且仅在执行数据库修改前进行检查。可以设置为一个随机生成的值来确保第三方不会使用到这个界面。
84 |
85 | - 所有的数据库操作使用 [数据库选项](https://alanhou.org/odoo-13-command-line-interface-odoo-bin/#reference-cmdline-server-database),包括数据库管理界面。要让数据库管理界面运行,要求 PostgreSQL 用户拥有 `createdb` 权限。
86 |
87 | - 用户总是可以删除他们所拥有的数据库。要让数据库管理界面完成不可使用,PostgreSQL用户需要通过
88 |
89 | ```
90 | no-createdb
91 | ```
92 |
93 | 创建数据库,并且该数据库必须要为另一个PostgreSQL 用户所持有。
94 |
95 | ### ⚠️警告
96 |
97 | 该PostgreSQL用户*必须*不能是超级用户
98 |
99 | #### 配置样例
100 |
101 | - 连接192.168.1.2上的PostgreSQL服务
102 | - 端口为 5432
103 | - 使用 ‘odoo’ 用户
104 | - 通过‘pwd’ 来作为密码
105 | - 过滤出以‘mycompany’名称开头的数据库
106 |
107 | 在 `/etc/odoo.conf` 中设置:
108 |
109 | ```
110 | [options]
111 | admin_passwd = mysupersecretpassword
112 | db_host = 192.168.1.2
113 | db_port = 5432
114 | db_user = odoo
115 | db_password = pwd
116 | dbfilter = ^mycompany.*$
117 | ```
118 |
119 |
120 |
121 | ### Odoo和PostgreSQL之间的SSL
122 |
123 | 自从Odoo 11.0开始,可以强制在Odoo 和 PostgreSQL之间使用ssl连接,在 Odoo中, db_sslmode控制着ssl安全连接,值有‘disable’, ‘allow’, ‘prefer’, ‘require’, ‘verify-ca’ 或 ‘verify-full’
124 |
125 | [PostgreSQL 文档](https://www.postgresql.org/docs/current/static/libpq-ssl.html)
126 |
127 |
128 |
129 | ## 内置服务端
130 |
131 | Odoo包含一个内置的HTTP服务端,可以使用多线程或多进程。
132 |
133 | 在生产使用中,推荐使用多进程服务端,因其增强了稳定性,让其更好的利用了计算资源并可以更好地进行监控和资源管控。
134 |
135 | - 多进程通过配置 [`worker进程数据为非0的数字`](https://alanhou.org/odoo-13-command-line-interface-odoo-bin/#cmdoption-odoo-bin-workers)来启用, worker的数量应根据服务器的核数来计算(可能要根据预期有多少个cron任务来为cron的worker预留空间)
136 | - Worker的限定可根据硬件配置来设置,这样才能避免资源耗尽
137 |
138 | ### ⚠️警告
139 |
140 | 多进程模式当前在Windows中尚无法使用
141 |
142 | ### Worker数量计算
143 |
144 | - 基本准备则 : (#CPU * 2) + 1
145 | - Cron worker需要使用CPU
146 | - 1 worker ~= 6个并发用户
147 |
148 | ### 内存大小的计算
149 |
150 | - 我们认为20%的请求是复杂的重度请求,而80%是简单请求
151 | - 重度worker,在对所有的计算字段及SQL请求...都进行了良好设计时, 可能会消耗约1G的内存
152 | - 轻量的worker,在相同的场景下,预计会消耗约150MB 的 RAM
153 |
154 | 所需RAM = #worker * ( (light_worker_ratio * light_worker_ram_estimation) + (heavy_worker_ratio * heavy_worker_ram_estimation) )
155 |
156 | ### LiveChat
157 |
158 | 在多进程中,会自动启动一个独立的LiveChat工作进程并监听[`长轮询端口`](https://alanhou.org/odoo-13-command-line-interface-odoo-bin/#cmdoption-odoo-bin-longpolling-port) 但客户端不会对其进行连接。
159 |
160 | 而你必须要有一个将以 `/longpolling/` 开头的URL请求重定向到长轮询端口的代理。其它的请求应当代理至[`普通HTTP端口`](https://alanhou.org/odoo-13-command-line-interface-odoo-bin/#cmdoption-odoo-bin-http-port)。
161 |
162 | 要实现这点,你需要在Odoo之前部署一个反向代理 ,比如Nginx或Apache。这么做时,需要转发更多的http头给Odoo,并在Odoo中激活proxy_mode来让 Odoo读取这些头部信息。
163 |
164 | ### 配置样例
165 |
166 | - 4 CPU, 8 线的服务器
167 | - 60个并发用户
168 | - 60 个用户 / 6 = 10 <- 理论上所需的worker数量
169 | - (4 * 2) + 1 = 9 <-理论最大worker数
170 | - 我们将使用8个worker + 1个用于cron。我们还将使用监控系统来测量cpu负载,并检查其是否在 7 和 7.5之间。
171 | - RAM = 9 * ((0.8*150) + (0.2*1024)) ~= 3个针对Odoo的Go RAM
172 |
173 | `/etc/odoo.conf`中的配置:
174 |
175 | ```
176 | [options]
177 | limit_memory_hard = 1677721600
178 | limit_memory_soft = 629145600
179 | limit_request = 8192
180 | limit_time_cpu = 600
181 | limit_time_real = 1200
182 | max_cron_threads = 1
183 | workers = 8
184 | ```
185 |
186 |
187 |
188 | ## HTTPS
189 |
190 | 不论是通过网站/网页客户端还是网页服务来进行访问,Odoo都以明文传输认证信息。这表示Odoo的安全部署必须使用 HTTPS[3 ](#switching)。SSL终端可通过任意SSL终端代理来实现,但要求进行如下设置:
191 |
192 | - 启用Odoo的[`代理模式`](https://alanhou.org/odoo-13-command-line-interface-odoo-bin/#cmdoption-odoo-bin-proxy-mode)。这应当仅在Odoo位于反向代理之后时启用
193 | - 设置SSL终端代理([Nginx终端示例](https://nginx.com/resources/admin-guide/nginx-ssl-termination/))
194 | - 设置代理本身 ([Nginx代理示例](https://nginx.com/resources/admin-guide/reverse-proxy/))
195 | - 你的SSL终端代理也就自动重定向非安全连接到安全端口上
196 |
197 | ### ⚠️警告
198 |
199 | 万一你使用 [POSBox](https://www.odoo.com/page/point-of-sale-hardware#part_2)来配合销售点(POS)模块,则必须对`/pos/web` 路由禁用HTTPS配置,以避免混合内容的错误。
200 |
201 | ### 配置样例
202 |
203 | - 重定向http请求到 https
204 | - 重定向请求至odoo
205 |
206 | 在 `/etc/odoo.conf`中设置:
207 |
208 | ```
209 | proxy_mode = True
210 | ```
211 |
212 | 在 `/etc/nginx/sites-enabled/odoo.conf` 中设置:
213 |
214 | ```
215 | #odoo server
216 | upstream odoo {
217 | server 127.0.0.1:8069;
218 | }
219 | upstream odoochat {
220 | server 127.0.0.1:8072;
221 | }
222 |
223 | # http -> https
224 | server {
225 | listen 80;
226 | server_name odoo.mycompany.com;
227 | rewrite ^(.*) https://$host$1 permanent;
228 | }
229 |
230 | server {
231 | listen 443;
232 | server_name odoo.mycompany.com;
233 | proxy_read_timeout 720s;
234 | proxy_connect_timeout 720s;
235 | proxy_send_timeout 720s;
236 |
237 | # Add Headers for odoo proxy mode
238 | proxy_set_header X-Forwarded-Host $host;
239 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
240 | proxy_set_header X-Forwarded-Proto $scheme;
241 | proxy_set_header X-Real-IP $remote_addr;
242 |
243 | # SSL parameters
244 | ssl on;
245 | ssl_certificate /etc/ssl/nginx/server.crt;
246 | ssl_certificate_key /etc/ssl/nginx/server.key;
247 | ssl_session_timeout 30m;
248 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
249 | ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
250 | ssl_prefer_server_ciphers on;
251 |
252 | # log
253 | access_log /var/log/nginx/odoo.access.log;
254 | error_log /var/log/nginx/odoo.error.log;
255 |
256 | # Redirect longpoll requests to odoo longpolling port
257 | location /longpolling {
258 | proxy_pass http://odoochat;
259 | }
260 |
261 | # Redirect requests to odoo backend server
262 | location / {
263 | proxy_redirect off;
264 | proxy_pass http://odoo;
265 | }
266 |
267 | # common gzip
268 | gzip_types text/css text/scss text/plain text/xml application/xml application/json application/javascript;
269 | gzip on;
270 | }
271 | ```
272 |
273 | ## 运行 Odoo为WSGI应用
274 |
275 | 也可以将Odoo挂载为标准的 [WSGI](https://wsgi.readthedocs.org/) 应用。Odoo 通过`odoo-wsgi.example.py`提供了WSGI的基础启动脚本。该脚本应当进行自定义 (可能是将其从配置目录拷贝出来)以在 `odoo.tools.config`中进行正确的配置,而不是通过命令行或配置文件配置。
276 |
277 | WSGI服务仅向网页客户端、网站及网页服务 API暴露主要的HTTP 端点。因Odoo不再控制worker的创建,它法再设置cron或 livechat工作进程。
278 |
279 | ### Cron工作进程
280 |
281 | 要将Odoo部署的cron任务运行为WSGI应用,要求:
282 |
283 | - 经典的Odoo ( 通过`odoo-bin`运行)
284 | - 连接到cron所要运行的数据库 (通过 [`odoo-bin -d`](https://alanhou.org/odoo-13-command-line-interface-odoo-bin/#cmdoption-odoo-bin-d))
285 | - 不应暴露到网络中。要确保cron运行器不能通过网络访问,可以通过 [`odoo-bin --no-http`](https://alanhou.org/odoo-13-command-line-interface-odoo-bin/#cmdoption-odoo-bin-no-http) 或在配置文件中设置 `http_enable = False` 完全禁用内置HTTP服务
286 |
287 | ### LiveChat
288 |
289 | 第二个 WSGI部署会存在问题的子系统是LiveChat:这里大部分 HTTP连接是相对较短的,并且为下一个请求快速清理worker进程, LiveChat要求对每个客户端的长连接以实现接近补时的通知。
290 |
291 | 这与基于进程的worker模型是相悖的,因为它会绑住 worker进程并阻止新用户访问系统。但时,这些长连接所做的事甚少,大多数时间停在那里等待通知。
292 |
293 | 在WSGI中支持livechat/通知的方案是:
294 |
295 | - 部署一个Odoo的线程版本 (代替基于进程的预创建版本)并仅将以 `/longpolling/` 开头的 URL 重定向到Odoo,这是最简单的并且长轮询URL会叠加成cron实例。
296 | - 通过 `odoo-gevent` 部署带事件Odoo并代理以 `/longpolling/`开头的请求至 [`长轮询端口`](https://alanhou.org/odoo-13-command-line-interface-odoo-bin/#cmdoption-odoo-bin-longpolling-port)。
297 |
298 | ## 静态文件服务
299 |
300 | 为开发方便,Odoo直接在模块中提供静态文件服务。这在性能方面可能并不算理想,因为静态文件通常会通过静态HTTP服务器来提供服务。
301 |
302 | Odoo静态文件位于每个模块的 `static/` 文件夹中,因此静态文件可通过将所有请求拦截至 `/*MODULE*/static/*FILE*`来进行服务的提供,并在各个addon路径中查找正确的模块(及文件)。
303 |
304 |
305 |
306 | ## 安全权限
307 |
308 | 对于入门人员,请记住保证信息系统的安全是一个持之以恒的过程,没有一劳永逸的法门。此时,你的安全状况和环境中最脆弱的链接如出一辙。
309 |
310 | 因此不要把本部分看作能够防止所有安全问题的终极宝典。它仅是对在安全动作计划中首要需做的事情的总结。其余的自于你的操作系统及发行版的最佳安全实践,这些最佳实践有用户、密码及访问控制管理等。
311 |
312 | 在部署面向互联网的服务时,应确保考虑如下安全相关的主题:
313 |
314 | - 保持设置一个复杂的超级管理员admin的密码,并在建立系统伊始就限制对数据库管理页面的访问。参见 [数据库管理器权限](#db-manager-security)。
315 | - 为所有的数据库管理员账户选择独立的登录名及复杂密码。不要使用‘admin’作为登录名。 不要使用这些登录名进行日常运维,仅在控制/管理软件时进行使用。*永远不要*使用默认密码,如admin/admin,即使在测试/上线前数据库中也不要使用。
316 | - **不要**在面向互联网的服务中安装 demo数据。带有演示数据的数据库包含默认登录名和密码,即使在上线前/dev 系统中也可能会用于进入你的系统并产生巨大的麻烦。
317 | - 使用适当的数据库过滤器( [`--db-filter`](https://alanhou.org/odoo-13-command-line-interface-odoo-bin/#cmdoption-odoo-bin-db-filter)) 来根据主机名限制你的数据识图的可见性。参见[dbfilter](#db-filter)。你也可以使用[`-d`](https://alanhou.org/odoo-13-command-line-interface-odoo-bin/#cmdoption-odoo-bin-d) 来提供自己的 (逗号分隔) 列表来用于过滤可用的数据库,而不是让系统获取数据库后台中的所有数据库。
318 | - 一旦配置了 `db_name` 和 `db_filter` 并仅对每个主机名匹配单个数据库,应当设置 `list_db` 配置项为 `False`来防止整个列出所有气数库,并阻止对数据库管理界面的访问(也可使用 [`--no-database-list`](https://alanhou.org/odoo-13-command-line-interface-odoo-bin/#cmdoption-odoo-bin-no-database-list) 命名行选项进行实现)
319 | - 确保PostgreSQL用户 ([`--db_user`](https://alanhou.org/odoo-13-command-line-interface-odoo-bin/#cmdoption-odoo-bin-r)) 不是超级用户,并且数据库为另一个用户所持有。例如,如果你在使用独立非特权的`db_user`时可能会由 `postgres` 超级用户所拥有。参见 [配置Odoo](#setup-deploy-odoo)。
320 | - 通过 GitHub或从 https://www.odoo.com/page/download 及[http://nightly.odoo.com](http://nightly.odoo.com/)下载最新版本定期安装最新构建在保持软件的更新。
321 | - 使用多进程模式配置服务端,做与你的日常使用相匹配的适当限制 (内存y/CPU/超时)。参见 [内置服务器](#builtin-server)。
322 | - 使用有效的 SSL 证书在提供 HTTPS 终端的网页服务端背后台运行Odoo,以防止对纯文本通讯的窃听。SSL证书很便宜,并且还存在很多免费的证书。配置web 代理来限制请求的大小、设置适当的超时,然后启用[`proxy mode`](https://alanhou.org/odoo-13-command-line-interface-odoo-bin/#cmdoption-odoo-bin-proxy-mode) (代理模式)选项。参见 [HTTPS](#https-proxy)。
323 | - 如果需要允许对服务端的远程 SSH访问,确保对**所有**账户而非`root`仅仅设置复杂密码。强烈推荐完全禁止基于密码的验证,而只使用公钥验证。同时考虑限制通过VPN的访问,仅允许防火墙中受信 IP 的访问,可同时运行诸如 `fail2ban` 或相似的暴力破解监测系统。
324 | - 考虑在代理或防火墙上实施相应的频率限制,以防止暴力破解攻击及DoS(拒绝服务)攻击。参见 [防止暴力攻击](#login-brute-force) 获取更多具体的方法。很多网络提供商提供对DDoS(分布式拒绝服务攻击)的自动防护,但这通常是可选的服务,请进行咨询了解更多详情。
325 | - 只有条件允许,请将面向公众的 demo/test/staging 实例放在与生产实际不同的机器上。并应用与生产相同的安全防护。
326 | - 如果托管着多个客户,使用容器或相应的“jail”技术来相互隔离用户数据及文件。
327 | - 设置对数据库和文件存储数据的日常备份,并将它们拷贝到服务端自身无法访问到的远程存储服务器。
328 |
329 |
330 |
331 | ### 防止暴力攻击
332 |
333 | 对于面向因特网的部署,针对用户密码的暴力破解攻击很常见,这一威胁在 Odoo 服务端也不应忽视。在尝试执行登录时Odoo发送一条日志记录,并报告结果:成功或失败,以及目标登录名和源 IP。
334 |
335 | 日志记录有如下的形式:
336 |
337 | 登录失败:
338 |
339 | ```
340 | 2018-07-05 14:56:31,506 24849 INFO db_name odoo.addons.base.res.res_users: Login failed for db:db_name login:admin from 127.0.0.1
341 | ```
342 |
343 | 登录成功:
344 |
345 | ```
346 | 2018-07-05 14:56:31,506 24849 INFO db_name odoo.addons.base.res.res_users: Login successful for db:db_name login:admin from 127.0.0.1
347 | ```
348 |
349 | 这些日志可轻易地由 `fail2ban`等入侵防护系统进行分析。
350 |
351 | 例如,如下的 fail2ban过滤器定义应当会匹配一次登录失败:
352 |
353 | ```
354 | [Definition]
355 | failregex = ^ \d+ INFO \S+ \S+ Login failed for db:\S+ login:\S+ from
356 | ignoreregex =
357 | ```
358 |
359 | 这可用于 jail定义来禁用对 HTTP(S)进行攻击的IP。
360 |
361 | 以下是在监测到1分钟内有10次登录尝试失败时禁用该 IP 15分钟的示例:
362 |
363 | ```
364 | [odoo-login]
365 | enabled = true
366 | port = http,https
367 | bantime = 900 ; 15 min ban
368 | maxretry = 10 ; if 10 attempts
369 | findtime = 60 ; within 1 min /!\ Should be adjusted with the TZ offset
370 | logpath = /var/log/odoo.log ; set the actual odoo log path here
371 | ```
372 |
373 |
374 |
375 | ### 数据库管理器权限
376 |
377 | [ 配置Odoo](#setup-deploy-odoo) 中提到了传递 `admin_passwd` 。
378 |
379 | 这一设置用于所有的数据库管理界面 (创建、删除、导出或还原数据库)。
380 |
381 | 如果必须禁止所有的管理界面, 应当设置 `list_db` 配置选项为`False`,以阻止对所有数据库选择及管理界面的访问。
382 |
383 | ### ⚠️警告
384 |
385 | 强烈建议禁用面向因特网系统中的数据库管理器。它是用作开发/演示的工作,以方便快速创建及管理数据库。并不是设计用于生产环境中的,可能会向攻击者暴露一些危险的功能。它也不是设计用来处理大量数据库的,并可能会触发内存极限。
386 |
387 | 在生产系统中,数据库管理操作应保持由系统管理员执行,包括配置新数据库及自动化备份。
388 |
389 | 确保配置一个适当的`db_name` 参数 (可选项还有`db_filter`),这样系统可以决定每个请求的目标数据库,否则会因被禁止自己选择数据库而无法访问。
390 |
391 | 如果管理界面必须仅能由一部分机器访问,使用代理服务器的功能来阻止所有以`/web/database`开头的路由的访问,但(可能)不包含用于显示数据库选择界面的`/web/database/selector` 。
392 |
393 | 如果数据库管理界面应保持可供访问,`admin_passwd` 设置必须由其 `admin` 默认值进行修改:这个密码在允许进行数据库修改操作前会进行检查。
394 |
395 | 它应进行安全的存储,并应是随机生成的,例如:
396 |
397 | ```
398 | $ python3 -c 'import base64, os; print(base64.b64encode(os.urandom(24)))'
399 | ```
400 |
401 | 这会在成一个32个字符的伪随机可打印字符串。
402 |
403 | ## 所支持的浏览器
404 |
405 | Odoo支持多个版本的多种浏览器。在浏览器版本间保持最新并没有什么不同。Odoo在当前浏览器版本中都支持。所支持的浏览器列表如下:
406 |
407 | - IE11,
408 | - Mozilla Firefox,
409 | - Google Chrome,
410 | - Safari,
411 | - Microsoft Edge
412 |
413 | [[1\]](#id1) 让多个Odoo软件使用同一个PostgreSQL数据库,或对这两个服务提供更多的计算资源。
414 |
415 | [[2\]](https://alanhou.org/odoo-13-deploying-odoo/#id2) 技术上存在一个 [socat](http://www.dest-unreach.org/socat/)这样的工具,可用于在网络中代理UNIX套接字,但那多针对仅通过UNIX套接字才能使用的软件
416 |
417 | [[3\]](#id6) 或通过任何内部包交换的网络进行访问,但那会要求安全交换、对 [ARP攻击](https://en.wikipedia.org/wiki/ARP_spoofing)的保护以及防止使用WiFi。哪怕是通过安全的包交换网络,也推荐使用HTTPS进行部署,可能产生的费用可通过“自签署”证书来降低,并且自控的网络中要比在互联网上的部署更为简单。
--------------------------------------------------------------------------------
/setting-up/email-gateway.md:
--------------------------------------------------------------------------------
1 | # Odoo email 网关
2 |
3 | 本文来自[Odoo 13官方文档之开发者文档](../README.md)系列文章
4 |
5 | Odoo邮件网关允许我们在Odoo中直接注入所有已接收邮件。其原理直截了当:你的SMTP服务为每个新新收邮件执行“mailgate”脚本。该脚本通过XML-RPC处理与Odoo数据库的连接,并通过MailThread.message_process()功能发送邮件。
6 |
7 | ## 前提条件
8 |
9 | - 对Odoo数据库的管理员权限。
10 | - 自己的邮件服务器,如 Postfix 或 Exim。
11 | - 有关如何配置邮件服务器的技术知识。
12 |
13 | ## Postfix篇
14 |
15 | 在alias配置 (/etc/aliases)中配置:
16 |
17 | ```
18 | email@address: "|/odoo-directory/addons/mail/static/scripts/odoo-mailgate.py -d -u -p "
19 | ```
20 |
21 | 郑源 - `Postfix` - `Postfix aliases` - `Postfix virtual`
22 |
23 | ## Exim篇
24 |
25 | ```
26 | *: |/odoo-directory/addons/mail/static/scripts/odoo-mailgate.py -d -u -p
27 | ```
28 |
29 | 资源 - `Exim`
30 |
31 | 如果你可以访问/管理自己的邮件服务器,使用`inbound messages`.
--------------------------------------------------------------------------------
/setting-up/installing-odoo.md:
--------------------------------------------------------------------------------
1 | # 安装Odoo
2 |
3 | 安装 Odoo 有很多种方式,或者是根本无需进行安装,这取决于你的用例。
4 |
5 | 本文旨在描述大部分的安装选项。
6 |
7 | - [在线使用](#setup-install-online)
8 |
9 | 在生产环境或测试使用Odoo最简便的方式。
10 |
11 | - [安装包](#setup-install-packaged)
12 |
13 | 适于测试Odoo、开发模块及用于通过额外的部署和维护工作来在长期生产环境使用。
14 |
15 | - [源码安装](#setup-install-source)
16 |
17 | 提供更强的灵活性:例如允许在同一个系统中运行多个Odoo版本。有益于开发模块,可用作生产部署的基础。
18 |
19 | - [Docker](https://www.odoo.com/documentation/13.0/setup/install.html#setup-install-docker)
20 |
21 | 如果你经常使用[docker](https://www.docker.com/)来进行开发或部署,可使用获取官方[docker](https://www.docker.com/)基础镜像。
22 |
23 |
24 |
25 | ## 版本
26 |
27 | Odoo有两种不同的[版本](https://www.odoo.com/pricing#pricing_table_features):社区版和企业版。使用企业版可以通过[SaaS](https://www.odoo.com/page/start)并获取仅限于企业版客户和合作伙伴的代码。社区版免费向所有人开放。
28 |
29 | 如果你在使用社区版并希望升级到企业版,请参考[社区版升级到企业版](https://www.odoo.com/documentation/13.0/setup/enterprise.html#setup-enterprise) ([源码安装](#setup-install-source)除外)。
30 |
31 |
32 |
33 | ## 在线使用
34 |
35 | ### Demo
36 |
37 | 要快速了解Odoo, 可使用[demo](https://demo.odoo.com/)实例。这是仅供使用几小时的共享实例,可在不进行安装购买的情况下浏览及测试功能。
38 |
39 | [Demo](https://demo.odoo.com/)实例无需本地安装,仅需通过浏览器访问即可。
40 |
41 | ### SaaS
42 |
43 | 轻松开启, 由Odoo S.A.全权管理及进行迁移,Odoo的 [SaaS](https://www.odoo.com/page/start)(Software as a Service)提供私有实例并可免费开始使用。可用于发现和测试Odoo并进行无本地安装非代码自定义(即与Odoo应用商店中的自定义模块不相兼容)。
44 |
45 | 既可用于测试Odoo,也可供长期生产使用。
46 |
47 | 类似[demo](https://demo.odoo.com/)实例,[SaaS](https://www.odoo.com/page/start)实例无需本地安装,仅通过浏览器即可访问。
48 |
49 |
50 |
51 | ## 安装包
52 |
53 | Odoo提供针对Windows、基于deb的发行版(Debian, Ubuntu, …)及基于RPM的发行版 (Fedora, CentOS, RHEL, …) 的安装包,包括社区版和企业版。
54 |
55 | 这些安装包自动安装(社区版的)所有依赖,但可能会很难进行最新版的更新。
56 |
57 | 带有所要求相关依赖的官方社区版安装包可通过[nightly](https://nightly.odoo.com/)服务器获取。 社区版和企业版的安装包均可通过[下载](https://www.odoo.com/page/download)页面进行下载(必须要通过付费用户或合作伙伴账户登录才能下载企业版安装包)。
58 |
59 | ### Windows
60 |
61 | - 通过[nightly](https://nightly.odoo.com/)服务器的下载安装包(仅社区版)或通过[下载页面](https://www.odoo.com/page/download)下载Windows 安装包(任意版本)。
62 |
63 | - 运行所下载文件
64 |
65 | ### 警告
66 |
67 | 在Windows 8中,你可能会看到标题为“Windows protected your PC”的警告。点击更多信息并继续运行。
68 |
69 | - 接受 [UAC](http://en.wikipedia.org/wiki/User_Account_Control) 弹窗
70 |
71 | - 执行各个安装步骤
72 |
73 | Odoo会在安装的最后自动启动。
74 |
75 | ### Linux
76 |
77 | #### Debian/Ubuntu
78 |
79 | Odoo 13.0的deb安装包当前支持[Debian Buster](https://www.debian.org/releases/buster/), [Ubuntu 18.04](http://releases.ubuntu.com/18.04/)及以上版本。
80 |
81 | ##### 准备工作
82 |
83 | Odoo需要有[PostgreSQL](http://www.postgresql.org/)服务才能正常运行。Odoo的deb包默认配置是使用在Odoo实例的相同主机上的PostgreSQL。以 root 用户执行如下命令来安装PostgreSQL服务:
84 |
85 | ```
86 | apt-get install postgresql -y
87 | ```
88 |
89 | 要进行PDF报表的打印,必须要自己安装[wkhtmltopdf](http://wkhtmltopdf.org/): Debian仓库中的[wkhtmltopdf](http://wkhtmltopdf.org/)版本无法支持页眉和页脚,因此不能直接用作依赖。推荐的版本是 0.12.5,可通过[wkhtmltopdf的下载页面](https://github.com/wkhtmltopdf/wkhtmltopdf/releases/tag/0.12.5)的存档版块进行获取。之前推荐的0.12.1版本是一个不错的替代。针对各种版本的更多详情以及各自的特殊情况可通过[wiki](https://github.com/odoo/odoo/wiki/Wkhtmltopdf)页面进行了解。
90 |
91 | ##### 仓库
92 |
93 | Odoo S.A.提供一个可用于Debian和Ubuntu不同发行版的仓库。可通过以 root 执行如下命令来安装Odoo社区版:
94 |
95 | ```
96 | wget -O - https://nightly.odoo.com/odoo.key | apt-key add -
97 | echo "deb http://nightly.odoo.com/13.0/nightly/deb/ ./" >> /etc/apt/sources.list.d/odoo.list
98 | apt-get update && apt-get install odoo
99 | ```
100 |
101 | 然后可以通过常规的 `apt-get upgrade` 命令来保持安装为最新版本。
102 |
103 | 截至目前,还没有针对企业版的仓库。
104 |
105 | ##### Deb安装包
106 |
107 | 作为上述仓库的替代方法,可通过如下方式下载deb安装包:
108 |
109 | - 社区版: [nightly](https://nightly.odoo.com/)
110 | - 企业版[下载页面](https://www.odoo.com/page/download)
111 |
112 | 然后可以使用 `gdebi`:
113 |
114 | ```
115 | gdebi
116 | ```
117 |
118 | 或 `dpkg`:
119 |
120 | ```
121 | dpkg -i # 这步可能会由于缺少依赖而失败
122 | apt-get install -f # 应该会安装所缺少的依赖
123 | dpkg -i
124 | ```
125 |
126 | 这会将Odoo安装为一个服务、创建必要的[PostgreSQL](http://www.postgresql.org/)用户并自动启动服务。
127 |
128 | ### 警告
129 |
130 | python3-xlwt Debian包在Debian Buster和Ubuntu 18.04中不存在。这个python模块在导出为 xls 格式时需要用到。
131 |
132 | 如果需要这一功能,可以手动进行安装。一种方式是简单地通过下面的pip3命令来安装:
133 |
134 | ```
135 | $ sudo pip3 install xlwt
136 | ```
137 |
138 | ### 警告
139 |
140 | Debian 9和Ubuntu中不提供针对python模块num2words的包。文本数量值在Odoo中不会进行渲染,使用l10n_mx_edi模块时会产生问题。
141 |
142 | 如果你需要这一功能,可以像下面这样安装该 python模块:
143 |
144 | ```
145 | $ sudo pip3 install num2words
146 | ```
147 |
148 | #### Fedora
149 |
150 | Odoo 13.0的rpm包支持Fedora 26。截至2017年,CentOS还没有对Odoo 13.0的所要求最小Python版本(3.5)的支持。
151 |
152 | **译者注:**CentOS 8中已添加了对 Python 3的默认支持
153 |
154 | ##### 准备工作
155 |
156 | Odoo需要有[PostgreSQL](http://www.postgresql.org/)服务来正常运行。这里假设可以使用sudo命令并进行了适当的配置,运行如下命令:
157 |
158 | ```
159 | $ sudo dnf install -y postgresql-server
160 | $ sudo postgresql-setup --initdb --unit postgresql
161 | $ sudo systemctl enable postgresql
162 | $ sudo systemctl start postgresql
163 | ```
164 |
165 | 要进行PDF报表的打印,必须要自己安装[wkhtmltopdf](http://wkhtmltopdf.org/): Debian仓库中的[wkhtmltopdf](http://wkhtmltopdf.org/)版本无法支持页眉和页脚,因此不能直接用作依赖。推荐的版本是 0.12.5,可通过[wkhtmltopdf的下载页面](https://github.com/wkhtmltopdf/wkhtmltopdf/releases/tag/0.12.5)的存档版块进行获取。之前推荐的0.12.1版本是一个不错的替代。针对各种版本的更多详情以及各自的特殊情况可通过[wiki](https://github.com/odoo/odoo/wiki/Wkhtmltopdf)页面进行了解。
166 |
167 | ##### 仓库
168 |
169 | Odoo S.A.提供一个可用于Fedora发行版的仓库。可通过执行如下命令来安装Odoo社区版:
170 |
171 | ```
172 | $ sudo dnf config-manager --add-repo = https://nightly.odoo.com/13.0/nightly/rpm/odoo.repo
173 | $ sudo dnf install -y odoo
174 | $ sudo systemctl enable odoo
175 | $ sudo systemctl start odoo
176 | ```
177 |
178 | ##### RPM包
179 |
180 | 代替使用以上所描述的仓库,可通过下面下载‘rpm’ 包:
181 |
182 | - 社区版: [nightly](https://nightly.odoo.com/)
183 | - 企业版 [下载](https://www.odoo.com/page/download)
184 |
185 | 下载后,包通过使用‘dnf’包管理器来进行安装:
186 |
187 | ```
188 | $ sudo dnf localinstall odoo_13.0.latest.noarch.rpm
189 | $ sudo systemctl enable odoo
190 | $ sudo systemctl start odoo
191 | ```
192 |
193 |
194 |
195 | ## 源码安装
196 |
197 | 源码安装并不真的是关乎安装,而是直接通过源码进行运行。
198 |
199 | 对于模块开发者这会更为方便,因为这样会比使用安装包安装更容易获取Odoo源代码(相关信息或构建这一文件并离线获取)。
200 |
201 | 这也会让启动和停止Odoo比通过安装包配置的服务更为灵活和显式,同时它允许使用[命令行参数](https://www.odoo.com/documentation/13.0/reference/cmdline.html#reference-cmdline)无需编辑配置文件即重载设置。
202 |
203 | 最后,它提供对于系统设置更强的控制,并允许在系统中同时轻易地保留(及运行)多个版本的Odoo。
204 |
205 | ### Windows
206 |
207 | #### 获取源码
208 |
209 | 有两种获取Odoo源代码的方式:zip**压缩包**或通过**git。**
210 |
211 | ##### 压缩包
212 |
213 | 社区版:
214 |
215 | - [官方下载页面](https://www.odoo.com/page/download)
216 | - [GitHub仓库](https://github.com/odoo/odoo)
217 | - [Nightly服务器](https://nightly.odoo.com/)
218 |
219 | 企业版:
220 |
221 | - [官方下载页面](https://www.odoo.com/page/download)
222 | - [GitHub仓库](https://github.com/odoo/enterprise)
223 |
224 | ##### Git
225 |
226 | 以下部分要求在电脑主安装了[git](https://git-scm.com/),并且你对于 git 命令有基础知识的掌握。
227 |
228 | 社区版:
229 |
230 | ```
231 | C:\> git clone https://github.com/odoo/odoo.git
232 | ```
233 |
234 | 企业版:(参见 [版本](#setup-install-editions)部分进行获取)
235 |
236 | ```
237 | C:\> git clone https://github.com/odoo/enterprise.git
238 | ```
239 |
240 | **企业版git仓库并不包含完整的Odoo源代码。**它仅是一个附加插件的集合。主服务端代码在社区版中。运行企业版实际是将addons-path选项设置为企业版的文件夹并运行社区版服务。你需要同时克隆社区版和企业版来得到一个运行中的Odoo企业版安装。
241 |
242 | #### 准备工作
243 |
244 | ##### Python
245 |
246 | Odoo运行要求Python 3.5或更新的版本。使用官方的[Python 3安装包](https://www.python.org/downloads/windows/)来在你的电脑上下载并安装Python 3。
247 |
248 | 在安装期间,勾选**Add Python 3 to PATH**,然后点击**Customize Installation**并确保勾选了**pip**。
249 |
250 | 如果已安装了Python 3,确保版本为3.5或以上,因为之前的版本与Odoo不兼容。
251 |
252 | ```
253 | C:\> python3 --version
254 | ```
255 |
256 | 同时验证针对该版本是否安装了[pip](https://pip.pypa.io/)。
257 |
258 | ```
259 | C:\> pip3 --version
260 | ```
261 |
262 | ##### PostgreSQL
263 |
264 | Odoo使用PostgreSQL来作为数据库管理系统。下载并安装 [最新版本的 PostgreSQL](https://www.postgresql.org/download/windows/)。
265 |
266 | 默认唯一的用户是`postgres`,但Odoo禁止通过`postgres`来进行连接,因此你需要新建一个PostgreSQL用户:
267 |
268 | 1. 在`PATH`中添加PostgreSQL的 `bin` 目录(默认为`C:\Program Files\PostgreSQL\\bin`)。
269 | 2. 使用pg admin 图形化工具创建一个postgres用户并设置密码:
270 | - 打开**pgAdminIII**.
271 | - 双击服务端来创建一个连接。
272 | - 选择Edit ‣ New Object ‣ New Login Role。
273 | - 在**Role Name**字段中输入用户名(例如`odoo`)。
274 | - 打开**Definition**标签栏并输入密码(如 `odoo`),然后点击**OK**。
275 |
276 | **译者注:**目前使用较多的为 pgAdmin 4,在 Login/Group Roles下进行操作
277 |
278 | ##### 依赖
279 |
280 | Odoo的依赖在Odoo社区版根目录下的`requirements.txt` 文件中列出了所需的依赖。其中大部分都可以通过**pip**进行安装。
281 |
282 | 推荐不要将不同Odoo实例间或者与系统的python模块包混放在到一起。可以使用[virtualenv](https://pypi.python.org/pypi/virtualenv)来创建隔离的Python环境。
283 |
284 | 导航到Odoo社区安装位置的路径 (`YourOdooCommunityPath`) 并对requirements文件运行**pip**命令:
285 |
286 | ```
287 | C:\> cd \YourOdooCommunityPath
288 | C:\YourOdooCommunityPath> C:\Python35\Scripts\pip.exe install -r requirements.txt
289 | ```
290 |
291 | ### 警告
292 |
293 | 一些依赖无法通过pip进行安装,要求进行手动安装。尤其是:
294 |
295 | - `psycopg` 必须通过[这个安装包](http://www.stickpeople.com/projects/python/win-psycopg/)进行安装。
296 | - 要支持页眉和页脚,`wkhtmltopdf`的安装版本必须为 [0.12.5](https://github.com/wkhtmltopdf/wkhtmltopdf/releases/tag/0.12.5)。查看[wiki](https://github.com/odoo/odoo/wiki/Wkhtmltopdf)页面获取更多有关更多版本的详情。
297 |
298 | 对于从右向左的语言 (如阿拉伯语或希伯来语),需要使用 `rtlcss` 包:
299 |
300 | 1. 下载并安装[nodejs](https://nodejs.org/en/download/)。
301 |
302 | 2. 安装
303 |
304 | ```
305 | rtlcss
306 | ```
307 |
308 | :
309 |
310 | ```
311 | C:\> npm install -g rtlcss
312 | ```
313 |
314 | 3. 编辑系统环境变量`PATH` 来添加 `rtlcss.cmd` 所处的文件夹(通常为: `C:\Users\\AppData\Roaming\npm\`).
315 |
316 | #### 运行Odoo
317 |
318 | 一旦设置了所有的依赖,可通过运行服务端的命令行`odoo-bin`来启动Odoo。它位于Odoo社区版的根目录下。
319 |
320 | 要进行服务端的配置,你可以指定 [命令行参数](https://www.odoo.com/documentation/13.0/reference/cmdline.html#reference-cmdline-server) 或 [配置文件](https://www.odoo.com/documentation/13.0/reference/cmdline.html#reference-cmdline-config)。
321 |
322 | 对于企业版,必须将`enterprise`插件的路径添加`addons-path`参数中。注意要将它放在`addons-path`中的其它路径之前,以正确地加载插件。
323 |
324 | 常用的必要配置有:
325 |
326 | - PostgreSQL用户和密码。
327 | - 默认以外的自定义插件路径,来加载你自己的模块。
328 |
329 | 一个典型的方式是通过如下命令运行服务端:
330 |
331 | ```
332 | C:\YourOdooCommunityPath> python3 odoo-bin -r dbuser -w dbpassword --addons-path=addons,../mymodules --db-filter=mydb$
333 | ```
334 |
335 | 其中`YourOdooCommunityPath` 是Odoo社区版所安装的路径, `dbuser`是PostgreSQL的登录用户,`dbpassword`是PostgreSQL的密码,`../mymodules`是一个带有附加插件的路径,而 `mydb` 是通过`localhost:8069`提供服务的默认数据库。
336 |
337 | ### Linux
338 |
339 | #### 获取源码
340 |
341 | 有两种获取Odoo源代码的方式:zip**压缩包**或通过**git**。
342 |
343 | ##### 压缩包
344 |
345 | 社区版:
346 |
347 | - [官方下载页面](https://www.odoo.com/page/download)
348 | - [GitHub仓库](https://github.com/odoo/odoo)
349 | - [Nightly服务器](https://nightly.odoo.com/)
350 |
351 | 企业版:
352 |
353 | - [官方下载页面](https://www.odoo.com/page/download)
354 | - [GitHub仓库](https://github.com/odoo/enterprise)
355 |
356 | ##### Git
357 |
358 | 以下部分要求在电脑主安装了[git](https://git-scm.com/),并且你已掌握了git 命令的基础知识。
359 |
360 | 社区版:
361 |
362 | ```
363 | $ git clone https://github.com/odoo/odoo.git
364 | ```
365 |
366 | **译者注:**可使用sudo git clone -b 13.0 --depth 1 https://github.com/odoo/odoo.git 来减少下载的代码量
367 |
368 | 企业版:(参见 [版本](#setup-install-editions)部分进行获取)
369 |
370 | ```
371 | $ git clone https://github.com/odoo/enterprise.git
372 | ```
373 |
374 | **企业版git仓库并不包含完整的Odoo源代码。**它仅是一个附加插件的集合。主服务端代码在社区版中。运行企业版实际是将addons-path选项设置为企业版的文件夹并运行社区版服务。你需要同时克隆社区版和企业版来得到一个运行中的Odoo企业版安装。
375 |
376 | #### 准备工作
377 |
378 | ##### Python
379 |
380 | Odoo运行要求Python 3.5或更新的版本。如尚未安装Python 3请使用官方的包管理器来在你的电脑上下载并安装。
381 |
382 | 如果已经安装了Python 3,请确保版本为3.5或以上,因为更早的版本与Odoo并不兼容。
383 |
384 | ```
385 | $ python3 --version
386 | ```
387 |
388 | 同时验证是否安装了相应版本的 [pip](https://pip.pypa.io/)。
389 |
390 | ```
391 | $ pip3 --version
392 | ```
393 |
394 | ##### PostgreSQL
395 |
396 | Odoo使用PostgreSQL来作为数据库管理系统。使用包管理器来下载并安装最新版本的PostgreSQL。
397 |
398 | 默认仅有用户`postgres`,但Odoo禁止通过`postgres`来进行连接,因此你需要新建一个PostgreSQL用户:
399 |
400 | ```
401 | $ sudo -u postgres createuser -s $USER
402 | $ createdb $USER
403 | ```
404 |
405 | 因为你的PostgreSQL用户名称与Unix登录名相同,可以无需密码来连接数据库。
406 |
407 | ##### 依赖
408 |
409 | Odoo的依赖在Odoo社区版根目录下的`requirements.txt` 文件中列出了所需的依赖。其中大部分都可以通过**pip**进行安装。
410 |
411 | 推荐不要将不同Odoo实例间或者与系统的python模块包混放在到一起。可以使用[virtualenv](https://pypi.python.org/pypi/virtualenv)来创建隔离的Python环境。
412 |
413 | 导航到Odoo社区安装位置的路径 (`YourOdooCommunityPath`) 并对requirements文件运行**pip**命令:
414 |
415 | ```
416 | $cd /YourOdooCommunityPath
417 | /YourOdooCommunityPath$ pip3 install -r requirements.txt
418 | ```
419 |
420 | ### 警告
421 |
422 | 对于使用原生代码的库 (Pillow, lxml, greenlet, gevent, psycopg2, ldap),可能在pip在能够安装依赖之前需要安装开发工具和原生依赖。在针对Python, PostgreSQL, libxml2, libxslt, libevent, libsasl2和libldap2的`-dev` 或 `-devel`包中均可获取。
423 |
424 | ### 警告
425 |
426 | 一些依赖无法通过pip进行安装,要求手动安装。尤其是:
427 |
428 | - 要支持页眉和页脚,`wkhtmltopdf`的安装版本必须为 [0.12.5](https://github.com/wkhtmltopdf/wkhtmltopdf/releases/tag/0.12.5)。查看[wiki](https://github.com/odoo/odoo/wiki/Wkhtmltopdf)页面获取更多有关更多版本的详情。
429 |
430 | 对于从右向左的语言 (如阿拉伯语或希伯来语),需要使用 `rtlcss` 包:
431 |
432 | 1. 通过包管理器下载并安装 **nodejs**和**npm**。
433 |
434 | 2. 安装
435 |
436 | ```
437 | rtlcss
438 | ```
439 |
440 | :
441 |
442 | ```
443 | $ sudo npm install -g rtlcss
444 | ```
445 |
446 | #### 运行Odoo
447 |
448 | 一旦配置了所有依赖, 可通过运行服务端命令行工具`odoo-bin`来启动Odoo。它位于Odoo社区版所在的根目录下。
449 |
450 | 要进行服务端的配置,可以通过[命令行参数](https://www.odoo.com/documentation/13.0/reference/cmdline.html#reference-cmdline-server)或 [配置文件](https://www.odoo.com/documentation/13.0/reference/cmdline.html#reference-cmdline-config)进行指定。
451 |
452 | 企业版中必须将`enterprise`插件的路径添加`addons-path`参数中。注意要将它放在`addons-path`中的其它路径之前,这样才能正确地加载插件。
453 |
454 | 通常所需配置有:
455 |
456 | - PostgreSQ用户和密码。除[psycopg2默认值](http://initd.org/psycopg/docs/module.html)以外没有其它的默认项: 通过当前用户无需密码在`5432`端口上连接 UNIX套接字。
457 | - 默认值以外的自定义插件路径,来加载你自己的模块。
458 |
459 | 通常运行服务端的方式为:
460 |
461 | ```
462 | /YourOdooCommunityPath$ python3 odoo-bin --addons-path=addons,../mymodules --db-filter=mydb$
463 | ```
464 |
465 | `YourOdooCommunityPath`是 Odoo社区版安装的路径, `../mymodules` 是带有附加插件的目录,而 `mydb`是通过`localhost:8069`提供服务的默认数据库。
466 |
467 | ### Mac OS
468 |
469 | #### 获取源码
470 |
471 | 有两种获取Odoo源代码的方式:zip**压缩包**或通过**git**。
472 |
473 | ##### 压缩包
474 |
475 | 社区版:
476 |
477 | - [官方下载页面](https://www.odoo.com/page/download)
478 | - [GitHub仓库](https://github.com/odoo/odoo)
479 | - [Nightly服务器](https://nightly.odoo.com/)
480 |
481 | 企业版:
482 |
483 | - [官方下载页面](https://www.odoo.com/page/download)
484 | - [GitHub仓库](https://github.com/odoo/enterprise)
485 |
486 | ##### Git
487 |
488 | 以下部分要求在电脑主安装了[git](https://git-scm.com/),并且你已掌握了git 命令的基础知识。
489 |
490 | 社区版:
491 |
492 | ```
493 | $ git clone https://github.com/odoo/odoo.git
494 | ```
495 |
496 | 企业版:(参见 [版本](#setup-install-editions)部分进行获取)
497 |
498 | ```
499 | $ git clone https://github.com/odoo/enterprise.git
500 | ```
501 |
502 | **企业版git仓库并不包含完整的Odoo源代码。**它仅是一个附加插件的集合。主服务端代码在社区版中。运行企业版实际是将addons-path选项设置为企业版的文件夹并运行社区版服务。你需要同时克隆社区版和企业版来得到一个运行中的Odoo企业版安装。
503 |
504 | #### 准备工作
505 |
506 | ##### Python
507 |
508 | Odoo要求使用Python 3.5或之后的版本进行运行。如未安装,请使用你喜欢的包管理器 ([homebrew](http://brew.sh/), [macports](https://www.macports.org/)) 来在电脑上下载并安装Python 3。
509 |
510 | 如果已安装了Python 3,请确保版本为3.5或以上,因为更老的版本与Odoo不兼容。
511 |
512 | ```
513 | $ python3 --version
514 | ```
515 |
516 | 同时验证已安装了对应版本的 [pip](https://pip.pypa.io/)。
517 |
518 | ```
519 | $ pip3 --version
520 | ```
521 |
522 | ##### PostgreSQL
523 |
524 | Odoo使用PostgreSQL来作为数据库管理系统。使用[postgres.app](https://postgresapp.com/)来下载并安装最新版本的PostgreSQL。
525 |
526 | 默认仅有用户`postgres`,但Odoo禁止通过`postgres`来进行连接,因此你需要新建一个PostgreSQL用户:
527 |
528 | ```
529 | $ sudo -u postgres createuser -s $USER
530 | $ createdb $USER
531 | ```
532 |
533 | 因为PostgreSQL用户与Unix登录名相同,所以可以无需密码直接连接该数据库。
534 |
535 | ##### 依赖
536 |
537 | Odoo的依赖在Odoo社区版根目录下的`requirements.txt` 文件中列出了所需的依赖。其中大部分都可以通过**pip**进行安装。
538 |
539 | 推荐不要将不同Odoo实例间或者与系统的python模块包混放在到一起。可以使用[virtualenv](https://pypi.python.org/pypi/virtualenv)来创建隔离的Python环境。
540 |
541 | 导航到Odoo社区安装位置的路径 (`YourOdooCommunityPath`) 并对requirements文件运行**pip**命令:
542 |
543 | ```
544 | $ cd /YourOdooCommunityPath
545 | /YourOdooCommunityPath$ pip3 install -r requirements.txt
546 | ```
547 |
548 | ### 警告
549 |
550 | 非Python依赖需要通过包管理器来进行安装:
551 |
552 | 1. 下载交安装
553 |
554 | 命令行工具:
555 |
556 | ```
557 | $ xcode-select --install
558 | ```
559 |
560 | 2. 下载并安装你所选择的包管理器 ([homebrew](http://brew.sh/), [macports](https://www.macports.org/))。
561 |
562 | 3. 安装非python依赖。
563 |
564 | ### 警告
565 |
566 | 有些依赖无法通过pip进行安装,要求进行手动安装。尤其是:
567 |
568 | - 要支持页眉和页脚,`wkhtmltopdf`安装的版本必须为[0.12.5](https://github.com/wkhtmltopdf/wkhtmltopdf/releases/tag/0.12.5)。参见[wiki](https://github.com/odoo/odoo/wiki/Wkhtmltopdf)页面获取更多版本的详情。
569 |
570 | 对于从右向左阅读的语言(如阿拉伯语或希伯来语),需要使用 `rtlcss` 包:
571 |
572 | 1. 通过自己喜欢的包管理器([homebrew](http://brew.sh/), [macports](https://www.macports.org/))下载并安装**nodejs** 。
573 |
574 | 2. 安装
575 |
576 | ```
577 | rtlcss
578 | ```
579 |
580 | :
581 |
582 | ```
583 | $ sudo npm install -g rtlcss
584 | ```
585 |
586 |
587 |
588 | ## Docker
589 |
590 | 如何通过Docker使用Odoo的完整文档中参见官方的Odoo [docker镜像](https://registry.hub.docker.com/_/odoo/)页面。
591 |
592 | ## 译者补充安装方法(Ubuntu)
593 |
594 | [](https://alanhou.org/homepage/wp-content/uploads/2019/10/2019101009424619.jpg)
595 |
596 | ```
597 | # 更新为国内apt源(以阿里云镜像为例)
598 | sudo sed -i "s/archive.ubuntu.com/mirrors.aliyun.com/g" /etc/apt/sources.list
599 | sudo sed -i "s/security.ubuntu.com/mirrors.aliyun.com/g" /etc/apt/sources.list
600 | sudo apt-get update
601 | sudo adduser -system -home=/opt/odoo -group odoo -shell /bin/bash # 添加odoo用户和组
602 | sudo apt-get install -y postgresql # 安装PostgreSQL数据库
603 | sudo service postgresql start # 启动 PostgreSQL
604 | # 创建数据库用户
605 | sudo su - postgres
606 | createuser --createdb --username postgres --no-createrole --no-superuser --pwprompt odoo
607 | exit
608 | sudo service postgresql restart
609 | # 安装依赖
610 | sudo apt-get install -y python3-pip
611 | sudo apt-get install virtualenv
612 | sudo apt-get install libsasl2-dev python-dev libldap2-dev libssl-dev python3-pypdf2
613 |
614 | sudo su - odoo
615 | git clone -b 13.0 --depth 1 https://github.com/odoo/odoo.git
616 | virtualenv -p python3 odoo-env
617 | source odoo-env/bin/activate
618 | pip3 install -r odoo/requirements.txt
619 |
620 | # 配置文件
621 | sudo vim /etc/odoo-server.conf
622 | # 示例配置内容
623 | [options]
624 | ; This is the password that allows database operations:
625 | ; admin_passwd = admin
626 | db_host = False
627 | db_port = False
628 | db_name = odoo
629 | db_user = odoo
630 | db_password = False
631 | logfile = /var/log/odoo/odoo-server.log
632 | addons_path = /opt/odoo/odoo/addons,/opt/odoo/odoo/odoo/addons
633 |
634 | # 设置权限
635 | sudo chown odoo: /etc/odoo-server.conf
636 | sudo chmod 640 /etc/odoo-server.conf
637 | sudo mkdir /var/log/odoo
638 | sudo chown odoo:root /var/log/odoo
639 |
640 | # 安装 wkhmtltopdf(选择对应的 Ubuntu 版本)
641 | wget "https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/0.12.5/wkhtmltox_0.12.5-1.xenial_amd64.deb" -O /tmp/wkhtml.deb
642 | sudo dpkg -i /tmp/wkhtml.deb # 忽略此处报错,直接执行下一步
643 | sudo apt-get -fy install
644 |
645 | # 设置服务启动
646 |
647 | vi /etc/systemd/system/odoo13.service
648 |
649 | # 内容
650 | [Unit]
651 | Description=Odoo13
652 | Requires=postgresql.service
653 | After=network.target postgresql.service
654 |
655 | [Service]
656 | Type=simple
657 | SyslogIdentifier=odoo13
658 | PermissionsStartOnly=true
659 | User=odoo
660 | Group=odoo
661 | WorkingDirectory=/opt/odoo
662 | Environment=/opt/odoo/odoo-env/bin/activate
663 | ExecStart=/opt/odoo/odoo-env/bin/python3 /opt/odoo/odoo/odoo-bin -c /etc/odoo-server.conf
664 | StandardOutput=journal+console
665 |
666 | [Install]
667 | WantedBy=multi-user.target
668 |
669 | # 启动服务
670 | sudo systemctl start odoo13
671 | ```
672 |
673 |
--------------------------------------------------------------------------------
/setting-up/updating-odoo-installation.md:
--------------------------------------------------------------------------------
1 | # 更新Odoo安装版本
2 |
3 | 本文来自[Odoo 13官方文档之开发者文档](../README.md)系列文章
4 |
5 | ## 引言
6 |
7 | 为获取最新的提升、安全修复、漏洞修正及性能提升,可能会需要不时更新Odoo的软件。
8 |
9 | 本指南仅用于在自己的托管基础设置上使用Odoo的情况。如果你使用的是Odoo的某种云方案,会自动为你执行升级操作。
10 |
11 | 与软件升级相关的用词通常很让人困惑,以下是一些基本定义:
12 |
13 | - 更新(Odoo软件)
14 |
15 | 代指获取当前 Odoo 版本的最新修订版的过程。例如,更新你的Odoo Enterprise 13.0到最新修订版。这并不会直接引起对Odoo数据库内容的修改,并且可通过重装此前修订版的源代码来进行取消。
16 |
17 | - 升级 (Odoo数据库)
18 |
19 | 表示一个复杂的数据处理操作,其中数据库的内容和结构会进行永久改变来让其与新的Odoo发行版进行兼容。这种操作是不可逆的并且通常是在你决定要切换到更新的 Odoo 发行版时由Odoo的[数据库升级服务](https://upgrade.odoo.com/)来完成。曾经这一过程也称之为“迁移”,因为它包含着在数据库内部移动数据,虽然数据库在升级后可能依然处于同一个物理空间内。
20 |
21 | 本文描述需要*更新*Odoo软件为最新版本所需要的通常步骤。如果想要了解有关升级数据库的更多信息,请访问 [Odoo升级页面 ](https://upgrade.odoo.com/)。
22 |
23 | ## 概述
24 |
25 | 更新Odoo仅需通过对当前所安装的Odoo 进行最新版本的最新安装即可。这样只要你不卸载PostgreSQL(Odoo所带的数据库引擎)会保留所有数据不进行任何修改。
26 |
27 | 升级的主要指南逻辑上参考 [安装指南](https://alanhou.org/odoo-13-installing-odoo/),其中讲解了最常见的安装方法。
28 |
29 | 升级也最宜由最初部署Odoo的人员实施,因为其流程非常相近。
30 |
31 | 我们经常推荐下载完整的最新Odoo版本,而不单是手动应用补丁,如安全公告中所发布的补丁。这些补丁通常是提供给那些高度客制化的安装,或那样希望在测试完整升级前临时测试最小化修改的技术人员。
32 |
33 | ## 第1步: 下载已更新的Odoo版本
34 |
35 | 主要下载页面是 https://www.odoo.com/page/download。如果看到下载 Odoo 企业版的“Buy”链接,请务必使用与 Odoo 企业版订阅相关联的账号登录到Odoo.com上。
36 |
37 | 此外,你可以使用在Odoo企业版购买确认邮件中所包含的独立下载链接 。
38 |
39 | 如果通过Github进行的安装则无需下载已升级的版本 (参照下面部分)
40 |
41 | ## 第2步: 进行数据库备份
42 |
43 | 升级的流程非常安全且不修改数据。但还是推荐最好在对软件执行修改前进行完整的数据库备份,并保存在另一台电脑上一个安全的地方。
44 |
45 | 如果未禁用数据库管理界面 (在 [这里](#security)查看为什么应禁用),,则可以使用它 (在数据库选择界面的下方链接) 来下载数据库备份。如果进行了禁用,使用和日常备份桢的流程。
46 |
47 | ## 第3步: 安装更新版本
48 |
49 | 选择与当前安装相匹配的方法:
50 |
51 | ### 包安装器
52 |
53 | 如果使用从官网下载的安装包进行的Odoo安装(推荐方法),升级非常之简单。你只需要下载与系统相对应的安装包(参见第1步)并在服务器上进行安装即可。它会按日进行更新并包含最新的漏洞修复。通常,你只需在当前软件的顶部双击包来进行安装。在安装该包之后,确保要重启Odoo服务或在准备妥当的情况下重启服务器。
54 |
55 | ### 源码安装(Tarball)
56 |
57 | 如果你最初是通过“tarball”版本(源码存档)进行的Odoo安装,则需替换安装路径为新的版本。首先从Odoo.com下载最新的 tarball。它会按日进行更新并包含最新的漏洞修复(参见第1步)。在下载完包之后,将其解压到服务器的临时位置中。
58 |
59 | 你将获取到一个带有源码版本标记的文件夹,例如“odoo-13.0+e.20190719”,其中包含一个“odoo.egg-info”文件夹并实际源码文件夹名为 “odoo” (Odoo 10及此后版本)或针对老版本名为“openerp”。可以忽略这个odoo.egg-info文件夹。定位到当前软件部署所在的文件夹,交将其替换为你刚刚解压的新的“odoo” 或 “openerp”文件夹。
60 |
61 | 确保要匹配文件夹布局,例如在新源码中所含的“addons”文件夹应与之前的路径完全一致。其次注意任意你在老文件夹中手动拷贝或修改的配置文件,将它们拷贝到亲的文件夹中。最后,重启Odoo服务或在准备妥当的情况下重启服务器。
62 |
63 | ### 源码安装(Github)
64 |
65 | 如果最初是通过对Github上的官方仓库进行完整克隆来完成安装的话,更新过程仅需你通过git拉取最新的源代码。进入各仓库的路径(主Odoo仓库,及企业版仓库),并运行如下命令:
66 |
67 | ```
68 | git fetch
69 | git rebase --autostash
70 | ```
71 |
72 | 如果在本地编辑过Odoo源代码的话最后那条命令可能会碰到源码冲突。错误消息会给出冲突的文件列表,你需要手动处理这些冲突,通过对它们进行编辑来决定保留哪一部分的代码。
73 |
74 | 而如果你倾向于抛弃所有的冲突修改,恢复为官方版本的话,可以使用如下命令:
75 |
76 | ```
77 | git reset --hard
78 | ```
79 |
80 | 最后,重启Odoo服务或重启服务器,一切应当会就绪了。
81 |
82 | ### Docker
83 |
84 | 请参见我们的 [Docker镜像文档](https://hub.docker.com/_/odoo/)了解最体的升级指南。
--------------------------------------------------------------------------------
/tutorials/profiling-odoo-code.md:
--------------------------------------------------------------------------------
1 | # Odoo代码性能测试
2 |
3 | 本文来自[Odoo 13官方文档之开发者文档](../README.md)系列文章
4 |
5 | ### ⚠️警告
6 |
7 | 本教程要求 [已安装Odoo](https://alanhou.org/odoo-13-installing-odoo/) 并 [编写Odoo代码](https://alanhou.org/odoo-13-building-module/)
8 |
9 | ## 方法的图形化
10 |
11 | Odoo内嵌有性能测试器。这一内嵌性能测试器的输出可用于生成由方法所触发调用、查询数、方法内部花费时间及方法及其调用的子方法所花费时间的图表。
12 |
13 | ```
14 | from odoo.tools.misc import profile
15 | [...]
16 | @profile('/temp/prof.profile')
17 | def mymethod(...)
18 | ```
19 |
20 | 这会生成一个名为/temp/prof.profile的文件
21 |
22 | 一个名为 *gprof2dot* 的工具会通过这一结果生成图表:
23 |
24 | ```
25 | gprof2dot -f pstats -o /temp/prof.xdot /temp/prof.profile
26 | ```
27 |
28 | 一个名为 *xdot* 的工具将显示所生成图表:
29 |
30 | ```
31 | xdot /temp/prof.xdot
32 | ```
33 |
34 | ## 方法日志
35 |
36 | 另一个性能优化器可用于记录方法的统计数据:
37 |
38 | ```
39 | from odoo.tools.profiler import profile
40 | [...]
41 | @profile
42 | @api.model
43 | def mymethod(...):
44 | ```
45 |
46 | 一旦待分析的方法完成重审后数据会显示为数据统计日志。
47 |
48 | ```
49 | 2018-03-28 06:18:23,196 22878 INFO openerp odoo.tools.profiler:
50 | calls queries ms
51 | project.task ------------------------ /home/odoo/src/odoo/addons/project/models/project.py, 638
52 |
53 | 1 0 0.02 @profile
54 | @api.model
55 | def create(self, vals):
56 | # context: no_log, because subtype already handle this
57 | 1 0 0.01 context = dict(self.env.context, mail_create_nolog=True)
58 |
59 | # for default stage
60 | 1 0 0.01 if vals.get('project_id') and not context.get('default_project_id'):
61 | context['default_project_id'] = vals.get('project_id')
62 | # user_id change: update date_assign
63 | 1 0 0.01 if vals.get('user_id'):
64 | vals['date_assign'] = fields.Datetime.now()
65 | # Stage change: Update date_end if folded stage
66 | 1 0 0.0 if vals.get('stage_id'):
67 | vals.update(self.update_date_end(vals['stage_id']))
68 | 1 108 631.8 task = super(Task, self.with_context(context)).create(vals)
69 | 1 0 0.01 return task
70 |
71 | Total:
72 | 1 108 631.85
73 | ```
74 |
75 | ## Dump栈
76 |
77 | 向Odoo进程发送SIGQUIT信号(仅对POSIX可用)会让这个进程输出带有消息级别的当前栈跟踪进行日志。在 odoo进程看起来卡住时,向进程发送这个信号准许来知道进程在做什么,并让进程继续其任务。
78 |
79 | ## 追踪代码执行
80 |
81 | 向Odoo进程发送SIGQUIT信号通常足够了,但要查看进程在哪些地方比预期的性能要差的话,我们可以使用 pyflame 工具来替我们执行。
82 |
83 | ### 安装pyflame和flamegraph
84 |
85 | ```
86 | # These instructions are given for Debian/Ubuntu distributions
87 | sudo apt install autoconf automake autotools-dev g++ pkg-config python-dev python3-dev libtool make
88 | git clone https://github.com/uber/pyflame.git
89 | git clone https://github.com/brendangregg/FlameGraph.git
90 | cd pyflame
91 | ./autogen.sh
92 | ./configure
93 | make
94 | sudo make install
95 | ```
96 |
97 | ### 记录已执行代码
98 |
99 | 既然已经安装了pyflame,现在我们可以使用pyflame来记录已执行代码。这个工具会每秒多次记录进程的 stacktrace(栈追踪)。一旦完成,我们将通过执行图表来进行展示。
100 |
101 | ```
102 | pyflame --exclude-idle -s 3600 -r 0.2 -p -o test.flame
103 | ```
104 |
105 | 其中的 是想要图表化的odoo的进程ID。这会等待直至进程灭亡,最大值为1小时,并每秒获取5次追踪。通过pyflame的输出,我们可以使用flamegraph工具生成一张SVG 图表:
106 |
107 | ```
108 | flamegraph.pl ./test.flame > ~/mycode.svg
109 | ```
110 |
111 | 
--------------------------------------------------------------------------------
/tutorials/theme-tutorial.md:
--------------------------------------------------------------------------------
1 | # 主题教程
2 |
3 | 本文来自[Odoo 13官方文档之开发者文档](../README.md)系列文章
4 |
5 | Odoo热爱自由。自由让设计师可以更进一步,自由让用户可以根据自己的需求来进行自定义。
6 |
7 | 准备好创建自己的主题了吗?很好。在开始前应该要知道一些内容。本教程旨在引导读者创建一个Odoo主题。
8 |
9 | 
10 |
11 | ## 针对网页设计师的导言
12 |
13 | 如果你是初次使用Odoo的网页设计师,就来对地方了。本导言将勾勒出Odoo主题创建的基本框架。
14 |
15 | Odoo团队创建了一个强大且易于使用的框架。使用这套工具无需预知任何特殊的语法。
16 |
17 | ### 从常用CMS转向Odoo
18 |
19 | 如果你经常使用同一种方式思考和工作,那么很可能得到的是相同的结果。如果想要一些全新的结果,那么请尝试一些新的事物。
20 |
21 | > header.php文件来哪里呢?
22 |
23 | 这通常是习惯于使用Wordpress或Joomla的网页设计师在初次使用 Odoo 时所会问到的问题。
24 |
25 | 
26 |
27 | 确实,在使用常见的CMS时,你需要对多个文件进行代码编写(如header.php, page.php, post.php等等)来为网站创建基本结构。使用这些系统,这一基本结构作为一个设计基础,可以随着时间推移不断更新来确保CMS的兼容性。因此可能在编写几个小时的代码之后还没有真正的开始设计的工作。
28 |
29 | 对于 Odoo 主题则**并非**如此。
30 |
31 | 我们认为主题设计应当简洁(而强大)。在创建我们的网站构造器时,我们决定从头开始而不是依赖于已有的其它框架。这一方法让我们有足够的自由去集中到对设计师真正重要的部分:样式、内容和它们背后的逻辑。而无需再挣扎于技术的细枝末节。
32 |
33 | ### Odoo默认主题结构
34 |
35 | Odoo自带默认的主题结构。这是一个基本的“主题”,它提供最小的结构和布局。在新建主题时,你实际上是在对进行扩展。它在设置中保持启用并扮演像前面提到的CMS基础结构相似的角色,只是你无需创建并维护。它会在你的Odoo安装包中自动进行升级,因为其包含于网站构造器模块之中,默认所有部分都平滑的集成于其中
36 |
37 | 结果是,你可以完全的自由集中于设计上,由这个结构来完成集成和功能的任务。
38 |
39 | 
40 |
41 | **主要功能:**
42 |
43 | - 页面、博客和电商的基本布局
44 | - 集成网站构造器
45 | - 基础代码片断
46 | - 自动化的Less/Sass编译
47 | - 自动化的Js和CSS最小化和合并
48 |
49 | **主要技术:**
50 |
51 | - Twitter Bootstrap
52 | - jQuery
53 | - jQuery UI
54 | - underscore.js
55 |
56 | ## “模块化”思维
57 |
58 | Odoo主题不是一个包含HTML或PHP文件的文件夹,它是一个使用XML编写的模块化的框架。以前从来没使用过XML文件?不必担心,在学习了本课程后,你就可以通过很基础的 HTML 知识来创建自己的每一个主题了。
59 |
60 | 使用经典的网页设计工作流,你通常要编写整个页面的布局。这样的结果是一个“静态”网页。当然可以更新内容,但你的客户会需要你来完成哪怕是最基础的修改。
61 |
62 | 为Odoo创建主题是一个全新的体验。你无需定义页面的整个布局,只要创建代码块(代码片断)来让用户选择在何处“拖放”它们,自己创建页面布局。我们称其为模块化设计。
63 |
64 | 想像Odoo主题是一个由创建并定义样式的元素和选项“列表”。作为设计师,你的目标是设计这些元素的样式来实现美妙的效果,而无需关注终端用户把它们放在哪里。
65 |
66 | 让我们开启“列表”元素之旅吧:
67 |
68 | 
69 |
70 | #### 代码片断 (或构造版块)
71 |
72 | 一段HTML代码。 用户可以使用我们内置的网站构造器界面拖放、修改并修改它们。你可以定义为每个片断定义一组选项和样式。用户可以根据他们的需求进行选择。
73 |
74 | 
75 |
76 | #### 页面
77 |
78 | 这些是普通的网页,只是对于终端用户可编辑并且可以你可以定义一个空的区域让用户可以通过把代码片断拖放到其中进行“填充”。
79 |
80 |
81 |
82 | 
83 |
84 | #### 样式
85 |
86 | 样式通过使用标准CSS文件(或Less/Sass)。你可以将样式定义为 **默认**或 **可选**。默认样式在主题中总是处于活跃状态,可选样式可以由用户启用或禁用。
87 |
88 | 
89 |
90 | #### 功能
91 |
92 | 借助于Odoo的模块化,所有内容可以做进下的自定义化。这表示有无限发挥你的创意的可能。添加功能很容易,为终端用户提供自定义选项也很简单。
93 |
94 | ### Odoo的XML文件概览
95 |
96 | 任意Odoo XML文件都以编码的定义开启。之后,可以在 `` 标签中编写代码
97 |
98 | ```
99 | [XML]
100 |
101 |
102 | ## 此处编写代码
103 |
104 | ```
105 |
106 | 几乎你所创建的所有元素和选项都要放在 `` 标签中,如下例所示。
107 |
108 | ```
109 | [XML]
110 |
111 | This is an HTML block
112 | And this is a subtitle
114 | ```
115 |
116 | ### 划重点
117 |
118 | 不要误解 `template` 的含义。template标签仅用于定义一段html代码或选项 - 但不定与元素的视觉排列相关。
119 |
120 | 上面的代码定义了一个标题,但它不会在任何地方显示,因为该*template*没有与**Odoo默认结构** 存在任何关联。要进行关联,可以使用**xpath**, **qWeb** 或两者混用。
121 |
122 | 继续阅读本教程来学习如何使用你自己的代码进行适当的扩展。
123 |
124 | ### 更新你的主题
125 |
126 | 因为XML文件仅在安装主题时进行加载,你需要在每次对xml文件进行修改时强制进行重载。
127 |
128 | 要进行重载,点击模块页面中的升级(Upgrade)按钮。
129 |
130 | 
131 |
132 | 
133 |
134 | ## 创建一个主题模块
135 |
136 | Odoo的主题像模块那样进行了打包。即使是为公司或客户设计一个很简单的网站,也需要将主题打包成Odoo模块。
137 |
138 | - `main folder`
139 |
140 | 创建一个文件夹并将其命名为: `theme_` 后接主题名。
141 |
142 | - `__manifest__.py`
143 |
144 | 创建一个空文档并将其保存到文件夹中,名为 `__manifest__.py`。这将包含你主题的配置信息。
145 |
146 | - `__init__.py`
147 |
148 | 创建一个空文件并命名为 `__init__.py`。这是必需的系统文件。创建后内容留空。
149 |
150 | - `views` 和 `static` 文件夹
151 |
152 | 在主文件夹中创建这两个文件夹。 在 `views` 中放置定义代码片断、页面和选项的 xml文件。`static` 文件夹是放置样式、图片和自定义 js 代码的相应位置。
153 |
154 | ### 划重点
155 |
156 | .在odoo 和 init 文件开头和结束处分别使用两个下划线字符,
157 |
158 | 最终结果类似下面这样:
159 |
160 | 
161 |
162 | ### 编辑 `__manifest__.py`
163 |
164 | 打开所创建的 `__manifest__.py` 文件并复制/粘贴如下内容:
165 |
166 | ```
167 | {
168 | 'name':'Tutorial theme',
169 | 'description': 'A description for your theme.',
170 | 'version':'1.0',
171 | 'author':'Your name',
172 |
173 | 'data': [
174 | ],
175 | 'category': 'Theme/Creative',
176 | 'depends': ['website'],
177 | }
178 | ```
179 |
180 | 将前4个属性值替换为你所想要的内容。这些值将用于标识你在Odoo后台中的新主题。
181 |
182 | `data` 属性将包含xml文件列表。现在是空的,但我们将添加新新建的文件。
183 |
184 | `category` 定义你的模块分类(一直是“Theme”)并且在斜杠后放置子类别。可以使用Odoo应用分类列表的子分类。 (https://www.odoo.com/apps/themes)
185 |
186 | `depends` 指定我们的主题正常运行所需的模块。对本课程的主题,只需要用到website。如果你还需要用到博客或电商功能,还需要添加下面这些模块。
187 |
188 | ```
189 | ...
190 | 'depends': ['website', 'website_blog', 'sale'],
191 | ...
192 | ```
193 |
194 | ### 安装你的主题
195 |
196 | 要安装主题,需要将主题文件夹放到Odoo安装所在的addons目录中。
197 |
198 | 然后,导航到Odoo的 **Website** 模块,进入Configuration ‣ Settings。
199 |
200 | 在 **Website** 版块中,点击 **Choose a theme** 按钮,然后悬停在主题上并点击 **Use this theme**。
201 |
202 | ## Odoo页面的结构
203 |
204 | Odoo页面是一个两种元素组合的视觉结果: **cross-pages** 和 **unique**。默认,Odoo提供一个**Header** 和一个 **Footer** (跨页面) 以及包含让页面独特的内容的一个的主元素。
205 |
206 | 跨页面元素在每个页面都相同。独立元素仅与特定页面相关。
207 |
208 | To inspect the 默认布局,仅使用网站构造器新建一个页面。点击Content ‣ New Page并添加一个页面名称。使用浏览器查看页面元素。
209 |
210 | ```
211 |
212 |
213 |
214 |
215 |
216 | ```
217 |
218 | ### 扩展默认 Header
219 |
220 | 默认,Odoo的header 包含一个响应式的导航菜单和公司的logo。你可以轻易地对已有内容添加新元素或样式。.
221 |
222 | 要进行添加,在**views**文件夹中创建一个**layout.xml** 文件并添加默认的Odoo xml 标记。
223 |
224 | ```
225 |
226 |
227 |
228 |
229 |
230 |
231 | ```
232 |
233 | 在`` 标签中新建一个模板,复制-粘贴以下代码:
234 |
235 | ```
236 |
237 |
253 | ```
254 |
255 | 第一个xpath将在header添加 id `my_header` 。最好的方法是将css规则定位到该元素并且避免影响到页面上的其它内容。
256 |
257 | ### 警告
258 |
259 | 在替换默认元素属性时要非常小心。因为你的主题会继承默认主题,你的修改优先级会高于未来Odoo的更新。
260 |
261 | 第二个xpath将紧随导航菜单之后添加一条欢迎消息。
262 |
263 | 最后一步是将layout.xml添加到主题所使用的xml文件列表中。此时应像下面这样编辑 `__manifest__.py` 文件:
264 |
265 | ```
266 | 'data': [ 'views/layout.xml' ],
267 | ```
268 |
269 | 更新你的主题
270 |
271 | 非常棒!我们成功的在header中添加了一个id并在导航菜单中添加了一个元素。这些修改将应用于网站的每个页面。
272 |
273 | 
274 |
275 | ## 创建一个具体的页面布局
276 |
277 | 想像我们要创建一个服务页面的具体布局。对于这一页面,我们需要在顶部添加一个服务列表并赋予客户使用代码片段设置页面剩余内容的可能性。
278 |
279 | 在*views* 文件夹内,创建一个 **pages.xml** 文件并添加默认的Odoo 标记。在 ``中,代替 ``的定义,我们将创建一个*page* 对象。
280 |
281 | ```
282 |
283 |
284 |
285 |
286 |
287 | Services page
288 | True
289 | /services
290 | qweb
291 | theme_tutorial.services_page
292 |
293 |
294 | Our Services
295 |
296 | Cloud Hosting
297 | Support
298 | Unlimited space
299 |
300 |
301 |
302 |
303 |
304 |
305 | ```
306 |
307 | 可以看到,页面中有很多额外的属性,如 *name* 或 可以进行访问的 *url* 。
308 |
309 | 我们成功地创建了一个新页面布局,但还没有告诉系统**如何使用它**。这时,我们可以使用 **QWeb**。将html代码包裹在 `` 标签中,如下例所示。
310 |
311 | ```
312 |
313 |
314 | Services page
315 | True
316 | /services
317 | qweb
318 | theme_tutorial.services_page
319 |
320 |
321 |
322 |
323 |
324 |
Our Services
325 |
326 | Cloud Hosting
327 | Support
328 | Unlimited space
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 | ```
337 |
338 | 使用 ``我们将通过自己的代码扩展Odoo的默认页面布局。
339 |
340 | 可以看坏以,我们将自己的代码放到了两个 ``中,一个带有 ID `wrap` ,另一个带有class `container`。这是提供最小化的布局。
341 |
342 | 下一步是添加一个空区域,用户可以在其中添加代码片断。要实现这点,只需在封闭`div#wrap`元素前创建一个`oe_structure`类的 `div` 。
343 |
344 | ```
345 |
346 |
347 |
348 |
349 |
350 | Services page
351 | True
352 | /services
353 | qweb
354 | theme_tutorial.services_page
355 |
356 |
357 |
358 |
359 |
360 |
Our Services
361 |
362 | Cloud Hosting
363 | Support
364 | Unlimited space
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 | ```
378 |
379 | 你可以创建所要数量的代码片断区域,并将它们放在页面的任意位置。
380 |
381 | 值得一提的是我们前面所见的`
`指令有替代方法可创建页面。
382 |
383 | ```
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
Our Services
393 |
394 | Cloud Hosting
395 | Support
396 | Unlimited space
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 | Services page
407 | True
408 | /services
409 |
410 |
411 |
412 |
413 | ```
414 |
415 | 这将允许页面内容使用 ``做更进一步的自定义
416 |
417 | 我们的页面准备基本完毕。现在需要在**__manifest__.py** 文件中添加**pages.xml**
418 |
419 | ```
420 | 'data': [
421 | 'views/layout.xml',
422 | 'views/pages.xml'
423 | ],
424 | ```
425 |
426 | 更新你的主题
427 |
428 | 太好了,我们的服务页面已准备就绪,可以通过`/services`(以上我们所选择的URL)来进行访问了。
429 |
430 | 你会注意到可以在 *Our Services*列表下方拖放代码片断。
431 |
432 | 现在让回到我们的*pages.xml* ,在页面模板后复制粘贴如下代码。
433 |
434 | ```
435 |
436 | Services
437 |
438 |
439 | 99
440 |
441 | ```
442 |
443 | 这段代码会为主菜单添加一个链接,指向我们所创建的页面。
444 |
445 | **sequence** 属性定义了链接在顶部菜单的位置。在本例中,我们设置其值为 `99` 来将其放到最后。如果你希望将其放在具体的位置上,需要替换该值为所需的值。
446 |
447 | 如你在`website`模块的*data.xml* 文件中所见,默认 **Home** 链接设置为`10` 而 **Contact us** 页面则设置为 `60` 。例如假如你想要将自己的链接放在**中间**,可以设置链接的排序为 `40`。
448 |
449 | ## 添加样式
450 |
451 | Odoo默认包含了 Bootstrap。这表示你可以马上使用所有的 Bootstrap 样式以及布局功能。
452 |
453 | 当然如果你想要提供一个独特的页面 Bootstrap 是不够的。以下步骤将引导你了解如何为自己的主题添加自定义样式。最终的结果不会很漂亮,但可以提供足够的信息让你自己继续进行构建。
454 |
455 | 我们先创建一个名为 **style.scss** 的空文件将将其放到static目录下名为 **scss** 的文件夹下。以下的规则会为我们的*服务*页面添加样式。复制粘贴并保存文件。
456 |
457 | ```
458 | .services {
459 | background: #EAEAEA;
460 | padding: 1em;
461 | margin: 2em 0 3em;
462 | li {
463 | display: block;
464 | position: relative;
465 | background-color: #16a085;
466 | color: #FFF;
467 | padding: 2em;
468 | text-align: center;
469 | margin-bottom: 1em;
470 | font-size: 1.5em;
471 | }
472 | }
473 | ```
474 |
475 | 我们的文件准备就绪了,但还没有包含到主题当中。
476 |
477 | 让我们导航到 view 文件夹并创建一个名为*a**ssets.xml*的XML文件。添加默认的 Odoo xml 标记并复制粘贴如下代码。记得用你主题的主文件夹名称替换掉 `theme folder` 。
478 |
479 | ```
480 |
481 |
482 |
483 |
484 |
485 | ```
486 |
487 | 我们刚刚创建了一个指定scss文件的template。如你所见,我们的模板有一个特殊属性,名为 `inherit_id`。 该属性告诉Odoo我们的模板引用了k 另一个template来实现正常操作。
488 |
489 | 本例中,我们引用了 `assets_frontend` 模板,它位于 `website` 模块中。 `assets_frontend` 指定了由网站构造器所加载的资源列表,我们的目标是在这个列表中添加自己的scss 文件。
490 |
491 | 这可以通过使用带有`expr="link[last()]"` 和 `position="after"`属性的xpath来实现,它表示“*将我的样式文件并将其放到资源列表的最后一个链接后面*”。
492 |
493 | 将其放到最后面,这样我们可以确保它最后加载并具有优先级。
494 |
495 | 最后在**__manifest__.py**文件中添加 **assets.xml。**
496 |
497 | 更新主题
498 |
499 | 我们的 scss 文件现在包含在主题当中了,它将会自动编译、最小化并与所有 Odoo 资源进行合并。
500 |
501 | 
502 |
503 | ## 创建代码片断
504 |
505 | 因代码片断是用户设计和布局页面的方式,它们是你设计时最重要的元素。我们来为服务页面创建一个代码片断。代码片断将显示3段文字并可由终端用户通过网站构造器界面进行编辑。导航至view文件夹并创建名为**snippets.xml** 的XML文件。添加默认的Odoo xml标记并复制/粘贴如下代码。template包含有可由代码片断显示的HTML标记。
506 |
507 | ```
508 |
509 |
510 |
511 |
512 |
513 |
514 |
Client Name
515 |
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
516 |
517 |
518 |
519 |
Client Name
520 |
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
521 |
522 |
523 |
524 |
Client Name
525 |
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
526 |
527 |
528 |
529 |
530 |
531 | ```
532 |
533 | 可以看到,我们对这三栏使用了Bootstrap默认类。不仅是关乎布局,这些类将**由网站构造器触发让用户可以调整它们的尺寸**。
534 |
535 | 前面的代码将创建代码片断内容,但我们需要将其放到编辑器栏中,这样用户可以将其拖放到页面中。复制/粘贴这个模板到你的 **snippets.xml** 文件中。
536 |
537 | ```
538 |
539 |
540 |
542 |
543 |
544 | ```
545 |
546 | 使用xpath,我们通过 id `snippet_structure`定位到具体的元素。这表示这个代码片段会出现在Structure标签栏中。如果希望修改目标标签栏,需要在xpath中替换掉`id`值。
547 |
548 | 
549 |
550 | | 标签栏名称 | Xpath表达式 |
551 | | ---------- | -------------------------------- |
552 | | Structure | `//div[@id='snippet_structure']` |
553 | | Content | `//div[@id='snippet_content']` |
554 | | Feature | `//div[@id='snippet_feature']` |
555 | | Effect | `//div[@id='snippet_effect']` |
556 |
557 | ``标签将调用我们的代码片断模板并赋值一个img文件夹中放置的缩略图。现在可以从代码片断栏中将其播放到页面中并查看效果。
558 |
559 | 
560 |
561 | ## 代码片断选项
562 |
563 | 选项允许发面都使用网站构造器界面编辑代码片断的外观。借助网站构造器的功能,你可以轻松地创建一个代码片断选项并自动将它们添加到界面中。
564 |
565 | ### 选项组属性
566 |
567 | 选项封装成组。组中有属性来定义所包含选项如何与用户界面进行交互。
568 |
569 | - `data-selector="[css selector(s)]"`
570 |
571 | 将所有选项绑定到针对具体元素的组。
572 |
573 | - `data-js=" custom method name "`
574 |
575 | 用于绑定自定义Javascript方法。
576 |
577 | - `data-drop-in="[css selector(s)]"`
578 |
579 | 定义代码片断所放置入的元素列表。
580 |
581 | - `data-drop-near="[css selector(s)]"`
582 |
583 | 定义可将代码片断放置在其附近的代码片断。
584 |
585 | ### 默认选项方法
586 |
587 | 选项向脚本应用标准CSS类。根据你所选择的方法,用户界面的行为会有所区别。
588 |
589 | - `data-select-class="[class name]"`
590 |
591 | 在相同组内的多个data-select-class定义一系列用户可选择应用的类。一次仅能启用一个选项。
592 |
593 | - `data-toggle-class="[class name]"`
594 |
595 | data-toggle-class用于从代码片断列表中应用一个或多个 CSS类。可同时应用多个。
596 |
597 | 我们来演示默认选项与用于一个基本示例。
598 |
599 | 我们一开始在views文件夹中新增一个文件,将其命名为**options.xml** 并在其中滞默认的Odoo XML标记。新建模板复制、粘贴如下内容:
600 |
601 | ```
602 |
603 |
604 |
605 |
613 |
614 |
615 |
616 | ```
617 |
618 | 以上的模板将扩展默认的**snippet_options模板**,将我们的选项添加到 **background** 选项之后options (xpath expr 属性)。要以具体的顺序放置选项,查看**website模块**中的 **snippet_options模板** 并将你的选项添加到所需位置的之前或之后。
619 |
620 | 可以看到,我们将所有的选项放到一个 DIV 标签内,那会将它们定位到正确的选择器上 (`data-selector=".snippet_testimonial"`)。
621 |
622 | 要定义我们的选项,我们对`li`元素中应用了 `data-select-class` 属性。在用户选择一个选项时,属性中所包含的类将自动应用于该元素。
623 |
624 | 因为 `selectClass` 方法避免了多选,最后一个“empty”选项将重置代码片断为默认值。
625 |
626 | 在 `__manifest__.py` 中添加**options.xml**并更新主题。
627 |
628 | 把我们的代码片断放到页面中,你会注意到新选项自动添加到了自定义菜单中。查看日志,你会注意到在选择一个选项时该类会应用到元素上。
629 |
630 | 我们来创建一些css规则,来为我们的选择添加视觉效果。打开 **style.scss** 文件并添加如下内容:
631 |
632 | ```
633 | .snippet_testimonial {
634 | border: 1px solid #EAEAEA;
635 | padding: 20px;
636 | }
637 |
638 | // These lines will add a default style for our snippet. Now let's create our custom rules for the options.
639 |
640 | .snippet_testimonial {
641 | border: 1px solid #EAEAEA;
642 | padding: 20px;
643 |
644 | &.opt_shadow img {
645 | box-shadow: 0 2px 5px rgba(51, 51, 51, 0.4);
646 | }
647 |
648 | &.opt_grey_bg {
649 | border: none;
650 | background-color: #EAEAEA;
651 | }
652 | }
653 | ```
654 |
655 | 太棒了!我们成功地为代码片断创建了选项。
656 |
657 | 每当发布者点击一个选项时,系统会添加在 data-select-class中指定的类。
658 |
659 | 通过将 `data-select-class` 替换为 `data-toggle-class` ,你将能够同时选择多个类。
660 |
661 | ### Javascript选项
662 |
663 | 如果你需要执行简单的类修改操作的话`data-select-class` 和 `data-toggle-class` 会非常好。但如果代码片断的自定义要求有更多操作呢?
664 |
665 | 如前面所述, `data-js` 属性可分配给一个选项组,来定义一个自定义方法。我们通过向早前创建的选项组div添加`data-js`属性来为*testimonials*代码片断创建一个选项。
666 |
667 | ```
668 |
669 | [...]
670 |
671 | ```
672 |
673 | 搞定。 从现在开始,网站构造器在发布者每次进入编辑模式时都会查找 `snippet_testimonial_options` 方法。
674 |
675 | 我们再进一步,创建一个名为**tutorial_editor.js** 的javascript文件并将其放到 **static** 文件夹下。复制、粘贴如下代码:
676 |
677 | ```
678 | (function() {
679 | 'use strict';
680 | var website = odoo.website;
681 | website.odoo_website = {};
682 | })();
683 | ```
684 |
685 | 很好,我们成功地创建了自己的javascript编辑器文件。 该文件将包含编辑模式下我们的代码片断所使用的javascript函数。我们来使用前面创建的`snippet_testimonial_options`方式】法为证言代码片断新建一个函数。
686 |
687 | ```
688 | (function() {
689 | 'use strict';
690 | var website = odoo.website;
691 | website.odoo_website = {};
692 |
693 | website.snippet.options.snippet_testimonial_options = website.snippet.Option.extend({
694 | onFocus: function() {
695 | alert("On focus!");
696 | }
697 | })
698 | })();
699 | ```
700 |
701 | 可以注意到,我们使用了一个名为 `onFocus`的方法来触发我们的函数。网站构造器提供了一些你可以用于触发自定义函数的事件。
702 |
703 | | 事件 | 描述 |
704 | | -------------- | ------------------------------------------------------------ |
705 | | `start` | 在编辑会话中发布者第一次选中代码片断时或播放到页面中时触发。 |
706 | | `onFocus` | 每次用户选择脚本时或将脚本播放到页面中时触发。 |
707 | | `onBlur` | 在代码片断失去焦点是发生该事件。 |
708 | | `onClone` | 在复制了代码片断后触发。 |
709 | | `onRemove` | 在删除代码片断之彰触发。 |
710 | | `onBuilt` | 在将代码片断拖放到拖放区域时触发。事件触发时,内容已插入到页面中。 |
711 | | `cleanForSave` | 在发布者保存页面时触发。 |
712 |
713 | 我们来往编辑器资源列表中新增一些javascript文件。回到 **assets.xml** 并新建像前一个那样的 template。这次我们需要继承 `assets_editor`来代替 `assets_frontend`。
714 |
715 | ```
716 |
717 |
718 |
719 |
720 |
721 | ```
722 |
723 | 更新你的主题
724 |
725 | 我们来测试新的javascript函数。进入编辑模式并播放入页面。你应该可以看到我们绑定到`onFocus`事件上的javascript警告。如果关闭然后点击代码片断外部,接着再次在其中进行点击,该事件会再次被触发。
726 |
727 | 
728 |
729 | ## 编辑手册指南
730 |
731 | Basically all the elements in a page can be edited by the publisher. Besides that, some element types and css classes will trigger special Website Builder functionalities when edited.
732 |
733 | ### 布局
734 |
735 | - ``
736 |
737 | Any section element can be edited like a block of content. The publisher can move or duplicate it. It’s also possible to set a background image or color. Section is the standard main container of any snippet.
738 |
739 | - `.row > .col-lg-*`
740 |
741 | Any large bootstrap columns directly descending from a .row element, will be resizable by the publisher.
742 |
743 | - `contenteditable="False"`
744 |
745 | This attribute will prevent editing to the element and all its children.
746 |
747 | - `contenteditable="True"`
748 |
749 | Apply it to an element inside a contenteditable=”False” element in order to create an exception and make the element and its children editable.
750 |
751 | - ` `
752 |
753 | In Edit Mode, any link can be edited and styled. Using the “Link Modal” it’s also possible to replace it with a button.
754 |
755 | ### Media
756 |
757 | - ` `
758 |
759 | Pictogram elements. Editing this element will open the Pictogram library to replace the icon. It’s also possible to transform the elements using CSS.
760 |
761 | - ` `
762 |
763 | Once clicked, the Image Library will open and you can replace images. Transformation is also possible for this kind of element.
764 |
765 | ```
766 |
771 | ```
772 |
773 | This html structure will create an `