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
4241 table_exists ,
4342 target_of ,
4443)
45- from .report import add_to_migration_reports
4644
4745_logger = logging .getLogger (__name__ )
4846
5351 basestring = unicode = str
5452
5553
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-
26954# fmt:off
27055if version_gte ("saas~14.3" ):
27156 def remove_asset (cr , name ):
@@ -274,7 +59,7 @@ def remove_asset(cr, name):
27459 remove_records (cr , "ir.asset" , [aid for aid , in cr .fetchall ()])
27560else :
27661 def remove_asset (cr , name ):
277- remove_view (cr , name , silent = True )
62+ views . remove_view (cr , name , silent = True )
27863# fmt:on
27964
28065
@@ -305,7 +90,7 @@ def remove_record(cr, name):
30590 # deleguate to the right method
30691 if model == "ir.ui.view" :
30792 _logger .log (NEARLYWARN , "Removing view %r" , name )
308- return remove_view (cr , view_id = res_id )
93+ return views . remove_view (cr , view_id = res_id )
30994
31095 if model == "ir.ui.menu" :
31196 _logger .log (NEARLYWARN , "Removing menu %r" , name )
@@ -333,7 +118,7 @@ def remove_records(cr, model, ids):
333118 )
334119 if theme_copy_model == "ir.ui.view" :
335120 for (view_id ,) in cr .fetchall ():
336- remove_view (cr , view_id = view_id )
121+ views . remove_view (cr , view_id = view_id )
337122 else :
338123 remove_records (cr , theme_copy_model , [rid for (rid ,) in cr .fetchall ()])
339124
@@ -348,7 +133,7 @@ def remove_records(cr, model, ids):
348133 remove_menus (cr , [menu_id for (menu_id ,) in cr .fetchall ()])
349134 elif inh .model == "ir.ui.view" :
350135 for (view_id ,) in cr .fetchall ():
351- remove_view (cr , view_id = view_id )
136+ views . remove_view (cr , view_id = view_id )
352137 else :
353138 remove_records (cr , inh .model , [rid for (rid ,) in cr .fetchall ()])
354139
@@ -400,7 +185,7 @@ def _rm_refs(cr, model, ids=None):
400185 if ref_model == "ir.ui.view" :
401186 cr .execute ("SELECT id" + query_tail , [needle ])
402187 for (view_id ,) in cr .fetchall ():
403- remove_view (cr , view_id = view_id , silent = True )
188+ views . remove_view (cr , view_id = view_id , silent = True )
404189 elif ref_model == "ir.ui.menu" :
405190 cr .execute ("SELECT id" + query_tail , [needle ])
406191 menu_ids = tuple (m [0 ] for m in cr .fetchall ())
0 commit comments