How to reproduce * install website_theme_install; * go on Apps -> Apply scheduled upgrades; * Hit Cancel; * there is a crash because somehow when writing on module self is a void recordset and self.name is therefore False and cannot starts with 'theme'; This commit fixes that by ensuring self is not void before checking its name.
285 lines
12 KiB
Python
285 lines
12 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import logging
|
|
import os
|
|
|
|
from odoo import api, fields, models
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class IrModuleModule(models.Model):
|
|
_name = "ir.module.module"
|
|
_description = 'Module'
|
|
_inherit = _name
|
|
|
|
image_ids = fields.One2many('ir.attachment', 'res_id',
|
|
domain=[('res_model', '=', _name), ('mimetype', '=like', 'image/%')],
|
|
string='Screenshots', readonly=True)
|
|
# for kanban view
|
|
is_installed_on_current_website = fields.Boolean(compute='_compute_is_installed_on_current_website')
|
|
|
|
@api.multi
|
|
def _compute_is_installed_on_current_website(self):
|
|
for module in self:
|
|
module.is_installed_on_current_website = module == self.env['website'].get_current_website().theme_id
|
|
|
|
@api.multi
|
|
def write(self, vals):
|
|
if self and vals.get('state') == 'installed' and self.name.startswith('theme_'):
|
|
_logger.info('Module %s has been loaded as theme template' % self.name)
|
|
return super(IrModuleModule, self).write(vals)
|
|
|
|
@api.multi
|
|
def _get_module_data(self, model_name):
|
|
IrModelData = self.env['ir.model.data']
|
|
records = self.env[model_name]
|
|
|
|
for module in self:
|
|
imd_ids = IrModelData.search([('module', '=', module.name), ('model', '=', model_name)]).mapped('res_id')
|
|
records |= self.env[model_name].with_context(active_test=False).browse(imd_ids)
|
|
return records
|
|
|
|
def _convert_attachment(self, attach, website, **kw):
|
|
new_attach = {
|
|
'key': attach.key,
|
|
'public': True,
|
|
'res_model': 'ir.ui.view',
|
|
'type': 'url',
|
|
'name': attach.name,
|
|
'url': attach.url,
|
|
'website_id': website.id,
|
|
'theme_template_id': attach.id,
|
|
}
|
|
return new_attach
|
|
|
|
def _convert_view(self, view, website, **kw):
|
|
inherit = view.inherit_id
|
|
if view.inherit_id and view.inherit_id._name == 'theme.ir.ui.view':
|
|
inherit = view.inherit_id.copy_ids.filtered(lambda x: x.website_id == website)
|
|
if not inherit:
|
|
# inherit_id not yet created, add to the queue
|
|
return False
|
|
new_view = {
|
|
'type': view.type or 'qweb',
|
|
'name': view.name,
|
|
'arch': view.arch,
|
|
'key': view.key,
|
|
'inherit_id': inherit and inherit.id,
|
|
'arch_fs': view.arch_fs,
|
|
'priority': view.priority,
|
|
'active': view.active,
|
|
'theme_template_id': view.id,
|
|
'website_id': website.id,
|
|
}
|
|
|
|
if view.mode: # if not provided, it will be computed automatically (if inherit_id or not)
|
|
new_view['mode'] = view.mode
|
|
|
|
return new_view
|
|
|
|
def _convert_page(self, page, website, **kw):
|
|
new_page = {
|
|
'url': page.url,
|
|
'view_id': page.view_id.copy_ids.filtered(lambda x: x.website_id == website),
|
|
'website_indexed': page.website_indexed,
|
|
'theme_template_id': page.id,
|
|
}
|
|
return new_page
|
|
|
|
def _convert_menu(self, menu, website, **kw):
|
|
page_id = menu.page_id.copy_ids.filtered(lambda x: x.website_id == website)
|
|
parent_id = menu.copy_ids.filtered(lambda x: x.website_id == website)
|
|
new_menu = {
|
|
'name': menu.name,
|
|
'url': menu.url,
|
|
'page_id': page_id,
|
|
'new_window': menu.new_window,
|
|
'sequence': menu.sequence,
|
|
'parent_id': parent_id,
|
|
'theme_template_id': menu.id,
|
|
}
|
|
return new_menu
|
|
|
|
def _convert(self, model):
|
|
if model == 'ir.ui.view':
|
|
return self._convert_view
|
|
elif model == 'ir.attachment':
|
|
return self._convert_attachment
|
|
elif model == 'website.menu':
|
|
return self._convert_menu
|
|
elif model == 'website.page':
|
|
return self._convert_page
|
|
else:
|
|
_logger.error('No converter found for %s', model)
|
|
|
|
def _update_records(self, old, new):
|
|
# This function:
|
|
# create new record if not in old
|
|
# update old record if already exists
|
|
# delete old record that are not in new
|
|
website = self.env['website'].get_current_website()
|
|
model = old._name
|
|
created = self.env[model]
|
|
updated = self.env[model]
|
|
deleted = self.env[model]
|
|
|
|
remaining = new
|
|
last_len = -1
|
|
while (len(remaining) != last_len):
|
|
last_len = len(remaining)
|
|
for rec in remaining:
|
|
rec_data = self._convert(model)(rec, website)
|
|
if not rec_data:
|
|
_logger.info('Record queued: %s' % rec.name)
|
|
continue
|
|
|
|
find = rec.with_context(active_test=False).mapped('copy_ids').filtered(lambda m: m.website_id == website)
|
|
|
|
# special case for attachment
|
|
# if module B override attachment from dependence A, we update it
|
|
if not find and model == 'ir.attachment':
|
|
find = rec.copy_ids.search([('key', '=', rec.key), ('website_id', '=', website.id)])
|
|
|
|
if old and model == 'ir.ui.view': # at update, ignore active field
|
|
rec_data.pop('active')
|
|
|
|
if find:
|
|
imd = self.env['ir.model.data'].search([('model', '=', find._name), ('res_id', '=', find.id)])
|
|
if imd and imd.noupdate:
|
|
_logger.info('Noupdate set for %s (%s)' % (find, imd))
|
|
continue
|
|
find.update(rec_data)
|
|
updated += find
|
|
else:
|
|
created += self.env[model].create(rec_data)
|
|
remaining -= rec
|
|
|
|
deleted = old - updated
|
|
deleted.unlink()
|
|
return (created, updated, deleted)
|
|
|
|
def _load_one_theme_module(self, website, with_update=True, **kw):
|
|
# load data from xml to template table
|
|
old_menus = self._get_module_data('theme.website.menu').with_context(active_test=False).mapped('copy_ids').filtered(lambda m: m.website_id == website)
|
|
old_pages = self._get_module_data('theme.website.page').with_context(active_test=False).mapped('copy_ids').filtered(lambda p: p.website_id == website)
|
|
old_attachs = self._get_module_data('theme.ir.attachment').with_context(active_test=False).mapped('copy_ids').filtered(lambda a: a.website_id == website)
|
|
old_views = self._get_module_data('theme.ir.ui.view').with_context(active_test=False).mapped('copy_ids').filtered(lambda v: v.website_id == website)
|
|
|
|
if with_update:
|
|
self.button_immediate_upgrade()
|
|
|
|
new_menus = self._get_module_data('theme.website.menu')
|
|
new_pages = self._get_module_data('theme.website.page')
|
|
new_attachs = self._get_module_data('theme.ir.attachment')
|
|
new_views = self._get_module_data('theme.ir.ui.view')
|
|
|
|
self._update_records(old_menus, new_menus)
|
|
self._update_records(old_pages, new_pages)
|
|
self._update_records(old_attachs, new_attachs)
|
|
self._update_records(old_views, new_views)
|
|
|
|
@api.multi
|
|
def _unload_one_theme_module(self, website):
|
|
self.ensure_one()
|
|
_logger.info('Unload theme %s for website %s from template.' % (self.mapped('name'), website.id))
|
|
menus = self._get_module_data('theme.website.menu')
|
|
menu_todel = menus.with_context(active_test=False).mapped('copy_ids').filtered(lambda m: m.website_id == website)
|
|
menu_todel.unlink()
|
|
|
|
pages = self._get_module_data('theme.website.page')
|
|
pages_todel = pages.with_context(active_test=False).mapped('copy_ids').filtered(lambda p: p.website_id == website)
|
|
pages_todel.unlink()
|
|
|
|
attachs = self._get_module_data('theme.ir.attachment')
|
|
attachs_todel = attachs.with_context(active_test=False).mapped('copy_ids').filtered(lambda a: a.website_id == website)
|
|
# double check - Some records that can be orphans or no more in the template view.
|
|
attachs_todel2 = self.env['ir.attachment'].with_context(active_test=False).search([('key', '=like', self.name + '_%'), ('website_id', '=', website.id)])
|
|
(attachs_todel + attachs_todel2).unlink()
|
|
|
|
views = self._get_module_data('theme.ir.ui.view')
|
|
views_todel = views.with_context(active_test=False).mapped('copy_ids').filtered(lambda v: v.website_id == website)
|
|
# double check - Some records that can be orphans or no more in the template view.
|
|
views_todel2 = self.env['ir.ui.view'].with_context(active_test=False).search([('key', '=like', self.name + '_%'), ('website_id', '=', website.id)])
|
|
(views_todel + views_todel2).unlink()
|
|
|
|
@api.multi
|
|
def _remove_theme_on_website(self, website):
|
|
self.ensure_one()
|
|
installed_deps = self + website.theme_id.upstream_dependencies(exclude_states=('',)).filtered(lambda x: x.name.startswith('theme_'))
|
|
for mod in installed_deps:
|
|
mod._unload_one_theme_module(website)
|
|
|
|
@api.multi
|
|
def _add_theme_on_website(self, website):
|
|
self.ensure_one()
|
|
mods_to_load = reversed(self + self.upstream_dependencies(exclude_states=('',)).filtered(lambda x: x.name.startswith('theme_')))
|
|
for mod in mods_to_load:
|
|
_logger.info('Load theme %s for website %s from template.' % (mod.name, website.id))
|
|
mod._load_one_theme_module(website, with_update=False)
|
|
self.env['theme.utils']._post_copy(mod)
|
|
|
|
@api.multi
|
|
def button_choose_theme(self):
|
|
self.ensure_one()
|
|
website = self.env['website'].get_current_website()
|
|
|
|
# Unload previous theme
|
|
if website.theme_id:
|
|
website.theme_id._remove_theme_on_website(website)
|
|
|
|
website.theme_id = self
|
|
|
|
# create template data of missing theme
|
|
next_action = False
|
|
if self.state != 'installed':
|
|
next_action = self.button_immediate_install()
|
|
# reload registry to check if 'theme.utils'._<theme_name>_post_copy exists e.g.
|
|
self.env.reset()
|
|
self = self.env()[self._name].browse(self.id)
|
|
|
|
# Copy new theme from template table to real table
|
|
self._add_theme_on_website(website)
|
|
|
|
# Alter next action for redirect
|
|
if not next_action:
|
|
next_action = website.button_go_website()
|
|
if next_action.get('tag') == 'reload' and not next_action.get('params', {}).get('menu_id'):
|
|
next_action = self.env.ref('website.action_website').read()[0]
|
|
|
|
return next_action
|
|
|
|
def button_remove_theme(self):
|
|
website = self.env['website'].get_current_website()
|
|
self._remove_theme_on_website(website)
|
|
website.theme_id = False
|
|
|
|
def button_refresh_theme(self):
|
|
website = self.env['website'].get_current_website()
|
|
self._load_one_theme_module(website, with_update=True)
|
|
|
|
@api.model
|
|
def update_list(self):
|
|
res = super(IrModuleModule, self).update_list()
|
|
|
|
IrAttachment = self.env['ir.attachment']
|
|
existing_urls = IrAttachment.search_read([['res_model', '=', self._name], ['type', '=', 'url']], ['url'])
|
|
existing_urls = [url_wrapped['url'] for url_wrapped in existing_urls]
|
|
|
|
for app in self.search([]):
|
|
terp = self.get_module_info(app.name)
|
|
images = terp.get('images', [])
|
|
for image in images:
|
|
image_path = os.path.join(app.name, image)
|
|
if image_path not in existing_urls:
|
|
image_name = os.path.basename(image_path)
|
|
IrAttachment.create({
|
|
'type': 'url',
|
|
'name': image_name,
|
|
'datas_fname': image_name,
|
|
'url': image_path,
|
|
'res_model': self._name,
|
|
'res_id': app.id,
|
|
})
|
|
return res
|