Commit Graph
5736 Commits
Author SHA1 Message Date
Thibault Delavallée 81e782250a [IMP] base: remove postmaster-odoo default bounce
Coming from odoo/odoo@a4597fe34f . This code is not necessary anymore since we better
handle From, SMTP-From, as well as allowing mail server filtering. We can
now use standard aliases and servers instead of relying on hardcoded value
"postmaster-odoo".

Task-2784996

closes odoo/odoo#85837

Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
2022-03-04 15:00:54 +00:00
Thibault Francois e1983aa413 [FIX] base{_import_module}: cloc should not count generated code
* Studio generate computed field "count" when the user add a button
in the button box. Since those lines are written by studio with drag and drop
it should not be counted for the maintenance subscription.

We can detect them because field created by the user in studio start with x_studio
and fields created outside studio does not have studio_customization
xml_id

* Add test to make no standard module introduce customization in the
database during the install the will be counted by cloc

see https://github.com/odoo/enterprise/pull/22664

closes odoo/odoo#85763

X-original-commit: 47ae24081fe5c0c86ce8507229dfb550ec585633
Signed-off-by: Christophe Simonis <chs@odoo.com>
Signed-off-by: Thibault Francois <tfr@odoo.com>
2022-03-04 11:16:52 +00:00
Martin Trigaux 91366e5256 [FIX] base: do not overwrite the overwrite value
The signature of the _load_module_terms was changed at ac63556e23 to
pass explicit parameters instead of values hidden in the context

The previous code used working on a copy of the context when modifing
the 'overwrite' key. This was no longer the case after ac63556e23.

In case multiple languages are updated at the same time (as when a
module is updated), the 'overwrite' value is set to True after the
first loop.

To reproduce:
1. Create empty db with language English and without demo data
2. Activate languages fr_FR, fr_BE and fr_CH
3. Install module "note"
4. Now change in the fr.po the msgstr for this msgid:
   "By sticky note Category" -> "Par catégorie de note collante FR"
   and in the fr_BE.po the msgstr for this msgid:
   "By sticky note Category" -> "Par catégorie de note collante BE"
5. Now update the module note without overwriting translations
   (--i18n-overwrite option not set)
Observed behaviour:
The translations are overriden

opw-2760359

closes odoo/odoo#85768

X-original-commit: 37b98d413debabcd966718b2834d34f349e3cfbc
Signed-off-by: Martin Trigaux (mat) <mat@odoo.com>
2022-03-03 16:17:59 +00:00
Andrius Laukavičius 04573a934b [FIX] base: update user groups faster
When users have same groups, implied_ids can be done in batches,
reducing write operations considerably.

Testing done:

800 users groups_id (all have same groups_id in the end) updated using
old code: took ~10s to complete.

With this improvement, it takes ~1.5s

closes odoo/odoo#85712

X-original-commit: 81994ea628cb492fef4e62c496d4ea1ab55d9a7e
Signed-off-by: Raphael Collet <rco@odoo.com>
2022-03-03 11:04:21 +00:00
Stefan RijnhartandRaphael Collet 4627e83d4b [FIX] base: updating 'base' should update all modules
If an installed module is only present in the dependency graph through a new,
uninstalled dependency, it was not be selected for upgrade when updating the
`base` module (c.q. `--update all`)

closes odoo/odoo#85704

X-original-commit: 6c80e7cfeafaaa4fb7c0d93cbe94c66068fc3170
Signed-off-by: Raphael Collet <rco@odoo.com>
Co-authored-by: Raphael Collet <rco@odoo.com>
2022-03-03 11:04:16 +00:00
Rémy Voet (ryv) e1785e820a [IMP] base: add prefetching group feature
The field prefetching mechanism was poorly customizable.  Before this,
we could only tell if a field was prefetched with other fields or not at
all.  We have no way to inform the framework, like: "When I need data of
that field, prefetch these other fields, which are likely be used in the
same transaction".

From now on, the `prefetch` attribute is used as a grouping key for
prefetching fields.  When a field is fetched, all the fields with the
same value for `prefetch` are taken for prefetching.

For example, consider a small set of fields that are rarely used, except
for one flow A using them.  You want to prefetch those fields only in
the flow A, and you want to fetch them in a single query.  With the new
feature, simply set `prefetch=A` for some string `A` on those fields,
and they will be grouped for prefetching.

closes odoo/odoo#85220

Signed-off-by: Rémy Voet <ryv@odoo.com>
2022-03-03 11:03:55 +00:00
Rémy Voet (ryv) ce1dc0c404 [IMP] base: make recompute faster
When we read data of one record, the method `recompute` (`_fetch_field`
-> `_read` -> `flush` -> `recompute`) can take more than 40 % of the
time of the `_read`, due to a huge number of recordset creation (from
`records_to_compute` and `records & recs`).  Avoid that waste of time by
postponing the test on records.

For example, on a database with modules crm, mrp, purchase, website,
sale_management, reading the prefetchable fields of the current company
took 1.45 ms ± 60.3 µs, and now takes 1.17 ms ± 110 µs (more than 20%
speedup).

closes odoo/odoo#83818

