diff -r 000000000000 -r 7bb070e90138 src/pyams_skin/metas.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_skin/metas.py Wed Dec 05 13:13:47 2018 +0100 @@ -0,0 +1,199 @@ +# +# Copyright (c) 2008-2015 Thierry Florac +# 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' + +from pyramid.interfaces import IRequest +from zope.interface import Interface, implementer + +from pyams_i18n.interfaces import II18n +from pyams_skin.interfaces.configuration import IConfiguration +from pyams_skin.interfaces.extension import IGoogleAnalyticsInfo +from pyams_skin.interfaces.metas import IHTMLContentMetas, IMetaHeader +from pyams_utils.adapter import ContextRequestViewAdapter, adapter_config +from pyams_utils.interfaces.site import ISiteRoot +from pyams_utils.interfaces.tales import ITALESExtension + + +# +# 'metas' TALES extension +# + +@adapter_config(name='metas', context=(Interface, IRequest, Interface), provides=ITALESExtension) +class MetasTalesExtension(ContextRequestViewAdapter): + """extension:metas TALES extension""" + + def render(self, context=None): + if context is None: + context = self.context + result = [] + for name, adapter in sorted(self.request.registry.getAdapters((context, self.request, self.view), + IHTMLContentMetas), + key=lambda x: getattr(x[1], 'order', 9999)): + result.extend([meta.render() for meta in adapter.get_metas()]) + return '\n\t'.join(result) + + +# +# Custom metas headers +# + +@implementer(IMetaHeader) +class HTMLTagMeta(object): + """HTML tag meta header""" + + def __init__(self, tag, content, **attrs): + self.tag = tag + self.content = content + self.attrs = attrs + + def render(self): + return '''<{tag} {attrs}>{content}'''.format(tag=self.tag, + attrs=' '.join(('{0}="{1}"'.format(*value) for value in + self.attrs.items())), + content=self.content) + + +@implementer(IMetaHeader) +class HTTPEquivMeta(object): + """HTTP-Equiv meta header""" + + def __init__(self, http_equiv, value): + self.http_equiv = http_equiv + self.value = value + + def render(self): + return ''''''.format(http_equiv=self.http_equiv, + value=self.value) + + +@implementer(IMetaHeader) +class ValueMeta(object): + """Basic value meta header""" + + def __init__(self, name, value): + self.name = name + self.value = value + + def render(self): + return ''''''.format(name=self.name, + value=self.value) + + +@implementer(IMetaHeader) +class ContentMeta(object): + """Content meta header""" + + def __init__(self, name, value): + self.name = name + self.value = value + + def render(self): + return ''''''.format(name=self.name, + value=self.value) + + +@implementer(IMetaHeader) +class PropertyMeta(object): + """Property meta header""" + + def __init__(self, property, value): + self.property = property + self.value = value + + def render(self): + return ''''''.format(property=self.property, + value=self.value) + + +@implementer(IMetaHeader) +class LinkMeta(object): + """Link meta header""" + + def __init__(self, rel, type, href): + self.rel = rel + self.type = type + self.href = href + + def render(self): + return ''''''.format(rel=self.rel, + type=self.type, + href=self.href) + + +# +# Default metas headers +# + +@adapter_config(name='layout', context=(Interface, Interface, Interface), provides=IHTMLContentMetas) +class LayoutMetasAdapter(ContextRequestViewAdapter): + """Basic layout metas adapter""" + + order = -1 + + @staticmethod + def get_metas(): + yield HTTPEquivMeta('X-UA-Compatible', 'IE=edge,chrome=1') + yield ContentMeta('HandheldFriendly', 'True') + yield ContentMeta('viewport', 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no') + + +@adapter_config(name='title', context=(Interface, Interface, Interface), provides=IHTMLContentMetas) +class TitleMetasAdapter(ContextRequestViewAdapter): + """Title metas adapter""" + + order = 1 + + def get_metas(self): + title = II18n(self.context).query_attribute('title', request=self.request) + yield HTMLTagMeta('title', title) + + +@adapter_config(name='title', context=(ISiteRoot, Interface, Interface), provides=IHTMLContentMetas) +class SiteRootTitleMetasAdapter(ContextRequestViewAdapter): + """Site root title metas adapter""" + + order = 1 + + def get_metas(self): + config = IConfiguration(self.context) + yield HTMLTagMeta('title', config.title) + yield ContentMeta('description', config.description) + + +@adapter_config(name='content-type', context=(Interface, Interface, Interface), provides=IHTMLContentMetas) +class ContentTypeMetasAdapter(ContextRequestViewAdapter): + """Content-type metas adapter""" + + order = 10 + + @staticmethod + def get_metas(): + yield HTTPEquivMeta('Content-Type', 'text/html; charset=utf-8') + yield ValueMeta('charset', 'utf-8') + + +@adapter_config(name='analytics', context=(Interface, Interface, Interface), provides=IHTMLContentMetas) +class VerificationCodeMetasAdapter(ContextRequestViewAdapter): + """Google verification code metas adapter""" + + order = 20 + + def __new__(cls, context, request, view): + info = IGoogleAnalyticsInfo(request.root) + if not info.verification_code: + return None + return ContextRequestViewAdapter.__new__(cls) + + def get_metas(self): + info = IGoogleAnalyticsInfo(self.request.root) + yield ContentMeta('google-site-verification', info.verification_code)