Dynamically switching back to Barceloneta based on some condition in Plone 6

I already this question some time ago but still haven't found a reasonable solution.

I need to switch the diazo theme from within my policy package based on some condition (specific URL, specific user or role etc) on the fly. Normal requests from anonymous site visitors should use the official site Diazo theme. Internal uses should use Barceloneta. I am not looking for solution the theme level. applyTheme() seems to change the theme for the whole site (theme control panel). I found some code in collective.lineagethemeselection but that seems to refer to the old skin system of CMF or is this approach still valid or what is the official way for dynamically switching the theme (from within a policy package)?

def set_theme_specific_layers(request, context, new_skin, current_skin):
    """mark the request with the browserlayer named new_skin
    and remove the one marked with current_skin

    we can't be sure plone.theme.layer.mark_layer is called after our traverser
    so we add the layer manually.
    """
    # check to see the skin has a IBrowserSkinType layer
    skin_iface = queryUtility(IBrowserSkinType, new_skin)
    if skin_iface:
        # remove theme specific layer of the current skin
        current_skin_iface = queryUtility(IBrowserSkinType, name=current_skin)

        if current_skin_iface is not None:
            noLongerProvides(request, current_skin_iface)

        # add the layer interface
        if skin_iface is not None and not skin_iface.providedBy(request):
            alsoProvides(request, skin_iface)


def apply_skin(obj, event):
    """Switch to the skin or theme selected for the child site.
    """
    if event.request.get('editskinswitched', False):
        # don't switch/set skin if collective.editskinswitcher has set
        # the edit skin already
        return

    alsoProvides(event.request, ILineageThemingLayer)
    wrapped_site = LineageSubsiteFacade(obj)
    skin = wrapped_site.default_skin
    if not skin:
        return
    current_skin = obj.getCurrentSkinName()
    obj.changeSkin(skin, event.request)
    set_theme_specific_layers(event.request, obj, skin, current_skin)

Can you make a new theme that consists of just one rules.xml which loads the other based on condition?

If something <xi include /path/to/barceloneta.xml

Ps: not sure what you mean by theme level

Please read questions carefully. I did not ask about theme-based solutions. Thanks.

This feels like the right spot. self.request might not be correct, but a quick zope.globalrequest.getRequest should help.

I achieved this Diazo theme variation in a project this way:

(PS.: tested in Plone 5.2.x only)

configure.zcml:

  <adapter
      factory=".themingpolicy.ThemingPolicy"
      for=".interfaces.MyAddOnLayer" />

themingpolicy.py:

from collections import namedtuple
from plone.app.theming.interfaces import IThemeSettings
from plone.app.theming.policy import ThemingPolicy as BaseThemePolicy
from zope.interface import implementer


_ThemeSettings = namedtuple(
    typename='ThemeSettings',
    field_names=[
        'enabled',
        'currentTheme',
        'rules',
        'absolutePrefix',
        'readNetwork',
        'hostnameBlacklist',
        'parameterExpressions',
        'doctype',
        'custom_css',
        'custom_css_timestamp',
    ],
)


@implementer(IThemeSettings)
class ThemeSettings(_ThemeSettings):
    pass


class ThemingPolicy(BaseThemePolicy):

    def getSettings(self):
        """Override: `BaseThemePolicy`."""
        if should_use_barceloneta():
            return self._make_settings(
                name='barceloneta',
                rules='/++theme++barceloneta/rules.xml',
                absolute_prefix='/++theme++barceloneta',

            )

        name = 'my.theme'
        prefix = f'/++theme++{name}'
        return self._make_settings(
            name=name,
            rules=f'{prefix}/rules.xml',
            absolute_prefix=prefix,
        )

    def getCacheStorage(self):
        """Override: `BaseThemePolicy`."""
        # Note: disabled this caching mechanism because I had some trouble with it.
        # Unfortunately I cannot remember the details.
        return {}

    def _make_settings(self, name, rules, absolute_prefix):
        return ThemeSettings(
            enabled=True,
            currentTheme=name,
            rules=rules,
            absolutePrefix=absolute_prefix,
            readNetwork=False,
            hostnameBlacklist=['localhost'],
            parameterExpressions={},
            doctype='<!DOCTYPE html>',
            custom_css=None,
            custom_css_timestamp=None,
        )