A hook solution to spy the Odoo web page πŸ•΅οΈ

ixkitixkit
6 min read

How does Odoo rendering a web page

Odoo is an Open source ERP software, also an rapid development web application framework, the Odoo main tech stack includes Python module and JavaScript framework, we think that two type rendering technique was used: QWeb and OWL.

πŸ‘‰ QWeb

QWeb is the primary templating engine used by Odoo. It is an XML templating engine1 and used mostly to generate HTML fragments and pages. Odoo HTTP layer base on python WSGI, the web page render on server side, most like other language solution, eg PHP echo or JSP that allows dynamic content injection into static contents, the static contents call "Template", a pice template code like bellows:

<template id="login_layout" inherit_id="web.login_layout" name="Website Login Layout" priority="20">
    <xpath expr="t" position="replace">
        <t t-call="website.layout">
            <div class="oe_website_login_container" t-out="0"/>
        </t>
    </xpath>
</template>

above code mixed two type scripts: xml + html, it define the page layout of the login page which use for end user from web browser, eg while user try to login the odoo application from the link 'http://localhost:8120/web/login', the ir_qweb.py duty to render the path page, the web page show bellow screen snapshot

Image description

We know that the page was rendering by many different template snippets, the page HTML elements was generated by a couple of QWeb templates, "t-call" or "inherit_id" , such operation primitive organize many different location template script into one big virtual template page that will be process by QWeb engine, top-level rendering probably contains many sub-templates that is spread out in different xml file, it is not easy to trace or debug or modify.
Limited the QWeb Templates syntax rule, it does not contains meta information like template location(path), the templating engine also does not provide feature to trace each template location(path) in an rendering session, this is a big(maybe not :D) question for developer.

πŸ‘‰ OWL

The official definition: OWL, A web framework for structured, dynamic and maintainable applications. it also use QWeb template as layout vector to rendering the web page, the problem still exist, if a web page includes many different Owl components, it is not easy to get the component template location on the disk, why we need know the location (path), because we need modify it or review the rendering logic as well.

πŸ”₯Shortcoming of template rendering engine

if the page is complex that use variant templates, then it is hard to know current web page component, we need insight the page structure, We often offer lot of time to find the template snippets code on disk, why we need waste time to search by keyword or memory, it must be another way to easy locate the template from web page directly, just one click⚑️

Solution πŸ”¨

so far, we only provide a hook solution to append the meta data (especially the location/path information) of the template while Odoo rendering the web page.

πŸ‘‰ QWeb

the ir_qweb.py file duty rendering the QWeb template in Python part, the official code comment bellows:


    Odoo
     ┗━► _render (returns MarkupSafe)
        ┗━► _compile (returns function)                                        ◄━━━━━━━━━━┓
           ┗━► _compile_node (returns code string array)                       ◄━━━━━━━━┓ ┃
              ┃  (skip the current node if found t-qweb-skip)                           ┃ ┃
              ┃  (add technical directives: t-tag-open, t-tag-close, t-inner-content)   ┃ ┃
              ┃                                                                         ┃ ┃
              ┣━► _directives_eval_order (defined directive order)                      ┃ ┃
              ┣━► _compile_directives (loop)    Consume all remaining directives ◄━━━┓  ┃ ┃
              ┃  ┃                              (e.g.: to change the indentation)    ┃  ┃ ┃
              ┃  ┣━► _compile_directive                                              ┃  ┃ ┃
              ┃  ┃    ┗━► t-nocache       ━━► _compile_directive_nocache            ━┫  ┃ ┃
              ┃  ┃    ┗━► t-cache         ━━► _compile_directive_cache              ━┫  ┃ ┃
              ┃  ┃    ┗━► t-groups        ━━► _compile_directive_groups             ━┫  ┃ ┃
              ┃  ┃    ┗━► t-foreach       ━━► _compile_directive_foreach            ━┫  ┃ ┃
              ┃  ┃    ┗━► t-if            ━━► _compile_directive_if                 ━┛  ┃ ┃
              ┃  ┃    ┗━► t-inner-content ━━► _compile_directive_inner_content ◄━━━━━┓ ━┛ ┃
              ┃  ┃    ┗━► t-options       ━━► _compile_directive_options             ┃    ┃
              ┃  ┃    ┗━► t-set           ━━► _compile_directive_set           ◄━━┓  ┃    ┃
              ┃  ┃    ┗━► t-call          ━━► _compile_directive_call            ━┛ ━┫ ━━━┛
              ┃  ┃    ┗━► t-att           ━━► _compile_directive_att                 ┃
              ┃  ┃    ┗━► t-tag-open      ━━► _compile_directive_open          ◄━━┓  ┃
              ┃  ┃    ┗━► t-tag-close     ━━► _compile_directive_close         ◄━━┫  ┃
              ┃  ┃    ┗━► t-out           ━━► _compile_directive_out             ━┛ ━┫ ◄━━┓
              ┃  ┃    ┗━► t-field         ━━► _compile_directive_field               ┃   ━┫
              ┃  ┃    ┗━► t-esc           ━━► _compile_directive_esc                 ┃   ━┛
              ┃  ┃    ┗━► t-*             ━━► ...                                    ┃
              ┃  ┃                                                                   ┃
              ┗━━┻━► _compile_static_node                                           ━┛

