diff options
Diffstat (limited to 'addons/website_theme_install/models')
4 files changed, 182 insertions, 2 deletions
diff --git a/addons/website_theme_install/models/__init__.py b/addons/website_theme_install/models/__init__.py index de58fda6e31..786d2febfb6 100644 --- a/addons/website_theme_install/models/__init__.py +++ b/addons/website_theme_install/models/__init__.py @@ -2,3 +2,5 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import ir_module_module +from . import ir_ui_view +from . import res_config_settings diff --git a/addons/website_theme_install/models/ir_module_module.py b/addons/website_theme_install/models/ir_module_module.py index 5350abea437..560bd37a932 100644 --- a/addons/website_theme_install/models/ir_module_module.py +++ b/addons/website_theme_install/models/ir_module_module.py @@ -2,9 +2,12 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. import os +import logging from odoo import api, fields, models +_logger = logging.getLogger(__name__) + class IrModuleModule(models.Model): _name = "ir.module.module" @@ -13,6 +16,71 @@ class IrModuleModule(models.Model): image_ids = fields.One2many('ir.attachment', 'res_id', domain=[('res_model', '=', _name), ('mimetype', '=like', 'image/%')], string='Screenshots', readonly=True) + 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().installed_theme_id + + @api.multi + def write(self, vals): + if vals.get('state') == 'installed': + self._handle_auto_installs() + + return super(IrModuleModule, self).write(vals) + + @api.multi + def _assign_views(self): + IrModelData = self.env['ir.model.data'] + for module in self: + views = self.env['ir.ui.view'].browse(IrModelData.search([('module', '=', module.name), + ('model', '=', 'ir.ui.view')]).mapped('res_id')) + views.write({ + 'theme_id': module.id + }) + + @api.model + def _handle_auto_installs(self): + for module in self.search([('auto_install', '=', True), ('name', '=like', 'theme%'), ('state', 'in', ('installed', 'to install'))]): + theme_deps = module.get_upstream_theme_dependencies() + websites = self.env['website'].search([]).filtered(lambda w: len(theme_deps - w.theme_ids) == 0) + if websites: + _logger.info('auto-loading %s on %s', module.name, websites.mapped('name')) + websites.write({'theme_ids': [(4, module.id, 0)]}) + module._assign_views() + + # put demo date on first website + module._make_demo_data_website_specific(websites[0]) + + @api.multi + def _get_module_data(self, model_name): + IrModelData = self.env['ir.model.data'] + records = self.env[model_name] + + for module in self: + records |= self.env[model_name].browse(IrModelData.search([('module', '=', module.name), ('model', '=', model_name)]).mapped('res_id')) + + return records + + @api.multi + def _make_demo_data_website_specific(self, website): + for module in self: + menus = module._get_module_data('website.menu') + menus.with_context(no_cow=True).write({'website_id': website.id}) + + pages = module._get_module_data('website.page') + pages.with_context(no_cow=True).write({'website_id': website.id}) + pages.with_context(no_cow=True).mapped('view_id').write({'website_id': website.id}) + + @api.multi + def _remove_website_specific_demo_data(self, website): + for module in self: + menus = module._get_module_data('website.menu') + menus.filtered(lambda menu: menu.website_id == website).unlink() + + pages = module._get_module_data('website.page') + pages.filtered(lambda page: page.website_id == website).unlink() @api.model def update_list(self): @@ -40,6 +108,11 @@ class IrModuleModule(models.Model): return res + def get_upstream_theme_dependencies(self): + upstream_deps = self.upstream_dependencies(exclude_states=('uninstallable', 'to remove')) + upstream_theme_deps = upstream_deps.filtered(lambda mod: any(mod.name.startswith(keyword) for keyword in ('theme_', 'snippet_', 'website_animate'))) + return upstream_theme_deps + @api.multi def button_choose_theme(self): theme_category = self.env.ref('base.module_category_theme', False) @@ -47,15 +120,71 @@ class IrModuleModule(models.Model): theme_category_id = theme_category.id if theme_category else 0 hidden_categories_ids = [theme_hidden_category.id if theme_hidden_category else 0] + current_website = self.env['website'].get_current_website() - self.search([ # Uninstall the theme(s) which is (are) installed + themes_to_uninstall = self.search([ # Uninstall the theme(s) which is (are) installed ('state', '=', 'installed'), ('category_id', 'not in', hidden_categories_ids), + ('id', 'in', current_website.theme_ids.ids), '|', ('category_id', '=', theme_category_id), ('category_id.parent_id', '=', theme_category_id) - ]).button_immediate_uninstall() + ]) + + if themes_to_uninstall: + themes_to_uninstall.button_immediate_uninstall() + + modules_belonging_to_theme = self + self.get_upstream_theme_dependencies() + current_website.installed_theme_id = self + current_website.theme_ids |= modules_belonging_to_theme next_action = self.button_immediate_install() # Then install the new chosen one + modules_belonging_to_theme._assign_views() + modules_belonging_to_theme._make_demo_data_website_specific(current_website) + self._handle_auto_installs() + _logger.info('installed themes %s on %s', modules_belonging_to_theme.mapped('name'), current_website.name) + 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 + + @api.multi + def button_uninstall(self): + if not any(mod.name.startswith('theme_') for mod in self): + return super(IrModuleModule, self).button_uninstall() + + # only one theme can be uninstalled + self.ensure_one() + + Website = self.env['website'] + current_website = Website.get_current_website() + if Website.search_count([('theme_ids', 'in', self.id)]) > 1: + modules_belonging_to_theme = self + self.get_upstream_theme_dependencies() + self.downstream_dependencies() + current_website.installed_theme_id -= modules_belonging_to_theme + current_website.theme_ids -= modules_belonging_to_theme + modules_belonging_to_theme._remove_website_specific_demo_data(current_website) + _logger.info('removed themes %s from %s', modules_belonging_to_theme.mapped('name'), current_website.name) + return True + else: + uninstalled_modules = self + self.downstream_dependencies() + res = super(IrModuleModule, self).button_uninstall() + + for website in Website.search([]): + website.installed_theme_id -= uninstalled_modules + website.theme_ids -= uninstalled_modules + _logger.info('removed themes %s from %s', uninstalled_modules.mapped('name'), website.name) + + # Attempt to uninstall upstream dependencies. This is + # necessary for uninstalling e.g. theme_zap, when doing so + # theme_treehouse should also be removed from the current + # website. This way the user can install a completely new + # theme. + # + # Make sure to only uninstall every module once with + # context. Otherwise it might get removed from the current + # website the first time and the second time it might end + # up being wrongly uninstalled. + if self._context.get('uninstall_upstream') is not False: + for upstream_dependency in self.get_upstream_theme_dependencies().filtered(lambda mod: mod.name.startswith('theme_')): + upstream_dependency.with_context(uninstall_upstream=False).button_uninstall() + + return res diff --git a/addons/website_theme_install/models/ir_ui_view.py b/addons/website_theme_install/models/ir_ui_view.py new file mode 100644 index 00000000000..a222fbd884f --- /dev/null +++ b/addons/website_theme_install/models/ir_ui_view.py @@ -0,0 +1,35 @@ +# coding: utf-8 +from odoo import api, fields, models + +class View(models.Model): + _inherit = 'ir.ui.view' + + @api.model + def _get_inheriting_views_arch_website(self, view_id): + res = self.env['website'].get_current_website() + + # In certain cases view validation as done by + # _validate_module_views will fail. When attempting to load + # inherited view CHILD: + # + # PARENT (theme_common) + # ^ + # | + # CHILD (other_theme) + # + # During the view validation no website_id will be in context + # so the view will fail to apply, since PARENT won't be + # selected by the inheriting view arch domain. + # + # This is not a problem however, CHILD will never be loaded on + # a website where CHILD is not installed. To simulate this, + # return a website which has the theme containing CHILD + # installed. + if res: + return res + else: + view_theme = self.browse(view_id).theme_id + if view_theme: + return self.env['website'].search([('theme_ids', 'in', view_theme.id)], limit=1) + else: + return self.env['website'] diff --git a/addons/website_theme_install/models/res_config_settings.py b/addons/website_theme_install/models/res_config_settings.py new file mode 100644 index 00000000000..307ecc932f8 --- /dev/null +++ b/addons/website_theme_install/models/res_config_settings.py @@ -0,0 +1,14 @@ +# coding: utf-8 +from odoo import api, fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = 'res.config.settings' + + def website_install_theme(self): + self.website_id._fix_to_session() + return { + 'type': 'ir.actions.act_url', + 'url': '/web#action=website_theme_install.theme_install_kanban_action', + 'target': 'self', + } |
