src/pyams_template/template.py
changeset 0 31ded33115d7
child 4 d9dc2f58c72b
equal deleted inserted replaced
-1:000000000000 0:31ded33115d7
       
     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 inspect
       
    18 import os
       
    19 import venusian
       
    20 
       
    21 # import interfaces
       
    22 from pyams_template.interfaces import IPageTemplate, IContentTemplate, ILayoutTemplate
       
    23 from pyramid.interfaces import IRequest
       
    24 from pyramid_chameleon.interfaces import IChameleonTranslate
       
    25 
       
    26 # import packages
       
    27 from pyramid.exceptions import ConfigurationError
       
    28 from pyramid_chameleon.zpt import PyramidPageTemplateFile
       
    29 from zope.component import queryUtility
       
    30 from zope.interface import directlyProvides
       
    31 
       
    32 
       
    33 configuration_settings = {}
       
    34 
       
    35 
       
    36 class TemplateFactory(object):
       
    37     """Template factory."""
       
    38 
       
    39     template = None
       
    40 
       
    41     def __init__(self, filename, contentType, macro=None):
       
    42         self.contentType = contentType
       
    43         self.template = PyramidPageTemplateFile(filename,
       
    44                                                 content_type=contentType,
       
    45                                                 macro=macro,
       
    46                                                 auto_reload=configuration_settings.get('reload_templates', False),
       
    47                                                 debug=configuration_settings.get('debug_templates', False),
       
    48                                                 translate=queryUtility(IChameleonTranslate))
       
    49         self.macro = self.template.macro = macro
       
    50 
       
    51     def __call__(self, view, request, context=None):
       
    52         return self.template
       
    53 
       
    54 
       
    55 class BoundViewTemplate(object):
       
    56     def __init__(self, pt, ob):
       
    57         object.__setattr__(self, 'im_func', pt)
       
    58         object.__setattr__(self, 'im_self', ob)
       
    59 
       
    60     def __call__(self, *args, **kw):
       
    61         if self.im_self is None:
       
    62             im_self, args = args[0], args[1:]
       
    63         else:
       
    64             im_self = self.im_self
       
    65         return self.im_func(im_self, *args, **kw)
       
    66 
       
    67     def __setattr__(self, name, v):
       
    68         raise AttributeError("Can't set attribute", name)
       
    69 
       
    70     def __repr__(self):
       
    71         return "<BoundViewTemplate of %r>" % self.im_self
       
    72 
       
    73 
       
    74 class ViewTemplate(object):
       
    75     def __init__(self, provides=IPageTemplate, name=u''):
       
    76         self.provides = provides
       
    77         self.name = name
       
    78 
       
    79     def __call__(self, instance, *args, **keywords):
       
    80         registry = instance.request.registry
       
    81         template = registry.queryMultiAdapter((instance, instance.request, instance.context),
       
    82                                               self.provides, name=self.name)
       
    83         if template is None:
       
    84             template = registry.getMultiAdapter((instance, instance.request),
       
    85                                                 self.provides, name=self.name)
       
    86 
       
    87         keywords.update({'context': instance.context,
       
    88                          'request': instance.request,
       
    89                          'view': instance,
       
    90                          'translate': queryUtility(IChameleonTranslate)})
       
    91         return template(*args, **keywords)
       
    92 
       
    93     def __get__(self, instance, type):
       
    94         return BoundViewTemplate(self, instance)
       
    95 
       
    96 get_view_template = ViewTemplate
       
    97 
       
    98 
       
    99 class GetPageTemplate(ViewTemplate):
       
   100 
       
   101     def __init__(self, name=u''):
       
   102         self.provides = IContentTemplate
       
   103         self.name = name
       
   104 
       
   105 get_page_template = GetPageTemplate
       
   106 
       
   107 
       
   108 class GetLayoutTemplate(ViewTemplate):
       
   109 
       
   110     def __init__(self, name=u''):
       
   111         self.provides = ILayoutTemplate
       
   112         self.name = name
       
   113 
       
   114 get_layout_template = GetLayoutTemplate
       
   115 
       
   116 
       
   117 class template_config(object):
       
   118     """Class decorator used to declare a template"""
       
   119 
       
   120     venusian = venusian  # for testing injection
       
   121 
       
   122     def __init__(self, **settings):
       
   123         if 'for_' in settings:
       
   124             if settings.get('context') is None:
       
   125                 settings['context'] = settings['for_']
       
   126         self.__dict__.update(settings)
       
   127 
       
   128     def __call__(self, wrapped):
       
   129         settings = self.__dict__.copy()
       
   130 
       
   131         def callback(context, name, ob):
       
   132             template = os.path.join(os.path.dirname(inspect.getfile(inspect.getmodule(ob))),
       
   133                                     settings.get('template'))
       
   134             if not os.path.isfile(template):
       
   135                 raise ConfigurationError("No such file", template)
       
   136 
       
   137             contentType = settings.get('contentType', 'text/html')
       
   138             macro = settings.get('macro')
       
   139             factory = TemplateFactory(template, contentType, macro)
       
   140             provides = settings.get('provides', IContentTemplate)
       
   141             directlyProvides(factory, provides)
       
   142 
       
   143             config = context.config.with_package(info.module)
       
   144             config.registry.registerAdapter(factory,
       
   145                                             (ob, settings.get('layer', IRequest)),
       
   146                                             provides, settings.get('name', ''))
       
   147 
       
   148         info = self.venusian.attach(wrapped, callback, category='pyams_pagelet')
       
   149 
       
   150         if info.scope == 'class':
       
   151             # if the decorator was attached to a method in a class, or
       
   152             # otherwise executed at class scope, we need to set an
       
   153             # 'attr' into the settings if one isn't already in there
       
   154             if settings.get('attr') is None:
       
   155                 settings['attr'] = wrapped.__name__
       
   156 
       
   157         settings['_info'] = info.codeinfo  # fbo "action_method"
       
   158         return wrapped
       
   159 
       
   160 
       
   161 class layout_config(object):
       
   162     """Class decorator used to declare a template"""
       
   163 
       
   164     venusian = venusian  # for testing injection
       
   165 
       
   166     def __init__(self, **settings):
       
   167         if 'for_' in settings:
       
   168             if settings.get('context') is None:
       
   169                 settings['context'] = settings['for_']
       
   170         self.__dict__.update(settings)
       
   171 
       
   172     def __call__(self, wrapped):
       
   173         settings = self.__dict__.copy()
       
   174 
       
   175         def callback(context, name, ob):
       
   176             template = os.path.join(os.path.dirname(inspect.getfile(ob)), settings.get('template'))
       
   177             if not os.path.isfile(template):
       
   178                 raise ConfigurationError("No such file", template)
       
   179 
       
   180             contentType = settings.get('contentType', 'text/html')
       
   181             macro = settings.get('macro')
       
   182             factory = TemplateFactory(template, contentType, macro)
       
   183             provides = settings.get('provides', ILayoutTemplate)
       
   184             directlyProvides(factory, provides)
       
   185 
       
   186             config = context.config.with_package(info.module)
       
   187             config.registry.registerAdapter(factory,
       
   188                                             (ob, settings.get('layer', IRequest)),
       
   189                                             provides, settings.get('name', ''))
       
   190 
       
   191         info = self.venusian.attach(wrapped, callback, category='pyams_pagelet')
       
   192 
       
   193         if info.scope == 'class':
       
   194             # if the decorator was attached to a method in a class, or
       
   195             # otherwise executed at class scope, we need to set an
       
   196             # 'attr' into the settings if one isn't already in there
       
   197             if settings.get('attr') is None:
       
   198                 settings['attr'] = wrapped.__name__
       
   199 
       
   200         settings['_info'] = info.codeinfo  # fbo "action_method"
       
   201         return wrapped