Related: odoo/enterprise#24645
Signed-off-by: Raphael Collet <rco@odoo.com>
Part-of: odoo/odoo#85220
2022-03-03 11:03:54 +00:00
Achraf (abz) 15f285a9ec [FIX] base: Typo in qweb docstring
replace 't-eif' with 't-elif'

closes odoo/odoo#85702

X-original-commit: f80cee828010ea4e74041402573cbf1e789c1f45
Signed-off-by: Vincent Schippefilt (vsc) <vsc@odoo.com>
Signed-off-by: Achraf <abz@odoo.com>
2022-03-02 23:08:27 +00:00
Vincent Schippefilt 4ef0c00b4b [ADD] models: add _read_group method to avoid sorting on the names of many2one
The public read_group method is used by back-end code as well as RPC calls to receive grouped data.
 Most of the time, if the data isn't shown to the user, it doesn't need to be sorted on the names of
 the many2one, and can just be sorted on their ID instead, avoiding extra joins in the resulting queries

 This is a first commit that will reduce the features of _read_group to the strict minimum necessary
 to obtain correct grouped data that is never shown to a user

Task-id: 2479334
Part-of: odoo/odoo#84908
2022-03-02 17:10:47 +00:00
Vincent Schippefilt a5e21abdf5 [ADD] models: adds a test for read_group date:agg group
Adds a test that verifies that the grouping with a date aggregate
specifier (the only group specifier supported until now) is working correctly

Part-of: odoo/odoo#84908
2022-03-02 17:10:47 +00:00
Vincent Schippefilt 209249dcec [REF] base: modernize read_group tests
reshape a bit the read_group tests by ensuring that:
- creation of user is made in batch
- assertions are always assert(actual, expected)

Part-of: odoo/odoo#84908
2022-03-02 17:10:46 +00:00
Jorge Pinna Puissant cac3ba62e5 [IMP] test_lint, *: add caughtErrors option to no-unused-vars
All named arguments on the catch block must be used. If for some motive
the named argument on the catch block is not used, its name must begin
with '_'.

closes odoo/odoo#85569

Related: odoo/enterprise#24867
Related: odoo/design-themes#552
Signed-off-by: Géry Debongnie <ged@odoo.com>
2022-03-02 13:05:42 +00:00
Jorge Pinna Puissant bc20eb4790 [IMP] test_lint, *: enable no-unused-vars rule
Part-of: odoo/odoo#85569
2022-03-02 13:05:42 +00:00
Jorge Pinna Puissant 8cf6643c7b [IMP] test_lint: don't check generated code
Part-of: odoo/odoo#85569
2022-03-02 13:05:41 +00:00
Raphael Collet 15215d650a [FIX] core: check rules before flush() in unlink()
Just before deleting records, we mark fields on dependent records as "to
compute", and their actual computation should happen after the records
have been deleted.  The problem is that checking security rules may
trigger the computations that have been prepared.  So we should check
security rules before marking dependent records.

closes odoo/odoo#85574

X-original-commit: 79908bc757a239c97bd2b4cda505b7b8df769325
Signed-off-by: Raphael Collet <rco@odoo.com>
2022-03-01 10:53:51 +00:00
Yannick Tivisse 0ca4c838ac [IMP] base: Batch calls to exists in _visible_menu_ids
Purpose
=======

Make 1 call per model (3-4 max) instead of 1 per menu.

Part-of: odoo/odoo#85133
2022-02-28 17:27:07 +00:00
Pierre-Yves Dufays 354b9e8ed2 [IMP] web, base: export field res_partner.name by default in import-compatible
mode

Purpose:

Include the name of the partners so that:
- it can easily be updated
- it is easier for users to recognize who is who

To generalize this modification, a new attribute has been added to the model
field descriptor (odoo/fields.py): default_export_compatible.
Setting this value to True on a field of a model will force that field to be
included by default in the exportation when "import-compatible export" is
selected.

Specification:

If the user goes:
    Contacts > List view > Select Records > Action Export
	     >  I want to update data
The field name is selected by default

The fields selected by default for export are the columns of the list so that
what is exported by default is what the user sees on the screen.
The display name is one of the column but is not importable.
When the option "I want to update data" is selected, only field that are
compatible for importation are selected and then display name is no longer
selected.
With this modification, the name is selected instead.

Technical:

- a new attribute "default_export_compatible" has been added in odoo/fields
- the controller /web/export/get_fields that lists the fields available
for exportation has been modified to add a default_export attribute on each
returned field. This new attribute is set to True when import_compat is True
and default_export_compatible is True on a given field.
The client uses that information to force by default the field for exportation.

Task 2734222

closes odoo/odoo#83697

Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
2022-02-28 16:33:50 +00:00
Victor Feyens 4f7d833cf0 [FIX] core: http documentation
* http.py:docstring of odoo.http.route:19: WARNING: Unexpected
indentation.
* http.py:docstring of odoo.http.route:20: WARNING: Block quote ends
without a blank line; unexpected unindent.
* http.py:docstring of odoo.http.route:30: WARNING: undefined label:
csrf
-> No section in the doc exists with reference csrf

* Add docstring for the default_lang method.

closes odoo/odoo#85505

