# Part of Odoo. See LICENSE file for full copyright and licensing details. import logging from lxml import html, etree from odoo import models from odoo.tools.translate import _ logger = logging.getLogger(__name__) class WebsitePageGenerator(models.Model): _name = 'website.page' _inherit = 'website.page' def _construct_homepage(self, homepage_data): self._construct_page(homepage_data) self._create_footer(homepage_data) self._create_header(homepage_data) self._apply_website_themes(homepage_data) self._apply_user_values(homepage_data) def _construct_page(self, page_data): html_block_list = page_data.get('body_html') if not html_block_list: return rendered_snippets = [] nb_snippets = len(html_block_list) for i, snippet in enumerate(html_block_list, start=1): try: # Remove \ufeff character from the html (it is a BOM character that is not supported by lxml) el = html.fromstring(snippet.replace('\ufeff', '')) # TODO: Add the data-snippet attribute to identify the snippet # for compatibility code # el.attrib['data-snippet'] = snippet["name"] # Tweak the shape of the first snippet to connect it # properly with the header color in some themes if i == 1: shape_el = el.xpath("//*[hasclass('o_we_shape')]") if shape_el: shape_el[0].attrib['class'] += ' o_header_extra_shape_mapping' # Tweak the shape of the last snippet to connect it # properly with the footer color in some themes if i == nb_snippets: shape_el = el.xpath("//*[hasclass('o_we_shape')]") if shape_el: shape_el[0].attrib['class'] += ' o_footer_extra_shape_mapping' rendered_snippet = etree.tostring(el, encoding='unicode') rendered_snippets.append(rendered_snippet) except ValueError as e: logger.warning("Error rendering snippet: %s", e) self.view_id.save(value=f'
{"".join(rendered_snippets)}
', xpath="(//div[hasclass('oe_structure')])[last()]") def _create_header(self, homepage_data): # Remove all website menus, keep only the top menu "container" self.env['website.menu'].search([('website_id', '=', self.website_id.id)]).unlink() # Create the top menu of which to bind the menu buttons to. top_menu = self.env['website.menu'].create({ 'name': _('Top Menu for Website %s', self.website_id.id), 'url': '/default-main-menu', 'website_id': self.website_id.id, }) for button in homepage_data.get('header', {}).get('buttons', []): if button.get('name', '').lower() in ['sign in', 'contact us']: continue # those are Odoo menu already menu_content = button.get('menu_content') if menu_content: menu_type = menu_content.get('type') if menu_type == 'simple_menu': # Create the parent menu parent_menu = self._create_menu(button, top_menu) # Create the submenu menu. children_menus = button.get('menu_content', {}).get('content', []) self.env['website.menu'].create([{ 'name': child_menu['name'], 'url': child_menu['href'], 'parent_id': parent_menu and parent_menu.id, 'website_id': self.website_id.id} for child_menu in children_menus]) elif menu_type == 'mega_menu': # Create mega menu instead button_name = button.get('name') button_href = button.get('href') mega_menu_content = button.get('menu_content', {}).get('content') if not button_name or not button_href or not mega_menu_content: return self.env['website.menu'].create({ 'name': button_name, 'url': button_href, 'website_id': self.website_id.id, 'parent_id': top_menu.id, 'is_mega_menu': True, 'mega_menu_content': mega_menu_content, }) else: self._create_menu(button, top_menu) # TODO: Need to try and set the level of transparency of the header overlay. header_position = homepage_data.get('header_position', 'regular') if header_position == 'regular': self.header_overlay = False elif header_position == 'over-the-content': self.header_overlay = True def _create_menu(self, menu_vals, top_menu): menu_name = menu_vals.get('name') menu_href = menu_vals.get('href') if not menu_name or not menu_href: return menu = self.env['website.menu'].create({ 'name': menu_name, 'url': menu_href, 'parent_id': top_menu.id, 'website_id': self.website_id.id, }) return menu def _apply_website_themes(self, homepage_data): values = {} color_palette = homepage_data.get('color_palette', {}) for i in range(1, 6): color = color_palette.get(f'o-color-{i}') if color: values[f'o-color-{i}'] = color header_color = homepage_data.get('header_color', {}) if header_color: values.update({ 'menu': header_color.get('menu', "'NULL'"), 'menu-gradient': header_color.get('menu-gradient', "'NULL'"), 'menu-custom': header_color.get('menu-custom', "'NULL'"), }) footer_color = homepage_data.get('footer_color', {}) if footer_color: values.update({ 'footer': footer_color.get('footer', "'NULL'"), 'footer-gradient': footer_color.get('footer-gradient', "'NULL'"), 'footer-custom': footer_color.get('footer-custom', "'NULL'"), }) # TODO: Try add this new color palette as an option to the odoo editor list of options. self.env['web_editor.assets'].make_scss_customization( '/website/static/src/scss/options/colors/user_color_palette.scss', values, ) def _apply_user_values(self, homepage_data): user_values = { 'font': 'null' } if homepage_data.get('footer'): user_values.update({'footer-template': f"'imported-footer-{self.website_id.id}'"}) fonts = homepage_data.get('fonts', {}) if fonts: google_fonts = fonts.get('google-fonts', 'null') google_fonts_tuple = 'null' if google_fonts != 'null': google_fonts_tuple = f"""('{"', '".join(google_fonts)}')""" user_values.update({ 'google-fonts': google_fonts_tuple, 'headings-font': fonts.get('h1', 'null'), 'h2-font': fonts.get('h2', 'null'), 'h3-font': fonts.get('h3', 'null'), 'h4-font': fonts.get('h4', 'null'), 'h5-font': fonts.get('h5', 'null'), 'h6-font': fonts.get('h6', 'null'), 'font': fonts.get('p', 'null'), 'buttons-font': fonts.get('button', 'null'), 'display-1-font': fonts.get('h1-display-1', 'null'), 'display-2-font': fonts.get('h1-display-2', 'null'), 'display-3-font': fonts.get('h1-display-3', 'null'), 'display-4-font': fonts.get('h1-display-4', 'null'), }) self.env['web_editor.assets'].make_scss_customization( '/website/static/src/scss/options/user_values.scss', user_values, ) def _create_footer(self, homepage_data): footer_html_list = homepage_data.get('footer', []) rendered_snippets = [] for snippet in footer_html_list: try: # Process the footer html content (for example, an element becomes .) el = html.fromstring(snippet) rendered_snippet = etree.tostring(el, encoding='unicode') rendered_snippets.append(rendered_snippet) except ValueError as e: logger.warning(e) # Now generate the footer template. new_footer_template = f""" """ self.env['ir.ui.view'].create({ 'name': 'Template WS Custom Footer', 'type': 'qweb', 'key': f'website_generator.template_ws_custom_footer_{self.website_id.id}', 'arch': new_footer_template, 'inherit_id': self.env['website'].with_context(website_id=self.website_id.id).viewref('website.layout').id, 'website_id': self.website_id.id, }) # And generate the footer snippet (such that it is selectable in the editor.) new_footer_snippet = f""" Imported """ self.env['ir.ui.view'].create({ 'name': 'WS Custom Footer', 'type': 'qweb', 'key': f'website.ws_custom_footer{self.website_id.id}', 'arch': new_footer_snippet, 'inherit_id': self.env.ref('website.snippet_options').id, 'website_id': self.website_id.id, })