Mercurial > libervia-web
view libervia_server/blog.py @ 439:d52f529a6d42
browser side: use of new log system (first draft):
- configuration is hardcoded in libervia.py, it will change in the (hopefuly) near future
- log level is DEBUG for the moment, will be changed to INFO when configuration will not be hardcoded anymore
- the basic log backend is used, in the future, a console.debug/info/etc should be used instead. A log widget which HTML colors is also an option
author | Goffi <goffi@goffi.org> |
---|---|
date | Thu, 08 May 2014 17:21:34 +0200 |
parents | 73f767a9ac2f |
children | 63017904c4d4 |
line wrap: on
line source
#!/usr/bin/python # -*- coding: utf-8 -*- # Libervia: a Salut à Toi frontend # Copyright (C) 2011, 2012, 2013, 2014 Jérôme Poisson <goffi@goffi.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 # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from sat.core.i18n import _ from sat_frontends.tools.strings import addURLToText from libervia_server.html_tools import sanitizeHtml 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 datetime import datetime from constants import Const as C import uuid import re class MicroBlog(Resource): isLeaf = True ERROR_TEMPLATE = """ <html> <head profile="http://www.w3.org/2005/10/profile"> <link rel="icon" type="image/png" href="%(root)ssat_logo_16.png"> <title>MICROBLOG ERROR</title> </head> <body> <h1 style='text-align: center; color: red;'>%(message)s</h1> </body> </html> """ def __init__(self, host): self.host = host Resource.__init__(self) # FIXME: this can be move to the beginning of Libervia.startService to avoid an initialization issue if not host.bridge.isConnected(C.SERVICE_PROFILE): host.bridge.connect(C.SERVICE_PROFILE) def render_GET(self, request): if not request.postpath: return MicroBlog.ERROR_TEMPLATE % {'root': '', 'message': "You must indicate a nickname"} else: prof_requested = request.postpath[0] #TODO: char check: only use alphanumerical chars + some extra(_,-,...) here prof_found = self.host.bridge.getProfileName(prof_requested) if not prof_found or prof_found == 'libervia': return MicroBlog.ERROR_TEMPLATE % {'root': '../' * len(request.postpath), 'message': "Invalid nickname"} else: def got_jid(pub_jid_s): pub_jid = JID(pub_jid_s) d2 = defer.Deferred() item_id = None if len(request.postpath) > 1: if request.postpath[1] == 'atom.xml': # return the atom feed d2.addCallbacks(self.render_atom_feed, self.render_error_blog, [request], None, [request, prof_found], None) self.host.bridge.getLastGroupBlogsAtom(pub_jid.userhost(), 10, 'libervia', d2.callback, d2.errback) return try: # check if the given path is a valid UUID uuid.UUID(request.postpath[1]) item_id = request.postpath[1] except ValueError: pass d2.addCallbacks(self.render_html_blog, self.render_error_blog, [request, prof_found], None, [request, prof_found], None) if item_id: # display one message and its comments self.host.bridge.getGroupBlogsWithComments(pub_jid.userhost(), [item_id], 'libervia', d2.callback, d2.errback) else: # display the last messages without comment self.host.bridge.getLastGroupBlogs(pub_jid.userhost(), 10, 'libervia', d2.callback, d2.errback) d1 = defer.Deferred() JID(self.host.bridge.asyncGetParamA('JabberID', 'Connection', 'value', C.SERVER_SECURITY_LIMIT, prof_found, callback=d1.callback, errback=d1.errback)) d1.addCallbacks(got_jid) return server.NOT_DONE_YET def render_html_blog(self, mblog_data, request, profile): """Retrieve the blog banner or other user's specific stuff before actually rendering the static blog @param mblog_data: list of microblog data or list of couple (microblog data, list of microblog data) @param request: HTTP request @param profile """ def check_banner(banner): """Regexp from http://answers.oreilly.com/topic/280-how-to-validate-urls-with-regular-expressions/""" if re.match(r"^(https?|ftp)://[a-z0-9-]+(\.[a-z0-9-]+)+(/[\w-]+)*/[\w-]+\.(gif|png|jpg)$", banner): style = {'banner': banner} else: style = {} self.__render_html_blog(mblog_data, style, request, profile) eb = lambda failure: self.render_error_blog(failure, request, profile) self.host.bridge.asyncGetParamA('Blog banner', 'Misc', 'value', C.SERVER_SECURITY_LIMIT, profile, callback=check_banner, errback=eb) def __render_html_blog(self, mblog_data, style, request, profile): """Actually render the static blog. If mblog_data is a list of dict, we are missing the comments items so we just display the main items. If mblog_data is a list of couple, each couple is associating a main item data with the list of its comments, so we render all. @param mblog_data: list of microblog data or list of couple (microblog data, list of microblog data) @param style: dict defining the blog's rendering parameters @param request: the HTTP request @profile """ if not isinstance(style, dict): style = {} user = sanitizeHtml(profile).encode('utf-8') root_url = '../' * len(request.postpath) base_url = root_url + 'blog/' + user banner = style['banner'].encode('utf-8') if 'banner' in style else '' banner_elt = "<img src='%(banner)s' alt='%(user)s'/>" % {'user': user, 'banner': banner} if banner else user request.write(""" <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <link rel="alternate" type="application/atom+xml" href="%(base)s/atom.xml"/> <link rel="stylesheet" type="text/css" href="%(root)scss/blog.css" /> <link rel="icon" type="image/png" href="%(root)ssat_logo_16.png"> <title>%(user)s's microblog</title> </head> <body> <div class="mblog_title"><a href="%(base)s">%(banner_elt)s</a></div> """ % {'base': base_url, 'root': root_url, 'user': user, 'banner_elt': banner_elt}) 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('published', 0)))) for entry in mblog_data: self.__render_html_entry(entry[0], base_url, request) comments = sorted(entry[1], key=lambda entry: (float(entry.get('published', 0)))) for comment in comments: self.__render_html_entry(comment, base_url, request) request.write('</body></html>') request.finish() def __render_html_entry(self, entry, base_url, request): """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)) datetime_ = datetime.fromtimestamp(timestamp) is_comment = entry['type'] == 'comment' if is_comment: author = (_("comment from %s") % entry['author']).encode('utf-8') item_link = '' else: author = ' ' item_link = ("%(base)s/%(item_id)s" % {'base': base_url, 'item_id': entry['id']}).encode('utf-8') def getText(key): if ('%s_xhtml' % key) in entry: return entry['%s_xhtml' % key].encode('utf-8') elif key in entry: processor = addURLToText if key.startswith('content') else sanitizeHtml return processor(entry[key]).encode('utf-8') return '' def addMainItemLink(elem): if not item_link or not elem: return elem return """<a href="%(link)s" class="item_link">%(elem)s</a>""" % {'link': item_link, 'elem': elem} header = addMainItemLink("""<div class="mblog_header"> <div class="mblog_metadata"> <div class="mblog_author">%(author)s</div> <div class="mblog_timestamp">%(date)s</div> </div> </div>""" % {'author': author, 'date': datetime_}) title = addMainItemLink(getText('title')) body = getText('content') if title: # insert the title within the body body = """<h1>%(title)s</h1>\n%(body)s""" % {'title': title, 'body': body} request.write("""<div class="mblog_entry %(extra_style)s"> %(header)s <span class="mblog_content">%(content)s</span> </div>""" % {'extra_style': 'mblog_comment' if entry['type'] == 'comment' else '', 'item_link': item_link, 'header': header, 'content': body}) 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(MicroBlog.ERROR_TEMPLATE % {'root': '../' * len(request.postpath), 'message': "Can't access requested data"}) request.finish()