Signed-off-by: Victor Feyens (vfe) <vfe@odoo.com>
2022-02-28 13:53:24 +00:00
Romain Derie 3e2d30fdbd [IMP] models.py, *: don't read ir_translation table if empty source
* website, website_blog (perf)

If the source values is only composed of empty elements (list of `None`
values), don't try to translate it, as there is nothing to translate
anyway.

While the translation method being called (xml_translate,
html_translate) won't be costly as they will return instantly if the
provided value is falsy, there is still an SQL Query made to get the
callable translation method, which we won't actually use.

This commit avoids that query if we already know we won't do anything
with the translation method.

task-2774979

X-original-commit: e1eb5a6e1610a9550a59ba229a83b9e70e10aa88
Part-of: odoo/odoo#85456
2022-02-28 13:53:18 +00:00
Julien Castiaux 8639f9b257 [FIX] website: restore debug mode in website pages
Install website, create a custom web page, we'll call it page_1. Ensure
you are not in debug mode (go to /web/health?debug=0 to disable it).
Open the web page enabling the debug-mode /page_1?debug=1, the page
opens but the debug mode is disabled.

Because web pages are served using another routing mechanism than
controlers we have to ensure we load the debug query-string in those
mechanisms too.

closes odoo/odoo#85340

Signed-off-by: Romain Derie (rde) <rde@odoo.com>
2022-02-28 13:53:09 +00:00
Adrien Minet cf8cc3b47f [FIX] base: sum of company-dependent fields in partner merge wizard
How to reproduce the bug:

- Install POS and data_cleaning
- Create 2 customers and set loyalty points
- Select both customers and merge them
- Once the merge is done, the loyalty points have not been summed

Bug:

When you merge 2 contacts that have both loyalty points, the resulting
contact won't have the sum of the 2 source contacts. Instead of that,
the resulting contact will only have the points of one of of the
contact.

closes odoo/odoo#84739

Opw: 2686208
X-original-commit: 56efd70e5c0e10302990d14730d801d3720f630e
Related: odoo/enterprise#24445
Signed-off-by: Simon Goffin <sig@odoo.com>
Signed-off-by: Minet Adrien (admi) <admi@odoo.com>
2022-02-28 12:55:52 +00:00
Thibault Delavallée e50bc749f6 [IMP] base: support decimalized in integer qweb formatting
Purpose of this commit is to allow decimalized formatting of numbers when
rendering integer fields in qweb. This is done through an option and will
be used notably in elearning.

Task-2607416

Part-of: odoo/odoo#74171
2022-02-28 11:07:39 +00:00
Raf Geens 731d0d85c2 [FIX] orm: string pattern for Reference wasn't correct
In reality the separator is a comma instead of a dot as can be seen
in the code a bit further below.

closes odoo/odoo#85396

X-original-commit: 2a6e9f166d493aaefc006e949ab45e42311ac886
Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
2022-02-25 12:35:51 +00:00
Samuel Degueldre 3620de052a [REF] web, *: remove/deprecate useless code in main web controller
*: website_sale_coupon, test_apikeys

The do_search_read method was only used in a single place: the
search_read controller, whose entire function body was just a function
call. The content of the do_search_read method has been moved to the
search_read controller.

The search_read controller is still pretty useless: it's basically
equivalent to making an RPC on a model and calling the method
web_search_read. As such, this controller should be removed, but lots of
code depends on it and adapting all the code that uses it is out of
scope for this PR. It will hopefully be removed alongside the legacy
views in the webclient (which are its main users).

The call_common method and /web/dataset/load route are completely
unused.

The /web/dataset/call controller is used in only two places and can
trivially be replaced with a call to /web/dataset/call_kw

The non-route methods were removed and the route methods now emit a
deprecation warning telling the user what to use instead.

closes odoo/odoo#84811

Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
2022-02-25 07:35:40 +00:00
Julien Castiaux 723f1f0d11 [REF] core: HTTPocalypse (15) tests
This commit is the 15th commit of a comprehensive refactor of our HTTP
framework. See odoo/odoo#78857 for complete historic, discussions and
rationnals.

Enhance the new test_http module with more tests.

Pr: odoo#78857
Task: 2571224
Related: odoo/enterprise#21849
Related: odoo/design-themes#524
Signed-off-by: Julien Castiaux <juc@odoo.com>
2022-02-24 13:30:51 +00:00
Julien Castiaux c0647b5c52 [REF] core: HTTPocalypse (14) changes all addons
This commit is the 14th commit of a comprehensive refactor of our HTTP
framework. See odoo/odoo#78857 for complete historic, discussions and
rationnals.

* `request.uid = x` => `request.update_env(user=x)`.
* `request.context = x` => `request.update_env(context=x)`.
* `request.context = dict(request.context, x=y)`
   => `request.update_context(x=y)`.
* `request.cr = None` => `request.cr.close()`.
* `http.mono_db()` => `request.db`.
* `http.dispatch_rpc()` => `service.dispatch_rpc()`.
* `@service.model.check` => `service.model.retrying()`.
* `request.endpoint`
   => `env['ir.http']._match(request.httprequest.path)[0].endpoint`.
