Source code for shuup.xtheme.engine

# -*- coding: utf-8 -*-
# This file is part of Shuup.
#
# Copyright (c) 2012-2021, Shuup Commerce Inc. All rights reserved.
#
# This source code is licensed under the OSL-3.0 license found in the
# LICENSE file in the root directory of this source tree.
from __future__ import unicode_literals

import six
import sys
from django.conf import settings
from jinja2.environment import Environment, Template
from jinja2.utils import concat, internalcode

from shuup.apps.provides import get_provide_objects
from shuup.xtheme._theme import get_middleware_current_theme
from shuup.xtheme.editing import add_edit_resources
from shuup.xtheme.resources import RESOURCE_CONTAINER_VAR_NAME, ResourceContainer, inject_resources


[docs]class XthemeTemplate(Template): """ A subclass of Jinja templates with additional post-processing magic. """
[docs] def render(self, *args, **kwargs): """ Render the template and postprocess it. :return: Rendered markup :rtype: str """ vars = dict(*args, **kwargs) vars[RESOURCE_CONTAINER_VAR_NAME] = ResourceContainer() ctx = self.new_context(vars) try: content = concat(self.root_render_func(ctx)) if ctx and ctx.name and ctx.name in settings.SHUUP_XTHEME_EXCLUDE_TEMPLATES_FROM_RESOUCE_INJECTION: return content return self._postprocess(ctx, content) except Exception: exc_info = sys.exc_info() return self.environment.handle_exception(exc_info, True)
def _postprocess(self, context, content): # if the context contains the `allow_resource_injection` key and # it's value is False, we don't inject resources in the content if context and context.get("allow_resource_injection", True) is False: return content for inject_func in get_provide_objects("xtheme_resource_injection"): if callable(inject_func): inject_func(context, content) add_edit_resources(context) content = inject_resources(context, content) return content
[docs]class XthemeEnvironment(Environment): """ Overrides the usual template class and allows dynamic switching of Xthemes. Enable by adding ``"environment": "shuup.xtheme.engine.XthemeEnvironment"`` in your ``TEMPLATES`` settings. """ # The Jinja2 source says: # > hook in default template class. if anyone reads this comment: ignore that # > it's possible to use custom templates ;-) # Well, I ain't ignoring that. template_class = XthemeTemplate
[docs] def get_template(self, name, parent=None, globals=None): """ Load a template from the loader. If a loader is configured this method asks the loader for the template and returns a :class:`Template`. :param name: Template name. :type name: str :param parent: If the `parent` parameter is not `None`, :meth:`join_path` is called to get the real template name before loading. :type parent: str|None :param globals: The `globals` parameter can be used to provide template wide globals. These variables are available in the context at render time. :type globals: dict|None :return: Template object :rtype: shuup.xtheme.engine.XthemeTemplate """ # Redirect to `get_or_select_template` to support live theme loading. return self.get_or_select_template(self._get_themed_template_names(name), parent=parent, globals=globals)
@internalcode
[docs] def get_or_select_template(self, template_name_or_list, parent=None, globals=None): """ Does a typecheck and dispatches to :meth:`select_template` or :meth:`get_template`. :param template_name_or_list: Template name or list :type template_name_or_list: str|Iterable[str] :param parent: If the `parent` parameter is not `None`, :meth:`join_path` is called to get the real template name before loading. :type parent: str|None :param globals: The `globals` parameter can be used to provide template wide globals. These variables are available in the context at render time. :return: Template object :rtype: shuup.xtheme.engine.XthemeTemplate """ # Overridden to redirect calls to super. if isinstance(template_name_or_list, six.string_types): return super(XthemeEnvironment, self).get_template(template_name_or_list, parent, globals) elif isinstance(template_name_or_list, Template): return template_name_or_list return super(XthemeEnvironment, self).select_template(template_name_or_list, parent, globals)
def _get_themed_template_names(self, name): """ Get theme-prefixed paths for the given template name. For instance, if the template_dir or identifier of the current theme is `mystery` and we're looking up `shuup/front/bar.jinja`, we'll look at `mystery/shuup/front/bar.jinja`, finally at `shuup/front/bar.jinja`. Mystery theme also can define default template dir let's say `pony`. In this scenario we're looking up `shuup/front/bar.jinja` from `mystery/shuup/front/bar.jinja` then at `pony/shuup/front/bar.jinja` and finally at the default `shuup/front/bar.jinja`. :param name: Template name :type name: str :return: A template name or a list thereof :rtype: str|list[str] """ if name.startswith("shuup/admin"): # Ignore the admin. return name # we strongly depend on the XthemMiddleware as it should set the current theme # for this thread based on the request it processes theme = get_middleware_current_theme() if not theme: return name theme_template = "%s/%s" % ((theme.template_dir or theme.identifier), name) default_template = ("%s/%s" % (theme.default_template_dir, name)) if theme.default_template_dir else None return [theme_template, default_template, name] if default_template else [theme_template, name]