mirror of
https://gitlab.com/itsulu-odoo/itsulu-blog-publisher.git
synced 2026-05-30 23:41:23 +00:00
port(14.0): mail template -> Jinja2, view attrs -> Odoo 14 domains
First static porting pass for the Odoo 14.0 branch:
- data/mail_template_data.xml: revert from 17.0 qweb (type="html",
<t t-out>) to Odoo 14 Jinja2 (${...}, % if, % for)
- views: convert 6 Odoo-17-style invisible="state == '...'" attributes
to Odoo 14 attrs="{'invisible': [domain]}" (blog_topic, blog_generation_log)
- PORTING.md: tick completed items, note remaining (data-test-id RNG,
blog.post.content on 14, pytest pinning for Py3.7)
Static pass only — not yet verified on a live Odoo 14 instance.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
5fdbcdadc9
commit
d0e974a25b
4 changed files with 56 additions and 51 deletions
20
PORTING.md
20
PORTING.md
|
|
@ -17,18 +17,22 @@ instance unverified.
|
|||
|
||||
## API porting checklist (verify on a real Odoo 14.0 instance)
|
||||
|
||||
- [ ] **Mail template syntax — REVERT to Jinja2.** Odoo 14 renders `mail.template` with Jinja2
|
||||
(`${object.blog_post_id.name}`, `% if`, `% for`), **not** the qweb `<t t-out>` used on 17.0.
|
||||
The 17.0 template in `data/mail_template_data.xml` must be converted back to Jinja for 14.0.
|
||||
- [ ] **Mail render API.** 14.0 uses `template.render_template(...)` / `_render_template(...)`,
|
||||
not 17.0's `_render_field`. Update `blog_generation_log.send_notification_email` + tests.
|
||||
- [x] **Mail template syntax — reverted to Jinja2.** `data/mail_template_data.xml` restored to
|
||||
Odoo 14's Jinja2 (`${...}`, `% if`, `% for`) instead of the 17.0 qweb `type="html"`.
|
||||
- [x] **View conditional attributes → `attrs`.** Converted the 6 Odoo-17-style
|
||||
`invisible="state == '…'"` attributes (blog_topic, blog_generation_log) to Odoo 14
|
||||
`attrs="{'invisible': [domain]}"`.
|
||||
- [~] **Mail render API.** Odoo 14 `_render_field(field, res_ids, engine='jinja')` exists — the
|
||||
tests' `_render_field('subject', [id])` should work now that the template is Jinja. Verify live.
|
||||
- [~] Python: `'ImageResult | None'` in `image_router.py` is a quoted (3.7-safe) annotation; no
|
||||
walrus / 3.9 subscript generics found.
|
||||
- [ ] `data-test-id` attributes in views — confirm Odoo 14 RNG accepts them (Playwright-only;
|
||||
may need removal/guarding on 14).
|
||||
- [ ] `blog.post` body field name on 14.0 (the 17.0 baseline writes `content` — confirm 14.0).
|
||||
- [ ] Python 3.6–3.8 only — remove any 3.9+ syntax (dict `|` merge, `str.removeprefix`, etc.).
|
||||
- [ ] `@api.model_create_multi`, `name_get`, and other ORM signatures valid on 14.0.
|
||||
- [ ] View/RELAXNG validation for 14.0 (attrs/states syntax differs from 17.0).
|
||||
- [ ] `ir.cron` + mail data XML formats.
|
||||
- [ ] pytest / pytest-odoo / pytest-bdd versions compatible with odoo:14.0's Python 3.7.
|
||||
- [ ] Full test suite green on a 14.0 template DB (K8s job, §8, base image `odoo:14.0`).
|
||||
- [ ] pytest-odoo / pytest-bdd versions compatible with the 14.0 Python.
|
||||
|
||||
## How to work this branch
|
||||
|
||||
|
|
|
|||
|
|
@ -2,47 +2,48 @@
|
|||
<odoo>
|
||||
<data noupdate="0">
|
||||
|
||||
<!-- Odoo 14 renders mail.template with Jinja2 (${...}, % if, % for) -->
|
||||
<record id="email_template_blog_published" model="mail.template">
|
||||
<field name="name">Blog Publisher — Post Published Notification</field>
|
||||
<field name="model_id" ref="model_itsulu_blog_generation_log"/>
|
||||
<field name="subject">[ITSulu Insights] Blog Post Published: {{ object.blog_post_id.name }}</field>
|
||||
<field name="email_from">{{ user.email_formatted }}</field>
|
||||
<field name="subject">[ITSulu Insights] Blog Post Published: ${object.blog_post_id.name}</field>
|
||||
<field name="email_from">${user.email_formatted}</field>
|
||||
<field name="auto_delete">True</field>
|
||||
<field name="body_html" type="html">
|
||||
<div style="font-family: Arial, sans-serif; line-height: 1.6; max-width: 700px;">
|
||||
<h2>Blog Post Published</h2>
|
||||
<p><strong>Title:</strong> <t t-out="object.blog_post_id.name"/></p>
|
||||
<p><strong>Blog:</strong> <t t-out="object.blog_post_id.blog_id.name"/></p>
|
||||
<p><strong>URL:</strong> <a t-attf-href="https://itsulu.com{{ object.blog_post_id.website_url }}">https://itsulu.com<t t-out="object.blog_post_id.website_url"/></a></p>
|
||||
<h3>Social Media Posts — Ready to Post</h3>
|
||||
<t t-foreach="object.blog_post_id.itsulu_social_id" t-as="social">
|
||||
<t t-if="social.twitter_post_a">
|
||||
<h4>Twitter</h4>
|
||||
<p><t t-out="social.twitter_post_a"/></p>
|
||||
</t>
|
||||
<t t-if="social.twitter_post_b">
|
||||
<h4>Twitter</h4>
|
||||
<p><t t-out="social.twitter_post_b"/></p>
|
||||
</t>
|
||||
<t t-if="social.bluesky_post_a">
|
||||
<h4>BlueSky</h4>
|
||||
<p><t t-out="social.bluesky_post_a"/></p>
|
||||
</t>
|
||||
<t t-if="social.bluesky_post_b">
|
||||
<h4>BlueSky</h4>
|
||||
<p><t t-out="social.bluesky_post_b"/></p>
|
||||
</t>
|
||||
<t t-if="social.mastodon_post">
|
||||
<h4>Mastodon</h4>
|
||||
<p><t t-out="social.mastodon_post"/></p>
|
||||
</t>
|
||||
<t t-if="social.linkedin_post">
|
||||
<h4>LinkedIn</h4>
|
||||
<p><t t-out="social.linkedin_post"/></p>
|
||||
</t>
|
||||
</t>
|
||||
</div>
|
||||
</field>
|
||||
<field name="body_html"><![CDATA[
|
||||
<div style="font-family: Arial, sans-serif; line-height: 1.6; max-width: 700px;">
|
||||
<h2>Blog Post Published</h2>
|
||||
<p><strong>Title:</strong> ${object.blog_post_id.name}</p>
|
||||
<p><strong>Blog:</strong> ${object.blog_post_id.blog_id.name}</p>
|
||||
<p><strong>URL:</strong> <a href="https://itsulu.com${object.blog_post_id.website_url}">https://itsulu.com${object.blog_post_id.website_url}</a></p>
|
||||
<h3>Social Media Posts — Ready to Post</h3>
|
||||
% for social in object.blog_post_id.itsulu_social_id:
|
||||
% if social.twitter_post_a:
|
||||
<h4>Twitter</h4>
|
||||
<p>${social.twitter_post_a}</p>
|
||||
% endif
|
||||
% if social.twitter_post_b:
|
||||
<h4>Twitter</h4>
|
||||
<p>${social.twitter_post_b}</p>
|
||||
% endif
|
||||
% if social.bluesky_post_a:
|
||||
<h4>BlueSky</h4>
|
||||
<p>${social.bluesky_post_a}</p>
|
||||
% endif
|
||||
% if social.bluesky_post_b:
|
||||
<h4>BlueSky</h4>
|
||||
<p>${social.bluesky_post_b}</p>
|
||||
% endif
|
||||
% if social.mastodon_post:
|
||||
<h4>Mastodon</h4>
|
||||
<p>${social.mastodon_post}</p>
|
||||
% endif
|
||||
% if social.linkedin_post:
|
||||
<h4>LinkedIn</h4>
|
||||
<p>${social.linkedin_post}</p>
|
||||
% endif
|
||||
% endfor
|
||||
</div>
|
||||
]]></field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
<form string="Generation Log" create="false" edit="false">
|
||||
<header>
|
||||
<button name="action_retry" type="object" string="↩ Retry Generation"
|
||||
invisible="state != 'error'"
|
||||
attrs="{'invisible': [('state', '!=', 'error')]}"
|
||||
class="btn-warning"
|
||||
data-test-id="btn-retry-log-form"/>
|
||||
</header>
|
||||
|
|
@ -43,7 +43,7 @@
|
|||
<group string="Result">
|
||||
<field name="state" widget="badge"/>
|
||||
<field name="blog_post_id"/>
|
||||
<field name="error_message" invisible="state != 'error'"/>
|
||||
<field name="error_message" attrs="{'invisible': [('state', '!=', 'error')]}"/>
|
||||
</group>
|
||||
<group string="Trigger">
|
||||
<field name="trigger_source"/>
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@
|
|||
decoration-warning="state=='skipped'"/>
|
||||
<field name="used_date" optional="show"/>
|
||||
<button name="action_mark_pending" type="object" string="↩ Reset"
|
||||
invisible="state == 'pending'"
|
||||
attrs="{'invisible': [('state', '=', 'pending')]}"
|
||||
class="btn-sm btn-secondary"/>
|
||||
<button name="action_mark_skipped" type="object" string="Skip"
|
||||
invisible="state != 'pending'"
|
||||
attrs="{'invisible': [('state', '!=', 'pending')]}"
|
||||
class="btn-sm btn-warning"/>
|
||||
</tree>
|
||||
</field>
|
||||
|
|
@ -36,10 +36,10 @@
|
|||
<form string="Blog Topic">
|
||||
<header>
|
||||
<button name="action_mark_pending" type="object" string="Reset to Pending"
|
||||
invisible="state == 'pending'"
|
||||
attrs="{'invisible': [('state', '=', 'pending')]}"
|
||||
class="btn-secondary"/>
|
||||
<button name="action_mark_skipped" type="object" string="Skip"
|
||||
invisible="state != 'pending'"
|
||||
attrs="{'invisible': [('state', '!=', 'pending')]}"
|
||||
class="btn-warning"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="pending,used"/>
|
||||
</header>
|
||||
|
|
|
|||
Loading…
Reference in a new issue