* `request.routing_iteration `=> `removed`.
* `request.jsonrequest` => `request.dispatcher.jsonrequest`.

Note that `request.params` is now set much later in the process. If you
are in a situation where you values from the query string or the
http body you can use `request.get_http_params()`.

Note that using the new `request.future_response`, it is possible to
add headers and cookies on the response object before the response
object is initialized. Please note that headers/cookies saved on
the future response will NOT be injected in case of error.

PR: odoo#78857
Task: 2571224
2022-02-24 13:30:51 +00:00
Julien Castiaux f04b90b6e8 [REF] core: HTTPocalypse (12) web ir.http & login
This commit is the 12th commit of a comprehensive refactor of our HTTP
framework. See odoo/odoo#78857 for complete historic, discussions and
rationnals.

The web module is twofold, on one side there are many controllers: /,
/web, /web/login, /web/database/selector, /web/dataset/call_kw, etc, on
the other side there is `session_info`: the method responsible to create
the web client's environ.

This module is kinda an exception as it is (with base) a server wide
module. In the case of the HTTP framework, it means that the controllers
of web are always accessible, i.e. going to / or /web/login will never
return a 404 Not Found even if the user is not connected to a database.

This is both a blessing and a curse. It is a blessing because the
controllers are always accessible it means that a new users can freely
access those routes. It is a curse because *any* user can access them,
even user who don't have a session yet thus who are not connected to a
database yet. From a developer standpoint, we have to put extra care to
correct serve users with and without a database. An example is the
/web/login route, the login/password pair is stored in a database,
without database it is impossible to validate a user login but users can
still access this route without db.

To solve this problem, there is the `ensure_db` function. This function
attempts to find a database using various sources (?db= query-string,
session db, mono db) and to save it on the user session. In case no db
is found, the user is redirected to the database selector. In a way,
this function grants a database to the user in a seamingly experience.
In a way, this function brings a welcome differentiation between
`auth='none'` with a database and `auth='none'` without a database. Such
differentiation only matters for the server wide modules as "regular"
module controllers are only accessible via the ir.http routing map, i.e.
it is not possible to declare a nodb controller outside of server wide
modules.

An important changement is the `session.authenticate` method, before it
was possible to call the method when the cursor was not yet initialized,
authenticate would open a cursor against the given database, setup a
registry and an environment and ultimately save everything on the
current request. Because the cursor is now greedily created, it is no
more possible to update the request environment when authenticating on
another database.

PR: odoo#78857
Task: 2571224
2022-02-24 13:30:50 +00:00
Julien Castiaux eb16546132 [REF] core: HTTPocalypse (11) ir.http base model
This commit is the 11th commit of a comprehensive refactor of our HTTP
framework. See odoo/odoo#78857 for complete historic, discussions and
rationnals. See also [REF] core: HTTPocalypse (9) ORM initialization.

Where `request._serve_db` is reponsible for initializing the ORM,
`request._serve_ir_http` is responsible to do the match -> authenticate
-> pre dispatch -> dispatch -> post dispatch sequence. This sequence is
very much similar to `_serve_nodb` with the notable addition of the
authenticate step.

Where `_serve_nodb` was working in a db-free environment, calling the
methods of (Http|Json)Dispatcher right away, `_serve_ir_http` delegates
to the `ir.http` abstract model which in turn use the dispatchers.

The ir.http model is crucial for several modules, web, portal and
website on top. They override the various methods to add advenced
capabilities to the http framework.

An important changement to the ir.http model in this work is regarding
the `_dispatch` method. Before this work it was that method that was
responsible of doing the sequence match -> pre dispatch -> ... . It was
decided to move the responsibility to http.py in order to prevent
modules from taking over the entire framework. One such take-over was
done in the `http_routing` addon and resulted in arguably very poor and
fragile code.

All modules must now carefully override the correct method. You need to
do smart stuff on the URL prior to matching an endpoint? You should
override `_match`. You need to save a special option from the query-
strings to the session before calling the controller? Override
`_pre_dispatch`.

PR: odoo#78857
Task: 2571224
2022-02-24 13:30:50 +00:00
Julien Castiaux 994777b018 [REF] core: HTTPocalypse (10) error handling
This commit is the 10th commit of a comprehensive refactor of our HTTP
framework. See odoo/odoo#78857 for complete historic, discussions and
rationnals.

One of the objectives of this comprehensive refactor was to simplify the
error reporting, to show developers shorter and more informative
traceback upon errors.

We argue that the current error reporting is too much bloated:

1. There is a "dual" traceback, one traceback for the frames more recent
   than `/base:ir.http_dispatch` (`@route` wrappers + controller) and a
   second traceback for the former frames (http framework + web server).
2. We trace too far back, we argue that it is useless to trace back to
   the thread/worker creation, that we should only trace back until the
   WSGI entry point.
3. There is too much noise, many frames are either implementation
   details or useless information. e.g. middlewares, `_dispatch`
   overrides, wrappers, "checks".

To solve the above problem we did the following:

1. All errors bubble up to `request.__call__` where it is logged, any
   wrapper that catch an error must log it or re-raise it. By logging
   exceptions in `__call__`, we ensure the traceback at that function.
