mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-19 16:51:59 +02:00
Initial commit: Core packages
This commit is contained in:
commit
12c29a983b
9512 changed files with 8379910 additions and 0 deletions
218
odoo-bringout-oca-ocb-web/web/controllers/utils.py
Normal file
218
odoo-bringout-oca-ocb-web/web/controllers/utils.py
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import copy
|
||||
import hashlib
|
||||
import io
|
||||
import logging
|
||||
import re
|
||||
from collections import OrderedDict, defaultdict
|
||||
|
||||
import babel.messages.pofile
|
||||
import werkzeug
|
||||
import werkzeug.exceptions
|
||||
import werkzeug.utils
|
||||
import werkzeug.wrappers
|
||||
import werkzeug.wsgi
|
||||
from lxml import etree
|
||||
from werkzeug.urls import iri_to_uri
|
||||
|
||||
from odoo.tools.translate import JAVASCRIPT_TRANSLATION_COMMENT, WEB_TRANSLATION_COMMENT
|
||||
from odoo.tools.misc import file_open
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def clean_action(action, env):
|
||||
action_type = action.setdefault('type', 'ir.actions.act_window_close')
|
||||
if action_type == 'ir.actions.act_window':
|
||||
action = fix_view_modes(action)
|
||||
|
||||
# When returning an action, keep only relevant fields/properties
|
||||
readable_fields = env[action['type']]._get_readable_fields()
|
||||
action_type_fields = env[action['type']]._fields.keys()
|
||||
|
||||
cleaned_action = {
|
||||
field: value
|
||||
for field, value in action.items()
|
||||
# keep allowed fields and custom properties fields
|
||||
if field in readable_fields or field not in action_type_fields
|
||||
}
|
||||
|
||||
# Warn about custom properties fields, because use is discouraged
|
||||
action_name = action.get('name') or action
|
||||
custom_properties = action.keys() - readable_fields - action_type_fields
|
||||
if custom_properties:
|
||||
_logger.warning("Action %r contains custom properties %s. Passing them "
|
||||
"via the `params` or `context` properties is recommended instead",
|
||||
action_name, ', '.join(map(repr, custom_properties)))
|
||||
|
||||
return cleaned_action
|
||||
|
||||
|
||||
def ensure_db(redirect='/web/database/selector', db=None):
|
||||
# This helper should be used in web client auth="none" routes
|
||||
# if those routes needs a db to work with.
|
||||
# If the heuristics does not find any database, then the users will be
|
||||
# redirected to db selector or any url specified by `redirect` argument.
|
||||
# If the db is taken out of a query parameter, it will be checked against
|
||||
# `http.db_filter()` in order to ensure it's legit and thus avoid db
|
||||
# forgering that could lead to xss attacks.
|
||||
if db is None:
|
||||
db = request.params.get('db') and request.params.get('db').strip()
|
||||
|
||||
# Ensure db is legit
|
||||
if db and db not in http.db_filter([db]):
|
||||
db = None
|
||||
|
||||
if db and not request.session.db:
|
||||
# User asked a specific database on a new session.
|
||||
# That mean the nodb router has been used to find the route
|
||||
# Depending on installed module in the database, the rendering of the page
|
||||
# may depend on data injected by the database route dispatcher.
|
||||
# Thus, we redirect the user to the same page but with the session cookie set.
|
||||
# This will force using the database route dispatcher...
|
||||
r = request.httprequest
|
||||
url_redirect = werkzeug.urls.url_parse(r.base_url)
|
||||
if r.query_string:
|
||||
# in P3, request.query_string is bytes, the rest is text, can't mix them
|
||||
query_string = iri_to_uri(r.query_string.decode())
|
||||
url_redirect = url_redirect.replace(query=query_string)
|
||||
request.session.db = db
|
||||
werkzeug.exceptions.abort(request.redirect(url_redirect.to_url(), 302))
|
||||
|
||||
# if db not provided, use the session one
|
||||
if not db and request.session.db and http.db_filter([request.session.db]):
|
||||
db = request.session.db
|
||||
|
||||
# if no database provided and no database in session, use monodb
|
||||
if not db:
|
||||
all_dbs = http.db_list(force=True)
|
||||
if len(all_dbs) == 1:
|
||||
db = all_dbs[0]
|
||||
|
||||
# if no db can be found til here, send to the database selector
|
||||
# the database selector will redirect to database manager if needed
|
||||
if not db:
|
||||
werkzeug.exceptions.abort(request.redirect(redirect, 303))
|
||||
|
||||
# always switch the session to the computed db
|
||||
if db != request.session.db:
|
||||
request.session = http.root.session_store.new()
|
||||
request.session.update(http.get_default_session(), db=db)
|
||||
request.session.context['lang'] = request.default_lang()
|
||||
werkzeug.exceptions.abort(request.redirect(request.httprequest.url, 302))
|
||||
|
||||
|
||||
def fix_view_modes(action):
|
||||
""" For historical reasons, Odoo has weird dealings in relation to
|
||||
view_mode and the view_type attribute (on window actions):
|
||||
|
||||
* one of the view modes is ``tree``, which stands for both list views
|
||||
and tree views
|
||||
* the choice is made by checking ``view_type``, which is either
|
||||
``form`` for a list view or ``tree`` for an actual tree view
|
||||
|
||||
This methods simply folds the view_type into view_mode by adding a
|
||||
new view mode ``list`` which is the result of the ``tree`` view_mode
|
||||
in conjunction with the ``form`` view_type.
|
||||
|
||||
TODO: this should go into the doc, some kind of "peculiarities" section
|
||||
|
||||
:param dict action: an action descriptor
|
||||
:returns: nothing, the action is modified in place
|
||||
"""
|
||||
if not action.get('views'):
|
||||
generate_views(action)
|
||||
|
||||
if action.pop('view_type', 'form') != 'form':
|
||||
return action
|
||||
|
||||
if 'view_mode' in action:
|
||||
action['view_mode'] = ','.join(
|
||||
mode if mode != 'tree' else 'list'
|
||||
for mode in action['view_mode'].split(','))
|
||||
action['views'] = [
|
||||
[id, mode if mode != 'tree' else 'list']
|
||||
for id, mode in action['views']
|
||||
]
|
||||
|
||||
return action
|
||||
|
||||
|
||||
# I think generate_views,fix_view_modes should go into js ActionManager
|
||||
def generate_views(action):
|
||||
"""
|
||||
While the server generates a sequence called "views" computing dependencies
|
||||
between a bunch of stuff for views coming directly from the database
|
||||
(the ``ir.actions.act_window model``), it's also possible for e.g. buttons
|
||||
to return custom view dictionaries generated on the fly.
|
||||
|
||||
In that case, there is no ``views`` key available on the action.
|
||||
|
||||
Since the web client relies on ``action['views']``, generate it here from
|
||||
``view_mode`` and ``view_id``.
|
||||
|
||||
Currently handles two different cases:
|
||||
|
||||
* no view_id, multiple view_mode
|
||||
* single view_id, single view_mode
|
||||
|
||||
:param dict action: action descriptor dictionary to generate a views key for
|
||||
"""
|
||||
view_id = action.get('view_id') or False
|
||||
if isinstance(view_id, (list, tuple)):
|
||||
view_id = view_id[0]
|
||||
|
||||
# providing at least one view mode is a requirement, not an option
|
||||
view_modes = action['view_mode'].split(',')
|
||||
|
||||
if len(view_modes) > 1:
|
||||
if view_id:
|
||||
raise ValueError('Non-db action dictionaries should provide '
|
||||
'either multiple view modes or a single view '
|
||||
'mode and an optional view id.\n\n Got view '
|
||||
'modes %r and view id %r for action %r' % (
|
||||
view_modes, view_id, action))
|
||||
action['views'] = [(False, mode) for mode in view_modes]
|
||||
return
|
||||
action['views'] = [(view_id, view_modes[0])]
|
||||
|
||||
|
||||
def _get_login_redirect_url(uid, redirect=None):
|
||||
""" Decide if user requires a specific post-login redirect, e.g. for 2FA, or if they are
|
||||
fully logged and can proceed to the requested URL
|
||||
"""
|
||||
if request.session.uid: # fully logged
|
||||
return redirect or ('/web' if is_user_internal(request.session.uid)
|
||||
else '/web/login_successful')
|
||||
|
||||
# partial session (MFA)
|
||||
url = request.env(user=uid)['res.users'].browse(uid)._mfa_url()
|
||||
if not redirect:
|
||||
return url
|
||||
|
||||
parsed = werkzeug.urls.url_parse(url)
|
||||
qs = parsed.decode_query()
|
||||
qs['redirect'] = redirect
|
||||
return parsed.replace(query=werkzeug.urls.url_encode(qs)).to_url()
|
||||
|
||||
|
||||
def is_user_internal(uid):
|
||||
return request.env['res.users'].browse(uid)._is_internal()
|
||||
|
||||
|
||||
def _local_web_translations(trans_file):
|
||||
messages = []
|
||||
try:
|
||||
with file_open(trans_file, filter_ext=('.po')) as t_file:
|
||||
po = babel.messages.pofile.read_po(t_file)
|
||||
except Exception:
|
||||
return
|
||||
for x in po:
|
||||
if x.id and x.string and (JAVASCRIPT_TRANSLATION_COMMENT in x.auto_comments
|
||||
or WEB_TRANSLATION_COMMENT in x.auto_comments):
|
||||
messages.append({'id': x.id, 'string': x.string})
|
||||
return messages
|
||||
Loading…
Add table
Add a link
Reference in a new issue