--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_viewlet/viewlet.py Wed Dec 05 13:23:08 2018 +0100
@@ -0,0 +1,216 @@
+#
+# 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 logging
+logger = logging.getLogger('PyAMS (viewlet)')
+
+import venusian
+
+# import interfaces
+from pyams_viewlet.interfaces import IViewlet, IViewletManager
+from pyramid.interfaces import IRequest, IView
+from zope.contentprovider.interfaces import IContentProvider
+
+# import packages
+from pyams_template.template import get_view_template
+from pyramid.exceptions import ConfigurationError
+from zope.interface import implementer, Interface
+
+
+@implementer(IContentProvider)
+class EmptyContentProvider(object):
+ """Empty content provider base class"""
+
+ permission = None
+
+ def __init__(self, context, request):
+ self.context = context
+ self.request = request
+
+ def __call__(self):
+ if self.permission and not self.request.has_permission(self.permission, context=self.context):
+ return ''
+ self.update()
+ return self.render()
+
+ def update(self):
+ pass
+
+ def render(self):
+ return ''
+
+
+class BaseContentProvider(EmptyContentProvider):
+ """Base template based content provider"""
+
+ render = get_view_template()
+
+
+@implementer(IContentProvider)
+class ViewContentProvider(BaseContentProvider):
+ """Template based content provider"""
+
+ def __init__(self, context, request, view):
+ super(ViewContentProvider, self).__init__(context, request)
+ self.view = self.__parent__ = view
+
+
+class contentprovider_config(object):
+ """Class decorator used to declare a content provider
+
+ You can provide same arguments as in 'viewlet' ZCML directive:
+ @name = name of the viewlet; may be unique for a given viewlet manager
+ @view = the view class or interface for which viewlet is displayed
+ @for = the context class or interface for which viewlet is displayed
+ @permission = name of a permission required to display the viewlet
+ @layer = request interface required to display the viewlet
+ """
+
+ venusian = venusian # for testing injection
+
+ def __init__(self, **settings):
+ if not settings.get('name'):
+ raise ConfigurationError("You must provide a name for a Viewlet")
+ if 'for_' in settings:
+ if settings.get('context') is None:
+ settings['context'] = settings['for_']
+ self.__dict__.update(settings)
+
+ def __call__(self, wrapped):
+ settings = self.__dict__.copy()
+
+ def callback(context, name, ob):
+ cdict = {
+ '__name__': settings.get('name'),
+ '__module__': ob.__module__
+ }
+ if 'permission' in settings:
+ settings['permission'] = settings.get('permission')
+
+ bases = (ob,)
+ if not IContentProvider.implementedBy(ob):
+ bases = bases + (ViewContentProvider,)
+ new_class = type('<ViewContentProvider %s>' % settings.get('name'), bases, cdict)
+
+ logger.debug("Registering content provider {0} ({1})".format(settings.get('name'),
+ str(new_class)))
+ config = context.config.with_package(info.module)
+ config.registry.registerAdapter(new_class,
+ (settings.get('context', Interface),
+ settings.get('layer', IRequest),
+ settings.get('view', IView)),
+ IContentProvider, settings.get('name'))
+
+ info = self.venusian.attach(wrapped, callback, category='pyams_viewlet')
+
+ 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
+
+
+@implementer(IViewlet)
+class EmptyViewlet(object):
+ """Empty viewlet base class"""
+
+ permission = None
+
+ def __init__(self, context, request, view, manager):
+ self.context = context
+ self.request = request
+ self.__parent__ = view
+ self.manager = manager
+
+ def update(self):
+ pass
+
+ def render(self):
+ return ''
+
+
+class Viewlet(EmptyViewlet):
+ """Viewlet adapter class used in meta directive as a mixin class."""
+
+ render = get_view_template()
+
+
+class viewlet_config(object):
+ """Class decorator used to declare a viewlet
+
+ You can provide same arguments as in 'viewlet' ZCML directive:
+ @name = name of the viewlet; may be unique for a given viewlet manager
+ @manager = manager class or interface holding the viewlet
+ @view = the view class or interface for which viewlet is displayed
+ @for = the context class or interface for which viewlet is displayed
+ @permission = name of a permission required to display the viewlet
+ @layer = request interface required to display the viewlet
+ @weight = weight of the viewlet when using a weight ordered viewlet manager
+ """
+
+ venusian = venusian # for testing injection
+
+ def __init__(self, **settings):
+ if not settings.get('name'):
+ raise ConfigurationError("You must provide a name for a Viewlet")
+ if 'for_' in settings:
+ if settings.get('context') is None:
+ settings['context'] = settings['for_']
+ self.__dict__.update(settings)
+
+ def __call__(self, wrapped):
+ settings = self.__dict__.copy()
+
+ def callback(context, name, ob):
+ cdict = {
+ '__name__': settings.get('name'),
+ '__module__': ob.__module__
+ }
+ if 'permission' in settings:
+ cdict['permission'] = settings.get('permission')
+ if 'weight' in settings:
+ cdict['weight'] = settings.get('weight')
+
+ bases = (ob,)
+ if not IViewlet.implementedBy(ob):
+ bases = bases + (Viewlet,)
+ new_class = type('<Viewlet %s>' % settings.get('name'), bases, cdict)
+
+ logger.debug("Registering viewlet {0} ({1})".format(settings.get('name'),
+ str(new_class)))
+ config = context.config.with_package(info.module)
+ config.registry.registerAdapter(new_class,
+ (settings.get('context', Interface),
+ settings.get('layer', IRequest),
+ settings.get('view', IView),
+ settings.get('manager', IViewletManager)),
+ IViewlet, settings.get('name'))
+
+ info = self.venusian.attach(wrapped, callback, category='pyams_viewlet')
+
+ 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