Mercurial > libervia-web
comparison libervia/pages/blog/view/page_meta.py @ 1216:b2d067339de3
python 3 port:
/!\ Python 3.6+ is now needed to use libervia
/!\ instability may occur and features may not be working anymore, this will improve with time
/!\ TxJSONRPC dependency has been removed
The same procedure as in backend has been applied (check backend commit ab2696e34d29 logs
for details). Removed now deprecated code (Pyjamas compiled browser part, legacy blog,
JSON RPC related code).
Adapted code to work without `html` and `themes` dirs.
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 13 Aug 2019 19:12:31 +0200 |
parents | 67ec22356457 |
children | 0f0c36992f3c |
comparison
equal
deleted
inserted
replaced
1215:f14ab8a25e8b | 1216:b2d067339de3 |
---|---|
1 #!/usr/bin/env python2.7 | 1 #!/usr/bin/env python3 |
2 # -*- coding: utf-8 -*- | 2 # -*- coding: utf-8 -*- |
3 import unicodedata | 3 import unicodedata |
4 import re | 4 import re |
5 import cgi | 5 import cgi |
6 from libervia.server.constants import Const as C | 6 from libervia.server.constants import Const as C |
17 from sat.core.log import getLogger | 17 from sat.core.log import getLogger |
18 | 18 |
19 log = getLogger(__name__) | 19 log = getLogger(__name__) |
20 | 20 |
21 """generic blog (with service/node provided)""" | 21 """generic blog (with service/node provided)""" |
22 name = u'blog_view' | 22 name = 'blog_view' |
23 template = u"blog/articles.html" | 23 template = "blog/articles.html" |
24 uri_handlers = {(u'pubsub', u'microblog'): 'microblog_uri'} | 24 uri_handlers = {('pubsub', 'microblog'): 'microblog_uri'} |
25 | 25 |
26 RE_TEXT_URL = re.compile(ur'[^a-zA-Z,_]+') | 26 RE_TEXT_URL = re.compile(r'[^a-zA-Z,_]+') |
27 TEXT_MAX_LEN = 60 | 27 TEXT_MAX_LEN = 60 |
28 TEXT_WORD_MIN_LENGHT = 4 | 28 TEXT_WORD_MIN_LENGHT = 4 |
29 URL_LIMIT_MARK = 90 # if canonical URL is longer than that, text will not be appended | 29 URL_LIMIT_MARK = 90 # if canonical URL is longer than that, text will not be appended |
30 | 30 |
31 | 31 |
32 def microblog_uri(self, uri_data): | 32 def microblog_uri(self, uri_data): |
33 args = [uri_data[u'path'], uri_data[u'node']] | 33 args = [uri_data['path'], uri_data['node']] |
34 if u'item' in uri_data: | 34 if 'item' in uri_data: |
35 args.extend([u'id', uri_data[u'item']]) | 35 args.extend(['id', uri_data['item']]) |
36 return self.getURL(*args) | 36 return self.getURL(*args) |
37 | 37 |
38 def parse_url(self, request): | 38 def parse_url(self, request): |
39 """URL is /[service]/[node]/[filter_keyword]/[item]|[other] | 39 """URL is /[service]/[node]/[filter_keyword]/[item]|[other] |
40 | 40 |
47 data = self.getRData(request) | 47 data = self.getRData(request) |
48 | 48 |
49 try: | 49 try: |
50 service = self.nextPath(request) | 50 service = self.nextPath(request) |
51 except IndexError: | 51 except IndexError: |
52 data['service'] = u'' | 52 data['service'] = '' |
53 else: | 53 else: |
54 try: | 54 try: |
55 data[u"service"] = jid.JID(service) | 55 data["service"] = jid.JID(service) |
56 except Exception: | 56 except Exception: |
57 log.warning(_(u"bad service entered: {}").format(service)) | 57 log.warning(_("bad service entered: {}").format(service)) |
58 self.pageError(request, C.HTTP_BAD_REQUEST) | 58 self.pageError(request, C.HTTP_BAD_REQUEST) |
59 | 59 |
60 try: | 60 try: |
61 node = self.nextPath(request) | 61 node = self.nextPath(request) |
62 except IndexError: | 62 except IndexError: |
63 node = u'@' | 63 node = '@' |
64 data['node'] = u'' if node == u'@' else node | 64 data['node'] = '' if node == '@' else node |
65 | 65 |
66 try: | 66 try: |
67 filter_kw = data['filter_keyword'] = self.nextPath(request) | 67 filter_kw = data['filter_keyword'] = self.nextPath(request) |
68 except IndexError: | 68 except IndexError: |
69 filter_kw = u'@' | 69 filter_kw = '@' |
70 else: | 70 else: |
71 if filter_kw == u'@': | 71 if filter_kw == '@': |
72 # No filter, this is used when a subpage is needed, notably Atom feed | 72 # No filter, this is used when a subpage is needed, notably Atom feed |
73 pass | 73 pass |
74 elif filter_kw == u'id': | 74 elif filter_kw == 'id': |
75 try: | 75 try: |
76 data[u'item'] = self.nextPath(request) | 76 data['item'] = self.nextPath(request) |
77 except IndexError: | 77 except IndexError: |
78 self.pageError(request, C.HTTP_BAD_REQUEST) | 78 self.pageError(request, C.HTTP_BAD_REQUEST) |
79 # we get one more argument in case text has been added to have a nice URL | 79 # we get one more argument in case text has been added to have a nice URL |
80 try: | 80 try: |
81 self.nextPath(request) | 81 self.nextPath(request) |
82 except IndexError: | 82 except IndexError: |
83 pass | 83 pass |
84 elif filter_kw == u'tag': | 84 elif filter_kw == 'tag': |
85 try: | 85 try: |
86 data[u'tag'] = self.nextPath(request) | 86 data['tag'] = self.nextPath(request) |
87 except IndexError: | 87 except IndexError: |
88 self.pageError(request, C.HTTP_BAD_REQUEST) | 88 self.pageError(request, C.HTTP_BAD_REQUEST) |
89 else: | 89 else: |
90 # invalid filter keyword | 90 # invalid filter keyword |
91 log.warning(_(u"invalid filter keyword: {filter_kw}").format( | 91 log.warning(_("invalid filter keyword: {filter_kw}").format( |
92 filter_kw=filter_kw)) | 92 filter_kw=filter_kw)) |
93 self.pageError(request, C.HTTP_BAD_REQUEST) | 93 self.pageError(request, C.HTTP_BAD_REQUEST) |
94 | 94 |
95 # if URL is parsed here, we'll have atom.xml available and we need to | 95 # if URL is parsed here, we'll have atom.xml available and we need to |
96 # add the link to the page | 96 # add the link to the page |
97 atom_url = self.getURLByPath( | 97 atom_url = self.getURLByPath( |
98 SubPage(u'blog_view'), | 98 SubPage('blog_view'), |
99 service, | 99 service, |
100 node, | 100 node, |
101 filter_kw, | 101 filter_kw, |
102 SubPage(u'blog_feed_atom'), | 102 SubPage('blog_feed_atom'), |
103 ) | 103 ) |
104 request.template_data[u'atom_url'] = atom_url | 104 request.template_data['atom_url'] = atom_url |
105 request.template_data.setdefault(u'links', []).append({ | 105 request.template_data.setdefault('links', []).append({ |
106 u"href": atom_url, | 106 "href": atom_url, |
107 u"type": "application/atom+xml", | 107 "type": "application/atom+xml", |
108 u"rel": "alternate", | 108 "rel": "alternate", |
109 u"title": "{service}'s blog".format(service=service)}) | 109 "title": "{service}'s blog".format(service=service)}) |
110 | 110 |
111 | 111 |
112 @defer.inlineCallbacks | 112 @defer.inlineCallbacks |
113 def appendComments(self, blog_items, identities, profile): | 113 def appendComments(self, blog_items, identities, profile): |
114 for blog_item in blog_items: | 114 for blog_item in blog_items: |
115 if identities is not None: | 115 if identities is not None: |
116 author = blog_item.author_jid | 116 author = blog_item.author_jid |
117 if not author: | 117 if not author: |
118 log.warning(_(u"no author found for item {item_id}").format(item_id=blog_item.id)) | 118 log.warning(_("no author found for item {item_id}").format(item_id=blog_item.id)) |
119 else: | 119 else: |
120 if author not in identities: | 120 if author not in identities: |
121 identities[author] = yield self.host.bridgeCall(u'identityGet', author, profile) | 121 identities[author] = yield self.host.bridgeCall('identityGet', author, profile) |
122 for comment_data in blog_item.comments: | 122 for comment_data in blog_item.comments: |
123 service = comment_data[u'service'] | 123 service = comment_data['service'] |
124 node = comment_data[u'node'] | 124 node = comment_data['node'] |
125 try: | 125 try: |
126 comments_data = yield self.host.bridgeCall(u'mbGet', | 126 comments_data = yield self.host.bridgeCall('mbGet', |
127 service, | 127 service, |
128 node, | 128 node, |
129 C.NO_LIMIT, | 129 C.NO_LIMIT, |
130 [], | 130 [], |
131 {C.KEY_ORDER_BY: C.ORDER_BY_CREATION}, | 131 {C.KEY_ORDER_BY: C.ORDER_BY_CREATION}, |
132 profile) | 132 profile) |
133 except Exception as e: | 133 except Exception as e: |
134 log.warning(_(u"Can't get comments at {node} (service: {service}): {msg}").format( | 134 log.warning(_("Can't get comments at {node} (service: {service}): {msg}").format( |
135 service=service, | 135 service=service, |
136 node=node, | 136 node=node, |
137 msg=e)) | 137 msg=e)) |
138 continue | 138 continue |
139 | 139 |
146 try: | 146 try: |
147 if item_id: | 147 if item_id: |
148 items_id = [item_id] | 148 items_id = [item_id] |
149 else: | 149 else: |
150 items_id = [] | 150 items_id = [] |
151 blog_data = yield self.host.bridgeCall(u'mbGet', | 151 blog_data = yield self.host.bridgeCall('mbGet', |
152 service.userhost(), | 152 service.userhost(), |
153 node, | 153 node, |
154 C.NO_LIMIT, | 154 C.NO_LIMIT, |
155 items_id, | 155 items_id, |
156 extra, | 156 extra, |
157 profile) | 157 profile) |
158 except Exception as e: | 158 except Exception as e: |
159 # FIXME: need a better way to test errors in bridge errback | 159 # FIXME: need a better way to test errors in bridge errback |
160 if u"forbidden" in unicode(e): | 160 if "forbidden" in str(e): |
161 self.pageError(request, 401) | 161 self.pageError(request, 401) |
162 else: | 162 else: |
163 log.warning(_(u"can't retrieve blog for [{service}]: {msg}".format( | 163 log.warning(_("can't retrieve blog for [{service}]: {msg}".format( |
164 service = service.userhost(), msg=e))) | 164 service = service.userhost(), msg=e))) |
165 blog_data = ([], {}) | 165 blog_data = ([], {}) |
166 | 166 |
167 defer.returnValue(data_objects.BlogItems(blog_data)) | 167 defer.returnValue(data_objects.BlogItems(blog_data)) |
168 | 168 |
169 @defer.inlineCallbacks | 169 @defer.inlineCallbacks |
170 def prepare_render(self, request): | 170 def prepare_render(self, request): |
171 data = self.getRData(request) | 171 data = self.getRData(request) |
172 page_max = data.get(u"page_max", 10) | 172 page_max = data.get("page_max", 10) |
173 # if the comments are not explicitly hidden, we show them | 173 # if the comments are not explicitly hidden, we show them |
174 service, node, item_id, show_comments = data.get(u'service', u''), data.get(u'node', u''), data.get(u'item'), data.get(u'show_comments', True) | 174 service, node, item_id, show_comments = data.get('service', ''), data.get('node', ''), data.get('item'), data.get('show_comments', True) |
175 profile = self.getProfile(request) | 175 profile = self.getProfile(request) |
176 if profile is None: | 176 if profile is None: |
177 profile = C.SERVICE_PROFILE | 177 profile = C.SERVICE_PROFILE |
178 profile_connected = False | 178 profile_connected = False |
179 else: | 179 else: |
184 extra = {} | 184 extra = {} |
185 else: | 185 else: |
186 extra = self.getPubsubExtra(request, page_max=page_max) | 186 extra = self.getPubsubExtra(request, page_max=page_max) |
187 tag = data.get('tag') | 187 tag = data.get('tag') |
188 if tag: | 188 if tag: |
189 extra[u'mam_filter_{}'.format(C.MAM_FILTER_CATEGORY)] = tag | 189 extra['mam_filter_{}'.format(C.MAM_FILTER_CATEGORY)] = tag |
190 | 190 |
191 ## main data ## | 191 ## main data ## |
192 # we get data from backend/XMPP here | 192 # we get data from backend/XMPP here |
193 items = yield getBlogItems(self, request, service, node, item_id, extra, profile) | 193 items = yield getBlogItems(self, request, service, node, item_id, extra, profile) |
194 | 194 |
195 ## navigation ## | 195 ## navigation ## |
196 # no let's fill service, node and pagination URLs | 196 # no let's fill service, node and pagination URLs |
197 template_data = request.template_data | 197 template_data = request.template_data |
198 if u'service' not in template_data: | 198 if 'service' not in template_data: |
199 template_data[u'service'] = service | 199 template_data['service'] = service |
200 if u'node' not in template_data: | 200 if 'node' not in template_data: |
201 template_data[u'node'] = node | 201 template_data['node'] = node |
202 target_profile = template_data.get(u'target_profile') | 202 target_profile = template_data.get('target_profile') |
203 | 203 |
204 if items: | 204 if items: |
205 if not item_id: | 205 if not item_id: |
206 self.setPagination(request, items.metadata) | 206 self.setPagination(request, items.metadata) |
207 else: | 207 else: |
210 # we must return an error | 210 # we must return an error |
211 self.pageError(request, C.HTTP_NOT_FOUND) | 211 self.pageError(request, C.HTTP_NOT_FOUND) |
212 | 212 |
213 ## identities ## | 213 ## identities ## |
214 # identities are used to show nice nickname or avatars | 214 # identities are used to show nice nickname or avatars |
215 identities = template_data[u'identities'] = self.host.getSessionData(request, session_iface.ISATSession).identities | 215 identities = template_data['identities'] = self.host.getSessionData(request, session_iface.ISATSession).identities |
216 | 216 |
217 ## Comments ## | 217 ## Comments ## |
218 # if comments are requested, we need to take them | 218 # if comments are requested, we need to take them |
219 if show_comments: | 219 if show_comments: |
220 yield appendComments(self, items, identities, profile) | 220 yield appendComments(self, items, identities, profile) |
221 | 221 |
222 ## URLs ## | 222 ## URLs ## |
223 # We will fill items_http_uri and tags_http_uri in template_data with suitable urls | 223 # We will fill items_http_uri and tags_http_uri in template_data with suitable urls |
224 # if we know the profile, we use it instead of service + blog (nicer url) | 224 # if we know the profile, we use it instead of service + blog (nicer url) |
225 if target_profile is None: | 225 if target_profile is None: |
226 blog_base_url_item = self.getPageByName(u'blog_view').getURL(service.full(), node or u'@', u'id') | 226 blog_base_url_item = self.getPageByName('blog_view').getURL(service.full(), node or '@', 'id') |
227 blog_base_url_tag = self.getPageByName(u'blog_view').getURL(service.full(), node or u'@', u'tag') | 227 blog_base_url_tag = self.getPageByName('blog_view').getURL(service.full(), node or '@', 'tag') |
228 else: | 228 else: |
229 blog_base_url_item = self.getURLByNames([(u'user', [target_profile]), (u'user_blog', [u'id'])]) | 229 blog_base_url_item = self.getURLByNames([('user', [target_profile]), ('user_blog', ['id'])]) |
230 blog_base_url_tag = self.getURLByNames([(u'user', [target_profile]), (u'user_blog', [u'tag'])]) | 230 blog_base_url_tag = self.getURLByNames([('user', [target_profile]), ('user_blog', ['tag'])]) |
231 # we also set the background image if specified by user | 231 # we also set the background image if specified by user |
232 bg_img = yield self.host.bridgeCall(u'asyncGetParamA', u'Background', u'Blog page', u'value', -1, template_data[u'target_profile']) | 232 bg_img = yield self.host.bridgeCall('asyncGetParamA', 'Background', 'Blog page', 'value', -1, template_data['target_profile']) |
233 if bg_img: | 233 if bg_img: |
234 template_data['dynamic_style'] = safe(u""" | 234 template_data['dynamic_style'] = safe(""" |
235 :root { | 235 :root { |
236 --bg-img: url("%s"); | 236 --bg-img: url("%s"); |
237 } | 237 } |
238 """ % cgi.escape(bg_img, True)) | 238 """ % cgi.escape(bg_img, True)) |
239 | 239 |
240 template_data[u'items'] = data[u'items'] = items | 240 template_data['items'] = data['items'] = items |
241 if request.args.get('reverse') == ['1']: | 241 if request.args.get('reverse') == ['1']: |
242 template_data[u'items'].items.reverse() | 242 template_data['items'].items.reverse() |
243 template_data[u'items_http_uri'] = items_http_uri = {} | 243 template_data['items_http_uri'] = items_http_uri = {} |
244 template_data[u'tags_http_uri'] = tags_http_uri = {} | 244 template_data['tags_http_uri'] = tags_http_uri = {} |
245 | 245 |
246 | 246 |
247 for item in items: | 247 for item in items: |
248 blog_canonical_url = u'/'.join([blog_base_url_item, utils.quote(item.id)]) | 248 blog_canonical_url = '/'.join([blog_base_url_item, utils.quote(item.id)]) |
249 if len(blog_canonical_url) > URL_LIMIT_MARK: | 249 if len(blog_canonical_url) > URL_LIMIT_MARK: |
250 blog_url = blog_canonical_url | 250 blog_url = blog_canonical_url |
251 else: | 251 else: |
252 # we add text from title or body at the end of URL | 252 # we add text from title or body at the end of URL |
253 # to make it more human readable | 253 # to make it more human readable |
254 text = item.title or item.content | 254 text = item.title or item.content |
255 # we change special chars to ascii one, trick found at https://stackoverflow.com/a/3194567 | 255 # we change special chars to ascii one, trick found at https://stackoverflow.com/a/3194567 |
256 text = unicodedata.normalize('NFD', text).encode('ascii', 'ignore') | 256 text = unicodedata.normalize('NFD', text).encode('ascii', 'ignore').decode('utf-8') |
257 text = RE_TEXT_URL.sub(u' ', text).lower() | 257 text = RE_TEXT_URL.sub(' ', text).lower() |
258 text = u'-'.join([t for t in text.split() if t and len(t)>=TEXT_WORD_MIN_LENGHT]) | 258 text = '-'.join([t for t in text.split() if t and len(t)>=TEXT_WORD_MIN_LENGHT]) |
259 while len(text) > TEXT_MAX_LEN: | 259 while len(text) > TEXT_MAX_LEN: |
260 if u'-' in text: | 260 if '-' in text: |
261 text = text.rsplit(u'-', 1)[0] | 261 text = text.rsplit('-', 1)[0] |
262 else: | 262 else: |
263 text = text[:TEXT_MAX_LEN] | 263 text = text[:TEXT_MAX_LEN] |
264 if text: | 264 if text: |
265 blog_url = blog_canonical_url + u'/' + text | 265 blog_url = blog_canonical_url + '/' + text |
266 else: | 266 else: |
267 blog_url = blog_canonical_url | 267 blog_url = blog_canonical_url |
268 | 268 |
269 items_http_uri[item.id] = self.host.getExtBaseURL(request, blog_url) | 269 items_http_uri[item.id] = self.host.getExtBaseURL(request, blog_url) |
270 for tag in item.tags: | 270 for tag in item.tags: |
271 if tag not in tags_http_uri: | 271 if tag not in tags_http_uri: |
272 tag_url = u'/'.join([blog_base_url_tag, utils.quote(tag)]) | 272 tag_url = '/'.join([blog_base_url_tag, utils.quote(tag)]) |
273 tags_http_uri[tag] = self.host.getExtBaseURL(request, tag_url) | 273 tags_http_uri[tag] = self.host.getExtBaseURL(request, tag_url) |
274 | 274 |
275 # if True, page should display a comment box | 275 # if True, page should display a comment box |
276 template_data[u'allow_commenting'] = data.get(u'allow_commenting', profile_connected) | 276 template_data['allow_commenting'] = data.get('allow_commenting', profile_connected) |
277 | 277 |
278 # last but not least, we add a xmpp: link to the node | 278 # last but not least, we add a xmpp: link to the node |
279 uri_args = {u'path': service.full()} | 279 uri_args = {'path': service.full()} |
280 if node: | 280 if node: |
281 uri_args[u'node'] = node | 281 uri_args['node'] = node |
282 if item_id: | 282 if item_id: |
283 uri_args[u'item'] = item_id | 283 uri_args['item'] = item_id |
284 template_data[u'xmpp_uri'] = uri.buildXMPPUri(u'pubsub', subtype='microblog', **uri_args) | 284 template_data['xmpp_uri'] = uri.buildXMPPUri('pubsub', subtype='microblog', **uri_args) |
285 | 285 |
286 | 286 |
287 @defer.inlineCallbacks | 287 @defer.inlineCallbacks |
288 def on_data_post(self, request): | 288 def on_data_post(self, request): |
289 profile = self.getProfile(request) | 289 profile = self.getProfile(request) |
290 if profile is None: | 290 if profile is None: |
291 self.pageError(request, C.HTTP_FORBIDDEN) | 291 self.pageError(request, C.HTTP_FORBIDDEN) |
292 type_ = self.getPostedData(request, u'type') | 292 type_ = self.getPostedData(request, 'type') |
293 if type_ == u'comment': | 293 if type_ == 'comment': |
294 service, node, body = self.getPostedData(request, (u'service', u'node', u'body')) | 294 service, node, body = self.getPostedData(request, ('service', 'node', 'body')) |
295 | 295 |
296 if not body: | 296 if not body: |
297 self.pageError(request, C.HTTP_BAD_REQUEST) | 297 self.pageError(request, C.HTTP_BAD_REQUEST) |
298 comment_data = {u"content": body} | 298 comment_data = {"content": body} |
299 try: | 299 try: |
300 yield self.host.bridgeCall(u'mbSend', | 300 yield self.host.bridgeCall('mbSend', |
301 service, | 301 service, |
302 node, | 302 node, |
303 data_format.serialise(comment_data), | 303 data_format.serialise(comment_data), |
304 profile) | 304 profile) |
305 except Exception as e: | 305 except Exception as e: |
306 if u"forbidden" in unicode(e): | 306 if "forbidden" in str(e): |
307 self.pageError(request, 401) | 307 self.pageError(request, 401) |
308 else: | 308 else: |
309 raise e | 309 raise e |
310 else: | 310 else: |
311 log.warning(_(u"Unhandled data type: {}").format(type_)) | 311 log.warning(_("Unhandled data type: {}").format(type_)) |