2. When one raises an HTTP error, e.g. 404/NotFound, the object is at a
   same time an exception and a response. We don't log it and use the
   response right way.
3. When one raises an exception, e.g. ValueError, the exception is sent
   to `handle_error`, its job is to return the HTTP error corresponding
   to the given exception, e.g. 500/InternalError in case of ValueError.
   The given exception's `__traceback__` and `__clause__` are not
   modified, we log the real exception but use the http error as
   response.

Minimalistic example showing a traceback before and after this work:
https://gist.github.com/Julien00859/d6a48d523cac42d0df2e3136f5ba3e54

PR: odoo#78857
Task: 2571224
2022-02-24 13:30:50 +00:00
Julien Castiaux f61aa39ff1 [REF] core: HTTPocalypse (9) ORM initialization
This commit is the 9th commit of a comprehensive refactor of our HTTP
framework. See odoo/odoo#78857 for complete historic, discussions and
rationnals. See also [REF] core: HTTPocalypse (11) ir.http base model.

This is a two-part commit with "(11) ir.http base model". In this commit
we focus on the initialization of the various ORM objects, namely: the
registry, the cursor, the environment, the user and the context. In the
other n°11 commit we focus on the ir.http model and its relation to the
current http.py module.

One of the objectives of this comprehensive refactor was to ease the
cognitive complexity of the http framework, in other words to make it
simplier. One of the problem identified quite early during the
preparation of this work is the way the various ORM objects are
initialized, modified and cleaned during the request lifetime.

Before this work, all the ORM internals were lazily initialized via
properties. It is the first time one uses `request.cr` that a cursor is
opened to `request.db` and stored on `request._cr`. It is the first time
one uses `request.env` that an environment is create with the current
`request.user` and `request.context`. Upon user or context modification,
the current environment is discarded, the next usage of `request.env`
will create yet another environment on the fly using the modified user
and/or context.

Using this model, no ressource is initialized if not necessary. It is
possible for nodb-compatible endpoint to be served via the db-compatible
router and ir.http without ever opening a cursor to the database.

But this model is harder to reason about and ultimately to maintain.

In this work we propose to drop the lazy approach for a greedy one. In
this work the first steps of `_serve_db`, the db-compatible counter-part
of `_serve_nodb`, are dedicated to setup a registry, open a cursor to
the database and create an environment using the session's user and
context. In this work, when one wants to change the environ's user or
context, he must call `request.update_env` or `request.update_context`,
both method will recreate the environment *now* with the given values.

The downside of this approach is that resources are always allocated
even when it is not necessary. We argue that, in general, the
controllers that do not use the ORM are rare thus it is rare we allocate
unecessary resources. We also argue that the cognitive benefits are more
than welcome and that the new APIs will help at writing more robust
applications.

PR: odoo#78857
Task: 2571224
2022-02-24 13:30:49 +00:00
Julien Castiaux 18382b165d [REF] core: HTTPocalypse (8) json dispatcher
This commit is the 8th commit of a comprehensive refactor of our HTTP
framework. See odoo/odoo#78857 for complete historic, discussions and
rationnals.

PR: odoo#78857
Task: 2571224
See also: [REF] core: HTTPocalypse (6) serve db-free routes
2022-02-24 13:30:49 +00:00
Julien Castiaux d7e054d669 [REF] core: HTTPocalypse (7) http dispatcher
This commit is the 7th commit of a comprehensive refactor of our HTTP
framework. See odoo/odoo#78857 for complete historic, discussions and
rationnals.

PR: odoo#78857
Task: 2571224
See also: [REF] core: HTTPocalypse (6) serve db-free routes
2022-02-24 13:30:48 +00:00
Julien Castiaux f4b0370943 [REF] core: HTTPocalypse (6) serve db-free routes
This commit is the 6th commit of a comprehensive refactor of our HTTP
framework. See odoo/odoo#78857 for complete historic, discussions and
rationnals.

There are some routes that must work even when the user is not connected
to a database yet. Two obvious examples are the index (/) and the
database selector (/web/database/selector). Accessing those routes
should be possible even when the user is not connected to a database yet,
the `nodb_routing_map` and the `_serve_nodb` method fulfill this need.

Along with the introduction of the `_serve_nodb` method, we also
introduce the match -> pre dispatch -> dispatch -> post dispatch
sequence. `match` matches an endpoint using the http path,
`pre_dispatch` prepares the system, `dispatch` is the actual endpoint
call, `post_dispatch` cleanups the system and add some http headers to
the http response.

At the beginning, when an endpoint is matched, we retrieve the `routing`
dictionnary that is attached to the endpoint method. This dictionnary
contains the information set by the `@route` decorator, among other
values is the routing type `http` or `json`. This routing type is used
to specialize the request via one of the dispatchers: HttpDispatcher for
http and JsonDispatcher for json. Via the dispatchers, it is possible to
add custom behavior in the form of (pre_|post_)dispatch override. One
example of such custom behavior is the way the request body will be
loaded: urllib.parse.parse_qs (http) vs json.loads (json).

