Skip to content

Commit e9dad7e

Browse files
authored
Merge pull request #166 from OpenSPP/fix/cel-codeeditor-mode
Bug: CEL Expressions tab crashes and hidden menus reset on upgrade
2 parents 1460aa5 + ffd42c5 commit e9dad7e

14 files changed

Lines changed: 283 additions & 4 deletions

File tree

spp_approval/README.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,16 @@ Dependencies
157157
Changelog
158158
=========
159159

160+
19.0.2.0.1
161+
~~~~~~~~~~
162+
163+
- Fix CEL Expressions tab crash: the ace editor fields used the invalid
164+
CodeEditor mode ``text``; changed to ``javascript`` (Odoo 19 only
165+
accepts ``javascript``/``xml``/``qweb``/``scss``/``python``).
166+
``javascript`` is used because the CEL dialect uses
167+
``&&``/``||``/``!``, ``true``/``false``/ ``null`` and ``? :``
168+
ternaries, which it highlights correctly.
169+
160170
19.0.2.0.0
161171
~~~~~~~~~~
162172

spp_approval/__manifest__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
{
33
"name": "OpenSPP Approval",
44
"summary": "Standardized approval workflows with multi-tier sequencing and CEL rules",
5-
"version": "19.0.2.0.0",
5+
"version": "19.0.2.0.1",
66
"license": "LGPL-3",
77
"development_status": "Production/Stable",
88
"author": "OpenSPP.org, OpenSPP Community",

spp_approval/readme/HISTORY.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
### 19.0.2.0.1
2+
3+
- Fix CEL Expressions tab crash: the ace editor fields used the invalid
4+
CodeEditor mode ``text``; changed to ``javascript`` (Odoo 19 only accepts
5+
``javascript``/``xml``/``qweb``/``scss``/``python``). ``javascript`` is
6+
used because the CEL dialect uses ``&&``/``||``/``!``, ``true``/``false``/
7+
``null`` and ``? :`` ternaries, which it highlights correctly.
8+
19
### 19.0.2.0.0
210

311
- Initial migration to OpenSPP2

spp_approval/static/description/index.html

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,17 @@ <h2><a class="toc-backref" href="#toc-entry-1">Changelog</a></h2>
536536
</div>
537537
</div>
538538
<div class="section" id="section-1">
539+
<h1>19.0.2.0.1</h1>
540+
<ul class="simple">
541+
<li>Fix CEL Expressions tab crash: the ace editor fields used the invalid
542+
CodeEditor mode <tt class="docutils literal">text</tt>; changed to <tt class="docutils literal">javascript</tt> (Odoo 19 only
543+
accepts <tt class="docutils literal">javascript</tt>/<tt class="docutils literal">xml</tt>/<tt class="docutils literal">qweb</tt>/<tt class="docutils literal">scss</tt>/<tt class="docutils literal">python</tt>).
544+
<tt class="docutils literal">javascript</tt> is used because the CEL dialect uses
545+
<tt class="docutils literal">&amp;&amp;</tt>/<tt class="docutils literal">||</tt>/<tt class="docutils literal">!</tt>, <tt class="docutils literal">true</tt>/<tt class="docutils literal">false</tt>/ <tt class="docutils literal">null</tt> and <tt class="docutils literal">? :</tt>
546+
ternaries, which it highlights correctly.</li>
547+
</ul>
548+
</div>
549+
<div class="section" id="section-2">
539550
<h1>19.0.2.0.0</h1>
540551
<ul class="simple">
541552
<li>Initial migration to OpenSPP2</li>

spp_approval/tests/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
from . import test_approval_review
55
from . import test_approval_security
66
from . import test_cel_evaluator_security
7+
from . import test_cel_view
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Part of OpenSPP. See LICENSE file for full copyright and licensing details.
2+
"""Guardrail for the CEL Expressions view's ace editor configuration.
3+
4+
Odoo 19's ``CodeEditor`` OWL component validates its ``mode`` prop against
5+
a fixed allow-list:
6+
7+
addons/web/static/src/core/code_editor/code_editor.js
8+
static MODES = ["javascript", "xml", "qweb", "scss", "python"];
9+
10+
A field declaring ``widget="ace" options="{'mode': '<invalid>'}"`` crashes
11+
the client the moment the editor mounts (the field is only rendered when
12+
its ``use_cel_*`` toggle is enabled). The original view used
13+
``'mode': 'text'`` — not in MODES — which is exactly that crash. This test
14+
parses the view arch and fails if any ace field declares a mode the
15+
component would reject, so the regression can't silently come back.
16+
"""
17+
18+
import ast
19+
20+
from lxml import etree
21+
22+
from odoo.tests import TransactionCase, tagged
23+
24+
# Mirror of CodeEditor.MODES (see module docstring). Kept here as a literal
25+
# because the allow-list lives in JS and isn't importable from Python.
26+
VALID_ACE_MODES = {"javascript", "xml", "qweb", "scss", "python"}
27+
28+
29+
@tagged("post_install", "-at_install")
30+
class TestCelViewAceMode(TransactionCase):
31+
"""The CEL Expressions form must only use ace modes CodeEditor accepts."""
32+
33+
def test_cel_ace_fields_use_valid_mode(self):
34+
view = self.env.ref("spp_approval.view_spp_approval_definition_form_cel")
35+
arch = etree.fromstring(view.arch)
36+
37+
ace_fields = arch.xpath("//field[@widget='ace']")
38+
self.assertTrue(
39+
ace_fields,
40+
"Expected at least one widget='ace' field in the CEL view — did the view change?",
41+
)
42+
43+
for field in ace_fields:
44+
options = field.get("options")
45+
self.assertTrue(
46+
options,
47+
f"ace field {field.get('name')!r} must declare options with a mode",
48+
)
49+
mode = ast.literal_eval(options).get("mode")
50+
self.assertIn(
51+
mode,
52+
VALID_ACE_MODES,
53+
f"ace field {field.get('name')!r} uses mode {mode!r}, which "
54+
f"CodeEditor rejects (valid: {sorted(VALID_ACE_MODES)}). "
55+
"This crashes the client when the field is rendered.",
56+
)

spp_approval/views/approval_definition_views_cel.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<field
1616
name="cel_condition"
1717
widget="ace"
18-
options="{'mode': 'text'}"
18+
options="{'mode': 'javascript'}"
1919
invisible="not use_cel_condition"
2020
placeholder="record.amount > 10000"
2121
/>
@@ -42,7 +42,7 @@
4242
<field
4343
name="cel_reviewer_expression"
4444
widget="ace"
45-
options="{'mode': 'text'}"
45+
options="{'mode': 'javascript'}"
4646
invisible="not use_cel_reviewer"
4747
placeholder="record.manager_id.user_id.id"
4848
/>

spp_hide_menus_base/README.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,15 @@ Dependencies
103103
Changelog
104104
=========
105105

106+
19.0.2.0.1
107+
~~~~~~~~~~
108+
109+
- Keep hidden menus hidden after a module upgrade resets their
110+
``group_ids`` via XML. Re-applying now runs from ``_register_hook`` so
111+
it covers every upgrade path (immediate, ``base.module.upgrade``
112+
wizard, and CLI ``-u``), not just the immediate path handled by
113+
``next()``.
114+
106115
19.0.2.0.0
107116
~~~~~~~~~~
108117

spp_hide_menus_base/__manifest__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
{
66
"name": "OpenSPP Hide Non-OpenSPP Menus: Base",
77
"category": "OpenSPP",
8-
"version": "19.0.2.0.0",
8+
"version": "19.0.2.0.1",
99
"summary": "Administrators can manage the visibility of OpenSPP navigation menus, streamlining the user interface for specific user groups. The module modifies ir.ui.menu records to control menu visibility, providing a foundation for other modules to selectively hide non-essential navigation items.",
1010
"sequence": 1,
1111
"author": "OpenSPP.org",

spp_hide_menus_base/models/hide_menu.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,17 @@ def hide_menu(self, menu_id=None):
4040
)
4141
rec.state = "hide"
4242

43+
def _reapply_hide(self):
44+
"""Re-apply hiding when module upgrade reset group_ids via XML."""
45+
try:
46+
hide_group = self.env.ref("spp_hide_menus_base.group_hide_menus_user")
47+
except ValueError:
48+
hide_group = self.env.ref("spp_hide_menus_base.group_menu_visibility")
49+
for rec in self:
50+
if rec.menu_id and hide_group not in rec.menu_id.group_ids:
51+
rec.default_group_ids = rec.menu_id.group_ids
52+
rec.menu_id.write({"group_ids": [Command.set([hide_group.id])]})
53+
4354
def show_menu(self):
4455
for rec in self:
4556
if rec.state == "hide" and rec.menu_id:

0 commit comments

Comments
 (0)