src/pyams_viewlet/viewlet.py
changeset 29 6ab01534cc92
child 37 38ec13042956
equal deleted inserted replaced
-1:000000000000 29:6ab01534cc92
       
     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 
       
    16 # import standard packages
       
    17 import logging
       
    18 logger = logging.getLogger('PyAMS (viewlet)')
       
    19 
       
    20 import venusian
       
    21 
       
    22 # import interfaces
       
    23 from pyams_viewlet.interfaces import IViewlet, IViewletManager
       
    24 from pyramid.interfaces import IRequest, IView
       
    25 from zope.contentprovider.interfaces import IContentProvider
       
    26 
       
    27 # import packages
       
    28 from pyams_template.template import get_view_template
       
    29 from pyramid.exceptions import ConfigurationError
       
    30 from zope.interface import implementer, Interface
       
    31 
       
    32 
       
    33 @implementer(IContentProvider)
       
    34 class EmptyContentProvider(object):
       
    35     """Empty content provider base class"""
       
    36 
       
    37     permission = None
       
    38 
       
    39     def __init__(self, context, request):
       
    40         self.context = context
       
    41         self.request = request
       
    42 
       
    43     def __call__(self):
       
    44         if self.permission and not self.request.has_permission(self.permission, context=self.context):
       
    45             return ''
       
    46         self.update()
       
    47         return self.render()
       
    48 
       
    49     def update(self):
       
    50         pass
       
    51 
       
    52     def render(self):
       
    53         return ''
       
    54 
       
    55 
       
    56 class BaseContentProvider(EmptyContentProvider):
       
    57     """Base template based content provider"""
       
    58 
       
    59     render = get_view_template()
       
    60 
       
    61 
       
    62 @implementer(IContentProvider)
       
    63 class ViewContentProvider(BaseContentProvider):
       
    64     """Template based content provider"""
       
    65 
       
    66     def __init__(self, context, request, view):
       
    67         super(ViewContentProvider, self).__init__(context, request)
       
    68         self.view = self.__parent__ = view
       
    69 
       
    70 
       
    71 class contentprovider_config(object):
       
    72     """Class decorator used to declare a content provider
       
    73 
       
    74     You can provide same arguments as in 'viewlet' ZCML directive:
       
    75     @name = name of the viewlet; may be unique for a given viewlet manager
       
    76     @view = the view class or interface for which viewlet is displayed
       
    77     @for = the context class or interface for which viewlet is displayed
       
    78     @permission = name of a permission required to display the viewlet
       
    79     @layer = request interface required to display the viewlet
       
    80     """
       
    81 
       
    82     venusian = venusian  # for testing injection
       
    83 
       
    84     def __init__(self, **settings):
       
    85         if not settings.get('name'):
       
    86             raise ConfigurationError("You must provide a name for a Viewlet")
       
    87         if 'for_' in settings:
       
    88             if settings.get('context') is None:
       
    89                 settings['context'] = settings['for_']
       
    90         self.__dict__.update(settings)
       
    91 
       
    92     def __call__(self, wrapped):
       
    93         settings = self.__dict__.copy()
       
    94 
       
    95         def callback(context, name, ob):
       
    96             cdict = {
       
    97                 '__name__': settings.get('name'),
       
    98                 '__module__': ob.__module__
       
    99             }
       
   100             if 'permission' in settings:
       
   101                 settings['permission'] = settings.get('permission')
       
   102 
       
   103             bases = (ob,)
       
   104             if not IContentProvider.implementedBy(ob):
       
   105                 bases = bases + (ViewContentProvider,)
       
   106             new_class = type('<ViewContentProvider %s>' % settings.get('name'), bases, cdict)
       
   107 
       
   108             logger.debug("Registering content provider {0} ({1})".format(settings.get('name'),
       
   109                                                                          str(new_class)))
       
   110             config = context.config.with_package(info.module)
       
   111             config.registry.registerAdapter(new_class,
       
   112                                             (settings.get('context', Interface),
       
   113                                              settings.get('layer', IRequest),
       
   114                                              settings.get('view', IView)),
       
   115                                             IContentProvider, settings.get('name'))
       
   116 
       
   117         info = self.venusian.attach(wrapped, callback, category='pyams_viewlet')
       
   118 
       
   119         if info.scope == 'class':
       
   120             # if the decorator was attached to a method in a class, or
       
   121             # otherwise executed at class scope, we need to set an
       
   122             # 'attr' into the settings if one isn't already in there
       
   123             if settings.get('attr') is None:
       
   124                 settings['attr'] = wrapped.__name__
       
   125 
       
   126         settings['_info'] = info.codeinfo  # fbo "action_method"
       
   127         return wrapped
       
   128 
       
   129 
       
   130 @implementer(IViewlet)
       
   131 class EmptyViewlet(object):
       
   132     """Empty viewlet base class"""
       
   133 
       
   134     permission = None
       
   135 
       
   136     def __init__(self, context, request, view, manager):
       
   137         self.context = context
       
   138         self.request = request
       
   139         self.__parent__ = view
       
   140         self.manager = manager
       
   141 
       
   142     def update(self):
       
   143         pass
       
   144 
       
   145     def render(self):
       
   146         return ''
       
   147 
       
   148 
       
   149 class Viewlet(EmptyViewlet):
       
   150     """Viewlet adapter class used in meta directive as a mixin class."""
       
   151 
       
   152     render = get_view_template()
       
   153 
       
   154 
       
   155 class viewlet_config(object):
       
   156     """Class decorator used to declare a viewlet
       
   157 
       
   158     You can provide same arguments as in 'viewlet' ZCML directive:
       
   159     @name = name of the viewlet; may be unique for a given viewlet manager
       
   160     @manager = manager class or interface holding the viewlet
       
   161     @view = the view class or interface for which viewlet is displayed
       
   162     @for = the context class or interface for which viewlet is displayed
       
   163     @permission = name of a permission required to display the viewlet
       
   164     @layer = request interface required to display the viewlet
       
   165     @weight = weight of the viewlet when using a weight ordered viewlet manager
       
   166     """
       
   167 
       
   168     venusian = venusian  # for testing injection
       
   169 
       
   170     def __init__(self, **settings):
       
   171         if not settings.get('name'):
       
   172             raise ConfigurationError("You must provide a name for a Viewlet")
       
   173         if 'for_' in settings:
       
   174             if settings.get('context') is None:
       
   175                 settings['context'] = settings['for_']
       
   176         self.__dict__.update(settings)
       
   177 
       
   178     def __call__(self, wrapped):
       
   179         settings = self.__dict__.copy()
       
   180 
       
   181         def callback(context, name, ob):
       
   182             cdict = {
       
   183                 '__name__': settings.get('name'),
       
   184                 '__module__': ob.__module__
       
   185             }
       
   186             if 'permission' in settings:
       
   187                 cdict['permission'] = settings.get('permission')
       
   188             if 'weight' in settings:
       
   189                 cdict['weight'] = settings.get('weight')
       
   190 
       
   191             bases = (ob,)
       
   192             if not IViewlet.implementedBy(ob):
       
   193                 bases = bases + (Viewlet,)
       
   194             new_class = type('<Viewlet %s>' % settings.get('name'), bases, cdict)
       
   195 
       
   196             logger.debug("Registering viewlet {0} ({1})".format(settings.get('name'),
       
   197                                                                 str(new_class)))
       
   198             config = context.config.with_package(info.module)
       
   199             config.registry.registerAdapter(new_class,
       
   200                                             (settings.get('context', Interface),
       
   201                                              settings.get('layer', IRequest),
       
   202                                              settings.get('view', IView),
       
   203                                              settings.get('manager', IViewletManager)),
       
   204                                             IViewlet, settings.get('name'))
       
   205 
       
   206         info = self.venusian.attach(wrapped, callback, category='pyams_viewlet')
       
   207 
       
   208         if info.scope == 'class':
       
   209             # if the decorator was attached to a method in a class, or
       
   210             # otherwise executed at class scope, we need to set an
       
   211             # 'attr' into the settings if one isn't already in there
       
   212             if settings.get('attr') is None:
       
   213                 settings['attr'] = wrapped.__name__
       
   214 
       
   215         settings['_info'] = info.codeinfo  # fbo "action_method"
       
   216         return wrapped