Files
odoo_source/addons/website/models/theme_models.py
T
qsm-odooandSamuel Degueldre 4612343e99 [FIX] website_theme_install: not copy fields for theme record templates
Themes define records which are used as templates for the creation of
base model records on theme installation on a website.
E.g. a "theme.ir.attachment" record's purpose is to be a template for an
"ir.attachment" record creation on theme installation on a website.

Those final records have extra fields to indicate from which theme
template they come from. If those records are ever to be duplicated,
they should not duplicate those links to the theme templates otherwise
it may cause issues when uninstalling/updating a theme (you want the
records linked to the theme to be deleted/updated but not the duplicated
ones, which do not act differently from user created ones).

Duplicate ones will be linked to the website anyway (just like "normal"
user created ones) and should only be automatically removed if that
website is deleted.

The issue is more visible from 14.0 where applying some modifications to
images via the editor (crop / filter / optimization / ...) will
duplicate the original image before modifying it.

closes odoo/odoo#63951

X-original-commit: 890935bbdf924b06b7dabd89802f6dd824159a4b
Signed-off-by: Quentin Smetz (qsm) <qsm@odoo.com>
Co-authored-by: Samuel Degueldre <sad@odoo.com>
2020-12-31 16:12:59 +00:00

290 lines
11 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
from odoo import api, fields, models
from odoo.tools.translate import xml_translate
from odoo.modules.module import get_resource_from_path
_logger = logging.getLogger(__name__)
class ThemeView(models.Model):
_name = 'theme.ir.ui.view'
_description = 'Theme UI View'
def compute_arch_fs(self):
if 'install_filename' not in self._context:
return ''
path_info = get_resource_from_path(self._context['install_filename'])
if path_info:
return '/'.join(path_info[0:2])
name = fields.Char(required=True)
key = fields.Char()
type = fields.Char()
priority = fields.Integer(default=16, required=True)
mode = fields.Selection([('primary', "Base view"), ('extension', "Extension View")])
active = fields.Boolean(default=True)
arch = fields.Text(translate=xml_translate)
arch_fs = fields.Char(default=compute_arch_fs)
inherit_id = fields.Reference(selection=[('ir.ui.view', 'ir.ui.view'), ('theme.ir.ui.view', 'theme.ir.ui.view')])
copy_ids = fields.One2many('ir.ui.view', 'theme_template_id', 'Views using a copy of me', copy=False, readonly=True)
customize_show = fields.Boolean()
def _convert_to_base_model(self, website, **kwargs):
self.ensure_one()
inherit = self.inherit_id
if self.inherit_id and self.inherit_id._name == 'theme.ir.ui.view':
inherit = self.inherit_id.with_context(active_test=False).copy_ids.filtered(lambda x: x.website_id == website)
if not inherit:
# inherit_id not yet created, add to the queue
return False
if inherit and inherit.website_id != website:
website_specific_inherit = self.env['ir.ui.view'].with_context(active_test=False).search([
('key', '=', inherit.key),
('website_id', '=', website.id)
], limit=1)
if website_specific_inherit:
inherit = website_specific_inherit
new_view = {
'type': self.type or 'qweb',
'name': self.name,
'arch': self.arch,
'key': self.key,
'inherit_id': inherit and inherit.id,
'arch_fs': self.arch_fs,
'priority': self.priority,
'active': self.active,
'theme_template_id': self.id,
'website_id': website.id,
'customize_show': self.customize_show,
}
if self.mode: # if not provided, it will be computed automatically (if inherit_id or not)
new_view['mode'] = self.mode
return new_view
class ThemeAttachment(models.Model):
_name = 'theme.ir.attachment'
_description = 'Theme Attachments'
name = fields.Char(required=True)
key = fields.Char(required=True)
url = fields.Char()
copy_ids = fields.One2many('ir.attachment', 'theme_template_id', 'Attachment using a copy of me', copy=False, readonly=True)
def _convert_to_base_model(self, website, **kwargs):
self.ensure_one()
new_attach = {
'key': self.key,
'public': True,
'res_model': 'ir.ui.view',
'type': 'url',
'name': self.name,
'url': self.url,
'website_id': website.id,
'theme_template_id': self.id,
}
return new_attach
class ThemeMenu(models.Model):
_name = 'theme.website.menu'
_description = 'Website Theme Menu'
name = fields.Char(required=True, translate=True)
url = fields.Char(default='')
page_id = fields.Many2one('theme.website.page', ondelete='cascade')
new_window = fields.Boolean('New Window')
sequence = fields.Integer()
parent_id = fields.Many2one('theme.website.menu', index=True, ondelete="cascade")
copy_ids = fields.One2many('website.menu', 'theme_template_id', 'Menu using a copy of me', copy=False, readonly=True)
def _convert_to_base_model(self, website, **kwargs):
self.ensure_one()
page_id = self.page_id.copy_ids.filtered(lambda x: x.website_id == website)
parent_id = self.copy_ids.filtered(lambda x: x.website_id == website)
new_menu = {
'name': self.name,
'url': self.url,
'page_id': page_id and page_id.id or False,
'new_window': self.new_window,
'sequence': self.sequence,
'parent_id': parent_id and parent_id.id or False,
'theme_template_id': self.id,
}
return new_menu
class ThemePage(models.Model):
_name = 'theme.website.page'
_description = 'Website Theme Page'
url = fields.Char()
view_id = fields.Many2one('theme.ir.ui.view', required=True, ondelete="cascade")
website_indexed = fields.Boolean('Page Indexed', default=True)
copy_ids = fields.One2many('website.page', 'theme_template_id', 'Page using a copy of me', copy=False, readonly=True)
def _convert_to_base_model(self, website, **kwargs):
self.ensure_one()
view_id = self.view_id.copy_ids.filtered(lambda x: x.website_id == website)
if not view_id:
# inherit_id not yet created, add to the queue
return False
new_page = {
'url': self.url,
'view_id': view_id.id,
'website_indexed': self.website_indexed,
'theme_template_id': self.id,
}
return new_page
class Theme(models.AbstractModel):
_name = 'theme.utils'
_description = 'Theme Utils'
_auto = False
def _post_copy(self, mod):
# Call specific theme post copy
theme_post_copy = '_%s_post_copy' % mod.name
if hasattr(self, theme_post_copy):
_logger.info('Executing method %s' % theme_post_copy)
method = getattr(self, theme_post_copy)
return method(mod)
return False
@api.model
def _reset_default_config(self):
# Reinitialize some css customizations
self.env['web_editor.assets'].make_scss_customization(
'/website/static/src/scss/options/user_values.scss',
{
'font': 'null',
'headings-font': 'null',
'navbar-font': 'null',
'buttons-font': 'null',
'color-palettes-number': 'null',
'btn-ripple': 'null',
'header-template': 'null',
'footer-template': 'null',
'footer-scrolltop': 'null',
}
)
# Reinitialize effets
self.disable_view('website.option_ripple_effect')
# Reinitialize header templates
self.enable_view('website.template_header_default')
self.disable_view('website.template_header_hamburger')
self.disable_view('website.template_header_vertical')
self.disable_view('website.template_header_sidebar')
self.disable_view('website.template_header_slogan')
self.disable_view('website.template_header_contact')
self.disable_view('website.template_header_minimalist')
self.disable_view('website.template_header_boxed')
self.disable_view('website.template_header_centered_logo')
self.disable_view('website.template_header_image')
self.disable_view('website.template_header_hamburger_full')
self.disable_view('website.template_header_magazine')
# Reinitialize footer templates
self.enable_view('website.footer_custom')
self.disable_view('website.template_footer_descriptive')
self.disable_view('website.template_footer_centered')
self.disable_view('website.template_footer_links')
self.disable_view('website.template_footer_minimalist')
self.disable_view('website.template_footer_contact')
self.disable_view('website.template_footer_call_to_action')
self.disable_view('website.template_footer_headline')
# Reinitialize footer scrolltop template
self.disable_view('website.option_footer_scrolltop')
@api.model
def _toggle_view(self, xml_id, active):
obj = self.env.ref(xml_id)
website = self.env['website'].get_current_website()
if obj._name == 'theme.ir.ui.view':
obj = obj.with_context(active_test=False)
obj = obj.copy_ids.filtered(lambda x: x.website_id == website)
else:
# If a theme post copy wants to enable/disable a view, this is to
# enable/disable a given functionality which is disabled/enabled
# by default. So if a post copy asks to enable/disable a view which
# is already enabled/disabled, we would not consider it otherwise it
# would COW the view for nothing.
View = self.env['ir.ui.view'].with_context(active_test=False)
has_specific = obj.key and View.search_count([
('key', '=', obj.key),
('website_id', '=', website.id)
]) >= 1
if not has_specific and active == obj.active:
return
obj.write({'active': active})
@api.model
def enable_view(self, xml_id):
self._toggle_view(xml_id, True)
@api.model
def disable_view(self, xml_id):
self._toggle_view(xml_id, False)
@api.model
def enable_header_off_canvas(self):
""" Enabling off canvas require to enable quite a lot of template so
this shortcut was made to make it easier.
"""
self.enable_view("website.option_header_off_canvas")
self.enable_view("website.option_header_off_canvas_template_header_hamburger")
self.enable_view("website.option_header_off_canvas_template_header_sidebar")
self.enable_view("website.option_header_off_canvas_template_header_hamburger_full")
class IrUiView(models.Model):
_inherit = 'ir.ui.view'
theme_template_id = fields.Many2one('theme.ir.ui.view', copy=False)
def write(self, vals):
no_arch_updated_views = other_views = self.env['ir.ui.view']
for record in self:
# Do not mark the view as user updated if original view arch is similar
arch = vals.get('arch', vals.get('arch_base'))
if record.theme_template_id and record.theme_template_id.arch == arch:
no_arch_updated_views += record
else:
other_views += record
res = super(IrUiView, other_views).write(vals)
if no_arch_updated_views:
vals['arch_updated'] = False
res &= super(IrUiView, no_arch_updated_views).write(vals)
return res
class IrAttachment(models.Model):
_inherit = 'ir.attachment'
key = fields.Char(copy=False)
theme_template_id = fields.Many2one('theme.ir.attachment', copy=False)
class WebsiteMenu(models.Model):
_inherit = 'website.menu'
theme_template_id = fields.Many2one('theme.website.menu', copy=False)
class WebsitePage(models.Model):
_inherit = 'website.page'
theme_template_id = fields.Many2one('theme.website.page', copy=False)