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