[IMP] website: improve reset view feature
- Store previous arch to be able to reset it (soft reset) - Add the possibility to reset from file if possible (hard reset) - Adapt frontend reset page to these new fields - `arch_fs` hack to check if view was modified got moved to new field `arch_updated` as we now need to keep track of the `arch_fs` to reset a broken view. Closes #32009 (task-1943001)
This commit is contained in:
@@ -23,7 +23,7 @@ class TestWebsiteResetViews(odoo.tests.HttpCase):
|
||||
self.authenticate("admin", "admin")
|
||||
resp = self.url_open(page)
|
||||
self.assertEqual(resp.status_code, 500, "Waiting 500")
|
||||
self.assertTrue('<button id="reset_templates_button"' in resp.text)
|
||||
self.assertTrue('<button data-mode="soft" class="reset_templates_button' in resp.text)
|
||||
data = {'templates': [self.find_template(resp)], 'redirect': page}
|
||||
resp = self.url_open('/website/reset_templates', data)
|
||||
self.assertEqual(resp.status_code, 200, "Waiting 200")
|
||||
|
||||
@@ -296,23 +296,11 @@ class Website(Home):
|
||||
This method is typically useful to reset specific views. In that case we
|
||||
read the XML file from the generic view.
|
||||
"""
|
||||
mode = kwargs.get('mode', 'soft')
|
||||
templates = request.httprequest.form.getlist('templates')
|
||||
for temp_id in templates:
|
||||
view = request.env['ir.ui.view'].browse(int(temp_id))
|
||||
if 'oe_structure' in view.key:
|
||||
# Particular xpathing view created in edit mode
|
||||
view.unlink()
|
||||
continue
|
||||
xml_view = view._get_original_view() # view might already be the xml_view
|
||||
if xml_view.arch_fs:
|
||||
view_file_arch = xml_view.with_context(read_arch_from_file=True).arch
|
||||
# Deactivate COW to not fix a generic view by creating a specific
|
||||
view.with_context(website_id=None).arch_db = view_file_arch
|
||||
if view == xml_view:
|
||||
view.model_data_id.write({
|
||||
'noupdate': False
|
||||
})
|
||||
|
||||
views = request.env['ir.ui.view'].browse([int(view_id) for view_id in templates])
|
||||
# Deactivate COW to not fix a generic view by creating a specific
|
||||
views.with_context(website_id=None).reset_arch(mode)
|
||||
return request.redirect(redirect)
|
||||
|
||||
@http.route(['/website/publish'], type='json', auth="public", website=True)
|
||||
|
||||
@@ -281,10 +281,6 @@ class Http(models.AbstractModel):
|
||||
values['views'] = View._views_get(exception_template).filtered(
|
||||
lambda v: line in v.arch
|
||||
)
|
||||
# Keep only views that we can reset
|
||||
values['views'] = values['views'].filtered(
|
||||
lambda view: view._get_original_view().arch_fs or 'oe_structure' in view.key
|
||||
)
|
||||
# Needed to show reset template on translated pages (`_prepare_qcontext` will set it for main lang)
|
||||
values['editable'] = request.uid and request.website.is_publisher()
|
||||
elif code == 403:
|
||||
|
||||
@@ -1036,8 +1036,9 @@
|
||||
<t t-if='views'>
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
var button = $('#reset_templates_button');
|
||||
var button = $('.reset_templates_button');
|
||||
button.click(function() {
|
||||
$('#reset_templates_mode').val($(this).data('mode'));
|
||||
var dialog = $('#reset_template_confirmation').modal('show');
|
||||
var input = dialog.find('input[type="text"]').val('').focus();
|
||||
var dialog_form = dialog.find('form');
|
||||
@@ -1121,14 +1122,13 @@
|
||||
<label>
|
||||
<input type="checkbox" name="templates" t-att-value="view.id" t-att-checked="'checked' if view_first else None"/>
|
||||
<t t-esc="view.name"/>
|
||||
<span t-if="not view._get_original_view()" class="text-danger">
|
||||
(!!! This view will be removed )
|
||||
</span>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
<input type="hidden" name="redirect" t-att-value="request.httprequest.path"/>
|
||||
<button id="reset_templates_button">Reset selected templates</button>
|
||||
<input type="hidden" id="reset_templates_mode" name="mode"/>
|
||||
<button data-mode="soft" class="reset_templates_button btn btn-info">Restore previous version (soft reset).</button>
|
||||
<button data-mode="hard" class="reset_templates_button btn btn-outline-danger">Reset to initial version (hard reset).</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -213,8 +213,10 @@ class View(models.Model):
|
||||
arch_db = fields.Text(string='Arch Blob', translate=xml_translate, oldname='arch',
|
||||
help="This field stores the view arch.")
|
||||
arch_fs = fields.Char(string='Arch Filename', help="""File from where the view originates.
|
||||
Useful to (hard) reset broken views or to read arch from file in dev-xml mode.
|
||||
Modifying the view arch will empty this field.""")
|
||||
Useful to (hard) reset broken views or to read arch from file in dev-xml mode.""")
|
||||
arch_updated = fields.Boolean(string='Modified Architecture')
|
||||
arch_prev = fields.Text(string='Previous View Architecture', help="""This field will save the current `arch_db` before writing on it.
|
||||
Useful to (soft) reset a broken view.""")
|
||||
inherit_id = fields.Many2one('ir.ui.view', string='Inherited View', ondelete='restrict', index=True)
|
||||
inherit_children_ids = fields.One2many('ir.ui.view', 'inherit_id', string='Views which inherit from this one')
|
||||
field_parent = fields.Char(string='Child Field')
|
||||
@@ -244,7 +246,7 @@ actual arch.
|
||||
* if False, the view currently does not extend its parent but can be enabled
|
||||
""")
|
||||
|
||||
@api.depends('arch_db', 'arch_fs')
|
||||
@api.depends('arch_db', 'arch_fs', 'arch_updated')
|
||||
def _compute_arch(self):
|
||||
def resolve_external_ids(arch_fs, view_xml_id):
|
||||
def replacer(m):
|
||||
@@ -257,7 +259,9 @@ actual arch.
|
||||
for view in self:
|
||||
arch_fs = None
|
||||
xml_id = view.xml_id or view.key
|
||||
if (self._context.get('read_arch_from_file') or 'xml' in config['dev_mode']) and view.arch_fs and xml_id:
|
||||
read_file = self._context.get('read_arch_from_file') or \
|
||||
('xml' in config['dev_mode'] and not view.arch_updated)
|
||||
if read_file and view.arch_fs and xml_id:
|
||||
# It is safe to split on / herebelow because arch_fs is explicitely stored with '/'
|
||||
fullpath = get_resource_path(*view.arch_fs.split('/'))
|
||||
if fullpath:
|
||||
@@ -278,6 +282,7 @@ actual arch.
|
||||
path_info = get_resource_from_path(self._context['install_filename'])
|
||||
if path_info:
|
||||
data['arch_fs'] = '/'.join(path_info[0:2])
|
||||
data['arch_updated'] = False
|
||||
view.write(data)
|
||||
|
||||
@api.depends('arch')
|
||||
@@ -290,6 +295,18 @@ actual arch.
|
||||
for view, view_wo_lang in zip(self, self.with_context(lang=None)):
|
||||
view_wo_lang.arch = view.arch_base
|
||||
|
||||
@api.multi
|
||||
def reset_arch(self, mode='soft'):
|
||||
for view in self:
|
||||
arch = False
|
||||
if mode == 'soft':
|
||||
arch = view.arch_prev
|
||||
elif mode == 'hard' and view.arch_fs:
|
||||
arch = view.with_context(read_arch_from_file=True).arch
|
||||
if arch:
|
||||
# Don't save current arch in previous since we reset, this arch is probably broken
|
||||
view.with_context(no_save_prev=True).write({'arch_db': arch})
|
||||
|
||||
@api.depends('write_date')
|
||||
def _compute_model_data_id(self):
|
||||
# get the first ir_model_data record corresponding to self
|
||||
@@ -428,6 +445,8 @@ actual arch.
|
||||
values['key'] = "%s.gen_key_%s" % (values.get('model'), str(uuid.uuid4())[:6])
|
||||
if not values.get('name'):
|
||||
values['name'] = "%s %s" % (values.get('model'), values['type'])
|
||||
# Create might be called with either `arch` (xml files), `arch_base` (form view) or `arch_db`.
|
||||
values['arch_prev'] = values.get('arch_base') or values.get('arch_db') or values.get('arch')
|
||||
values.update(self._compute_defaults(values))
|
||||
|
||||
self.clear_caches()
|
||||
@@ -435,10 +454,10 @@ actual arch.
|
||||
|
||||
@api.multi
|
||||
def write(self, vals):
|
||||
# If view is modified we remove the arch_fs information thus activating the arch_db
|
||||
# version. An `init` of the view will restore the arch_fs for the --dev mode
|
||||
# Keep track if view was modified. That will be useful for the --dev mode
|
||||
# to prefer modified arch over file arch.
|
||||
if ('arch' in vals or 'arch_base' in vals) and 'install_filename' not in self._context:
|
||||
vals['arch_fs'] = False
|
||||
vals['arch_updated'] = True
|
||||
|
||||
# drop the corresponding view customizations (used for dashboards for example), otherwise
|
||||
# not all users would see the updated views
|
||||
@@ -447,6 +466,8 @@ actual arch.
|
||||
custom_view.unlink()
|
||||
|
||||
self.clear_caches()
|
||||
if 'arch_db' in vals and not self.env.context.get('no_save_prev'):
|
||||
vals['arch_prev'] = self.arch_db
|
||||
return super(View, self).write(self._compute_defaults(vals))
|
||||
|
||||
def unlink(self):
|
||||
|
||||
Reference in New Issue
Block a user