src/pyams_skin/skin.py
changeset 155 cd3ab32436f0
parent 0 bb4aabe07487
child 156 b8b688fc964e
--- a/src/pyams_skin/skin.py	Thu Oct 06 16:18:03 2016 +0200
+++ b/src/pyams_skin/skin.py	Mon Oct 10 11:32:08 2016 +0200
@@ -22,6 +22,7 @@
 
 # import packages
 from pyams_utils.registry import utility_config
+from pyams_utils.traversing import get_parent
 from pyramid.events import subscriber
 from pyramid.threadlocal import get_current_request
 from pyramid_zope_request import PyramidPublisherRequest
@@ -43,40 +44,48 @@
 class SkinnableContent(object):
     """Skinnable content base class"""
 
-    def get_skin(self):
-        """Get current skin"""
-        raise NotImplementedError("SkinnableContent subclasses must implement `get_skin` method")
+    _inherit_skin = FieldProperty(ISkinnable['inherit_skin'])
+
+    skin = FieldProperty(IUserSkinnable['skin'])
+
+    @property
+    def can_inherit_skin(self):
+        return get_parent(self, ISkinnable, allow_context=False) is not None
+
+    @property
+    def inherit_skin(self):
+        return self._inherit_skin if self.can_inherit_skin else False
+
+    @inherit_skin.setter
+    def inherit_skin(self, value):
+        if value and not self.can_inherit_skin:
+            value = False
+        self._inherit_skin = value
+
+    @property
+    def skin_parent(self):
+        if (not self._inherit_skin) and self.skin:
+            return self
+        parent = get_parent(self, ISkinnable)
+        if parent is not None:
+            return parent.skin_parent
+
+    def get_skin(self, request=None):
+        parent = self.skin_parent
+        if parent is self:
+            return self.skin
+        elif parent is not None:
+            skin = parent.skin
+            if skin is not None:
+                if request is None:
+                    request = get_current_request()
+                return request.registry.queryUtility(ISkin, skin)
 
 
 @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"""
@@ -98,3 +107,23 @@
     else:
         request.registry.notify(SkinChangedEvent(request))
         request.annotations['__skin__'] = skin
+
+
+@subscriber(IBeforeTraverseEvent, context_selector=ISkinnable)
+def handle_content_skin(event):
+    """Apply skin when traversing skinnable object"""
+    request = event.request
+    try:
+        skin = event.object.get_skin(request)
+    except NotImplementedError:
+        pass
+    else:
+        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)