Mercurial > libervia-web
comparison src/server/blog.py @ 586:3eb3a2c0c011
browser and server side: uses RSM (XEP-0059)
author | souliane <souliane@mailoo.org> |
---|---|
date | Fri, 28 Nov 2014 00:31:27 +0100 |
parents | e588335b6aa8 |
children | c8cca1a373dd |
comparison
equal
deleted
inserted
replaced
585:bade589dbd5a | 586:3eb3a2c0c011 |
---|---|
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.core.i18n import _, D_ |
21 from sat_frontends.tools.strings import addURLToText | 21 from sat_frontends.tools.strings import addURLToText |
22 from sat.core.log import getLogger | 22 from sat.core.log import getLogger |
23 log = getLogger(__name__) | 23 log = getLogger(__name__) |
24 | 24 |
25 from twisted.internet import defer | 25 from twisted.internet import defer |
128 else: | 128 else: |
129 def got_jid(pub_jid_s): | 129 def got_jid(pub_jid_s): |
130 pub_jid = JID(pub_jid_s) | 130 pub_jid = JID(pub_jid_s) |
131 d2 = defer.Deferred() | 131 d2 = defer.Deferred() |
132 item_id = None | 132 item_id = None |
133 try: | 133 atom = None |
134 max_items = int(request.args['max_items'][0]) | 134 rsm_ = {} |
135 except (ValueError, KeyError): | 135 |
136 max_items = 10 | |
137 if len(request.postpath) > 1: | 136 if len(request.postpath) > 1: |
138 if request.postpath[1] == 'atom.xml': # return the atom feed | 137 if request.postpath[1] == 'atom.xml': # return the atom feed |
139 d2.addCallbacks(self.render_atom_feed, self.render_error_blog, [request], None, [request, prof_found], None) | 138 atom = True |
140 self.host.bridge.getLastGroupBlogsAtom(pub_jid.userhost(), max_items, C.SERVICE_PROFILE, d2.callback, d2.errback) | 139 else: |
141 return | 140 try: # check if the given path is a valid UUID |
142 try: # check if the given path is a valid UUID | 141 uuid.UUID(request.postpath[1]) |
143 uuid.UUID(request.postpath[1]) | 142 item_id = request.postpath[1] |
144 item_id = request.postpath[1] | 143 except ValueError: |
145 except ValueError: | 144 pass |
146 pass | 145 |
146 # retrieve RSM request data from URL parameters | |
147 try: | |
148 max_items = int(request.args['max'][0]) | |
149 except (ValueError, KeyError): | |
150 max_items = C.RSM_MAX_ITEMS if item_id else C.RSM_MAX_COMMENTS | |
151 rsm_['max'] = unicode(max_items) | |
152 try: | |
153 rsm_['index'] = request.args['index'][0] | |
154 except (ValueError, KeyError): | |
155 try: | |
156 rsm_['before'] = request.args['before'][0] | |
157 except KeyError: | |
158 try: | |
159 rsm_['after'] = request.args['after'][0] | |
160 except KeyError: | |
161 pass | |
162 | |
163 if atom is not None: | |
164 d2.addCallbacks(self.render_atom_feed, self.render_error_blog, [request], None, [request, prof_found], None) | |
165 self.host.bridge.getGroupBlogsAtom(pub_jid.userhost(), rsm_, C.SERVICE_PROFILE, d2.callback, d2.errback) | |
166 return | |
167 | |
147 d2.addCallbacks(self.render_html_blog, self.render_error_blog, [request, prof_found], None, [request, prof_found], None) | 168 d2.addCallbacks(self.render_html_blog, self.render_error_blog, [request, prof_found], None, [request, prof_found], None) |
148 if item_id: # display one message and its comments | 169 if item_id: |
149 self.host.bridge.getGroupBlogsWithComments(pub_jid.userhost(), [item_id], C.SERVICE_PROFILE, d2.callback, d2.errback) | 170 if max_items > 0: # display one message and its comments |
150 else: # display the last messages without comment | 171 self.host.bridge.getGroupBlogsWithComments(pub_jid.userhost(), [item_id], {}, max_items, C.SERVICE_PROFILE, d2.callback, d2.errback) |
151 self.host.bridge.getLastGroupBlogs(pub_jid.userhost(), max_items, C.SERVICE_PROFILE, d2.callback, d2.errback) | 172 else: # display one message, count its comments |
173 self.host.bridge.getGroupBlogs(pub_jid.userhost(), [item_id], {}, True, C.SERVICE_PROFILE, d2.callback, d2.errback) | |
174 else: | |
175 if max_items == 1: # display one message and its comments | |
176 self.host.bridge.getGroupBlogsWithComments(pub_jid.userhost(), [], rsm_, C.RSM_MAX_COMMENTS, C.SERVICE_PROFILE, d2.callback, d2.errback) | |
177 else: # display the last messages, count their comments | |
178 self.host.bridge.getGroupBlogs(pub_jid.userhost(), [], rsm_, True, C.SERVICE_PROFILE, d2.callback, d2.errback) | |
152 | 179 |
153 d1 = defer.Deferred() | 180 d1 = defer.Deferred() |
154 JID(self.host.bridge.asyncGetParamA('JabberID', 'Connection', 'value', C.SERVER_SECURITY_LIMIT, prof_found, callback=d1.callback, errback=d1.errback)) | 181 JID(self.host.bridge.asyncGetParamA('JabberID', 'Connection', 'value', C.SERVER_SECURITY_LIMIT, prof_found, callback=d1.callback, errback=d1.errback)) |
155 d1.addCallbacks(got_jid) | 182 d1.addCallbacks(got_jid) |
156 | 183 |
157 return server.NOT_DONE_YET | 184 return server.NOT_DONE_YET |
158 | 185 |
159 def render_html_blog(self, mblog_data, request, profile): | 186 def render_html_blog(self, mblog_data, request, profile): |
160 """Retrieve the user parameters before actually rendering the static blog | 187 """Retrieve the user parameters before actually rendering the static blog |
161 @param mblog_data: list of microblog data or list of couple (microblog data, list of microblog data) | 188 |
189 @param mblog_data (list): couple (list, dict) with: | |
190 - a list of microblog data, or a list of couple containing: | |
191 - microblog data (main item) | |
192 - couple (comments data, RSM response data for the comments) | |
193 - RSM response data for the main items | |
162 @param request: HTTP request | 194 @param request: HTTP request |
163 @param profile | 195 @param profile |
164 """ | 196 """ |
165 d_list = [] | 197 d_list = [] |
166 options = {} | 198 options = {} |
182 | 214 |
183 def __render_html_blog(self, mblog_data, options, request, profile): | 215 def __render_html_blog(self, mblog_data, options, request, profile): |
184 """Actually render the static blog. If mblog_data is a list of dict, we are missing | 216 """Actually render the static blog. If mblog_data is a list of dict, we are missing |
185 the comments items so we just display the main items. If mblog_data is a list of couple, | 217 the comments items so we just display the main items. If mblog_data is a list of couple, |
186 each couple is associating a main item data with the list of its comments, so we render all. | 218 each couple is associating a main item data with the list of its comments, so we render all. |
187 @param mblog_data: list of microblog data or list of couple (microblog data, list of microblog data) | 219 |
220 @param mblog_data (list): couple (list, dict) with: | |
221 - a list of microblog data, or a list of couple containing: | |
222 - microblog data (main item) | |
223 - couple (comments data, RSM response data for the comments) | |
224 - RSM response data for the main items | |
188 @param options: dict defining the blog's parameters | 225 @param options: dict defining the blog's parameters |
189 @param request: the HTTP request | 226 @param request: the HTTP request |
190 @profile | 227 @profile |
191 """ | 228 """ |
192 if not isinstance(options, dict): | 229 if not isinstance(options, dict): |
223 'keywords': getOption(C.STATIC_BLOG_PARAM_KEYWORDS), | 260 'keywords': getOption(C.STATIC_BLOG_PARAM_KEYWORDS), |
224 'description': getOption(C.STATIC_BLOG_PARAM_DESCRIPTION), | 261 'description': getOption(C.STATIC_BLOG_PARAM_DESCRIPTION), |
225 'title': getOption(C.STATIC_BLOG_PARAM_TITLE) or "%s's microblog" % user, | 262 'title': getOption(C.STATIC_BLOG_PARAM_TITLE) or "%s's microblog" % user, |
226 'favicon': os.path.normpath(root_url + getOption('avatar')), | 263 'favicon': os.path.normpath(root_url + getOption('avatar')), |
227 'banner_elt': getImageOption(C.STATIC_BLOG_PARAM_BANNER, getOption(C.STATIC_BLOG_PARAM_TITLE) or user)}) | 264 'banner_elt': getImageOption(C.STATIC_BLOG_PARAM_BANNER, getOption(C.STATIC_BLOG_PARAM_TITLE) or user)}) |
228 mblog_data = [(entry if isinstance(entry, tuple) else (entry, [])) for entry in mblog_data] | 265 mblog_data, main_rsm = mblog_data |
229 mblog_data = sorted(mblog_data, key=lambda entry: (-float(entry[0].get('published', 0)))) | 266 display_single = len(mblog_data) == 1 |
230 for entry in mblog_data: | 267 |
231 self.__render_html_entry(entry[0], base_url, request) | 268 # build the navigation links |
232 comments = sorted(entry[1], key=lambda entry: (float(entry.get('published', 0)))) | 269 count = int(main_rsm['count']) if 'count' in main_rsm else 0 |
270 if count > 0: | |
271 index = int(main_rsm['index']) | |
272 if index > 0: | |
273 before_link = ("%(base)s?before=%(item_id)s" % {'base': base_url, 'item_id': main_rsm['first']}).encode('utf-8') | |
274 if display_single: | |
275 before_link += '&max=1' | |
276 tmp_text = D_("Later message") | |
277 class_ = 'later_message' | |
278 else: | |
279 tmp_text = D_("Later messages") | |
280 class_ = 'later_messages' | |
281 before_tag = """<a href="%(link)s" class="%(class)s">%(text)s</a>""" % {'link': before_link, 'class': class_, 'text': tmp_text} | |
282 else: | |
283 before_tag = None | |
284 if index + len(mblog_data) < count: | |
285 after_link = ("%(base)s?after=%(item_id)s" % {'base': base_url, 'item_id': main_rsm['last']}).encode('utf-8') | |
286 if display_single: | |
287 after_link += '&max=1' | |
288 text = D_("Older message") | |
289 class_ = 'older_message' | |
290 else: | |
291 text = D_("Older messages") | |
292 class_ = 'older_messages' | |
293 after_tag = """<a href="%(link)s" class="%(class)s">%(text)s</a>""" % {'link': after_link, 'class': class_, 'text': text} | |
294 else: | |
295 after_tag = None | |
296 | |
297 # display navigation header | |
298 request.write("""<div class="header">""") | |
299 if before_tag: | |
300 request.write(before_tag) | |
301 request.write(" ") | |
302 if display_single and after_tag: | |
303 request.write(after_tag) | |
304 request.write("""</div>""") | |
305 | |
306 mblog_data = [(entry if isinstance(entry, tuple) else (entry, ([], {}))) for entry in mblog_data] | |
307 mblog_data = sorted(mblog_data, key=lambda entry: (-float(entry[0].get('updated', 0)))) | |
308 for main_data, comments_data in mblog_data: | |
309 self.__render_html_entry(main_data, base_url, request) | |
310 comments, comments_rsm = comments_data | |
311 | |
312 # eventually display the link to show all comments | |
313 comments_count = int(main_data['comments_count']) | |
314 delta = comments_count - len(comments) | |
315 if display_single and delta > 0: | |
316 link = ("%(base)s/%(item_id)s?max=%(max)s" % {'base': base_url, | |
317 'item_id': main_data['id'], | |
318 'max': main_data['comments_count']}).encode('utf-8') | |
319 text = D_("Show %(count)d previous %(comments)s") % {'count': delta, | |
320 'comments': D_('comments') if delta > 1 else D_('comment')} | |
321 request.write("""<a href="%(link)s" class="comments_link">%(text)s</a>""" % {'link': link, 'text': text}) | |
322 | |
323 comments = sorted(comments, key=lambda entry: (float(entry.get('published', 0)))) | |
233 for comment in comments: | 324 for comment in comments: |
234 self.__render_html_entry(comment, base_url, request) | 325 self.__render_html_entry(comment, base_url, request) |
326 | |
327 # display navigation footer | |
328 request.write("""<div class="footer">""") | |
329 if not display_single and after_tag: | |
330 request.write(after_tag) | |
331 request.write("""</div>""") | |
332 | |
235 request.write('</body></html>') | 333 request.write('</body></html>') |
236 request.finish() | 334 request.finish() |
237 | 335 |
238 def __render_html_entry(self, entry, base_url, request): | 336 def __render_html_entry(self, entry, base_url, request): |
239 """Render one microblog entry. | 337 """Render one microblog entry. |
242 @param request: the HTTP request | 340 @param request: the HTTP request |
243 """ | 341 """ |
244 timestamp = float(entry.get('published', 0)) | 342 timestamp = float(entry.get('published', 0)) |
245 datetime_ = datetime.fromtimestamp(timestamp) | 343 datetime_ = datetime.fromtimestamp(timestamp) |
246 is_comment = entry['type'] == 'comment' | 344 is_comment = entry['type'] == 'comment' |
247 if is_comment: | |
248 author = (_("comment from %s") % entry['author']).encode('utf-8') | |
249 item_link = '' | |
250 else: | |
251 author = ' ' | |
252 item_link = ("%(base)s/%(item_id)s" % {'base': base_url, 'item_id': entry['id']}).encode('utf-8') | |
253 | 345 |
254 def getText(key): | 346 def getText(key): |
255 if ('%s_xhtml' % key) in entry: | 347 if ('%s_xhtml' % key) in entry: |
256 return entry['%s_xhtml' % key].encode('utf-8') | 348 return entry['%s_xhtml' % key].encode('utf-8') |
257 elif key in entry: | 349 elif key in entry: |
262 def addMainItemLink(elem): | 354 def addMainItemLink(elem): |
263 if not item_link or not elem: | 355 if not item_link or not elem: |
264 return elem | 356 return elem |
265 return """<a href="%(link)s" class="item_link">%(elem)s</a>""" % {'link': item_link, 'elem': elem} | 357 return """<a href="%(link)s" class="item_link">%(elem)s</a>""" % {'link': item_link, 'elem': elem} |
266 | 358 |
267 header = addMainItemLink("""<div class="mblog_header"> | 359 if is_comment: |
268 <div class="mblog_metadata"> | 360 author = (_("from %s") % entry['author']).encode('utf-8') |
269 <div class="mblog_author">%(author)s</div> | 361 item_link = '' |
270 <div class="mblog_timestamp">%(date)s</div> | 362 footer = '' |
271 </div> | 363 else: |
272 </div>""" % {'author': author, 'date': datetime_}) | 364 author = ' ' |
365 item_link = ("%(base)s/%(item_id)s" % {'base': base_url, 'item_id': entry['id']}).encode('utf-8') | |
366 comments_count = int(entry['comments_count']) | |
367 comments_text = (D_('comments') if comments_count > 1 else D_('comment')).encode('utf-8') | |
368 footer = addMainItemLink("""<div class="mblog_footer mblog_footer_main"> | |
369 <div class="mblog_metadata"> | |
370 <div class="mblog_comments">%(count)s %(comments)s</div> | |
371 </div> | |
372 </div>""" % {'count': comments_count, | |
373 'comments': comments_text}) | |
374 | |
375 header = """<div class="mblog_header %(class)s"> | |
376 <div class="mblog_metadata"> | |
377 <div class="mblog_author">%(author)s</div> | |
378 <div class="mblog_timestamp">%(date)s</div> | |
379 </div> | |
380 </div>""" % {'author': author, 'date': datetime_, | |
381 'class': '' if is_comment else 'mblog_header_main'} | |
382 if not is_comment: | |
383 header = addMainItemLink(header) | |
273 | 384 |
274 title = addMainItemLink(getText('title')) | 385 title = addMainItemLink(getText('title')) |
275 body = getText('content') | 386 body = getText('content') |
276 if title: # insert the title within the body | 387 if title: # insert the title within the body |
277 body = """<h1>%(title)s</h1>\n%(body)s""" % {'title': title, 'body': body} | 388 body = """<h1>%(title)s</h1>\n%(body)s""" % {'title': title, 'body': body} |
278 | 389 |
279 request.write("""<div class="mblog_entry %(extra_style)s"> | 390 request.write("""<div class="mblog_entry %(extra_style)s"> |
280 %(header)s | 391 %(header)s |
281 <span class="mblog_content">%(content)s</span> | 392 <span class="mblog_content">%(content)s</span> |
282 </div>""" % | 393 %(footer)s |
283 {'extra_style': 'mblog_comment' if entry['type'] == 'comment' else '', | 394 </div>""" % {'extra_style': 'mblog_comment' if entry['type'] == 'comment' else '', |
284 'item_link': item_link, | 395 'item_link': item_link, |
285 'header': header, | 396 'header': header, |
286 'content': body}) | 397 'content': body, |
398 'footer': footer}) | |
287 | 399 |
288 def render_atom_feed(self, feed, request): | 400 def render_atom_feed(self, feed, request): |
289 request.write(feed.encode('utf-8')) | 401 request.write(feed.encode('utf-8')) |
290 request.finish() | 402 request.finish() |
291 | 403 |