# HG changeset patch # User souliane # Date 1393347047 -3600 # Node ID 2d782349b88a3f1ccc00ddaabd9a64f260ceacf2 # Parent 893451e35686e3906c4e36f5a7635b384a1d85f0 server_side: display blog comments when you click on a main item header or title diff -r 893451e35686 -r 2d782349b88a libervia.py --- 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", diff -r 893451e35686 -r 2d782349b88a libervia_server/__init__.py --- 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 diff -r 893451e35686 -r 2d782349b88a libervia_server/blog.py --- 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 . +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 = "%(user)s" % {'user': user, 'banner': banner} if banner else user request.write(""" - - + + %(user)s's microblog -
%(banner_elt)s
- """ % {'user': user, 'banner_elt': banner_elt}) - mblog_data = sorted(mblog_data, key=lambda entry: (-float(entry.get('published', 0)))) + + """ % {'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 = "

%s

\n%s" % (title, body) - request.write("""
%(date)s - %(content)s
""" % { - '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('') 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 """%(elem)s""" % {'link': item_link, 'elem': elem} + + header = addMainItemLink("""
+ +
""" % {'author': author, 'date': datetime_}) + + title = addMainItemLink(getText('title')) + body = getText('content') + if title: # insert the title within the body + body = """

%(title)s

\n%(body)s""" % {'title': title, 'body': body} + + request.write("""
+ %(header)s + %(content)s +
""" % + {'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() diff -r 893451e35686 -r 2d782349b88a server_css/blog.css --- 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 . */ -.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%; }