src/pyams_skin/skin.py
changeset 0 bb4aabe07487
child 155 cd3ab32436f0
equal deleted inserted replaced
-1:000000000000 0:bb4aabe07487
       
     1 #
       
     2 # Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net>
       
     3 # All Rights Reserved.
       
     4 #
       
     5 # This software is subject to the provisions of the Zope Public License,
       
     6 # Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
       
     7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
       
     8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
       
     9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
       
    10 # FOR A PARTICULAR PURPOSE.
       
    11 #
       
    12 
       
    13 __docformat__ = 'restructuredtext'
       
    14 
       
    15 # import standard library
       
    16 
       
    17 # import interfaces
       
    18 from pyams_skin.interfaces import ISkin, ISkinnable, IUserSkinnable, SkinChangedEvent
       
    19 from pyams_skin.layer import IBaseLayer, IPyAMSLayer
       
    20 from pyams_utils.interfaces.site import ISiteRoot
       
    21 from zope.traversing.interfaces import IBeforeTraverseEvent
       
    22 
       
    23 # import packages
       
    24 from pyams_utils.registry import utility_config
       
    25 from pyramid.events import subscriber
       
    26 from pyramid.threadlocal import get_current_request
       
    27 from pyramid_zope_request import PyramidPublisherRequest
       
    28 from zope.interface import implementer, directlyProvidedBy, directlyProvides
       
    29 from zope.schema.fieldproperty import FieldProperty
       
    30 
       
    31 from pyams_skin import _
       
    32 
       
    33 
       
    34 @utility_config(name='PyAMS default skin', provides=ISkin)
       
    35 class PyAMSSkin(object):
       
    36     """PyAMS default skin"""
       
    37 
       
    38     label = _("Default PyAMS skin")
       
    39     layer = IPyAMSLayer
       
    40 
       
    41 
       
    42 @implementer(ISkinnable)
       
    43 class SkinnableContent(object):
       
    44     """Skinnable content base class"""
       
    45 
       
    46     def get_skin(self):
       
    47         """Get current skin"""
       
    48         raise NotImplementedError("SkinnableContent subclasses must implement `get_skin` method")
       
    49 
       
    50 
       
    51 @implementer(IUserSkinnable)
       
    52 class UserSkinnableContent(SkinnableContent):
       
    53     """User skinnable content base class"""
       
    54 
       
    55     skin = FieldProperty(IUserSkinnable['skin'])
       
    56 
       
    57     def get_skin(self, request=None):
       
    58         skin = self.skin
       
    59         if skin is not None:
       
    60             if request is None:
       
    61                 request = get_current_request()
       
    62             return request.registry.queryUtility(ISkin, skin)
       
    63 
       
    64 
       
    65 @subscriber(IBeforeTraverseEvent, context_selector=ISkinnable)
       
    66 def handle_content_skin(event):
       
    67     """Apply skin when traversing skinnable object"""
       
    68     request = event.request
       
    69     skin = event.object.get_skin(request)
       
    70     if skin is not None:
       
    71         apply_skin(request, skin)
       
    72 
       
    73 
       
    74 @subscriber(IBeforeTraverseEvent, context_selector=ISiteRoot)
       
    75 def handle_root_skin(event):
       
    76     """Apply skin when traversing site root"""
       
    77     if not ISkinnable.providedBy(event.object):
       
    78         apply_skin(event.request, PyAMSSkin)
       
    79 
       
    80 
       
    81 def apply_skin(request, skin):
       
    82     """Apply given skin to request"""
       
    83 
       
    84     def _apply(request, skin):
       
    85         ifaces = [iface for iface in directlyProvidedBy(request)
       
    86                   if not issubclass(iface, IBaseLayer)]
       
    87         # Add the new skin.
       
    88         if isinstance(skin, str):
       
    89             skin = request.registry.queryUtility(ISkin, skin)
       
    90         if skin is not None:
       
    91             ifaces.append(skin.layer)
       
    92             directlyProvides(request, *ifaces)
       
    93 
       
    94     _apply(request, skin)
       
    95     if isinstance(request, PyramidPublisherRequest):
       
    96         request = request._request
       
    97         _apply(request, skin)
       
    98     else:
       
    99         request.registry.notify(SkinChangedEvent(request))
       
   100         request.annotations['__skin__'] = skin