--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_template/template.py Thu Feb 19 10:29:02 2015 +0100
@@ -0,0 +1,201 @@
+#
+# 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 library
+import inspect
+import os
+import venusian
+
+# import interfaces
+from pyams_template.interfaces import IPageTemplate, IContentTemplate, ILayoutTemplate
+from pyramid.interfaces import IRequest
+from pyramid_chameleon.interfaces import IChameleonTranslate
+
+# import packages
+from pyramid.exceptions import ConfigurationError
+from pyramid_chameleon.zpt import PyramidPageTemplateFile
+from zope.component import queryUtility
+from zope.interface import directlyProvides
+
+
+configuration_settings = {}
+
+
+class TemplateFactory(object):
+ """Template factory."""
+
+ template = None
+
+ def __init__(self, filename, contentType, macro=None):
+ self.contentType = contentType
+ self.template = PyramidPageTemplateFile(filename,
+ content_type=contentType,
+ macro=macro,
+ auto_reload=configuration_settings.get('reload_templates', False),
+ debug=configuration_settings.get('debug_templates', False),
+ translate=queryUtility(IChameleonTranslate))
+ self.macro = self.template.macro = macro
+
+ def __call__(self, view, request, context=None):
+ return self.template
+
+
+class BoundViewTemplate(object):
+ def __init__(self, pt, ob):
+ object.__setattr__(self, 'im_func', pt)
+ object.__setattr__(self, 'im_self', ob)
+
+ def __call__(self, *args, **kw):
+ if self.im_self is None:
+ im_self, args = args[0], args[1:]
+ else:
+ im_self = self.im_self
+ return self.im_func(im_self, *args, **kw)
+
+ def __setattr__(self, name, v):
+ raise AttributeError("Can't set attribute", name)
+
+ def __repr__(self):
+ return "<BoundViewTemplate of %r>" % self.im_self
+
+
+class ViewTemplate(object):
+ def __init__(self, provides=IPageTemplate, name=u''):
+ self.provides = provides
+ self.name = name
+
+ def __call__(self, instance, *args, **keywords):
+ registry = instance.request.registry
+ template = registry.queryMultiAdapter((instance, instance.request, instance.context),
+ self.provides, name=self.name)
+ if template is None:
+ template = registry.getMultiAdapter((instance, instance.request),
+ self.provides, name=self.name)
+
+ keywords.update({'context': instance.context,
+ 'request': instance.request,
+ 'view': instance,
+ 'translate': queryUtility(IChameleonTranslate)})
+ return template(*args, **keywords)
+
+ def __get__(self, instance, type):
+ return BoundViewTemplate(self, instance)
+
+get_view_template = ViewTemplate
+
+
+class GetPageTemplate(ViewTemplate):
+
+ def __init__(self, name=u''):
+ self.provides = IContentTemplate
+ self.name = name
+
+get_page_template = GetPageTemplate
+
+
+class GetLayoutTemplate(ViewTemplate):
+
+ def __init__(self, name=u''):
+ self.provides = ILayoutTemplate
+ self.name = name
+
+get_layout_template = GetLayoutTemplate
+
+
+class template_config(object):
+ """Class decorator used to declare a template"""
+
+ venusian = venusian # for testing injection
+
+ def __init__(self, **settings):
+ 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):
+ template = os.path.join(os.path.dirname(inspect.getfile(inspect.getmodule(ob))),
+ settings.get('template'))
+ if not os.path.isfile(template):
+ raise ConfigurationError("No such file", template)
+
+ contentType = settings.get('contentType', 'text/html')
+ macro = settings.get('macro')
+ factory = TemplateFactory(template, contentType, macro)
+ provides = settings.get('provides', IContentTemplate)
+ directlyProvides(factory, provides)
+
+ config = context.config.with_package(info.module)
+ config.registry.registerAdapter(factory,
+ (ob, settings.get('layer', IRequest)),
+ provides, settings.get('name', ''))
+
+ info = self.venusian.attach(wrapped, callback, category='pyams_pagelet')
+
+ 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
+
+
+class layout_config(object):
+ """Class decorator used to declare a template"""
+
+ venusian = venusian # for testing injection
+
+ def __init__(self, **settings):
+ 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):
+ template = os.path.join(os.path.dirname(inspect.getfile(ob)), settings.get('template'))
+ if not os.path.isfile(template):
+ raise ConfigurationError("No such file", template)
+
+ contentType = settings.get('contentType', 'text/html')
+ macro = settings.get('macro')
+ factory = TemplateFactory(template, contentType, macro)
+ provides = settings.get('provides', ILayoutTemplate)
+ directlyProvides(factory, provides)
+
+ config = context.config.with_package(info.module)
+ config.registry.registerAdapter(factory,
+ (ob, settings.get('layer', IRequest)),
+ provides, settings.get('name', ''))
+
+ info = self.venusian.attach(wrapped, callback, category='pyams_pagelet')
+
+ 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