[IMP] IrQweb: merge Qweb engine file qweb.py and ir_qweb.py
QWeb is the primary templating engine used by Odoo. It is an XML templating engine and used mostly to generate XML, HTML fragments and pages. To create new XML template, please see :doc:`QWeb Templates documentation <https://www.odoo.com/documentation/15.0/developer/reference/frontend/qweb.html>` In **input** you have an XML template giving the corresponding input etree. Each etree input nodes are used to generate a python function. This fonction is called and will give the XML **output**. The ``_compile`` method is responsible to generate the function from the etree, that function is a python generator that yield one output line at a time. This generator is consumed by ``_render``. The generated function is orm cached. In the graphic below you can see theresume of the call of the methods performed in the IrQweb class. Odoo ┗━► _render (returns MarkupSafe) ┗━► _compile (returns function) ◄━━━━━━━━━┓ ┗━► _compile_node (returns code string array) ◄━━━━━━━┓ ┃ ┃ (add technical directives: t-inner-content, t-tag) ┃ ┃ ┣━► _directives_eval_order (defined directive order) ┃ ┃ ┃ ┃ ┃ ┣━► _compile_directives (recursive) ◄━━━━┓ ┃ ┃ ┃ ┣━► _compile_directive ┃ ┃ ┃ ┃ ┃ ┗━► t-if ━━► _compile_directive_if ━┫ ┃ ┃ ┃ ┃ ┗━► t-foreach ━━► _compile_directive_foreach ━┫ ┃ ┃ ┃ ┃ ┗━► t-* ━━► ... ━┛ ┃ ┃ ┃ ┃ ┗━► t-inner-content ━━► _compile_directive_inner_content ◄━━━━┓ ━┛ ┃ ┃ ┃ ┗━► t-tag ━━► _compile_directive_tag ━┫ ┃ ┃ ┃ ┗━► t-call ━━► _compile_directive_call ━┫ ━━━┛ ┃ ┃ ┗━► t-out ━━► _compile_directive_out ◄━┓ ━┫ ┃ ┃ ┗━► t-field ━━► _compile_directive_field ━┛ ┃ ┃ ┃ ┃ ┗━━┻━► _compile_static_node ━┛ Part-of: odoo/odoo#81024
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
|
||||
from odoo.tests import Form
|
||||
from odoo.addons.hr.tests.common import TestHrCommon
|
||||
from odoo.addons.base.models.qweb import QWebException
|
||||
from odoo.addons.base.models.ir_qweb import QWebException
|
||||
|
||||
|
||||
class TestMultiCompany(TestHrCommon):
|
||||
|
||||
@@ -19,7 +19,7 @@ import odoo
|
||||
from odoo import api, models, registry, exceptions, tools, http
|
||||
from odoo.addons.base.models import ir_http
|
||||
from odoo.addons.base.models.ir_http import RequestUID
|
||||
from odoo.addons.base.models.qweb import QWebException
|
||||
from odoo.addons.base.models.ir_qweb import QWebException
|
||||
from odoo.http import request
|
||||
from odoo.osv import expression
|
||||
from odoo.tools import config, ustr, pycompat
|
||||
|
||||
@@ -47,6 +47,7 @@ from . import ir_http
|
||||
from . import ir_model
|
||||
from . import ir_model_fields
|
||||
from . import ir_ui_view
|
||||
from . import ir_qweb
|
||||
from . import res_company
|
||||
from . import res_config_settings
|
||||
from . import res_partner
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import models
|
||||
|
||||
class QWebCodeFound(Exception):
|
||||
"""
|
||||
Exception raised when a qweb compilation encounter dynamic content if the
|
||||
option `raise_on_code` is True.
|
||||
"""
|
||||
|
||||
class IrQweb(models.AbstractModel):
|
||||
_inherit = 'ir.qweb'
|
||||
|
||||
def _get_template_cache_keys(self):
|
||||
return super()._get_template_cache_keys() + ['raise_on_code']
|
||||
|
||||
def _compile_directives(self, el, options, indent):
|
||||
if options.get('raise_on_code'):
|
||||
raise QWebCodeFound()
|
||||
return super()._compile_directives(el, options, indent)
|
||||
@@ -10,8 +10,9 @@ from lxml import html
|
||||
from markupsafe import Markup
|
||||
from werkzeug import urls
|
||||
|
||||
from .ir_qweb import QWebCodeFound
|
||||
from odoo import _, api, fields, models, tools
|
||||
from odoo.addons.base.models.qweb import QWebCodeFound
|
||||
from odoo.addons.base.models.ir_qweb import QWebException
|
||||
from odoo.exceptions import UserError, AccessError
|
||||
from odoo.tools import is_html_empty, safe_eval
|
||||
from odoo.tools.rendering_tools import convert_inline_template_to_qweb, parse_inline_template, render_inline_template, template_env_globals
|
||||
@@ -284,10 +285,10 @@ class MailRenderMixin(models.AbstractModel):
|
||||
)
|
||||
# remove the rendered tag <div> that was added in order to wrap potentially multiples nodes into one.
|
||||
render_result = render_result[5:-6]
|
||||
except QWebCodeFound:
|
||||
group = self.env.ref('mail.group_mail_template_editor')
|
||||
raise AccessError(_('Only users belonging to the "%s" group can modify dynamic templates.', group.name))
|
||||
except Exception as e:
|
||||
if isinstance(e, QWebException) and isinstance(e.error, QWebCodeFound):
|
||||
group = self.env.ref('mail.group_mail_template_editor')
|
||||
raise AccessError(_('Only users belonging to the "%s" group can modify dynamic templates.', group.name))
|
||||
_logger.info("Failed to render template : %s", template_src, exc_info=True)
|
||||
raise UserError(_("Failed to render QWeb template : %s)", e))
|
||||
results[record.id] = render_result
|
||||
|
||||
@@ -39,8 +39,8 @@ REMOTE_CONNECTION_TIMEOUT = 2.5
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class QWeb(models.AbstractModel):
|
||||
""" QWeb object for rendering editor stuff
|
||||
class IrQWeb(models.AbstractModel):
|
||||
""" IrQWeb object for rendering editor stuff
|
||||
"""
|
||||
_inherit = 'ir.qweb'
|
||||
|
||||
@@ -114,12 +114,12 @@ class QWeb(models.AbstractModel):
|
||||
def _compile_directive_tag(self, el, options, indent):
|
||||
if el.get('t-placeholder'):
|
||||
el.set('t-att-placeholder', el.attrib.pop('t-placeholder'))
|
||||
return super(QWeb, self)._compile_directive_tag(el, options, indent)
|
||||
return super(IrQWeb, self)._compile_directive_tag(el, options, indent)
|
||||
|
||||
# order and ignore
|
||||
|
||||
def _directives_eval_order(self):
|
||||
directives = super(QWeb, self)._directives_eval_order()
|
||||
directives = super(IrQWeb, self)._directives_eval_order()
|
||||
directives.insert(directives.index('call'), 'snippet')
|
||||
directives.insert(directives.index('call'), 'snippet-call')
|
||||
directives.insert(directives.index('call'), 'install')
|
||||
|
||||
@@ -34,8 +34,8 @@ class AssetsBundleMultiWebsite(AssetsBundle):
|
||||
extra = website_id_path + extra
|
||||
return super(AssetsBundleMultiWebsite, self).get_debug_asset_url(extra, name, extension)
|
||||
|
||||
class QWeb(models.AbstractModel):
|
||||
""" QWeb object for rendering stuff in the website context """
|
||||
class IrQWeb(models.AbstractModel):
|
||||
""" IrQWeb object for rendering stuff in the website context """
|
||||
|
||||
_inherit = 'ir.qweb'
|
||||
|
||||
@@ -47,14 +47,14 @@ class QWeb(models.AbstractModel):
|
||||
'img': 'src',
|
||||
}
|
||||
|
||||
def get_asset_bundle(self, xmlid, files, env=None, css=True, js=True):
|
||||
def _get_asset_bundle(self, xmlid, files, env=None, css=True, js=True):
|
||||
return AssetsBundleMultiWebsite(xmlid, files, env=env)
|
||||
|
||||
def _post_processing_att(self, tagName, atts, options):
|
||||
if atts.get('data-no-post-process'):
|
||||
return atts
|
||||
|
||||
atts = super(QWeb, self)._post_processing_att(tagName, atts, options)
|
||||
atts = super(IrQWeb, self)._post_processing_att(tagName, atts, options)
|
||||
|
||||
if tagName == 'img' and 'loading' not in atts:
|
||||
atts['loading'] = 'lazy' # default is auto
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import assetsbundle
|
||||
from . import qweb
|
||||
|
||||
from . import ir_model
|
||||
from . import ir_sequence
|
||||
|
||||
+1424
-124
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,7 @@ from lxml.builder import E
|
||||
|
||||
from odoo.modules import get_module_resource
|
||||
from odoo.tests.common import TransactionCase
|
||||
from odoo.addons.base.models.qweb import QWebException
|
||||
from odoo.addons.base.models.ir_qweb import QWebException
|
||||
from odoo.tools import misc, mute_logger
|
||||
from odoo.tools.json import scriptsafe as json_scriptsafe
|
||||
|
||||
@@ -697,7 +697,9 @@ class TestQWebBasic(TransactionCase):
|
||||
("(lambda a: a + 5)(x)", {'x': 10}, 15),
|
||||
("(lambda a: a + x)(5)", {'x': 10}, 15),
|
||||
("sum(x for x in range(4)) + ((x))", {'x': 10}, 16),
|
||||
("['test_' + x for x in ['a', 'b']]", {}, ['test_a', 'test_b'])
|
||||
("['test_' + x for x in ['a', 'b']]", {}, ['test_a', 'test_b']),
|
||||
("""1 and 2 and 0
|
||||
or 9""", {}, 9),
|
||||
]
|
||||
|
||||
IrQweb = self.env['ir.qweb']
|
||||
@@ -882,6 +884,16 @@ class TestQWebBasic(TransactionCase):
|
||||
rendered = self.env['ir.qweb']._render(t.id)
|
||||
self.assertEqual(rendered.strip(), result.strip())
|
||||
|
||||
def test_out(self):
|
||||
t = self.env['ir.ui.view'].create({
|
||||
'name': 'test',
|
||||
'type': 'qweb',
|
||||
'arch_db': '''<t t-name="out-format"><div t-out="a">Default</div></t>'''
|
||||
})
|
||||
result = """<div>1</div>"""
|
||||
rendered = self.env['ir.qweb']._render(t.id, {'a': 1})
|
||||
self.assertEqual(rendered.strip(), result.strip())
|
||||
|
||||
def test_out_format_1(self):
|
||||
t = self.env['ir.ui.view'].create({
|
||||
'name': 'test',
|
||||
@@ -1161,7 +1173,7 @@ class TestQWebStaticXml(TransactionCase):
|
||||
|
||||
return lambda: self.run_test_file(os.path.join(path, f))
|
||||
|
||||
@mute_logger('odoo.addons.base.models.qweb') # tests t-raw which is deprecated
|
||||
@mute_logger('odoo.addons.base.models.ir_qweb') # tests t-raw which is deprecated
|
||||
def run_test_file(self, path):
|
||||
self.env.user.tz = 'Europe/Brussels'
|
||||
doc = etree.parse(path).getroot()
|
||||
|
||||
@@ -21,7 +21,7 @@ from odoo.addons.base.models.ir_attachment import IrAttachment
|
||||
from odoo.modules.module import get_resource_path, get_manifest
|
||||
from odoo.tests import HttpCase, tagged
|
||||
from odoo.tests.common import TransactionCase
|
||||
from odoo.addons.base.models.qweb import QWebException
|
||||
from odoo.addons.base.models.ir_qweb import QWebException
|
||||
from odoo.tools import mute_logger, func
|
||||
|
||||
|
||||
|
||||
@@ -313,7 +313,7 @@ class QwebTracker():
|
||||
def wrap_compile_directive(cls, method_compile_directive):
|
||||
@functools.wraps(method_compile_directive)
|
||||
def _tracked_compile_directive(self, el, options, directive, indent):
|
||||
if not options.get('profile') or directive in ('content', 'tag'):
|
||||
if not options.get('profile') or directive in ('inner-content', 'tag'):
|
||||
return method_compile_directive(self, el, options, directive, indent)
|
||||
|
||||
enter = self._indent(f"self.env.context['qweb_tracker'].enter_directive({directive!r}, {el.attrib!r}, {options['last_path_node']!r})", indent)
|
||||
|
||||
Reference in New Issue
Block a user