diff src/server/blog.py @ 712:bf562fb9c273

server_side: use Jinja2 template engine for static blog
author souliane <souliane@mailoo.org>
date Mon, 13 Jul 2015 18:11:38 +0200
parents e9a6cbb924e6
children 29b84af2ff7b
line wrap: on
line diff
--- a/src/server/blog.py	Mon Jul 13 13:33:01 2015 +0200
+++ b/src/server/blog.py	Mon Jul 13 18:11:38 2015 +0200
@@ -3,6 +3,7 @@
 
 # Libervia: a Salut à Toi frontend
 # Copyright (C) 2011, 2012, 2013, 2014, 2015 Jérôme Poisson <goffi@goffi.org>
+# Copyright (C) 2013, 2014, 2015 Adrien Cossa <souliane@mailoo.org>
 
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Affero General Public License as published by
@@ -20,15 +21,16 @@
 from sat.core.i18n import _, D_
 from sat_frontends.tools.strings import addURLToText
 from sat.core.log import getLogger
+from django.conf.urls import url
 log = getLogger(__name__)
 
 from twisted.internet import defer
 from twisted.web import server
 from twisted.web.resource import Resource
 from twisted.words.protocols.jabber.jid import JID
+from jinja2 import Environment, PackageLoader
 from datetime import datetime
 from sys import path
-import importlib
 import uuid
 import re
 import os
@@ -45,22 +47,22 @@
         self.host = host
 
         # add Libervia's themes directory to the python path
-        path.append(os.path.dirname(self.host.themes_dir))
+        path.append(os.path.dirname(os.path.normpath(self.host.themes_dir)))
+        themes = os.path.basename(os.path.normpath(self.host.themes_dir))
+        self.env = Environment(loader=PackageLoader(themes, self.THEME))
 
     def useTemplate(self, request, tpl, data=None):
         root_url = '../' * len(request.postpath)
         theme_url = os.path.join(root_url, 'themes', self.THEME)
 
