|
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 from pyramid.interfaces import IRequest |
|
16 from zope.interface import Interface, implementer |
|
17 |
|
18 from pyams_i18n.interfaces import II18n |
|
19 from pyams_skin.interfaces.configuration import IConfiguration |
|
20 from pyams_skin.interfaces.extension import IGoogleAnalyticsInfo |
|
21 from pyams_skin.interfaces.metas import IHTMLContentMetas, IMetaHeader |
|
22 from pyams_utils.adapter import ContextRequestViewAdapter, adapter_config |
|
23 from pyams_utils.interfaces.site import ISiteRoot |
|
24 from pyams_utils.interfaces.tales import ITALESExtension |
|
25 |
|
26 |
|
27 # |
|
28 # 'metas' TALES extension |
|
29 # |
|
30 |
|
31 @adapter_config(name='metas', context=(Interface, IRequest, Interface), provides=ITALESExtension) |
|
32 class MetasTalesExtension(ContextRequestViewAdapter): |
|
33 """extension:metas TALES extension""" |
|
34 |
|
35 def render(self, context=None): |
|
36 if context is None: |
|
37 context = self.context |
|
38 result = [] |
|
39 for name, adapter in sorted(self.request.registry.getAdapters((context, self.request, self.view), |
|
40 IHTMLContentMetas), |
|
41 key=lambda x: getattr(x[1], 'order', 9999)): |
|
42 result.extend([meta.render() for meta in adapter.get_metas()]) |
|
43 return '\n\t'.join(result) |
|
44 |
|
45 |
|
46 # |
|
47 # Custom metas headers |
|
48 # |
|
49 |
|
50 @implementer(IMetaHeader) |
|
51 class HTMLTagMeta(object): |
|
52 """HTML tag meta header""" |
|
53 |
|
54 def __init__(self, tag, content, **attrs): |
|
55 self.tag = tag |
|
56 self.content = content |
|
57 self.attrs = attrs |
|
58 |
|
59 def render(self): |
|
60 return '''<{tag} {attrs}>{content}</{tag}>'''.format(tag=self.tag, |
|
61 attrs=' '.join(('{0}="{1}"'.format(*value) for value in |
|
62 self.attrs.items())), |
|
63 content=self.content) |
|
64 |
|
65 |
|
66 @implementer(IMetaHeader) |
|
67 class HTTPEquivMeta(object): |
|
68 """HTTP-Equiv meta header""" |
|
69 |
|
70 def __init__(self, http_equiv, value): |
|
71 self.http_equiv = http_equiv |
|
72 self.value = value |
|
73 |
|
74 def render(self): |
|
75 return '''<meta http-equiv="{http_equiv}" content="{value}" />'''.format(http_equiv=self.http_equiv, |
|
76 value=self.value) |
|
77 |
|
78 |
|
79 @implementer(IMetaHeader) |
|
80 class ValueMeta(object): |
|
81 """Basic value meta header""" |
|
82 |
|
83 def __init__(self, name, value): |
|
84 self.name = name |
|
85 self.value = value |
|
86 |
|
87 def render(self): |
|
88 return '''<meta {name}="{value}" />'''.format(name=self.name, |
|
89 value=self.value) |
|
90 |
|
91 |
|
92 @implementer(IMetaHeader) |
|
93 class ContentMeta(object): |
|
94 """Content meta header""" |
|
95 |
|
96 def __init__(self, name, value): |
|
97 self.name = name |
|
98 self.value = value |
|
99 |
|
100 def render(self): |
|
101 return '''<meta name="{name}" content="{value}" />'''.format(name=self.name, |
|
102 value=self.value) |
|
103 |
|
104 |
|
105 @implementer(IMetaHeader) |
|
106 class PropertyMeta(object): |
|
107 """Property meta header""" |
|
108 |
|
109 def __init__(self, property, value): |
|
110 self.property = property |
|
111 self.value = value |
|
112 |
|
113 def render(self): |
|
114 return '''<meta property="{property}" content="{value}" />'''.format(property=self.property, |
|
115 value=self.value) |
|
116 |
|
117 |
|
118 @implementer(IMetaHeader) |
|
119 class LinkMeta(object): |
|
120 """Link meta header""" |
|
121 |
|
122 def __init__(self, rel, type, href): |
|
123 self.rel = rel |
|
124 self.type = type |
|
125 self.href = href |
|
126 |
|
127 def render(self): |
|
128 return '''<link rel="{rel}" type="{type}" href="{href}" />'''.format(rel=self.rel, |
|
129 type=self.type, |
|
130 href=self.href) |
|
131 |
|
132 |
|
133 # |
|
134 # Default metas headers |
|
135 # |
|
136 |
|
137 @adapter_config(name='layout', context=(Interface, Interface, Interface), provides=IHTMLContentMetas) |
|
138 class LayoutMetasAdapter(ContextRequestViewAdapter): |
|
139 """Basic layout metas adapter""" |
|
140 |
|
141 order = -1 |
|
142 |
|
143 @staticmethod |
|
144 def get_metas(): |
|
145 yield HTTPEquivMeta('X-UA-Compatible', 'IE=edge,chrome=1') |
|
146 yield ContentMeta('HandheldFriendly', 'True') |
|
147 yield ContentMeta('viewport', 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no') |
|
148 |
|
149 |
|
150 @adapter_config(name='title', context=(Interface, Interface, Interface), provides=IHTMLContentMetas) |
|
151 class TitleMetasAdapter(ContextRequestViewAdapter): |
|
152 """Title metas adapter""" |
|
153 |
|
154 order = 1 |
|
155 |
|
156 def get_metas(self): |
|
157 title = II18n(self.context).query_attribute('title', request=self.request) |
|
158 yield HTMLTagMeta('title', title) |
|
159 |
|
160 |
|
161 @adapter_config(name='title', context=(ISiteRoot, Interface, Interface), provides=IHTMLContentMetas) |
|
162 class SiteRootTitleMetasAdapter(ContextRequestViewAdapter): |
|
163 """Site root title metas adapter""" |
|
164 |
|
165 order = 1 |
|
166 |
|
167 def get_metas(self): |
|
168 config = IConfiguration(self.context) |
|
169 yield HTMLTagMeta('title', config.title) |
|
170 yield ContentMeta('description', config.description) |
|
171 |
|
172 |
|
173 @adapter_config(name='content-type', context=(Interface, Interface, Interface), provides=IHTMLContentMetas) |
|
174 class ContentTypeMetasAdapter(ContextRequestViewAdapter): |
|
175 """Content-type metas adapter""" |
|
176 |
|
177 order = 10 |
|
178 |
|
179 @staticmethod |
|
180 def get_metas(): |
|
181 yield HTTPEquivMeta('Content-Type', 'text/html; charset=utf-8') |
|
182 yield ValueMeta('charset', 'utf-8') |
|
183 |
|
184 |
|
185 @adapter_config(name='analytics', context=(Interface, Interface, Interface), provides=IHTMLContentMetas) |
|
186 class VerificationCodeMetasAdapter(ContextRequestViewAdapter): |
|
187 """Google verification code metas adapter""" |
|
188 |
|
189 order = 20 |
|
190 |
|
191 def __new__(cls, context, request, view): |
|
192 info = IGoogleAnalyticsInfo(request.root) |
|
193 if not info.verification_code: |
|
194 return None |
|
195 return ContextRequestViewAdapter.__new__(cls) |
|
196 |
|
197 def get_metas(self): |
|
198 info = IGoogleAnalyticsInfo(self.request.root) |
|
199 yield ContentMeta('google-site-verification', info.verification_code) |