|
2 | 2 | import logging
|
3 | 3 | import os
|
4 | 4 | import re
|
5 |
| -from contextlib import contextmanager |
6 | 5 | from operator import itemgetter
|
7 | 6 |
|
8 | 7 | import lxml
|
|
12 | 11 | from odoo import release
|
13 | 12 | from odoo.tools.convert import xml_import
|
14 | 13 | from odoo.tools.misc import file_open
|
15 |
| - from odoo.tools.translate import xml_translate |
16 | 14 | except ImportError:
|
17 | 15 | from openerp import release
|
18 | 16 | from openerp.tools.convert import xml_import
|
|
42 | 40 | table_exists,
|
43 | 41 | target_of,
|
44 | 42 | )
|
45 |
| -from .report import add_to_migration_reports |
| 43 | +from .views.records import add_view, edit_view, remove_view # noqa: F401 |
46 | 44 |
|
47 | 45 | _logger = logging.getLogger(__name__)
|
48 | 46 |
|
|
53 | 51 | basestring = unicode = str
|
54 | 52 |
|
55 | 53 |
|
56 |
| -def remove_view(cr, xml_id=None, view_id=None, silent=False, key=None): |
57 |
| - """ |
58 |
| - Recursively delete the given view and its inherited views, as long as they |
59 |
| - are part of a module. Will crash as soon as a custom view exists anywhere |
60 |
| - in the hierarchy. |
61 |
| -
|
62 |
| - Also handle multi-website COWed views. |
63 |
| - """ |
64 |
| - assert bool(xml_id) ^ bool(view_id) |
65 |
| - if xml_id: |
66 |
| - view_id = ref(cr, xml_id) |
67 |
| - if view_id: |
68 |
| - module, _, name = xml_id.partition(".") |
69 |
| - cr.execute("SELECT model FROM ir_model_data WHERE module=%s AND name=%s", [module, name]) |
70 |
| - |
71 |
| - [model] = cr.fetchone() |
72 |
| - if model != "ir.ui.view": |
73 |
| - raise ValueError("%r should point to a 'ir.ui.view', not a %r" % (xml_id, model)) |
74 |
| - else: |
75 |
| - # search matching xmlid for logging or renaming of custom views |
76 |
| - xml_id = "?" |
77 |
| - if not key: |
78 |
| - cr.execute("SELECT module, name FROM ir_model_data WHERE model='ir.ui.view' AND res_id=%s", [view_id]) |
79 |
| - if cr.rowcount: |
80 |
| - xml_id = "%s.%s" % cr.fetchone() |
81 |
| - |
82 |
| - # From given or determined xml_id, the views duplicated in a multi-website |
83 |
| - # context are to be found and removed. |
84 |
| - if xml_id != "?" and column_exists(cr, "ir_ui_view", "key"): |
85 |
| - cr.execute("SELECT id FROM ir_ui_view WHERE key = %s AND id != %s", [xml_id, view_id]) |
86 |
| - for [v_id] in cr.fetchall(): |
87 |
| - remove_view(cr, view_id=v_id, silent=silent, key=xml_id) |
88 |
| - |
89 |
| - if not view_id: |
90 |
| - return |
91 |
| - |
92 |
| - cr.execute( |
93 |
| - """ |
94 |
| - SELECT v.id, x.module || '.' || x.name, v.name |
95 |
| - FROM ir_ui_view v LEFT JOIN |
96 |
| - ir_model_data x ON (v.id = x.res_id AND x.model = 'ir.ui.view' AND x.module !~ '^_') |
97 |
| - WHERE v.inherit_id = %s; |
98 |
| - """, |
99 |
| - [view_id], |
100 |
| - ) |
101 |
| - for child_id, child_xml_id, child_name in cr.fetchall(): |
102 |
| - if child_xml_id: |
103 |
| - if not silent: |
104 |
| - _logger.info( |
105 |
| - "remove deprecated built-in view %s (ID %s) as parent view %s (ID %s) is going to be removed", |
106 |
| - child_xml_id, |
107 |
| - child_id, |
108 |
| - xml_id, |
109 |
| - view_id, |
110 |
| - ) |
111 |
| - remove_view(cr, child_xml_id, silent=True) |
112 |
| - else: |
113 |
| - if not silent: |
114 |
| - _logger.warning( |
115 |
| - "deactivate deprecated custom view with ID %s as parent view %s (ID %s) is going to be removed", |
116 |
| - child_id, |
117 |
| - xml_id, |
118 |
| - view_id, |
119 |
| - ) |
120 |
| - disable_view_query = """ |
121 |
| - UPDATE ir_ui_view |
122 |
| - SET name = (name || ' - old view, inherited from ' || %%s), |
123 |
| - inherit_id = NULL |
124 |
| - %s |
125 |
| - WHERE id = %%s |
126 |
| - """ |
127 |
| - # In 8.0, disabling requires setting mode to 'primary' |
128 |
| - extra_set_sql = "" |
129 |
| - if column_exists(cr, "ir_ui_view", "mode"): |
130 |
| - extra_set_sql = ", mode = 'primary' " |
131 |
| - |
132 |
| - # Column was not present in v7 and it's older version |
133 |
| - if column_exists(cr, "ir_ui_view", "active"): |
134 |
| - extra_set_sql += ", active = false " |
135 |
| - |
136 |
| - disable_view_query = disable_view_query % extra_set_sql |
137 |
| - cr.execute(disable_view_query, (key or xml_id, child_id)) |
138 |
| - add_to_migration_reports( |
139 |
| - {"id": child_id, "name": child_name}, |
140 |
| - "Disabled views", |
141 |
| - ) |
142 |
| - if not silent: |
143 |
| - _logger.info("remove deprecated %s view %s (ID %s)", key and "COWed" or "built-in", key or xml_id, view_id) |
144 |
| - |
145 |
| - remove_records(cr, "ir.ui.view", [view_id]) |
146 |
| - |
147 |
| - |
148 |
| -@contextmanager |
149 |
| -def edit_view(cr, xmlid=None, view_id=None, skip_if_not_noupdate=True, active=True): |
150 |
| - """Contextmanager that may yield etree arch of a view. |
151 |
| - As it may not yield, you must use `skippable_cm` |
152 |
| -
|
153 |
| - with util.skippable_cm(), util.edit_view(cr, 'xml.id') as arch: |
154 |
| - arch.attrib['string'] = 'My Form' |
155 |
| -
|
156 |
| - When view_id is passed to identify a view, view's arch will always yield to be edited because |
157 |
| - we assume that xmlid for such view does not exist to check its noupdate flag. |
158 |
| -
|
159 |
| - If view's noupdate=false then the arch will not be yielded for edit unless skip_if_not_noupdate=False, |
160 |
| - because when noupdate=False we assume it is a standard view that will be updated by the ORM later on anyways. |
161 |
| -
|
162 |
| - If view's noupdate=True, the view will be yielded for edit. |
163 |
| -
|
164 |
| - If the `active` argument is not None, the view will be (de)activated accordingly. |
165 |
| -
|
166 |
| - For more details, see discussion in: https://github.com/odoo/upgrade-specific/pull/4216 |
167 |
| - """ |
168 |
| - assert bool(xmlid) ^ bool(view_id), "You Must specify either xmlid or view_id" |
169 |
| - noupdate = True |
170 |
| - if xmlid: |
171 |
| - if "." not in xmlid: |
172 |
| - raise ValueError("Please use fully qualified name <module>.<name>") |
173 |
| - |
174 |
| - module, _, name = xmlid.partition(".") |
175 |
| - cr.execute( |
176 |
| - """ |
177 |
| - SELECT res_id, noupdate |
178 |
| - FROM ir_model_data |
179 |
| - WHERE module = %s |
180 |
| - AND name = %s |
181 |
| - """, |
182 |
| - [module, name], |
183 |
| - ) |
184 |
| - data = cr.fetchone() |
185 |
| - if data: |
186 |
| - view_id, noupdate = data |
187 |
| - |
188 |
| - if view_id and not (skip_if_not_noupdate and not noupdate): |
189 |
| - arch_col = "arch_db" if column_exists(cr, "ir_ui_view", "arch_db") else "arch" |
190 |
| - jsonb_column = column_type(cr, "ir_ui_view", arch_col) == "jsonb" |
191 |
| - cr.execute( |
192 |
| - """ |
193 |
| - SELECT {arch} |
194 |
| - FROM ir_ui_view |
195 |
| - WHERE id=%s |
196 |
| - """.format( |
197 |
| - arch=arch_col, |
198 |
| - ), |
199 |
| - [view_id], |
200 |
| - ) |
201 |
| - [arch] = cr.fetchone() or [None] |
202 |
| - if arch: |
203 |
| - |
204 |
| - def parse(arch): |
205 |
| - arch = arch.encode("utf-8") if isinstance(arch, unicode) else arch |
206 |
| - return lxml.etree.fromstring(arch.replace(b" \n", b"\n").strip()) |
207 |
| - |
208 |
| - if jsonb_column: |
209 |
| - |
210 |
| - def get_trans_terms(value): |
211 |
| - terms = [] |
212 |
| - xml_translate(terms.append, value) |
213 |
| - return terms |
214 |
| - |
215 |
| - translation_terms = {lang: get_trans_terms(value) for lang, value in arch.items()} |
216 |
| - arch_etree = parse(arch["en_US"]) |
217 |
| - yield arch_etree |
218 |
| - new_arch = lxml.etree.tostring(arch_etree, encoding="unicode") |
219 |
| - terms_en = translation_terms["en_US"] |
220 |
| - arch_column_value = Json( |
221 |
| - { |
222 |
| - lang: xml_translate(dict(zip(terms_en, terms)).get, new_arch) |
223 |
| - for lang, terms in translation_terms.items() |
224 |
| - } |
225 |
| - ) |
226 |
| - else: |
227 |
| - arch_etree = parse(arch) |
228 |
| - yield arch_etree |
229 |
| - arch_column_value = lxml.etree.tostring(arch_etree, encoding="unicode") |
230 |
| - |
231 |
| - set_active = ", active={}".format(bool(active)) if active is not None else "" |
232 |
| - cr.execute( |
233 |
| - "UPDATE ir_ui_view SET {arch}=%s{set_active} WHERE id=%s".format(arch=arch_col, set_active=set_active), |
234 |
| - [arch_column_value, view_id], |
235 |
| - ) |
236 |
| - |
237 |
| - |
238 |
| -def add_view(cr, name, model, view_type, arch_db, inherit_xml_id=None, priority=16): |
239 |
| - inherit_id = None |
240 |
| - if inherit_xml_id: |
241 |
| - inherit_id = ref(cr, inherit_xml_id) |
242 |
| - if not inherit_id: |
243 |
| - raise ValueError( |
244 |
| - "Unable to add view '%s' because its inherited view '%s' cannot be found!" % (name, inherit_xml_id) |
245 |
| - ) |
246 |
| - arch_col = "arch_db" if column_exists(cr, "ir_ui_view", "arch_db") else "arch" |
247 |
| - jsonb_column = column_type(cr, "ir_ui_view", arch_col) == "jsonb" |
248 |
| - arch_column_value = Json({"en_US": arch_db}) if jsonb_column else arch_db |
249 |
| - cr.execute( |
250 |
| - """ |
251 |
| - INSERT INTO ir_ui_view(name, "type", model, inherit_id, mode, active, priority, %s) |
252 |
| - VALUES(%%(name)s, %%(view_type)s, %%(model)s, %%(inherit_id)s, %%(mode)s, 't', %%(priority)s, %%(arch_db)s) |
253 |
| - RETURNING id |
254 |
| - """ |
255 |
| - % arch_col, |
256 |
| - { |
257 |
| - "name": name, |
258 |
| - "view_type": view_type, |
259 |
| - "model": model, |
260 |
| - "inherit_id": inherit_id, |
261 |
| - "mode": "extension" if inherit_id else "primary", |
262 |
| - "priority": priority, |
263 |
| - "arch_db": arch_column_value, |
264 |
| - }, |
265 |
| - ) |
266 |
| - return cr.fetchone()[0] |
267 |
| - |
268 |
| - |
269 | 54 | # fmt:off
|
270 | 55 | if version_gte("saas~14.3"):
|
271 | 56 | def remove_asset(cr, name):
|
|
0 commit comments