I like the comment, it is great, , so maybe we can do something according the guide :-) the class IrQWeb duty to parse the template and rendering data to HTML, the method _load(self, ref) use to read/load template code, so what we do just append the template location/path information to the original template in the method _load().

let use a example to demo the key steps: A template 'brand_promotion' that use to display the odoo brand on Home web page, the code bellows:

<template id="brand_promotion" name="Brand Promotion">
        <div class="o_brand_promotion">
            <t t-call="web.brand_promotion_message">
                <t t-set="_message"></t>
                <t t-set="_utm_medium" t-valuef="portal"/>
            </t>
        </div>
    </template>

the template rendering effect on web page screen snapshot as bellow:

Image description

the template 'brand_promotion' snippets rendering effect on the whole web page :

Image description

We want it can be spy or self explained meta information like tooltip, Odoo also provide the developer mode for debug, it can display some button or web component, but the tooltip information is limited even helpless, let's make it more prefect πŸ”¨

Our solution is append new element spy div wrap the original element, the new element div duty attach the template meta information that can be use for tooltip view.

the result of the html snippets most like bellows:

<div
  id="spy->web.brand_promotion"
  data-tooltip-template="spy.TooltipView"
  data-spy="qweb"
  data-tooltip-info=
        '{ 
            "debug": true,
            "class": "ir.ui.view",
            "view": { 
                "id": 183,
                "name": "Brand Promotion",
                "model": false,
                "key": "web.brand_promotion",
                "type": "qweb",
                "mode": "primary",
                "arch_fs": "web/views/webclient_templates.xml",
                "children_views": [ {
                    "id": 507, "name": "Brand Promotion", "model": false, "key": "website.brand_promotion", "type": "qweb", "mode": "extension", "arch_fs": "website/views/website_templates.xml"
                } 
                ],
                "template_code": "<t name=&amp;?quoteBrand Promotion&amp;?quote t-name=&amp;?quoteweb.brand_promotion&amp;?quote>\n        <div class=&amp;?quoteo_brand_promotion&amp;?quote>\n            <t t-call=&amp;?quoteweb.brand_promotion_message&amp;?quote>\n            <t t-set=&amp;?quote_message&amp;?quote>\n                Create a <a target=&amp;?quote_blank&amp;?quote href=&amp;?quotehttp://www.odoo.com/app/website?utm_source=db&amp;amp;utm_medium=website&amp;?quote>free website</a>\n            </t>\n            <t t-set=&amp;?quote_utm_medium&amp;?quote t-valuef=&amp;?quotewebsite&amp;?quote/>\n        </t>\n    </div>\n    </t>"
            }
          }' 

>
  <div class="o_brand_promotion">
     ... original html code ...
  </div>
</div>

let's see the last render effect while appended the meta tooltip view, it show popover tip view while mouse focus the element on web page bellows:

Image description

aha, that is what we want, while mouse moveover the brand element, a tooltip view popup, it disply key meta information of the source template of the element: template name, location(arch_fs) etc, we can insight the web page now! the page element is self-explanatory, especial show the location/path information, it should very helpful for debug or extends while developing application base the Odoo framework.

πŸ‘‰ OWL

The frontend web page of the Odoo backend (no webiste module) based on OWL framework, the code 'addons/web/static/lib/owl/owl.js' is the core logic set, the class 'TemplateSet' also 'App' which duty to parse the template script and generate the dom object in web browser side, the method '_compileTemplate' of the class 'TemplateSet' duty to process the template, we can hook the method for our purpose: append meta information on the template snippets.

the effect bellows:

it works good as expectation βœ…

if you like it you can find it from the Odoo Appstore.


Enjoy & Happy Coding! ☺︎

0
Subscribe to my newsletter

Read articles from ixkit directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

ixkit
ixkit

πŸ‘¨πŸ»β€πŸ’» βš’ πŸš€