Mercurial > libervia-web
comparison src/server/blog.py @ 879:2e0e9cf9efb4
blog (atom feed): fixed atom handling:
- atom generation code moved from backend to Libervia
- replaced hardcoded urls by urls based on request path
- added xmpp: uri as alternate links
- feed id was not unique (it was the node), now it is the quoted xmpp uri
- atom:updated was using current time which is wrong, now take the more recent item "updated" value
- link elements where missing attributes (e.g.: type)
- added correct http(s) links for each items
- microblog items (without title) now use an abstract of content as title
- use 20 as max_items for atom feeds (may change in the future)
- removed bad use of encode
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 09 Mar 2016 17:33:00 +0100 |
parents | de17f7313cbe |
children | ccbad50e1426 |
comparison
equal
deleted
inserted
replaced
878:2aaac0605ae2 | 879:2e0e9cf9efb4 |
---|---|
21 from sat.core.i18n import _, D_ | 21 from sat.core.i18n import _, D_ |
22 from sat_frontends.tools.strings import addURLToText, fixXHTMLLinks | 22 from sat_frontends.tools.strings import addURLToText, fixXHTMLLinks |
23 from sat.core.log import getLogger | 23 from sat.core.log import getLogger |
24 log = getLogger(__name__) | 24 log = getLogger(__name__) |
25 from sat.tools import common | 25 from sat.tools import common |
26 from sat.tools import xml_tools | |
26 from dbus.exceptions import DBusException | 27 from dbus.exceptions import DBusException |
27 from twisted.internet import defer | 28 from twisted.internet import defer |
28 from twisted.web import server | 29 from twisted.web import server |
29 from twisted.web.resource import Resource | 30 from twisted.web.resource import Resource |
30 from twisted.words.protocols.jabber.jid import JID | 31 from twisted.words.protocols.jabber.jid import JID |
32 from twisted.words.xish import domish | |
31 from jinja2 import Environment, PackageLoader | 33 from jinja2 import Environment, PackageLoader |
32 from datetime import datetime | 34 from datetime import datetime |
33 import re | 35 import re |
34 import os | 36 import os |
35 import sys | 37 import sys |
36 import urllib | 38 import urllib |
37 | 39 |
38 from libervia.server.html_tools import sanitizeHtml, convertNewLinesToXHTML | 40 from libervia.server.html_tools import sanitizeHtml, convertNewLinesToXHTML |
39 from libervia.server.constants import Const as C | 41 from libervia.server.constants import Const as C |
40 | 42 |
43 NS_ATOM = 'http://www.w3.org/2005/Atom' | |
44 ATOM_MAX_ITEMS = 20 | |
41 PARAMS_TO_GET = (C.STATIC_BLOG_PARAM_TITLE, C.STATIC_BLOG_PARAM_BANNER, C.STATIC_BLOG_PARAM_KEYWORDS, C.STATIC_BLOG_PARAM_DESCRIPTION) | 45 PARAMS_TO_GET = (C.STATIC_BLOG_PARAM_TITLE, C.STATIC_BLOG_PARAM_BANNER, C.STATIC_BLOG_PARAM_KEYWORDS, C.STATIC_BLOG_PARAM_DESCRIPTION) |
42 re_strip_empty_div = re.compile(r"<div ?/>|<div> *?</div>") | 46 re_strip_empty_div = re.compile(r"<div ?/>|<div> *?</div>") |
43 | 47 |
44 # TODO: chech disco features and use max_items when RSM is not available | 48 # TODO: chech disco features and use max_items when RSM is not available |
49 # FIXME: Deferred are not used | |
50 # FIXME: change navigation links handling, this is is fragile | |
51 # TODO: refactorise this | |
45 | 52 |
46 | 53 |
47 def getDefaultQueryData(request): | 54 def getDefaultQueryData(request): |
48 """Return query data which must be present in all links | 55 """Return query data which must be present in all links |
49 | 56 |
172 pub_jid = JID(pub_jid_s) | 179 pub_jid = JID(pub_jid_s) |
173 | 180 |
174 request.extra_dict = {} # will be used for RSM and MAM | 181 request.extra_dict = {} # will be used for RSM and MAM |
175 self.parseURLParams(request) | 182 self.parseURLParams(request) |
176 if request.item_id: | 183 if request.item_id: |
184 # FIXME: this part seems useless | |
177 # we want a specific item | 185 # we want a specific item |
178 item_ids = [request.item_id] | 186 # item_ids = [request.item_id] |
179 # max_items = 1 | 187 # max_items = 1 |
180 max_items = 0 # FIXME | 188 max_items = 0 # FIXME |
181 else: | 189 else: |
182 item_ids = [] | |
183 # max_items = int(request.extra_dict['rsm_max']) # FIXME | 190 # max_items = int(request.extra_dict['rsm_max']) # FIXME |
184 max_items = 0 | 191 max_items = 0 |
185 # TODO: use max_items only when RSM is not available | 192 # TODO: use max_items only when RSM is not available |
186 | 193 |
187 if request.atom: | 194 if request.atom: |
188 request.extra_dict.update(request.mam_extra) | 195 request.extra_dict.update(request.mam_extra) |
189 self.host.bridge.mbGetAtom(pub_jid.userhost(), '', max_items, item_ids, | 196 self.getAtom(pub_jid, ATOM_MAX_ITEMS, request.extra_dict, request.extra_comments_dict, request, profile) |
190 request.extra_dict, C.SERVICE_PROFILE, | 197 |
191 lambda feed: self.renderAtomFeed(feed, request), | |
192 lambda failure: self.renderError(failure, request, pub_jid)) | |
193 elif request.item_id: | 198 elif request.item_id: |
194 # we can't merge mam_extra now because we'll use item_ids | 199 # we can't merge mam_extra now because we'll use item_ids |
195 self.getItemById(pub_jid, request.item_id, request.extra_dict, | 200 self.getItemById(pub_jid, request.item_id, request.extra_dict, |
196 request.extra_comments_dict, request, profile) | 201 request.extra_comments_dict, request, profile) |
197 else: | 202 else: |
375 # TODO: use max_comments only when RSM is not available | 380 # TODO: use max_comments only when RSM is not available |
376 self.host.bridge.mbGetFromManyWithComments(C.JID, [pub_jid.userhost()], max_items, | 381 self.host.bridge.mbGetFromManyWithComments(C.JID, [pub_jid.userhost()], max_items, |
377 max_comments, extra_dict, extra_comments_dict, | 382 max_comments, extra_dict, extra_comments_dict, |
378 C.SERVICE_PROFILE, callback=getResult) | 383 C.SERVICE_PROFILE, callback=getResult) |
379 | 384 |
385 def getAtom(self, pub_jid, max_items, extra_dict, extra_comments_dict, request, profile): | |
386 """ | |
387 | |
388 @param pub_jid (jid.JID): publisher JID | |
389 @param max_items(int): maximum number of item to get, C.NO_LIMIT for no limit | |
390 @param extra_dict (dict): extra configuration for initial items only | |
391 @param extra_comments_dict (dict): extra configuration for comments only | |
392 @param request: HTTP request | |
393 @param profile | |
394 """ | |
395 def gotItems(data): | |
396 # Generate a clean atom feed with uri linking to this blog | |
397 # from microblog data | |
398 items, metadata= data | |
399 feed_elt = domish.Element((NS_ATOM, u'feed')) | |
400 title = _(u"{user}'s blog").format(user=profile) | |
401 feed_elt.addElement(u'title', content=title) | |
402 url_path = request.URLPath() | |
403 base_blog_url = u"{0.scheme}://{0.netloc}/blog/{user}".format(url_path, user=profile) | |
404 | |
405 # atom link | |
406 link_feed_elt = feed_elt.addElement('link') | |
407 link_feed_elt['href'] = u'{base}/atom.xml'.format(base=base_blog_url) | |
408 link_feed_elt['type'] = u'application/atom+xml' | |
409 link_feed_elt['rel'] = u'self' | |
410 | |
411 # blog link | |
412 link_blog_elt = feed_elt.addElement('link') | |
413 link_blog_elt['rel'] = u'alternate' | |
414 link_blog_elt['type'] = u'text/html' | |
415 link_blog_elt['href'] = base_blog_url | |
416 | |
417 # blog link XMPP uri | |
418 blog_xmpp_uri = metadata['uri'] | |
419 link_blog_elt = feed_elt.addElement('link') | |
420 link_blog_elt['rel'] = u'alternate' | |
421 link_blog_elt['type'] = u'application/xmpp+xml' | |
422 link_blog_elt['href'] = blog_xmpp_uri | |
423 | |
424 feed_elt.addElement('id', content=urllib.quote(blog_xmpp_uri)) | |
425 updated_unix = max([float(item['updated']) for item in items]) | |
426 updated_dt = datetime.fromtimestamp(updated_unix) | |
427 feed_elt.addElement(u'updated', u'{}Z'.format(updated_dt.isoformat("T"))) | |
428 | |
429 for item in items: | |
430 entry_elt = feed_elt.addElement(u'entry') | |
431 | |
432 # Title | |
433 try: | |
434 title = item['title'] | |
435 except KeyError: | |
436 # for microblog (without title), we use an abstract of content as title | |
437 title = u'{}...'.format(u' '.join(item['content'][:70].split())) | |
438 entry_elt.addElement(u'title', content=title) | |
439 | |
440 # HTTP link | |
441 http_link_elt = entry_elt.addElement(u'link') | |
442 http_link_elt['rel'] = u'alternate' | |
443 http_link_elt['type'] = u'text/html' | |
444 http_link_elt['href'] = u'{base}/{quoted_id}'.format(base=base_blog_url, quoted_id=urllib.quote(item['id'])) | |
445 # XMPP link | |
446 xmpp_link_elt = entry_elt.addElement(u'link') | |
447 xmpp_link_elt['rel'] = u'alternate' | |
448 xmpp_link_elt['type'] = u'application/xmpp+xml' | |
449 xmpp_link_elt['href'] = u'{blog_uri};item={item_id}'.format(blog_uri=blog_xmpp_uri, item_id=item['id']) | |
450 | |
451 # date metadata | |
452 entry_elt.addElement(u'id', content=item['atom_id']) | |
453 updated = datetime.fromtimestamp(float(item['updated'])) | |
454 entry_elt.addElement(u'updated', u'{}Z'.format(updated.isoformat("T"))) | |
455 published = datetime.fromtimestamp(float(item['published'])) | |
456 entry_elt.addElement(u'published', u'{}Z'.format(published.isoformat("T"))) | |
457 | |
458 # author metadata | |
459 author_elt = entry_elt.addElement(u'author') | |
460 author_elt.addElement('name', item.get('author', profile)) | |
461 try: | |
462 author_elt.addElement('uri', u'xmpp:{}'.format(item['author_jid'])) | |
463 except KeyError: | |
464 pass | |
465 try: | |
466 author_elt.addElement('email', item['author_email']) | |
467 except KeyError: | |
468 pass | |
469 | |
470 # content | |
471 try: | |
472 content_xhtml = item['content_xhtml'] | |
473 except KeyError: | |
474 content_elt = entry_elt.addElement('content', content='content') | |
475 content_elt['type'] = 'text' | |
476 else: | |
477 content_elt = entry_elt.addElement('content') | |
478 content_elt['type'] = 'xhtml' | |
479 content_elt.addChild(xml_tools.ElementParser()(content_xhtml, namespace=C.NS_XHTML)) | |
480 | |
481 atom_feed = u'<?xml version="1.0" encoding="utf-8"?>\n{}'.format(feed_elt.toXml()) | |
482 self.renderAtomFeed(atom_feed, request), | |
483 | |
484 self.host.bridge.mbGet(pub_jid.userhost(), '', max_items, [], extra_dict, C.SERVICE_PROFILE, callback=gotItems) | |
485 | |
380 ## rendering | 486 ## rendering |
381 | 487 |
382 def _updateDict(self, value, dict_, key): | 488 def _updateDict(self, value, dict_, key): |
383 dict_[key] = value | 489 dict_[key] = value |
384 | 490 |