Mercurial > libervia-web
comparison libervia_server/blog.py @ 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 | 35a43d0dc032 |
comparison
equal
deleted
inserted
replaced
388:893451e35686 | 389:2d782349b88a |
---|---|
15 # GNU Affero General Public License for more details. | 15 # GNU Affero General Public License for more details. |
16 | 16 |
17 # You should have received a copy of the GNU Affero General Public License | 17 # You should have received a copy of the GNU Affero General Public License |
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. | 18 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | 19 |
20 from sat.core.i18n import _ | |
20 from sat_frontends.tools.strings import addURLToText | 21 from sat_frontends.tools.strings import addURLToText |
21 from libervia_server.html_tools import sanitizeHtml | 22 from libervia_server.html_tools import sanitizeHtml |
22 from twisted.internet import defer | 23 from twisted.internet import defer |
23 from twisted.web import server | 24 from twisted.web import server |
24 from twisted.web.resource import Resource | 25 from twisted.web.resource import Resource |
25 from twisted.words.protocols.jabber.jid import JID | 26 from twisted.words.protocols.jabber.jid import JID |
26 from datetime import datetime | 27 from datetime import datetime |
27 from constants import Const | 28 from constants import Const |
29 import uuid | |
28 import re | 30 import re |
29 | 31 |
30 | 32 |
31 class MicroBlog(Resource): | 33 class MicroBlog(Resource): |
32 isLeaf = True | 34 isLeaf = True |
59 return MicroBlog.ERROR_TEMPLATE % "Invalid nickname" | 61 return MicroBlog.ERROR_TEMPLATE % "Invalid nickname" |
60 else: | 62 else: |
61 def got_jid(pub_jid_s): | 63 def got_jid(pub_jid_s): |
62 pub_jid = JID(pub_jid_s) | 64 pub_jid = JID(pub_jid_s) |
63 d2 = defer.Deferred() | 65 d2 = defer.Deferred() |
64 if len(request.postpath) > 1 and request.postpath[1] == 'atom.xml': | 66 item_id = None |
65 d2.addCallbacks(self.render_atom_feed, self.render_error_blog, [request], None, [request, prof_found], None) | 67 if len(request.postpath) > 1: |
66 self.host.bridge.getLastGroupBlogsAtom(pub_jid.userhost(), 10, 'libervia', d2.callback, d2.errback) | 68 if request.postpath[1] == 'atom.xml': # return the atom feed |
67 else: | 69 d2.addCallbacks(self.render_atom_feed, self.render_error_blog, [request], None, [request, prof_found], None) |
68 d2.addCallbacks(self._render_html_blog, self.render_error_blog, [request, prof_found], None, [request, prof_found], None) | 70 self.host.bridge.getLastGroupBlogsAtom(pub_jid.userhost(), 10, 'libervia', d2.callback, d2.errback) |
71 return | |
72 try: # check if the given path is a valid UUID | |
73 uuid.UUID(request.postpath[1]) | |
74 item_id = request.postpath[1] | |
75 except ValueError: | |
76 pass | |
77 d2.addCallbacks(self.render_html_blog, self.render_error_blog, [request, prof_found], None, [request, prof_found], None) | |
78 if item_id: # display one message and its comments | |
79 self.host.bridge.getGroupBlogsWithComments(pub_jid.userhost(), [item_id], 'libervia', d2.callback, d2.errback) | |
80 else: # display the last messages without comment | |
69 self.host.bridge.getLastGroupBlogs(pub_jid.userhost(), 10, 'libervia', d2.callback, d2.errback) | 81 self.host.bridge.getLastGroupBlogs(pub_jid.userhost(), 10, 'libervia', d2.callback, d2.errback) |
70 | 82 |
71 d1 = defer.Deferred() | 83 d1 = defer.Deferred() |
72 JID(self.host.bridge.asyncGetParamA('JabberID', 'Connection', 'value', Const.SERVER_SECURITY_LIMIT, prof_found, callback=d1.callback, errback=d1.errback)) | 84 JID(self.host.bridge.asyncGetParamA('JabberID', 'Connection', 'value', Const.SERVER_SECURITY_LIMIT, prof_found, callback=d1.callback, errback=d1.errback)) |
73 d1.addCallbacks(got_jid) | 85 d1.addCallbacks(got_jid) |
74 | 86 |
75 return server.NOT_DONE_YET | 87 return server.NOT_DONE_YET |
76 | 88 |
77 def _render_html_blog(self, mblog_data, request, profile): | 89 def render_html_blog(self, mblog_data, request, profile): |
78 """Retrieve the blog banner or other user's specific stuff before actually rendering the static blog""" | 90 """Retrieve the blog banner or other user's specific stuff before actually rendering the static blog |
91 @param mblog_data: list of microblog data or list of couple (microblog data, list of microblog data) | |
92 @param request: HTTP request | |
93 @param profile | |
94 """ | |
79 def check_banner(banner): | 95 def check_banner(banner): |
80 """Regexp from http://answers.oreilly.com/topic/280-how-to-validate-urls-with-regular-expressions/""" | 96 """Regexp from http://answers.oreilly.com/topic/280-how-to-validate-urls-with-regular-expressions/""" |
81 if re.match(r"^(https?|ftp)://[a-z0-9-]+(\.[a-z0-9-]+)+(/[\w-]+)*/[\w-]+\.(gif|png|jpg)$", banner): | 97 if re.match(r"^(https?|ftp)://[a-z0-9-]+(\.[a-z0-9-]+)+(/[\w-]+)*/[\w-]+\.(gif|png|jpg)$", banner): |
82 style = {'banner': banner} | 98 style = {'banner': banner} |
83 else: | 99 else: |
84 style = {} | 100 style = {} |
85 self.render_html_blog(mblog_data, style, request, profile) | 101 self.__render_html_blog(mblog_data, style, request, profile) |
86 eb = lambda failure: self.render_error_blog(failure, request, profile) | 102 eb = lambda failure: self.render_error_blog(failure, request, profile) |
87 self.host.bridge.asyncGetParamA('Blog banner', 'Misc', 'value', Const.SERVER_SECURITY_LIMIT, profile, callback=check_banner, errback=eb) | 103 self.host.bridge.asyncGetParamA('Blog banner', 'Misc', 'value', Const.SERVER_SECURITY_LIMIT, profile, callback=check_banner, errback=eb) |
88 | 104 |
89 def render_html_blog(self, mblog_data, style, request, profile): | 105 def __render_html_blog(self, mblog_data, style, request, profile): |
90 """Actually rendering the static blog | 106 """Actually render the static blog. If mblog_data is a list of dict, we are missing |
91 @param mblog_data: list of microblog data | 107 the comments items so we just display the main items. If mblog_data is a list of couple, |
108 each couple is associating a main item data with the list of its comments, so we render all. | |
109 @param mblog_data: list of microblog data or list of couple (microblog data, list of microblog data) | |
92 @param style: dict defining the blog's rendering parameters | 110 @param style: dict defining the blog's rendering parameters |
93 @param request: the HTTP request | 111 @param request: the HTTP request |
94 @profile | 112 @profile |
95 """ | 113 """ |
96 if not isinstance(style, dict): | 114 if not isinstance(style, dict): |
97 style = {} | 115 style = {} |
98 user = sanitizeHtml(profile).encode('utf-8') | 116 user = sanitizeHtml(profile).encode('utf-8') |
117 root_url = '../' * len(request.postpath) | |
118 base_url = root_url + 'blog/' + user | |
99 banner = style['banner'].encode('utf-8') if 'banner' in style else '' | 119 banner = style['banner'].encode('utf-8') if 'banner' in style else '' |
100 banner_elt = "<img src='%(banner)s' alt='%(user)s'/>" % {'user': user, 'banner': banner} if banner else user | 120 banner_elt = "<img src='%(banner)s' alt='%(user)s'/>" % {'user': user, 'banner': banner} if banner else user |
101 request.write(""" | 121 request.write(""" |
102 <html> | 122 <html> |
103 <head> | 123 <head> |
104 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> | 124 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> |
105 <link rel="alternate" type="application/atom+xml" href="%(user)s/atom.xml"/> | 125 <link rel="alternate" type="application/atom+xml" href="%(base)s/atom.xml"/> |
106 <link rel="stylesheet" type="text/css" href="../css/blog.css" /> | 126 <link rel="stylesheet" type="text/css" href="%(root)scss/blog.css" /> |
107 <title>%(user)s's microblog</title> | 127 <title>%(user)s's microblog</title> |
108 </head> | 128 </head> |
109 <body> | 129 <body> |
110 <div class='mblog_title'>%(banner_elt)s</div> | 130 <div class="mblog_title"><a href="%(base)s">%(banner_elt)s</a></div> |
111 """ % {'user': user, 'banner_elt': banner_elt}) | 131 """ % {'base': base_url, |
112 mblog_data = sorted(mblog_data, key=lambda entry: (-float(entry.get('published', 0)))) | 132 'root': root_url, |
133 'user': user, | |
134 'banner_elt': banner_elt}) | |
135 mblog_data = [(entry if isinstance(entry, tuple) else (entry, [])) for entry in mblog_data] | |
136 mblog_data = sorted(mblog_data, key=lambda entry: (-float(entry[0].get('published', 0)))) | |
113 for entry in mblog_data: | 137 for entry in mblog_data: |
114 timestamp = float(entry.get('published', 0)) | 138 self.__render_html_entry(entry[0], base_url, request) |
115 _datetime = datetime.fromtimestamp(timestamp) | 139 comments = sorted(entry[1], key=lambda entry: (float(entry.get('published', 0)))) |
116 | 140 for comment in comments: |
117 def getText(key): | 141 self.__render_html_entry(comment, base_url, request) |
118 if ('%s_xhtml' % key) in entry: | |
119 return entry['%s_xhtml' % key].encode('utf-8') | |
120 elif key in entry: | |
121 processor = addURLToText if key.startswith('content') else sanitizeHtml | |
122 return processor(entry[key]).encode('utf-8') | |
123 return '' | |
124 | |
125 body = getText('content') | |
126 title = getText('title') | |
127 if title: | |
128 body = "<h1>%s</h1>\n%s" % (title, body) | |
129 request.write("""<div class='mblog_entry'><span class='mblog_timestamp'>%(date)s</span> | |
130 <span class='mblog_content'>%(content)s</span></div>""" % { | |
131 'date': _datetime, | |
132 'content': body}) | |
133 request.write('</body></html>') | 142 request.write('</body></html>') |
134 request.finish() | 143 request.finish() |
144 | |
145 def __render_html_entry(self, entry, base_url, request): | |
146 """Render one microblog entry. | |
147 @param entry: the microblog entry | |
148 @param base_url: the base url of the blog | |
149 @param request: the HTTP request | |
150 """ | |
151 timestamp = float(entry.get('published', 0)) | |
152 datetime_ = datetime.fromtimestamp(timestamp) | |
153 is_comment = entry['type'] == 'comment' | |
154 if is_comment: | |
155 author = (_("comment from %s") % entry['author']).encode('utf-8') | |
156 item_link = '' | |
157 else: | |
158 author = ' ' | |
159 item_link = ("%(base)s/%(item_id)s" % {'base': base_url, 'item_id': entry['id']}).encode('utf-8') | |
160 | |
161 def getText(key): | |
162 if ('%s_xhtml' % key) in entry: | |
163 return entry['%s_xhtml' % key].encode('utf-8') | |
164 elif key in entry: | |
165 processor = addURLToText if key.startswith('content') else sanitizeHtml | |
166 return processor(entry[key]).encode('utf-8') | |
167 return '' | |
168 | |
169 def addMainItemLink(elem): | |
170 if not item_link or not elem: | |
171 return elem | |
172 return """<a href="%(link)s" class="item_link">%(elem)s</a>""" % {'link': item_link, 'elem': elem} | |
173 | |
174 header = addMainItemLink("""<div class="mblog_header"> | |
175 <div class="mblog_metadata"> | |
176 <div class="mblog_author">%(author)s</div> | |
177 <div class="mblog_timestamp">%(date)s</div> | |
178 </div> | |
179 </div>""" % {'author': author, 'date': datetime_}) | |
180 | |
181 title = addMainItemLink(getText('title')) | |
182 body = getText('content') | |
183 if title: # insert the title within the body | |
184 body = """<h1>%(title)s</h1>\n%(body)s""" % {'title': title, 'body': body} | |
185 | |
186 request.write("""<div class="mblog_entry %(extra_style)s"> | |
187 %(header)s | |
188 <span class="mblog_content">%(content)s</span> | |
189 </div>""" % | |
190 {'extra_style': 'mblog_comment' if entry['type'] == 'comment' else '', | |
191 'item_link': item_link, | |
192 'header': header, | |
193 'content': body}) | |
135 | 194 |
136 def render_atom_feed(self, feed, request): | 195 def render_atom_feed(self, feed, request): |
137 request.write(feed.encode('utf-8')) | 196 request.write(feed.encode('utf-8')) |
138 request.finish() | 197 request.finish() |
139 | 198 |