summaryrefslogtreecommitdiff
path: root/addons/website_theme_install/models
diff options
context:
space:
mode:
Diffstat (limited to 'addons/website_theme_install/models')
-rw-r--r--addons/website_theme_install/models/__init__.py2
-rw-r--r--addons/website_theme_install/models/ir_module_module.py133
-rw-r--r--addons/website_theme_install/models/ir_ui_view.py35
-rw-r--r--addons/website_theme_install/models/res_config_settings.py14
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',
+ }