src/pyams_pagelet/pagelet.py
changeset 0 44692d47182f
child 1 b90ce037f872
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_pagelet/pagelet.py	Thu Feb 19 10:54:13 2015 +0100
@@ -0,0 +1,150 @@
+#
+# Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net>
+# 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 packages
+import venusian
+
+# import interfaces
+from pyams_pagelet.interfaces import IPagelet, IPageletRenderer, PageletCreatedEvent
+from pyams_template.interfaces import IContentTemplate, ILayoutTemplate
+from pyramid.interfaces import IRequest
+from pyramid_chameleon.interfaces import IChameleonTranslate
+
+# import packages
+from pyams_utils.adapter import adapter_config
+from pyramid.httpexceptions import HTTPUnauthorized
+from pyramid.response import Response
+from zope.component import queryUtility
+from zope.interface import implementer, Interface
+
+
+REDIRECT_STATUS_CODES = (301, 302, 303)
+
+
+@implementer(IPagelet)
+class Pagelet(object):
+    """Content provider with layout support"""
+
+    template = None
+    layout = None
+    permission = None
+
+    def __init__(self, context, request):
+        self.context = context
+        self.request = request
+        if self.permission and not request.has_permission(self.permission):
+            raise HTTPUnauthorized('You are not authorized to access the page called `%s`.' %
+                                   request.view_name)
+        request.registry.notify(PageletCreatedEvent(self))
+
+    def update(self):
+        self.request.annotations['view'] = self
+
+    def render(self):
+        request = self.request
+        cdict = {'context': self.context,
+                 'request': request,
+                 'view': self,
+                 'translate': queryUtility(IChameleonTranslate)}
+        if self.template is None:
+            registry = request.registry
+            template = registry.queryMultiAdapter((self, request, self.context),
+                                                  IContentTemplate)
+            if template is None:
+                template = registry.getMultiAdapter((self, request), IContentTemplate)
+            return template(**cdict)
+        return self.template(**cdict)
+
+    def __call__(self, **kwargs):
+        """Call update and return layout template"""
+        self.update()
+        if self.request.response.status_code in REDIRECT_STATUS_CODES:
+            return ''
+
+        request = self.request
+        cdict = {'context': self.context,
+                 'request': request,
+                 'view': self,
+                 'translate': queryUtility(IChameleonTranslate)}
+        cdict.update(kwargs)
+        if self.layout is None:
+            registry = request.registry
+            layout = registry.queryMultiAdapter((self, request, self.context),
+                                                ILayoutTemplate)
+            if layout is None:
+                layout = registry.getMultiAdapter((self, request), ILayoutTemplate)
+            return Response(layout(**cdict))
+        return Response(self.layout(**cdict))
+
+
+@adapter_config(name='pagelet', context=(Interface, IRequest, IPagelet), provides=IPageletRenderer)
+class PageletRenderer(object):
+    """Pagelet renderer"""
+
+    def __init__(self, context, request, pagelet):
+        self.__updated = False
+        self.__parent__ = pagelet
+        self.context = context
+        self.request = request
+
+    def update(self):
+        pass
+
+    def render(self):
+        return self.__parent__.render()
+
+
+class pagelet_config(object):
+    """Function or class decorator used to declare a pagelet"""
+
+    venusian = venusian  # for testing injection
+
+    def __init__(self, **settings):
+        if 'for_' in settings:
+            if settings.get('context') is None:
+                settings['context'] = settings.pop('for_')
+        if 'layer' in settings:
+            settings['request_type'] = settings.pop('layer')
+        self.__dict__.update(settings)
+
+    def __call__(self, wrapped):
+        settings = self.__dict__.copy()
+        depth = settings.pop('_depth', 0)
+
+        def callback(context, name, ob):
+            cdict = {}
+            cdict['__name__'] = settings.get('name')
+            cdict['permission'] = settings.get('permission')
+            new_class = type(ob.__name__, (ob, Pagelet), cdict)
+
+            config = context.config.with_package(info.module)
+            config.registry.registerAdapter(new_class,
+                                            (settings.get('context', Interface),
+                                             settings.get('request_type', IRequest)),
+                                            IPagelet, settings.get('name'))
+            config.add_view(view=new_class, **settings)
+
+        info = self.venusian.attach(wrapped, callback, category='pyams_pagelet',
+                                    depth=depth + 1)
+
+        if info.scope == 'class':
+            # if the decorator was attached to a method in a class, or
+            # otherwise executed at class scope, we need to set an
+            # 'attr' into the settings if one isn't already in there
+            if settings.get('attr') is None:
+                settings['attr'] = wrapped.__name__
+
+        settings['_info'] = info.codeinfo  # fbo "action_method"
+        return wrapped