Before this work, it was not possible for multiple dispatchers to be
compatible with a same request mimetype. Because `JsonRequest` was
implementing the support for `application/json` (and like) mimetypes,
all requests having a body's mimetype `application/json` were considered
JSON-RPC2 (the protocol implemented by `JsonRequest`) even if the
request was actually bare json data. With the introduction of the
`is_compatible_with` classmethod, multiple dispatchers can be compatible
with a same mimetype and it is up to the `@route(type=...)` param to
define which dispatcher should be used with this route.

PR: odoo#78857
Task: 2571224
2022-02-24 13:30:48 +00:00
Julien Castiaux 6e6966ea87 [REF] core: HTTPocalypse (5) session
This commit is the 5th commit of a comprehensive refactor of our HTTP
framework. See odoo/odoo#78857 for complete historic, discussions and
rationnals.

A session is a file that contains user data that must be preserved
across several requests. It is a small (=at most a few kb) file that is
stored on the filesystem in the session store. Each filename is a random
sha-1 string, the same sha-1 is saved in the request's cookies. The
browser has no access to the session, it only possesses the random sha-1
cookie which the server use to retrieve the session upon each request.

It is in the session that the database that the user is connected to is
stored. The first time the user accesses the server the database is
determined using the HTTP Host header of the request plus the list of
available databases and is saved into the user's session. Later requests
merely ensure the database stored in the session is still enabled.

The session "feeling" is the same as before the httpocalypse. The
session is still a DotDict-like structure with methods such as
`authenticate` and `logout`. The difference is the implementation, in
master we were using the werkzeug session, with this work we decided to
get rid of those off-the-shelf sessions to implement our own object.

The major difference between the two implementations is the way we
determine when a session is "dirty", when the session should be written
back on the filesystem. In werkzeug it is a `modified` flag attribute
that is set as soon as `__setitem__` is called, in our implemention we
keep an immutable copy of the original session, at the end the of
request lifecycle we compare the live session to the copy to determine
if it was changed. The former was setting the `modified` flag even when
one was setting the same value as existing in the session. The later
approach is not affected by this limitation.

PR: odoo#78857
Task: 2571224
2022-02-24 13:30:48 +00:00
Julien Castiaux 347a3ccf76 [REF] core: HTTPocalypse (4) route binding
This commit is the 4th commit of a comprehensive refactor of our HTTP
framework. See odoo/odoo#78857 for complete historic, discussions and
rationnals.

Complete refactor of the "registry" of controllers. The `ControllerType`
metaclass have been replaced by an abstract class with a py3.7
`__init_subclass__`. The `Endpoint` class is gone too, replaced by a
clever usage of `functools.partial`. This refactor is "pure", it doesn't
add any new feature, it only merely adds a few warnings.

The four `@route`, `Controller`, `_generate_routing_rule`, `routing_map`
work as follow:

1. A reference to each immediate child class of `Controller` (not grand-
children) is registered in a global list indexed by module (thus a
dictionnary) everytime the server starts. Remember that every first-
child (not grand-children) of `Controller` is the primary controller,
the one that can later be extended by other controllers (the grand-
children) in other modules. Remember that it is possible to get each
class's children via the `__subclasses__` dunder method.

2. Every controller method that is decorated with `@route` is granted an
attribute: `original_routing`, a dictionnary containing the `@route`
arguments. When a controller method has the `original_routing`
attribute, this method is called an `endpoint`.

3. `_generate_routing_rules` receives the list of installed module
names, this list is topologicaly sorted according to the modules
dependencies. That is `base` comes before `web` in this list. The
objectif of this function is to pair each route to an endpoint whoose
class's MRO respect the above-mentioned order. Whe achieve this by
carefully crafting classes at runtime, classes inheritating from the
correct "source code" controllers in accordance to the topology. This
method is also responsible of merging each method's `original_routing`
into one `routing` dictionnary, the very `rule.endpoint.routing` dict
that is used through the rest of the http framework.

4. Each route-endpoint pair is saved into a `routing_map`, an object
that bind each route to its endpoint. This object exposes a `match`
method used to find back the endpoint given its route (=http path).

In addition to this refactor, we added some new helpers in our tools,
among them `submap` that implement a kind of `dict() - set() -> dict()`
operator. Filtering a dict on a set of keys is a common operation but
the python standard library lacks a dedicated operator.

PR: odoo#78857
Task: 2571224
2022-02-24 13:30:48 +00:00
Julien Castiaux 77df31f67a [REF] core: HTTPocalypse (3) serve static files
This commit is the 3rd commit of a comprehensive refactor of our HTTP
framework. See odoo/odoo#78857 for complete historic, discussions and
rationnals.

Static files were historically served thanks to a werkzeug middleware,
the usage of that middleware prevented easy modification of the response
headers. Using `http.send_file` and `request._serve_static` we achieve
the same result as the middleware with less code and less frames in the
callstack.

The historic `http.send_file` have been splitted in two. One to send
a file via its path on the filesystem (prefered function) and one to
send a file already open. It is preferred to use `http.send_filepath`.

During development we tried to remove the fallback from the
`_serve_static` function into the more capables (and resource hungry)
db-compatible router. The purpose was to quickly respond to all requests
requesting static files at the cost of reduced customization. We have
been prooved wrong, e.g. the website themes all depends on the fallback
mechanism to deliver static files.

