Mercurial > libervia-web
changeset 389:2d782349b88a
server_side: display blog comments when you click on a main item header or title
author | souliane <souliane@mailoo.org> |
---|---|
date | Tue, 25 Feb 2014 17:50:47 +0100 |
parents | 893451e35686 |
children | 76583fab7ea0 |
files | libervia.py libervia_server/__init__.py libervia_server/blog.py server_css/blog.css |
diffstat | 4 files changed, 144 insertions(+), 40 deletions(-) [+] |
line wrap: on
line diff
--- a/libervia.py Tue Feb 25 11:31:11 2014 +0100 +++ b/libervia.py Tue Feb 25 17:50:47 2014 +0100 @@ -121,7 +121,7 @@ "getLastMblogs", "getMassiveLastMblogs", "getMblogComments", "getProfileJid", "getHistory", "getPresenceStatus", "joinMUC", "mucLeave", "getRoomsJoined", "inviteMUC", "launchTarotGame", "getTarotCardsPaths", "tarotGameReady", - "tarotGamePlayCards", "launchRadioCollective", + "tarotGamePlayCards", "launchRadioCollective", "getMblogs", "getMblogsWithComments", "getWaitingSub", "subscription", "delContact", "updateContact", "getCard", "getEntityData", "getParamsUI", "asyncGetParamA", "setParam", "launchAction", "disconnect", "chatStateComposing", "getNewAccountDomain", "confirmationAnswer",
--- a/libervia_server/__init__.py Tue Feb 25 11:31:11 2014 +0100 +++ b/libervia_server/__init__.py Tue Feb 25 17:50:47 2014 +0100 @@ -280,6 +280,24 @@ else: raise Exception("Invalid data") + def jsonrpc_getMblogs(self, publisher_jid, item_ids): + """Get specified microblogs posted by a contact + @param publisher_jid: jid of the publisher + @param item_ids: list of microblogs items IDs + @return list of microblog data (dict)""" + profile = ISATSession(self.session).profile + d = self.asyncBridgeCall("getGroupBlogs", publisher_jid, item_ids, profile) + return d + + def jsonrpc_getMblogsWithComments(self, publisher_jid, item_ids): + """Get specified microblogs posted by a contact and their comments + @param publisher_jid: jid of the publisher + @param item_ids: list of microblogs items IDs + @return list of couple (microblog data, list of microblog data)""" + profile = ISATSession(self.session).profile + d = self.asyncBridgeCall("getGroupBlogsWithComments", publisher_jid, item_ids, profile) + return d + def jsonrpc_getLastMblogs(self, publisher_jid, max_item): """Get last microblogs posted by a contact @param publisher_jid: jid of the publisher
--- a/libervia_server/blog.py Tue Feb 25 11:31:11 2014 +0100 +++ b/libervia_server/blog.py Tue Feb 25 17:50:47 2014 +0100 @@ -17,6 +17,7 @@ # 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 @@ -25,6 +26,7 @@ from twisted.words.protocols.jabber.jid import JID from datetime import datetime from constants import Const +import uuid import re @@ -61,11 +63,21 @@ def got_jid(pub_jid_s): pub_jid = JID(pub_jid_s) d2 = defer.Deferred() - if len(request.postpath) > 1 and request.postpath[1] == 'atom.xml': - 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) - else: - d2.addCallbacks(self._render_html_blog, self.render_error_blog, [request, prof_found], None, [request, prof_found], None) + 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() @@ -74,21 +86,27 @@ 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""" + 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) + 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', Const.SERVER_SECURITY_LIMIT, profile, callback=check_banner, errback=eb) - def render_html_blog(self, mblog_data, style, request, profile): - """Actually rendering the static blog - @param mblog_data: list of microblog data + 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 @@ -96,43 +114,84 @@ 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="%(user)s/atom.xml"/> - <link rel="stylesheet" type="text/css" href="../css/blog.css" /> + <link rel="alternate" type="application/atom+xml" href="%(base)s/atom.xml"/> + <link rel="stylesheet" type="text/css" href="%(root)scss/blog.css" /> <title>%(user)s's microblog</title> </head> <body> - <div class='mblog_title'>%(banner_elt)s</div> - """ % {'user': user, 'banner_elt': banner_elt}) - mblog_data = sorted(mblog_data, key=lambda entry: (-float(entry.get('published', 0)))) + <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: - timestamp = float(entry.get('published', 0)) - _datetime = datetime.fromtimestamp(timestamp) - - 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 '' - - body = getText('content') - title = getText('title') - if title: - body = "<h1>%s</h1>\n%s" % (title, body) - request.write("""<div class='mblog_entry'><span class='mblog_timestamp'>%(date)s</span> - <span class='mblog_content'>%(content)s</span></div>""" % { - 'date': _datetime, - 'content': body}) + 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()
--- a/server_css/blog.css Tue Feb 25 11:31:11 2014 +0100 +++ b/server_css/blog.css Tue Feb 25 17:50:47 2014 +0100 @@ -16,8 +16,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -.mblog_title { +.mblog_title, .mblog_title a { text-align: center; + text-decoration: none; font-size: x-large; font-weight: bold; margin-bottom: 40px; @@ -39,11 +40,28 @@ color: rgb(51, 51, 51); } -.mblog_timestamp { - display: block; +.mblog_comment { +} + +.mblog_header { font-size: small; border-bottom: 1px dashed LightGrey; color: gray; + display: table; + width: 100%; +} + +.mblog_metadata { + display: table-row; + width: 100%; +} + +.mblog_author { + display: table-cell; +} + +.mblog_timestamp { + display: table-cell; text-align: right; } @@ -52,10 +70,19 @@ padding-top: 5px; } -h1, h2, h3, h4, h5, h6 { +.item_link { + text-decoration: none; +} + +.mblog_entry h1, h2, h3, h4, h5, h6 { border-bottom: 1px solid rgb(170, 170, 170); } +.mblog_entry h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { + text-decoration: none; + color: rgb(51, 51, 51); +} + img { max-width: 100%; }