Source code for pyams_viewlet.viewlet

#
# 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.
#

"""PyAMS_viewlet.viewlet module

This module provides base content providers and viewlets classes, as well as a decorators
which can be used instead of ZCML declarations to register content providers and viewlets.
"""

import logging

import venusian
from pyramid.exceptions import ConfigurationError
from pyramid.interfaces import IRequest, IView
from zope.contentprovider.interfaces import IContentProvider
from zope.interface import Interface, implementer

from pyams_template.template import get_view_template
from pyams_viewlet.interfaces import IViewlet, IViewletManager

__docformat__ = 'restructuredtext'


LOGGER = logging.getLogger('PyAMS (viewlet)')


[docs]@implementer(IContentProvider) class EmptyContentProvider: """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()
[docs] def update(self): """See `IContentProvider` interface"""
[docs] def render(self): # pylint: disable=no-self-use """See `IContentProvider` interface""" return ''
[docs]class BaseContentProvider(EmptyContentProvider): """Base template based content provider""" resources = ()
[docs] def update(self): for resource in self.resources: resource.need()
render = get_view_template()
[docs]@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
[docs]class contentprovider_config: # pylint: disable=invalid-name """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 content provider") 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, obj): # pylint: disable=unused-argument cdict = { '__name__': settings.get('name'), '__module__': obj.__module__ } if 'permission' in settings: settings['permission'] = settings.get('permission') bases = (obj,) if not IContentProvider.implementedBy(obj): # pylint: disable=no-value-for-parameter 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))) registry = settings.get('registry') if registry is None: config = context.config.with_package(info.module) # pylint: disable=no-member registry = config.registry 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': # pylint: disable=no-member # 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 # pylint: disable=no-member return wrapped
[docs]@implementer(IViewlet) class EmptyViewlet: """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
[docs] def update(self): """See `IContentProvider` interface"""
[docs] def render(self): # pylint: disable=no-self-use """See `IContentProvider` interface""" return ''
[docs]class Viewlet(EmptyViewlet): """Viewlet adapter class used in meta directive as a mixin class.""" render = get_view_template()
[docs]class viewlet_config: # pylint: disable=invalid-name """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, obj): # pylint: disable=unused-argument cdict = { '__name__': settings.get('name'), '__module__': obj.__module__ } if 'permission' in settings: cdict['permission'] = settings.get('permission') if 'weight' in settings: cdict['weight'] = settings.get('weight') bases = (obj,) if not IViewlet.implementedBy(obj): # pylint: disable=no-value-for-parameter 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))) registry = settings.get('registry') if registry is None: config = context.config.with_package(info.module) # pylint: disable=no-member registry = config.registry 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': # pylint: disable=no-member # 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 # pylint: disable=no-member return wrapped