src/pyams_portal/portlet.py
changeset 0 6f99128c6d48
child 5 670b7956c689
equal deleted inserted replaced
-1:000000000000 0:6f99128c6d48
       
     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 library
       
    17 import logging
       
    18 logger = logging.getLogger('PyAMS (portal)')
       
    19 
       
    20 import venusian
       
    21 
       
    22 # import interfaces
       
    23 from pyams_portal.interfaces import IPortlet, IPortletRenderer, IPortletConfiguration, \
       
    24     IPortalPage, IPortletPreviewer
       
    25 from zope.schema.interfaces import IVocabularyFactory
       
    26 
       
    27 # import packages
       
    28 from persistent import Persistent
       
    29 from pyams_utils.request import check_request
       
    30 from pyams_viewlet.viewlet import ContentProvider
       
    31 from pyramid.exceptions import ConfigurationError
       
    32 from zope.container.contained import Contained
       
    33 from zope.interface import implementer, provider
       
    34 from zope.schema.fieldproperty import FieldProperty
       
    35 from zope.schema.vocabulary import getVocabularyRegistry, SimpleVocabulary, SimpleTerm
       
    36 
       
    37 
       
    38 @implementer(IPortletConfiguration)
       
    39 class PortletConfiguration(Persistent, Contained):
       
    40     """Portlet configuration"""
       
    41 
       
    42     template = None
       
    43     portlet_name = None
       
    44     slot_name = FieldProperty(IPortletConfiguration['slot_name'])
       
    45     position = FieldProperty(IPortletConfiguration['position'])
       
    46     visible = FieldProperty(IPortletConfiguration['visible'])
       
    47     _inherit_parent = FieldProperty(IPortletConfiguration['inherit_parent'])
       
    48 
       
    49     def __init__(self, portlet):
       
    50         self.portlet_name = portlet.name
       
    51 
       
    52     @property
       
    53     def can_inherit(self):
       
    54         return IPortalPage.providedBy(self.__parent__)
       
    55 
       
    56     @property
       
    57     def inherit_parent(self):
       
    58         return self._inherit_parent if self.can_inherit else False
       
    59 
       
    60     @inherit_parent.setter
       
    61     def inherit_parent(self, value):
       
    62         self._inherit_parent = value
       
    63 
       
    64 
       
    65 @implementer(IPortlet)
       
    66 class Portlet(object):
       
    67     """Base portlet content provider"""
       
    68 
       
    69     permission = FieldProperty(IPortlet['permission'])
       
    70 
       
    71     toolbar_image = None
       
    72     toolbar_css_class = 'fa fa-fw fa-2x fa-edit'
       
    73 
       
    74 
       
    75 @provider(IVocabularyFactory)
       
    76 class PortletVocabulary(SimpleVocabulary):
       
    77     """Portlet vocabulary"""
       
    78 
       
    79     def __init__(self, context):
       
    80         request = check_request()
       
    81         translate = request.localizer.translate
       
    82         utils = request.registry.getUtilitiesFor(IPortlet)
       
    83         terms = [SimpleTerm(name, title=translate(util.label))
       
    84                  for name, util in sorted(utils, key=lambda x: translate(x[1].label))]
       
    85         super(PortletVocabulary, self).__init__(terms)
       
    86 
       
    87 getVocabularyRegistry().register('PyAMS portal portlets', PortletVocabulary)
       
    88 
       
    89 
       
    90 class PortletContentProvider(ContentProvider):
       
    91     """Bae portlet content provider"""
       
    92 
       
    93     def __init__(self, context, request, view, portlet_config):
       
    94         super(PortletContentProvider, self).__init__(context, request, view)
       
    95         self.__parent__ = view
       
    96         self.configuration = portlet_config
       
    97         self.portlet = self.request.registry.getUtility(IPortlet, name=portlet_config.portlet_name)
       
    98 
       
    99     def __call__(self):
       
   100         if self.portlet.permission and not self.request.has_permission(self.portlet.permission):
       
   101             return ''
       
   102         self.update()
       
   103         return self.render()
       
   104 
       
   105 
       
   106 @implementer(IPortletPreviewer)
       
   107 class PortletPreviewer(PortletContentProvider):
       
   108     """Portlet previewer adapter"""
       
   109 
       
   110 
       
   111 @implementer(IPortletRenderer)
       
   112 class PortletRenderer(PortletContentProvider):
       
   113     """Portlet renderer adapter"""
       
   114 
       
   115 
       
   116 class portlet_config(object):
       
   117     """Class decorator used to declare a portlet"""
       
   118 
       
   119     venusian = venusian  # for testing injection
       
   120 
       
   121     def __init__(self, **settings):
       
   122         self.__dict__.update(settings)
       
   123 
       
   124     def __call__(self, wrapped):
       
   125         settings = self.__dict__.copy()
       
   126         depth = settings.pop('_depth', 0)
       
   127 
       
   128         def callback(context, name, ob):
       
   129             name = settings.get('name') or getattr(ob, 'name', None)
       
   130             if name is None:
       
   131                 raise ConfigurationError("You must provide a name for a portlet")
       
   132 
       
   133             permission = settings.get('permission')
       
   134             if permission is not None:
       
   135                 ob.permission = permission
       
   136 
       
   137             if type(ob) is type:
       
   138                 factory = ob
       
   139                 component = None
       
   140             else:
       
   141                 factory = None
       
   142                 component = ob
       
   143 
       
   144             config = context.config.with_package(info.module)
       
   145             logger.debug("Registering portlet {0} named '{1}'".format(str(component) if component else str(factory),
       
   146                                                                       name))
       
   147             config.registry.registerUtility(component=component, factory=factory,
       
   148                                             provided=IPortlet, name=name)
       
   149 
       
   150         info = self.venusian.attach(wrapped, callback, category='pyams_portal',
       
   151                                     depth=depth + 1)
       
   152         if info.scope == 'class':
       
   153             # if the decorator was attached to a method in a class, or
       
   154             # otherwise executed at class scope, we need to set an
       
   155             # 'attr' into the settings if one isn't already in there
       
   156             if settings.get('attr') is None:
       
   157                 settings['attr'] = wrapped.__name__
       
   158 
       
   159         settings['_info'] = info.codeinfo  # fbo "action_method"
       
   160         return wrapped