src/pyams_pagelet/pagelet.py
changeset 12 fc3542685741
parent 7 1cfafd51022f
equal deleted inserted replaced
11:bd5143e87b1d 12:fc3542685741
     8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
     9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
    10 # FOR A PARTICULAR PURPOSE.
    10 # FOR A PARTICULAR PURPOSE.
    11 #
    11 #
    12 
    12 
       
    13 """PyAMS_pagelet.pagelet module
       
    14 
       
    15 This module provides the core pagelet implementation, and a "pagelet_config" decorator which
       
    16 can be use to register pagelets instead of ZCML directives.
       
    17 """
       
    18 
       
    19 import logging
       
    20 
       
    21 import venusian
       
    22 from pyramid.httpexceptions import HTTPUnauthorized
       
    23 from pyramid.interfaces import IRequest
       
    24 from pyramid.response import Response
       
    25 from pyramid_chameleon.interfaces import IChameleonTranslate
       
    26 from zope.component import queryUtility
       
    27 from zope.interface import Interface, implementer
       
    28 
       
    29 from pyams_pagelet.interfaces import IPagelet, IPageletRenderer, PageletCreatedEvent
       
    30 from pyams_template.interfaces import IContentTemplate, ILayoutTemplate
       
    31 from pyams_utils.adapter import adapter_config
       
    32 
       
    33 
    13 __docformat__ = 'restructuredtext'
    34 __docformat__ = 'restructuredtext'
    14 
    35 
    15 
    36 LOGGER = logging.getLogger('PyAMS (pagelet)')
    16 # import standard packages
       
    17 import logging
       
    18 logger = logging.getLogger('PyAMS (pagelet)')
       
    19 
       
    20 import venusian
       
    21 
       
    22 # import interfaces
       
    23 from pyams_pagelet.interfaces import IPagelet, IPageletRenderer, PageletCreatedEvent
       
    24 from pyams_template.interfaces import IContentTemplate, ILayoutTemplate
       
    25 from pyramid.interfaces import IRequest
       
    26 from pyramid_chameleon.interfaces import IChameleonTranslate
       
    27 
       
    28 # import packages
       
    29 from pyams_utils.adapter import adapter_config
       
    30 from pyramid.httpexceptions import HTTPUnauthorized
       
    31 from pyramid.response import Response
       
    32 from zope.component import queryUtility
       
    33 from zope.interface import implementer, Interface
       
    34 
       
    35 
    37 
    36 REDIRECT_STATUS_CODES = (301, 302, 303)
    38 REDIRECT_STATUS_CODES = (301, 302, 303)
    37 
    39 
    38 
    40 
    39 @implementer(IPagelet)
    41 @implementer(IPagelet)
    40 class Pagelet(object):
    42 class Pagelet:
    41     """Content provider with layout support"""
    43     """Content provider with layout support"""
    42 
    44 
    43     template = None
    45     template = None
    44     layout = None
    46     layout = None
    45     permission = None
    47     permission = None
    51             raise HTTPUnauthorized('You are not authorized to access the page called `%s`.' %
    53             raise HTTPUnauthorized('You are not authorized to access the page called `%s`.' %
    52                                    request.view_name)
    54                                    request.view_name)
    53         request.registry.notify(PageletCreatedEvent(self))
    55         request.registry.notify(PageletCreatedEvent(self))
    54 
    56 
    55     def update(self):
    57     def update(self):
    56         self.request.annotations['view'] = self
    58         """See `zope.contentprovider.interfaces.IContentProvider`"""
       
    59         annotations = getattr(self.request, 'annotations', None)
       
    60         if annotations is not None:
       
    61             annotations['view'] = self
    57 
    62 
    58     def render(self):
    63     def render(self):
       
    64         """See `zope.contentprovider.interfaces.IContentProvider`"""
    59         request = self.request
    65         request = self.request
    60         cdict = {'context': self.context,
    66         cdict = {
    61                  'request': request,
    67             'context': self.context,
    62                  'view': self,
    68             'request': request,
    63                  'translate': queryUtility(IChameleonTranslate)}
    69             'view': self,
       
    70             'translate': queryUtility(IChameleonTranslate)
       
    71         }
    64         if self.template is None:
    72         if self.template is None:
    65             registry = request.registry
    73             registry = request.registry
    66             template = registry.queryMultiAdapter((self, request, self.context),
    74             template = registry.queryMultiAdapter((self, request, self.context),
    67                                                   IContentTemplate)
    75                                                   IContentTemplate)
    68             if template is None:
    76             if template is None:
    69                 template = registry.getMultiAdapter((self, request), IContentTemplate)
    77                 template = registry.getMultiAdapter((self, request), IContentTemplate)
    70             return template(**cdict)
    78             return template(**cdict)
    71         return self.template(**cdict)
    79         return self.template(**cdict)  # pylint: disable=not-callable
    72 
    80 
    73     def __call__(self, **kwargs):
    81     def __call__(self, **kwargs):
    74         """Call update and return layout template"""
    82         """Call update and return layout template"""
    75         self.update()
    83         self.update()
    76         if self.request.response.status_code in REDIRECT_STATUS_CODES:
    84         if self.request.response.status_code in REDIRECT_STATUS_CODES:
    77             return ''
    85             return ''
    78 
    86 
    79         request = self.request
    87         request = self.request
    80         cdict = {'context': self.context,
    88         cdict = {
    81                  'request': request,
    89             'context': self.context,
    82                  'view': self,
    90             'request': request,
    83                  'translate': queryUtility(IChameleonTranslate)}
    91             'view': self,
       
    92             'translate': queryUtility(IChameleonTranslate)
       
    93         }
    84         cdict.update(kwargs)
    94         cdict.update(kwargs)
    85         if self.layout is None:
    95         if self.layout is None:
    86             registry = request.registry
    96             registry = request.registry
    87             layout = registry.queryMultiAdapter((self, request, self.context),
    97             layout = registry.queryMultiAdapter((self, request, self.context),
    88                                                 ILayoutTemplate)
    98                                                 ILayoutTemplate)
    89             if layout is None:
    99             if layout is None:
    90                 layout = registry.getMultiAdapter((self, request), ILayoutTemplate)
   100                 layout = registry.getMultiAdapter((self, request), ILayoutTemplate)
    91             return Response(layout(**cdict))
   101             return Response(layout(**cdict))
    92         return Response(self.layout(**cdict))
   102         return Response(self.layout(**cdict))  # pylint: disable=not-callable
    93 
   103 
    94 
   104 
    95 @adapter_config(name='pagelet', context=(Interface, IRequest, IPagelet), provides=IPageletRenderer)
   105 @adapter_config(name='pagelet', context=(Interface, IRequest, IPagelet), provides=IPageletRenderer)
    96 class PageletRenderer(object):
   106 class PageletRenderer:
    97     """Pagelet renderer"""
   107     """Pagelet renderer"""
    98 
   108 
    99     def __init__(self, context, request, pagelet):
   109     def __init__(self, context, request, pagelet):
   100         self.__updated = False
   110         self.__updated = False
   101         self.__parent__ = pagelet
   111         self.__parent__ = pagelet
   102         self.context = context
   112         self.context = context
   103         self.request = request
   113         self.request = request
   104 
   114 
   105     def update(self):
   115     def update(self):
   106         pass
   116         """See `zope.contentprovider.interfaces.IContentProvider`"""
   107 
   117 
   108     def render(self):
   118     def render(self):
       
   119         """See `zope.contentprovider.interfaces.IContentProvider`"""
   109         return self.__parent__.render()
   120         return self.__parent__.render()
   110 
   121 
   111 
   122 
   112 class pagelet_config(object):
   123 class pagelet_config:  # pylint: disable=invalid-name
   113     """Function or class decorator used to declare a pagelet"""
   124     """Function or class decorator used to declare a pagelet"""
   114 
   125 
   115     venusian = venusian  # for testing injection
   126     venusian = venusian  # for testing injection
   116 
   127 
   117     def __init__(self, **settings):
   128     def __init__(self, **settings):
   124 
   135 
   125     def __call__(self, wrapped):
   136     def __call__(self, wrapped):
   126         settings = self.__dict__.copy()
   137         settings = self.__dict__.copy()
   127         depth = settings.pop('_depth', 0)
   138         depth = settings.pop('_depth', 0)
   128 
   139 
   129         def callback(context, name, ob):
   140         def callback(context, name, obj):  # pylint: disable=unused-argument
       
   141             """Venusian decorator callback"""
   130             cdict = {
   142             cdict = {
   131                 '__name__': settings.get('name'),
   143                 '__name__': settings.get('name'),
   132                 '__module__': ob.__module__,
   144                 '__module__': obj.__module__,
   133                 'permission': settings.get('permission')
   145                 'permission': settings.get('permission')
   134             }
   146             }
   135             new_class = type(ob.__name__, (ob, Pagelet), cdict)
   147             new_class = type(obj.__name__, (obj, Pagelet), cdict)
   136 
   148 
   137             config = context.config.with_package(info.module)
   149             LOGGER.debug('Registering pagelet view "{0}" for {1} ({2})'.format(
   138             logger.debug('Registering pagelet view "{0}" for {1} ({2})'.format(settings.get('name'),
   150                 settings.get('name'), str(settings.get('context', Interface)), str(new_class)))
   139                                                                                str(settings.get('context', Interface)),
   151             config = context.config.with_package(info.module)  # pylint: disable=no-member
   140                                                                                str(new_class)))
   152             registry = settings.get('registry') or config.registry
   141             config.registry.registerAdapter(new_class,
   153             registry.registerAdapter(new_class,
   142                                             (settings.get('context', Interface),
   154                                      (settings.get('context', Interface),
   143                                              settings.get('request_type', IRequest)),
   155                                       settings.get('request_type', IRequest)),
   144                                             IPagelet, settings.get('name'))
   156                                      IPagelet, settings.get('name'))
   145             config.add_view(view=new_class, **settings)
   157             config.add_view(view=new_class, **settings)
   146 
   158 
   147         info = self.venusian.attach(wrapped, callback, category='pyams_pagelet',
   159         info = self.venusian.attach(wrapped, callback, category='pyams_pagelet',
   148                                     depth=depth + 1)
   160                                     depth=depth + 1)
   149 
   161 
   150         if info.scope == 'class':
   162         if info.scope == 'class':  # pylint: disable=no-member
   151             # if the decorator was attached to a method in a class, or
   163             # if the decorator was attached to a method in a class, or
   152             # otherwise executed at class scope, we need to set an
   164             # otherwise executed at class scope, we need to set an
   153             # 'attr' into the settings if one isn't already in there
   165             # 'attr' into the settings if one isn't already in there
   154             if settings.get('attr') is None:
   166             if settings.get('attr') is None:
   155                 settings['attr'] = wrapped.__name__
   167                 settings['attr'] = wrapped.__name__
   156 
   168 
   157         settings['_info'] = info.codeinfo  # fbo "action_method"
   169         settings['_info'] = info.codeinfo  # pylint: disable=no-member
   158         return wrapped
   170         return wrapped