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