PR: odoo#78857
Task: 2571224
2022-02-24 13:30:47 +00:00
Julien Castiaux 1b62f71ca8 [REF] core: HTTPocalypse (2) structure
This commit is the 2nd commit of a comprehensive refactor of our HTTP
framework. See odoo/odoo#78857 for complete historic, discussions and
rationnals.

This commit poses the global structure of the new HTTP framework. Major
classes are present (yet empty) with a minimal docstring explaining
their general purpose.

PR: odoo#78857
Task: 2571224
2022-02-24 13:30:47 +00:00
Julien Castiaux c3714eafbd [REF] core: HTTPocalypse (1) rationnals
This commit is the 1st commit of a comprehensive refactor of our HTTP
framework. See odoo/odoo#78857 for complete historic, discussions and
rationnals.

Our application framework is quite complicated. There are a few explicit
middlewares: `ProxyFix`, `DisableCacheMiddleware`, `SharedDataMiddleware`.
There are many (13, in total) implicit ones in the form of
`ir.http._dispatch` overrides. There are a few frames that don't have
much values by themselves and are very much implementation details
there are: `application_unproxied`, `_call_function`, `checked_call` and
`EndPoint.__call__`.

The ORM initialisation is quite magical, the cursor, registry and
environment are all setup lazily thanks to class properties. The
context and uid of the environment are aliased on the request. The
abuse of properties makes it impossible to reason about where and when
the environment is actually instantiated and modified.

The dispatch of a request follow the next scheme:
`odoo.http/Root.dispatch` -> `odoo.addons.base.models/ir.http._dispatch`
-> `odoo.http/(Http|Json)Request.dispatch` -> `odoo.http/@route` ->
controller. Because the request becomes http or json specialized quite
late, the many ir.http override all need to parsimony try/except their
code in order to manually call `_handle_exception()` uppon error, they
cannot let the error bubble-up. This back-and-forth between odoo.http
and ir.http is source of some headaches.

Speaking about error, the `odoo.http/WebRequest._handle_exception`
implementation is quite complicated, it basically craft a new exception
out of the passed exception object in order to correct its traceback.
If the error had been bubbled-up instead of handled by
`_handle_exception()` such python hack would not have been necessary.

The objectives of this refactor are about refounding the technical dept:

* We want shorter error reporting;
* We want simpler request and ir.http APIs;
* We want better integration of all the ir.http extensions.

PR: odoo#78857
Task: 2571224
2022-02-24 13:30:47 +00:00
Xavier Morel 49af835e20 [FIX] base: attachment copy ACL
Currently, `Attachment.copy` has an *explicit* check for write access
on the underlying record.

This doesn't necessarily make sense e.g. a user copying an attachment
from a template to an email may not have write access to the template,
but that should not be an issue. And indeed if the copy is performed
"by hand" (read then create) things work fine[^1].

Since both `read` and `create` are checked, the explicit `copy` check
doesn't seem necessary. Drop it, and try to add some tests around
`copy`. Move `test_06_linked_record_permission` to its own `TestCase`
and split it for readability.

A secondary issue is that the `check` in `create` was performed in the
context of the `self` being copied, leading to the same issue as
`copy` being repeated (that is, `a.copy()` would call `a.create()`
which would call `a.check('write')`, even though we're semantically
creating a record from scratch). The behavior is really intended for
`write` where we want to check if we have `write` access to both the
"source" and the "destination" records.

Explicitly opt-out of having any source data in the `create` before
performing the `check` calls, this way we correctly and only check for
the writability of the destination, and only check for the readability
of the source.

issue 2746483

[^1] well not entirely true, see 4th paragraph

X-original-commit: 60723fb311ed7cc1cf901fe90e8745835cf4d54d
Part-of: odoo/odoo#85271
2022-02-24 10:15:55 +00:00
Raf Geens 6f3cd5fd94 [IMP] cli: Set global neutralization parameter
This flag allows code to detect it's running on a neutralized database.

closes odoo/odoo#85259

X-original-commit: 892efa2c8b85b86155d50055dca568592569d411
Signed-off-by: Christophe Monniez (moc) <moc@odoo.com>
Signed-off-by: Raf Geens <raf@odoo.com>
2022-02-24 07:17:52 +00:00
Raf Geens a4c2263afa [IMP] base: Disable cron toggle on neutralized databases
In some cases like `fetchmail.server` and `calendar.alarm`, modifying
the record can result in the corresponding cron job being enabled as a
side effect through `ir.cron.toggle`, even if it was archived before. In
`fetchmail.server`'s case on a neutralized database, this can result in
the cron job unintentionally affecting a production mailbox.

Since `ir.cron.toggle` is inherently risky on a neutralized database,
this PR disables it when an `ir.config_parameter` indicates we are on
one.

This parameter can be set both by the neutralization API introduced in
Odoo 15.2 and by legacy neutralization tools for older Odoo versions.

closes odoo/odoo#85258

