[MERGE] forward port of branch 8.0 up to 491372e
[odoo/odoo.git] / addons / website_forum / controllers / main.py
1 # -*- coding: utf-8 -*-
2
3 import werkzeug.urls
4 import werkzeug.wrappers
5 import simplejson
6 import lxml
7 from urllib2 import urlopen
8
9 from openerp import tools
10 from openerp.addons.web import http
11 from openerp.addons.web.controllers.main import login_redirect
12 from openerp.addons.web.http import request
13 from openerp.addons.website.controllers.main import Website as controllers
14 from openerp.addons.website.models.website import slug
15
16 controllers = controllers()
17
18
19 class WebsiteForum(http.Controller):
20     _post_per_page = 10
21     _user_per_page = 30
22
23     def _get_notifications(self):
24         badge_subtype = request.env.ref('gamification.mt_badge_granted')
25         if badge_subtype:
26             msg = request.env['mail.message'].search([('subtype_id', '=', badge_subtype.id), ('to_read', '=', True)])
27         else:
28             msg = list()
29         return msg
30
31     def _prepare_forum_values(self, forum=None, **kwargs):
32         values = {
33             'user': request.env.user,
34             'is_public_user': request.env.user.id == request.website.user_id.id,
35             'notifications': self._get_notifications(),
36             'header': kwargs.get('header', dict()),
37             'searches': kwargs.get('searches', dict()),
38             'no_introduction_message': request.httprequest.cookies.get('no_introduction_message', False),
39             'validation_email_sent': request.session.get('validation_email_sent', False),
40             'validation_email_done': request.session.get('validation_email_done', False),
41         }
42         if forum:
43             values['forum'] = forum
44         elif kwargs.get('forum_id'):
45             values['forum'] = request.env['forum.forum'].browse(kwargs.pop('forum_id'))
46         values.update(kwargs)
47         return values
48
49     # User and validation
50     # --------------------------------------------------
51
52     @http.route('/forum/send_validation_email', type='json', auth='user', website=True)
53     def send_validation_email(self, forum_id=None, **kwargs):
54         request.env['res.users'].send_forum_validation_email(request.uid, forum_id=forum_id)
55         request.session['validation_email_sent'] = True
56         return True
57
58     @http.route('/forum/validate_email', type='http', auth='public', website=True)
59     def validate_email(self, token, id, email, forum_id=None, **kwargs):
60         if forum_id:
61             try:
62                 forum_id = int(forum_id)
63             except ValueError:
64                 forum_id = None
65         done = request.env['res.users'].process_forum_validation_token(token, int(id), email, forum_id=forum_id)
66         if done:
67             request.session['validation_email_done'] = True
68         if forum_id:
69             return request.redirect("/forum/%s" % int(forum_id))
70         return request.redirect('/forum')
71
72     @http.route('/forum/validate_email/close', type='json', auth='public', website=True)
73     def validate_email_done(self):
74         request.session['validation_email_done'] = False
75         return True
76
77     # Forum
78     # --------------------------------------------------
79
80     @http.route(['/forum'], type='http', auth="public", website=True)
81     def forum(self, **kwargs):
82         forums = request.env['forum.forum'].search([])
83         return request.website.render("website_forum.forum_all", {'forums': forums})
84
85     @http.route('/forum/new', type='http', auth="user", methods=['POST'], website=True)
86     def forum_create(self, forum_name="New Forum", **kwargs):
87         forum_id = request.env['forum.forum'].create({'name': forum_name})
88         return request.redirect("/forum/%s" % slug(forum_id))
89
90     @http.route('/forum/notification_read', type='json', auth="user", methods=['POST'], website=True)
91     def notification_read(self, **kwargs):
92         request.env['mail.message'].browse([int(kwargs.get('notification_id'))]).set_message_read(read=True)
93         return True
94
95     @http.route(['/forum/<model("forum.forum"):forum>',
96                  '/forum/<model("forum.forum"):forum>/page/<int:page>',
97                  '''/forum/<model("forum.forum"):forum>/tag/<model("forum.tag", "[('forum_id','=',forum[0])]"):tag>/questions''',
98                  '''/forum/<model("forum.forum"):forum>/tag/<model("forum.tag", "[('forum_id','=',forum[0])]"):tag>/questions/page/<int:page>''',
99                  ], type='http', auth="public", website=True)
100     def questions(self, forum, tag=None, page=1, filters='all', sorting=None, search='', post_type=None, **post):
101         Post = request.env['forum.post']
102
103         domain = [('forum_id', '=', forum.id), ('parent_id', '=', False), ('state', '=', 'active')]
104         if search:
105             domain += ['|', ('name', 'ilike', search), ('content', 'ilike', search)]
106         if tag:
107             domain += [('tag_ids', 'in', tag.id)]
108         if filters == 'unanswered':
109             domain += [('child_ids', '=', False)]
110         elif filters == 'followed':
111             domain += [('message_follower_ids', '=', request.env.user.partner_id.id)]
112         if post_type:
113             domain += [('post_type', '=', post_type)]
114
115         if not sorting:
116             sorting = forum.default_order
117
118         question_count = Post.search_count(domain)
119
120         if tag:
121             url = "/forum/%s/tag/%s/questions" % (slug(forum), slug(tag))
122         else:
123             url = "/forum/%s" % slug(forum)
124
125         url_args = {
126             'sorting': sorting
127         }
128         if search:
129             url_args['search'] = search
130         if filters:
131             url_args['filters'] = filters
132         pager = request.website.pager(url=url, total=question_count, page=page,
133                                       step=self._post_per_page, scope=self._post_per_page,
134                                       url_args=url_args)
135
136         question_ids = Post.search(domain, limit=self._post_per_page, offset=pager['offset'], order=sorting)
137
138         values = self._prepare_forum_values(forum=forum, searches=post)
139         values.update({
140             'main_object': tag or forum,
141             'question_ids': question_ids,
142             'question_count': question_count,
143             'pager': pager,
144             'tag': tag,
145             'filters': filters,
146             'sorting': sorting,
147             'search': search,
148             'post_type': post_type,
149         })
150         return request.website.render("website_forum.forum_index", values)
151
152     @http.route(['/forum/<model("forum.forum"):forum>/faq'], type='http', auth="public", website=True)
153     def forum_faq(self, forum, **post):
154         values = self._prepare_forum_values(forum=forum, searches=dict(), header={'is_guidelines': True}, **post)
155         return request.website.render("website_forum.faq", values)
156
157     @http.route('/forum/get_tags', type='http', auth="public", methods=['GET'], website=True)
158     def tag_read(self, **post):
159         tags = request.env['forum.tag'].search_read([], ['name'])
160         data = [tag['name'] for tag in tags]
161         return simplejson.dumps(data)
162
163     @http.route(['/forum/<model("forum.forum"):forum>/tag'], type='http', auth="public", website=True)
164     def tags(self, forum, page=1, **post):
165         tags = request.env['forum.tag'].search([('forum_id', '=', forum.id), ('posts_count', '>', 0)], limit=None, order='posts_count DESC')
166         values = self._prepare_forum_values(forum=forum, searches={'tags': True}, **post)
167         values.update({
168             'tags': tags,
169             'main_object': forum,
170         })
171         return request.website.render("website_forum.tag", values)
172
173     # Questions
174     # --------------------------------------------------
175
176     @http.route('/forum/get_url_title', type='json', auth="user", methods=['POST'], website=True)
177     def get_url_title(self, **kwargs):
178         arch = lxml.html.parse(urlopen(kwargs.get('url')))
179         return arch.find(".//title").text
180
181     @http.route(['''/forum/<model("forum.forum"):forum>/question/<model("forum.post", "[('forum_id','=',forum[0]),('parent_id','=',False)]"):question>'''], type='http', auth="public", website=True)
182     def question(self, forum, question, **post):
183         # increment view counter
184         question.sudo().set_viewed()
185         if question.parent_id:
186             redirect_url = "/forum/%s/question/%s" % (slug(forum), slug(question.parent_id))
187             return werkzeug.utils.redirect(redirect_url, 301)
188         filters = 'question'
189         values = self._prepare_forum_values(forum=forum, searches=post)
190         values.update({
191             'main_object': question,
192             'question': question,
193             'header': {'question_data': True},
194             'filters': filters,
195             'reversed': reversed,
196         })
197         return request.website.render("website_forum.post_description_full", values)
198
199     @http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/toggle_favourite', type='json', auth="user", methods=['POST'], website=True)
200     def question_toggle_favorite(self, forum, question, **post):
201         if not request.session.uid:
202             return {'error': 'anonymous_user'}
203         # TDE: add check for not public
204         favourite = False if question.user_favourite else True
205         if favourite:
206             favourite_ids = [(4, request.uid)]
207         else:
208             favourite_ids = [(3, request.uid)]
209         question.sudo().write({'favourite_ids': favourite_ids})
210         return favourite
211
212     @http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/ask_for_close', type='http', auth="user", methods=['POST'], website=True)
213     def question_ask_for_close(self, forum, question, **post):
214         reasons = request.env['forum.post.reason'].search([])
215
216         values = self._prepare_forum_values(**post)
217         values.update({
218             'question': question,
219             'question': question,
220             'forum': forum,
221             'reasons': reasons,
222         })
223         return request.website.render("website_forum.close_post", values)
224
225     @http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/edit_answer', type='http', auth="user", website=True)
226     def question_edit_answer(self, forum, question, **kwargs):
227         for record in question.child_ids:
228             if record.create_uid.id == request.uid:
229                 answer = record
230                 break
231         return werkzeug.utils.redirect("/forum/%s/post/%s/edit" % (slug(forum), slug(answer)))
232
233     @http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/close', type='http', auth="user", methods=['POST'], website=True)
234     def question_close(self, forum, question, **post):
235         question.close(reason_id=int(post.get('reason_id', False)))
236         return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question)))
237
238     @http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/reopen', type='http', auth="user", methods=['POST'], website=True)
239     def question_reopen(self, forum, question, **kwarg):
240         question.reopen()
241         return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question)))
242
243     @http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/delete', type='http', auth="user", methods=['POST'], website=True)
244     def question_delete(self, forum, question, **kwarg):
245         question.active = False
246         return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question)))
247
248     @http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/undelete', type='http', auth="user", methods=['POST'], website=True)
249     def question_undelete(self, forum, question, **kwarg):
250         question.active = True
251         return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question)))
252
253     # Post
254     # --------------------------------------------------
255
256     @http.route(['/forum/<model("forum.forum"):forum>/ask'], type='http', auth="public", website=True)
257     def forum_post(self, forum, post_type=None, **post):
258         if not request.session.uid:
259             return login_redirect()
260         user = request.env.user
261         if not post_type in ['question', 'link', 'discussion']:  # fixme: make dynamic
262             return werkzeug.utils.redirect('/forum/%s' % slug(forum))
263         if not user.email or not tools.single_email_re.match(user.email):
264             return werkzeug.utils.redirect("/forum/%s/user/%s/edit?email_required=1" % (slug(forum), self._uid))
265         values = self._prepare_forum_values(forum=forum, searches={},  header={'ask_hide': True})
266         return request.website.render("website_forum.new_%s" % post_type, values)
267
268     @http.route(['/forum/<model("forum.forum"):forum>/new',
269                  '/forum/<model("forum.forum"):forum>/<model("forum.post"):post_parent>/reply'],
270                 type='http', auth="public", methods=['POST'], website=True)
271     def post_create(self, forum, post_parent=None, post_type=None, **post):
272         cr, uid, context = request.cr, request.uid, request.context
273         if not request.session.uid:
274             return login_redirect()
275
276         post_tag_ids = []
277         Tag = request.env['forum.tag']
278         if post.get('post_tags', False) and post.get('post_tags').strip('[]'):
279             tags = post.get('post_tags').strip('[]').replace('"', '').split(",")
280             for tag in tags:
281                 tag_rec = Tag.search([('name', '=', tag)])
282                 if tag_rec:
283                     post_tag_ids.append((4, tag_rec.id))
284                 else:
285                     post_tag_ids.append((0, 0, {'name': tag, 'forum_id': forum.id}))
286
287         new_question = request.env['forum.post'].create({
288             'forum_id': forum.id,
289             'name': post.get('post_name', ''),
290             'content': post.get('content', False),
291             'content_link': post.get('content_link', False),
292             'parent_id': post_parent and post_parent.id or False,
293             'tag_ids': post_tag_ids,
294             'post_type': post_parent and post_parent.post_type or post_type,  # tde check in selection field
295         })
296         return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), post_parent and slug(post_parent) or new_question.id))
297
298     @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/comment', type='http', auth="public", methods=['POST'], website=True)
299     def post_comment(self, forum, post, **kwargs):
300         if not request.session.uid:
301             return login_redirect()
302         question = post.parent_id if post.parent_id else post
303         if kwargs.get('comment') and post.forum_id.id == forum.id:
304             # TDE FIXME: check that post_id is the question or one of its answers
305             post.with_context(mail_create_nosubcribe=True).message_post(
306                 body=kwargs.get('comment'),
307                 type='comment',
308                 subtype='mt_comment')
309         return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question)))
310
311     @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/toggle_correct', type='json', auth="public", website=True)
312     def post_toggle_correct(self, forum, post, **kwargs):
313         if post.parent_id is False:
314             return request.redirect('/')
315         if not request.session.uid:
316             return {'error': 'anonymous_user'}
317
318         # set all answers to False, only one can be accepted
319         (post.parent_id.child_ids - post).write(dict(is_correct=False))
320         post.is_correct = not post.is_correct
321         return post.is_correct
322
323     @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/delete', type='http', auth="user", methods=['POST'], website=True)
324     def post_delete(self, forum, post, **kwargs):
325         question = post.parent_id
326         post.unlink()
327         if question:
328             werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question)))
329         return werkzeug.utils.redirect("/forum/%s" % slug(forum))
330
331     @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/edit', type='http', auth="user", website=True)
332     def post_edit(self, forum, post, **kwargs):
333         tags = ""
334         for tag_name in post.tag_ids:
335             tags += tag_name.name + ","
336         values = self._prepare_forum_values(forum=forum)
337         values.update({
338             'tags': tags,
339             'post': post,
340             'is_answer': bool(post.parent_id),
341             'searches': kwargs
342         })
343         return request.website.render("website_forum.edit_post", values)
344
345     @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/save', type='http', auth="user", methods=['POST'], website=True)
346     def post_save(self, forum, post, **kwargs):
347         post_tags = []
348         if kwargs.get('post_tag') and kwargs.get('post_tag').strip('[]'):
349             Tag = request.env['forum.tag']
350             tags = kwargs.get('post_tag').strip('[]').replace('"', '').split(",")
351             for tag in tags:
352                 tag_rec = Tag.search([('name', '=', tag)])
353                 if tag_rec:
354                     post_tags += tag_rec.ids
355                 else:
356                     new_tag = Tag.create({'name': tag, 'forum_id': forum.id})
357                     post_tags.append(new_tag.id)
358         vals = {
359             'tag_ids': [(6, 0, post_tags)],
360             'name': kwargs.get('post_name'),
361             'content': kwargs.get('content'),
362         }
363         post.write(vals)
364         question = post.parent_id if post.parent_id else post
365         return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question)))
366
367     @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/upvote', type='json', auth="public", website=True)
368     def post_upvote(self, forum, post, **kwargs):
369         if not request.session.uid:
370             return {'error': 'anonymous_user'}
371         if request.uid == post.create_uid.id:
372             return {'error': 'own_post'}
373         upvote = True if not post.user_vote > 0 else False
374         return post.vote(upvote=upvote)
375
376     @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/downvote', type='json', auth="public", website=True)
377     def post_downvote(self, forum, post, **kwargs):
378         if not request.session.uid:
379             return {'error': 'anonymous_user'}
380         if request.uid == post.create_uid.id:
381             return {'error': 'own_post'}
382         upvote = True if post.user_vote < 0 else False
383         return post.vote(upvote=upvote)
384
385     # User
386     # --------------------------------------------------
387
388     @http.route(['/forum/<model("forum.forum"):forum>/users',
389                  '/forum/<model("forum.forum"):forum>/users/page/<int:page>'],
390                 type='http', auth="public", website=True)
391     def users(self, forum, page=1, **searches):
392         User = request.env['res.users']
393         step = 30
394         tag_count = len(User.search([('karma', '>', 1), ('website_published', '=', True)]))
395         pager = request.website.pager(url="/forum/%s/users" % slug(forum), total=tag_count, page=page, step=step, scope=30)
396         user_obj = User.sudo().search([('karma', '>', 1), ('website_published', '=', True)], limit=step, offset=pager['offset'], order='karma DESC')
397         # put the users in block of 3 to display them as a table
398         users = [[] for i in range(len(user_obj) / 3 + 1)]
399         for index, user in enumerate(user_obj):
400             users[index / 3].append(user)
401         searches['users'] = 'True'
402
403         values = self._prepare_forum_values(forum=forum, searches=searches)
404         values .update({
405             'users': users,
406             'main_object': forum,
407             'notifications': self._get_notifications(),
408             'pager': pager,
409         })
410
411         return request.website.render("website_forum.users", values)
412
413     @http.route(['/forum/<model("forum.forum"):forum>/partner/<int:partner_id>'], type='http', auth="public", website=True)
414     def open_partner(self, forum, partner_id=0, **post):
415         if partner_id:
416             partner = request.env['res.partner'].sudo().search([('id', '=', partner_id)])
417             if partner and partner.user_ids:
418                 return werkzeug.utils.redirect("/forum/%s/user/%d" % (slug(forum), partner.user_ids[0].id))
419         return werkzeug.utils.redirect("/forum/%s" % slug(forum))
420
421     @http.route(['/forum/user/<int:user_id>/avatar'], type='http', auth="public", website=True)
422     def user_avatar(self, user_id=0, **post):
423         response = werkzeug.wrappers.Response()
424         User = request.env['res.users']
425         Website = request.env['website']
426         user = User.sudo().search([('id', '=', user_id)])
427         if not user.exists() or (user_id != request.session.uid and user.karma < 1):
428             return Website._image_placeholder(response)
429         return Website._image('res.users', user.id, 'image', response)
430
431     @http.route(['/forum/<model("forum.forum"):forum>/user/<int:user_id>'], type='http', auth="public", website=True)
432     def open_user(self, forum, user_id=0, **post):
433         User = request.env['res.users']
434         Post = request.env['forum.post']
435         Vote = request.env['forum.post.vote']
436         Activity = request.env['mail.message']
437         Followers = request.env['mail.followers']
438         Data = request.env["ir.model.data"]
439
440         user = User.sudo().search([('id', '=', user_id)])
441         if not user or user.karma < 1:
442             return werkzeug.utils.redirect("/forum/%s" % slug(forum))
443         values = self._prepare_forum_values(forum=forum, **post)
444         if user_id != request.session.uid and not user.website_published:
445             return request.website.render("website_forum.private_profile", values)
446         # questions and answers by user
447         user_questions, user_answers = [], []
448         user_question_ids = Post.search([
449             ('parent_id', '=', False),
450             ('forum_id', '=', forum.id), ('create_uid', '=', user.id)],
451             order='create_date desc')
452         count_user_questions = len(user_question_ids)
453         # displaying only the 20 most recent questions
454         user_questions = user_question_ids[:20]
455
456         user_answer_ids = Post.search([
457             ('parent_id', '!=', False),
458             ('forum_id', '=', forum.id), ('create_uid', '=', user.id)],
459             order='create_date desc')
460         count_user_answers = len(user_answer_ids)
461         # displaying only the 20  most recent answers
462         user_answers = user_answer_ids[:20]
463
464         # showing questions which user following
465         post_ids = [follower.res_id for follower in Followers.sudo().search([('res_model', '=', 'forum.post'), ('partner_id', '=', user.partner_id.id)])]
466         followed = Post.search([('id', 'in', post_ids), ('forum_id', '=', forum.id), ('parent_id', '=', False)])
467
468         # showing Favourite questions of user.
469         favourite = Post.search([('favourite_ids', '=', user.id), ('forum_id', '=', forum.id), ('parent_id', '=', False)])
470
471         # votes which given on users questions and answers.
472         data = Vote.read_group([('forum_id', '=', forum.id), ('recipient_id', '=', user.id)], ["vote"], groupby=["vote"])
473         up_votes, down_votes = 0, 0
474         for rec in data:
475             if rec['vote'] == '1':
476                 up_votes = rec['vote_count']
477             elif rec['vote'] == '-1':
478                 down_votes = rec['vote_count']
479
480         # Votes which given by users on others questions and answers.
481         vote_ids = Vote.search([('user_id', '=', user.id)])
482
483         # activity by user.
484         model, comment = Data.get_object_reference('mail', 'mt_comment')
485         activities = Activity.search([('res_id', 'in', (user_question_ids+user_answer_ids).ids), ('model', '=', 'forum.post'), ('subtype_id', '!=', comment)], order='date DESC', limit=100)
486
487         posts = {}
488         for act in activities:
489             posts[act.res_id] = True
490         posts_ids = Post.search([('id', 'in', posts.keys())])
491         posts = dict(map(lambda x: (x.id, (x.parent_id or x, x.parent_id and x or False)), posts_ids))
492
493         # TDE CLEANME MASTER: couldn't it be rewritten using a 'menu' key instead of one key for each menu ?
494         if user == request.env.user:
495             post['my_profile'] = True
496         else:
497             post['users'] = True
498
499         values.update({
500             'uid': request.env.user.id,
501             'user': user,
502             'main_object': user,
503             'searches': post,
504             'questions': user_questions,
505             'count_questions': count_user_questions,
506             'answers': user_answers,
507             'count_answers': count_user_answers,
508             'followed': followed,
509             'favourite': favourite,
510             'up_votes': up_votes,
511             'down_votes': down_votes,
512             'activities': activities,
513             'posts': posts,
514             'vote_post': vote_ids,
515         })
516         return request.website.render("website_forum.user_detail_full", values)
517
518     @http.route('/forum/<model("forum.forum"):forum>/user/<model("res.users"):user>/edit', type='http', auth="user", website=True)
519     def edit_profile(self, forum, user, **kwargs):
520         countries = request.env['res.country'].search([])
521         values = self._prepare_forum_values(forum=forum, searches=kwargs)
522         values.update({
523             'email_required': kwargs.get('email_required'),
524             'countries': countries,
525             'notifications': self._get_notifications(),
526         })
527         return request.website.render("website_forum.edit_profile", values)
528
529     @http.route('/forum/<model("forum.forum"):forum>/user/<model("res.users"):user>/save', type='http', auth="user", methods=['POST'], website=True)
530     def save_edited_profile(self, forum, user, **kwargs):
531         values = {
532             'name': kwargs.get('name'),
533             'website': kwargs.get('website'),
534             'email': kwargs.get('email'),
535             'city': kwargs.get('city'),
536             'country_id': int(kwargs.get('country')) if kwargs.get('country') else False,
537             'website_description': kwargs.get('description'),
538         }
539         if request.uid == user.id:  # the controller allows to edit only its own privacy settings; use partner management for other cases
540             values['website_published'] = kwargs.get('website_published') == 'True'
541         user.write(values)
542         return werkzeug.utils.redirect("/forum/%s/user/%d" % (slug(forum), user.id))
543
544     # Badges
545     # --------------------------------------------------
546
547     @http.route('/forum/<model("forum.forum"):forum>/badge', type='http', auth="public", website=True)
548     def badges(self, forum, **searches):
549         Badge = request.env['gamification.badge']
550         badges = Badge.sudo().search([('challenge_ids.category', '=', 'forum')])
551         badges = sorted(badges, key=lambda b: b.stat_count_distinct, reverse=True)
552         values = self._prepare_forum_values(forum=forum, searches={'badges': True})
553         values.update({
554             'badges': badges,
555         })
556         return request.website.render("website_forum.badge", values)
557
558     @http.route(['''/forum/<model("forum.forum"):forum>/badge/<model("gamification.badge"):badge>'''], type='http', auth="public", website=True)
559     def badge_users(self, forum, badge, **kwargs):
560         users = [badge_user.user_id for badge_user in badge.owner_ids]
561         values = self._prepare_forum_values(forum=forum, searches={'badges': True})
562         values.update({
563             'badge': badge,
564             'users': users,
565         })
566         return request.website.render("website_forum.badge_user", values)
567
568     # Messaging
569     # --------------------------------------------------
570
571     @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/comment/<model("mail.message"):comment>/convert_to_answer', type='http', auth="user", methods=['POST'], website=True)
572     def convert_comment_to_answer(self, forum, post, comment, **kwarg):
573         post = request.env['forum.post'].convert_comment_to_answer(comment.id)
574         if not post:
575             return werkzeug.utils.redirect("/forum/%s" % slug(forum))
576         question = post.parent_id if post.parent_id else post
577         return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question)))
578
579     @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/convert_to_comment', type='http', auth="user", methods=['POST'], website=True)
580     def convert_answer_to_comment(self, forum, post, **kwarg):
581         question = post.parent_id
582         new_msg_id = post.convert_answer_to_comment()[0]
583         if not new_msg_id:
584             return werkzeug.utils.redirect("/forum/%s" % slug(forum))
585         return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question)))
586
587     @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/comment/<model("mail.message"):comment>/delete', type='json', auth="user", website=True)
588     def delete_comment(self, forum, post, comment, **kwarg):
589         if not request.session.uid:
590             return {'error': 'anonymous_user'}
591         return post.unlink_comment(comment.id)[0]