diff -r 000000000000 -r bb4aabe07487 src/pyams_skin/skin.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_skin/skin.py Thu Feb 19 10:59:00 2015 +0100 @@ -0,0 +1,100 @@ +# +# Copyright (c) 2008-2015 Thierry Florac +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# + +__docformat__ = 'restructuredtext' + +# import standard library + +# import interfaces +from pyams_skin.interfaces import ISkin, ISkinnable, IUserSkinnable, SkinChangedEvent +from pyams_skin.layer import IBaseLayer, IPyAMSLayer +from pyams_utils.interfaces.site import ISiteRoot +from zope.traversing.interfaces import IBeforeTraverseEvent + +# import packages +from pyams_utils.registry import utility_config +from pyramid.events import subscriber +from pyramid.threadlocal import get_current_request +from pyramid_zope_request import PyramidPublisherRequest +from zope.interface import implementer, directlyProvidedBy, directlyProvides +from zope.schema.fieldproperty import FieldProperty + +from pyams_skin import _ + + +@utility_config(name='PyAMS default skin', provides=ISkin) +class PyAMSSkin(object): + """PyAMS default skin""" + + label = _("Default PyAMS skin") + layer = IPyAMSLayer + + +@implementer(ISkinnable) +class SkinnableContent(object): + """Skinnable content base class""" + + def get_skin(self): + """Get current skin""" + raise NotImplementedError("SkinnableContent subclasses must implement `get_skin` method") + + +@implementer(IUserSkinnable) +class UserSkinnableContent(SkinnableContent): + """User skinnable content base class""" + + skin = FieldProperty(IUserSkinnable['skin']) + + def get_skin(self, request=None): + skin = self.skin + if skin is not None: + if request is None: + request = get_current_request() + return request.registry.queryUtility(ISkin, skin) + + +@subscriber(IBeforeTraverseEvent, context_selector=ISkinnable) +def handle_content_skin(event): + """Apply skin when traversing skinnable object""" + request = event.request + skin = event.object.get_skin(request) + if skin is not None: + apply_skin(request, skin) + + +@subscriber(IBeforeTraverseEvent, context_selector=ISiteRoot) +def handle_root_skin(event): + """Apply skin when traversing site root""" + if not ISkinnable.providedBy(event.object): + apply_skin(event.request, PyAMSSkin) + + +def apply_skin(request, skin): + """Apply given skin to request""" + + def _apply(request, skin): + ifaces = [iface for iface in directlyProvidedBy(request) + if not issubclass(iface, IBaseLayer)] + # Add the new skin. + if isinstance(skin, str): + skin = request.registry.queryUtility(ISkin, skin) + if skin is not None: + ifaces.append(skin.layer) + directlyProvides(request, *ifaces) + + _apply(request, skin) + if isinstance(request, PyramidPublisherRequest): + request = request._request + _apply(request, skin) + else: + request.registry.notify(SkinChangedEvent(request)) + request.annotations['__skin__'] = skin