X-original-commit: 4fba314faa1c8f67ac1dd93351ebf8ce39c5caf7
Signed-off-by: Christophe Monniez (moc) <moc@odoo.com>
Signed-off-by: Raf Geens <raf@odoo.com>
2022-02-24 07:17:48 +00:00
Raf Geens 0a9f67c82a [REM] base: Remove cron code commenting
In neutralized databases, modifying records like `fetchmail.server`
re-activates the related archived cron job as a side effect, which
could impact production mailboxes. The initial solution to this was to
comment out the server action code linked to the cron job as an extra
precaution.

The impact of that solution is too broad, and it also means users have
to be aware of the need to uncomment the code if they do want a cron job
to run on a neutralized copy. It's been replaced by disabling
`ir.cron.toggle` instead:
https://github.com/odoo/odoo/pull/85192

closes odoo/odoo#85260

X-original-commit: 6c950959e1f4bed82d62796f0d89d82784e556b6
Signed-off-by: Christophe Monniez (moc) <moc@odoo.com>
2022-02-24 06:25:02 +00:00
Julien Castiaux e655e8da8a Revert "[IMP] base: add prefetching group feature"
This reverts commit 041fe5e21e.

Part-of: odoo/odoo#85199
2022-02-23 12:59:52 +00:00
Julien Castiaux 7534358351 Revert "[IMP] base: make recompute faster"
This reverts commit 59ea46c97e.

Part-of: odoo/odoo#85199
2022-02-23 12:59:51 +00:00
Rémy Voet (ryv) 59ea46c97e [IMP] base: make recompute faster
When we read data of one record, the method `recompute` (`_fetch_field`
-> `_read` -> `flush` -> `recompute`) can take more than 40 % of the
time of the `_read`, due to a huge number of recordset creation (from
`records_to_compute` and `records & recs`).  Avoid that waste of time by
postponing the test on records.

For example, on a database with modules crm, mrp, purchase, website,
sale_management, reading the prefetchable fields of the current company
took 1.45 ms ± 60.3 µs, and now takes 1.17 ms ± 110 µs (more than 20%
speedup).

closes odoo/odoo#83818

Related: odoo/enterprise#24645
Signed-off-by: Raphael Collet <rco@odoo.com>
2022-02-23 10:02:00 +00:00
Rémy Voet (ryv) 041fe5e21e [IMP] base: add prefetching group feature
The field prefetching mechanism was poorly customizable.  Before this,
we could only tell if a field was prefetched with other fields or not at
all.  We have no way to inform the framework, like: "When I need data of
that field, prefetch these other fields, which are likely be used in the
same transaction".

From now on, the `prefetch` attribute is used as a grouping key for
prefetching fields.  When a field is fetched, all the fields with the
same value for `prefetch` are taken for prefetching.

For example, consider a small set of fields that are rarely used, except
for one flow A using them.  You want to prefetch those fields only in
the flow A, and you want to fetch them in a single query.  With the new
feature, simply set `prefetch=A` for some string `A` on those fields,
and they will be grouped for prefetching.

Part-of: odoo/odoo#83818
2022-02-23 10:01:59 +00:00
Ronald Portier f278589d2a [FIX] cli: support export of pot files
The export_translation method supports the "new language" option but
it was not possible if giving a .pot file.

closes odoo/odoo#85141

X-original-commit: 93e23025252784f39f37a7ffd109425ae56b1d36
Signed-off-by: Martin Trigaux (mat) <mat@odoo.com>
2022-02-22 16:16:40 +00:00
Pouya Malekinejad b843407fed [IMP] models: small optimization in onchange
closes odoo/odoo#84865

Signed-off-by: Vincent Schippefilt (vsc) <vsc@odoo.com>
2022-02-22 16:16:24 +00:00
Vincent Schippefilt e5523f06ce [FIX] base: unique module name declared twice
before this commit, there was 2 unique indexes on module name
after this commit, only 1 unique index remains (the one declared in base/ir_module.py) to keep the error message

closes odoo/odoo#85116

Signed-off-by: Raphael Collet <rco@odoo.com>
2022-02-22 15:20:13 +00:00
Yannick Tivisse daa9a1911e [FIX] core: avoid access error in (parent/child)_of_domain
Purpose
=======

Loading a view trying to retrieve the hierarchy of a record using the field
parent_path could lead to an access error if records are mixed up.

Note:
Easily achievable for an end user. It could happen in a multi company
environment (you activate 2 companies at the same time) while configuring
the departments (using _parent_store=True), and you say that you have:

R&D (company=1):
     - R&D Belgium (company=1)
     - R&D India (company=2)
Then you go back to a single-company environment, you click on the form
view of an employee and crack, since there is a multi-company rule on the
departments, and that the search panel is loading the hierarchy for
display purpose.

Use sudo to avoid access rights issues, as the forbidden records will be
filtered automatically by the constructed domain like this:

```py
parent_ids = [
    int(label)
    for rec in left_model.sudo().browse(ids)
    for label in rec.parent_path.split('/')[:-1]
]
domain = [('id', 'in', parent_ids)]
```

This actually makes the search consistent with the case where
_parent_store=False.

closes odoo/odoo#85042

X-original-commit: a7e13b32e6c47a3fdcfad8a6c6337b89580ad70a
Signed-off-by: Raphael Collet <rco@odoo.com>
2022-02-21 17:01:12 +00:00