[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:
Romain Derie
2019-03-29 18:53:56 +00:00
parent 11222f504d
commit 81df3c5f22
5 changed files with 38 additions and 33 deletions
@@ -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")
+4 -16
View File
@@ -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)
-4
View File
@@ -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:
+5 -5
View File
@@ -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>
+28 -7
View File
@@ -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):