-        # import the theme module
-        themes = os.path.basename(os.path.dirname(os.path.dirname(self.host.themes_dir)))
-        theme = importlib.import_module("%s.templates" % self.THEME, themes)
-        data_ = {'theme': theme_url,
-                 'images': os.path.join(theme_url, 'images'),
+        data_ = {'images': os.path.join(theme_url, 'images'),
                  'styles': os.path.join(theme_url, 'styles'),
                  }
         if data:
             data_.update(data)
-        return getattr(theme, tpl.upper()).encode('utf-8').format(**data_)
+
+        template = self.env.get_template('%s.html' % tpl)
+        return template.render(**data_).encode('utf-8')
 
 
 class MicroBlog(Resource, TemplateProcessor):
@@ -126,20 +128,20 @@
         jid_s = (profile + '@' + self.host.bridge.getNewAccountDomain()).lower()
         if jid_s in self.avatars_cache:
             return defer.succeed(self.avatars_cache[jid_s])
-        # FIXME: request_id is no more need when actionResult is removed
+        # FIXME: request_id is no more needed when actionResult is removed
         request_id = self.host.bridge.getCard(jid_s, C.SERVICE_PROFILE)
         self.waiting_deferreds[jid_s] = (request_id, defer.Deferred())
         return self.waiting_deferreds[jid_s][1]
 
     def render_GET(self, request):
         if not request.postpath:
-            return self.useTemplate(request, "error", {'message': "You must indicate a nickname"})
+            return self.useTemplate(request, "static_blog_error", {'message': "You must indicate a nickname"})
 
         prof_requested = request.postpath[0]
-        #TODO: char check: only use alphanumerical chars + some extra(_,-,...) here
+        #TODO : char check: only use alphanumeric chars + some extra(_,-,...) here
         prof_found = self.host.bridge.getProfileName(prof_requested)
         if not prof_found or prof_found == C.SERVICE_PROFILE:
-            return self.useTemplate(request, "error", {'message': "Invalid nickname"})
+            return self.useTemplate(request, "static_blog_error", {'message': "Invalid nickname"})
 
         d = defer.Deferred()
         JID(self.host.bridge.asyncGetParamA('JabberID', 'Connection', 'value', C.SERVER_SECURITY_LIMIT, prof_found, callback=d.callback, errback=d.errback))
@@ -266,43 +268,48 @@
         def getOption(key):
             return sanitizeHtml(options[key]).encode('utf-8') if key in options else ''
 
-        def getImageOption(key, default, alt):
+        def getImageParams(key, default, alt):
             """regexp from http://answers.oreilly.com/topic/280-how-to-validate-urls-with-regular-expressions/"""
             url = options[key].encode('utf-8') if key in options else ''
             regexp = r"^(https?|ftp)://[a-z0-9-]+(\.[a-z0-9-]+)+(/[\w-]+)*/[\w-]+\.(gif|png|jpg)$"
             if re.match(regexp, url):
                 url = url
-                suffix = "<br/>"
             else:
                 url = default
-                suffix = ""
-            return self.useTemplate(request, "banner", {'alt': alt, 'url': url, 'suffix': suffix})
+            return BlogImage(url, alt)
 
         avatar = os.path.normpath(root_url + getOption('avatar'))
         title = getOption(C.STATIC_BLOG_PARAM_TITLE) or user
         data = {'base_url': base_url,
-                'user': user,
                 'keywords': getOption(C.STATIC_BLOG_PARAM_KEYWORDS),
                 'description': getOption(C.STATIC_BLOG_PARAM_DESCRIPTION),
-                'title': getOption(C.STATIC_BLOG_PARAM_TITLE) or "%s's microblog" % user,
+                'title': title,
                 'favicon': avatar,
-                'banner_elt': getImageOption(C.STATIC_BLOG_PARAM_BANNER, avatar, title),
-                'title_elt': title,
+                'banner_img': getImageParams(C.STATIC_BLOG_PARAM_BANNER, avatar, title)
                 }
 
         mblog_data, main_rsm = mblog_data
         mblog_data = [(entry if isinstance(entry, tuple) else (entry, ([], {}))) for entry in mblog_data]
-        mblog_data = sorted(mblog_data, key=lambda entry: (-float(entry[0].get('updated', 0))))
+        mblog_data.sort(key=lambda entry: (-float(entry[0].get('updated', 0))))
 
-        data.update(self.getNavigationLinks(request, mblog_data, main_rsm, base_url))
-        request.write(self.useTemplate(request, 'header', data))
+        data['navlinks'] = NavigationLinks(request, mblog_data, main_rsm, base_url)
+        data['messages'] = [BlogMessage(request, base_url, entry, comments[0]) for entry, comments in mblog_data]
 
-        BlogMessages(self.host, request, base_url, mblog_data).render()
-
-        request.write(self.useTemplate(request, "footer", data))
+        request.write(self.useTemplate(request, 'static_blog', data))
         request.finish()
 
-    def getNavigationLinks(self, request, mblog_data, rsm_data, base_url):
+    def render_atom_feed(self, feed, request):
+        request.write(feed.encode('utf-8'))
+        request.finish()
+
+    def render_error_blog(self, error, request, profile):
+        request.write(self.useTemplate(request, "static_blog_error", {'message': "Can't access requested data"}))
+        request.finish()
+
+
+class NavigationLinks(object):
+
+    def __init__(self, request, mblog_data, rsm_data, base_url):
         """Build the navigation links.
 
         @param mblog_data (dict): the microblogs that are displayed on the page
@@ -310,10 +317,9 @@
         @param base_url (unicode): the base URL for this user's blog
         @return: dict
         """
-        data = {}
         for key in ('later_message', 'later_messages', 'older_message', 'older_messages'):
             count = int(rsm_data.get('count', 0))
-            data[key] = ''  # key must exist when using the template
+            setattr(self, key, '')  # key must exist when using the template
             if count <= 0 or (request.display_single == key.endswith('s')):
                 continue
 
@@ -337,33 +343,64 @@
 
             link = "%(base_url)s?%(post_arg)s=%(item_id)s%(suffix)s" % link_data
 
-            link_data = {'link': link, 'class': key, 'text': key.replace('_', ' ')}
-            data[key] = (self.useTemplate(request, 'nav_link', link_data)).encode('utf-8')
+            setattr(self, key, BlogLink(link, key, key.replace('_', ' ')))
 
-        return data
+
+class BlogImage(object):
 
-    def render_atom_feed(self, feed, request):
-        request.write(feed.encode('utf-8'))
-        request.finish()
+    def __init__(self, url_, alt):
+        self.url = url_
+        self.alt = alt
+
 
-    def render_error_blog(self, error, request, profile):
-        request.write(self.useTemplate(request, "error", {'message': "Can't access requested data"}))
-        request.finish()
+class BlogLink(object):
+
+    def __init__(self, url_, style, text):
+        self.url = url_
+        self.style = style
+        self.text = text
 
 
-class BlogMessages(TemplateProcessor):
+class BlogMessage(object):
+
+    def __init__(self, request, base_url, entry, comments=None):
+        """
+
+        @param request: HTTP request
+        @param base_url (unicode): the base URL
+        @param entry (dict{unicode:unicode]): microblog entry received from the backend
+        @param comments (list[dict]): comments
+        """
+        timestamp = float(entry.get('published', 0))
+        is_comment = entry['type'] == 'comment'
+
+        self.date = datetime.fromtimestamp(timestamp)
+        self.type = entry['type']
+        self.style = 'mblog_comment' if entry['type'] == 'comment' else ''
+        self.content = self.getText(entry, 'content')
 
-    def __init__(self, host, request, base_url, mblog_data):
-        TemplateProcessor.__init__(self, host)
-        self.request = request
-        self.base_url = base_url
-        self.mblog_data = mblog_data
+        if is_comment:
+            self.author = (_("from %s") % entry['author']).encode('utf-8')
+        else:
+            self.author = '&nbsp;'
+            self.url = (u"%s/%s" % (base_url, entry['id'])).encode('utf-8')
+            self.title = self.getText(entry, 'title')
+
+            comments_count = int(entry['comments_count'])
+            count_text = lambda count: D_('comments') if count > 1 else D_('comment')
 
-    def render(self):
-        for entry, comments_data in self.mblog_data:
-            comments, comments_rsm = comments_data
-            comments = sorted(comments, key=lambda entry: (float(entry.get('published', 0))))
-            self.render_html(entry, comments)
+            self.comments_text = "%s %s" % (comments_count, count_text(comments_count))
+
+            delta = comments_count - len(comments)
+            if request.display_single and delta > 0:
+                prev_url = "%s?max=%s" % (self.url, entry['comments_count'])
+                prev_text = D_("show %(count)d previous %(comments)s") % \
+                    {'count': delta, 'comments': count_text(delta)}
+                self.all_comments_link = BlogLink(prev_url, "comments_link", prev_text)
+
+        if comments:
+            comments.sort(key=lambda entry: float(entry.get('published', 0)))
+            self.comments = [BlogMessage(request, base_url, comment) for comment in comments]
 
     def getText(self, entry, key):
         if ('%s_xhtml' % key) in entry:
@@ -372,51 +409,3 @@
             processor = addURLToText if key.startswith('content') else sanitizeHtml
             return convertNewLinesToXHTML(processor(entry[key])).encode('utf-8')
         return None
-
-    def render_html(self, entry, comments=None):
-        """Render one microblog entry.
-        @param entry: the microblog entry
-        @param base_url: the base url of the blog
-        @param request: the HTTP request
-        """
-        timestamp = float(entry.get('published', 0))
-        is_comment = entry['type'] == 'comment'
-
-        data = {'date': datetime.fromtimestamp(timestamp),
-                'comments_link': '',
-                'previous_comments': '',
-                }
-
-        if is_comment:
-            author = (_("from %s") % entry['author']).encode('utf-8')
-        else:
-            author = '&nbsp;'
-            message_link = (u"%s/%s" % (self.base_url, entry['id'])).encode('utf-8')
-
-            count_text = lambda count: D_('comments') if count > 1 else D_('comment')
-
-            comments_count = int(entry['comments_count'])
-            delta = comments_count - len(comments)
-            if self.request.display_single and delta > 0:
-                data['comments_link'] = ("%s?max=%s" % (message_link, entry['comments_count']))
-                data['previous_comments'] = D_("Show %(count)d previous %(comments)s") % \
-                    {'count': delta, 'comments': count_text(delta)}
-
-            data.update({'comments_count': comments_count,
-                         'comments_text': count_text(comments_count),
-                         'message_link': message_link,
-                         'message_title': self.getText(entry, 'title'),
-                         })
-
-        data.update({'author': author,
-                     'extra_style': 'mblog_comment' if entry['type'] == 'comment' else '',
-                     'content': self.getText(entry, 'content'),
-                     })
-
-        tpl = "%s%s" % ("" if data.get('message_title', None) else "micro_", "comment" if is_comment else "message")
-        self.request.write(self.useTemplate(self.request, tpl, data))
-
-        if comments:
-            for comment in comments:
-                self.render_html(comment)
-