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