summaryrefslogtreecommitdiff
path: root/addons/website_forum/controllers/main.py
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/website_forum/controllers/main.py
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/website_forum/controllers/main.py')
-rw-r--r--addons/website_forum/controllers/main.py705
1 files changed, 705 insertions, 0 deletions
diff --git a/addons/website_forum/controllers/main.py b/addons/website_forum/controllers/main.py
new file mode 100644
index 00000000..febdd5aa
--- /dev/null
+++ b/addons/website_forum/controllers/main.py
@@ -0,0 +1,705 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+import json
+import lxml
+import requests
+import logging
+import werkzeug.exceptions
+import werkzeug.urls
+import werkzeug.wrappers
+
+from datetime import datetime
+
+from odoo import http, tools, _
+from odoo.addons.http_routing.models.ir_http import slug
+from odoo.addons.website.models.ir_http import sitemap_qs2dom
+from odoo.addons.website_profile.controllers.main import WebsiteProfile
+from odoo.addons.portal.controllers.portal import _build_url_w_params
+
+from odoo.http import request
+
+_logger = logging.getLogger(__name__)
+
+
+class WebsiteForum(WebsiteProfile):
+ _post_per_page = 10
+ _user_per_page = 30
+
+ def _prepare_user_values(self, **kwargs):
+ values = super(WebsiteForum, self)._prepare_user_values(**kwargs)
+ values['forum_welcome_message'] = request.httprequest.cookies.get('forum_welcome_message', False)
+ values.update({
+ 'header': kwargs.get('header', dict()),
+ 'searches': kwargs.get('searches', dict()),
+ })
+ if kwargs.get('forum'):
+ values['forum'] = kwargs.get('forum')
+ elif kwargs.get('forum_id'):
+ values['forum'] = request.env['forum.forum'].browse(kwargs.pop('forum_id'))
+ return values
+
+ # Forum
+ # --------------------------------------------------
+
+ @http.route(['/forum'], type='http', auth="public", website=True, sitemap=True)
+ def forum(self, **kwargs):
+ domain = request.website.website_domain()
+ forums = request.env['forum.forum'].search(domain)
+ if len(forums) == 1:
+ return werkzeug.utils.redirect('/forum/%s' % slug(forums[0]), code=302)
+
+ return request.render("website_forum.forum_all", {
+ 'forums': forums
+ })
+
+ @http.route('/forum/new', type='json', auth="user", methods=['POST'], website=True)
+ def forum_create(self, forum_name="New Forum", forum_mode="questions", forum_privacy="public", forum_privacy_group=False, add_menu=False):
+ forum = {
+ 'name': forum_name,
+ 'mode': forum_mode,
+ 'privacy': forum_privacy,
+ 'website_id': request.website.id,
+ }
+ if forum_privacy == 'private' and forum_privacy_group:
+ forum['authorized_group_id'] = forum_privacy_group
+ forum_id = request.env['forum.forum'].create(forum)
+ if add_menu:
+ group = [int(forum_privacy_group)] if forum_privacy == 'private' else [request.env.ref('base.group_portal').id, request.env.ref('base.group_user').id]
+ menu_id = request.env['website.menu'].create({
+ 'name': forum_name,
+ 'url': "/forum/%s" % slug(forum_id),
+ 'parent_id': request.website.menu_id.id,
+ 'website_id': request.website.id,
+ 'group_ids': [(6, 0, group)]
+ })
+ forum_id.menu_id = menu_id
+ return "/forum/%s" % slug(forum_id)
+
+ def sitemap_forum(env, rule, qs):
+ Forum = env['forum.forum']
+ dom = sitemap_qs2dom(qs, '/forum', Forum._rec_name)
+ dom += env['website'].get_current_website().website_domain()
+ for f in Forum.search(dom):
+ loc = '/forum/%s' % slug(f)
+ if not qs or qs.lower() in loc:
+ yield {'loc': loc}
+
+ @http.route(['/forum/<model("forum.forum"):forum>',
+ '/forum/<model("forum.forum"):forum>/page/<int:page>',
+ '''/forum/<model("forum.forum"):forum>/tag/<model("forum.tag"):tag>/questions''',
+ '''/forum/<model("forum.forum"):forum>/tag/<model("forum.tag"):tag>/questions/page/<int:page>''',
+ ], type='http', auth="public", website=True, sitemap=sitemap_forum)
+ def questions(self, forum, tag=None, page=1, filters='all', my=None, sorting=None, search='', **post):
+ if not forum.can_access_from_current_website():
+ raise werkzeug.exceptions.NotFound()
+
+ Post = request.env['forum.post']
+
+ domain = [('forum_id', '=', forum.id), ('parent_id', '=', False), ('state', '=', 'active'), ('can_view', '=', True)]
+ if search:
+ domain += ['|', ('name', 'ilike', search), ('content', 'ilike', search)]
+ if tag:
+ domain += [('tag_ids', 'in', tag.id)]
+ if filters == 'unanswered':
+ domain += [('child_ids', '=', False)]
+ elif filters == 'solved':
+ domain += [('has_validated_answer', '=', True)]
+ elif filters == 'unsolved':
+ domain += [('has_validated_answer', '=', False)]
+
+ user = request.env.user
+
+ if my == 'mine':
+ domain += [('create_uid', '=', user.id)]
+ elif my == 'followed':
+ domain += [('message_partner_ids', '=', user.partner_id.id)]
+ elif my == 'tagged':
+ domain += [('tag_ids.message_partner_ids', '=', user.partner_id.id)]
+ elif my == 'favourites':
+ domain += [('favourite_ids', '=', user.id)]
+
+ if sorting:
+ # check that sorting is valid
+ # retro-compatibily for V8 and google links
+ try:
+ Post._generate_order_by(sorting, None)
+ except ValueError:
+ sorting = False
+
+ if not sorting:
+ sorting = forum.default_order
+
+ question_count = Post.search_count(domain)
+
+ if tag:
+ url = "/forum/%s/tag/%s/questions" % (slug(forum), slug(tag))
+ else:
+ url = "/forum/%s" % slug(forum)
+
+ url_args = {
+ 'sorting': sorting
+ }
+ if search:
+ url_args['search'] = search
+ if filters:
+ url_args['filters'] = filters
+ if my:
+ url_args['my'] = my
+ pager = request.website.pager(url=url, total=question_count, page=page,
+ step=self._post_per_page, scope=self._post_per_page,
+ url_args=url_args)
+
+ question_ids = Post.search(domain, limit=self._post_per_page, offset=pager['offset'], order=sorting)
+
+ values = self._prepare_user_values(forum=forum, searches=post, header={'ask_hide': not forum.active})
+ values.update({
+ 'main_object': tag or forum,
+ 'edit_in_backend': not tag,
+ 'question_ids': question_ids,
+ 'question_count': question_count,
+ 'pager': pager,
+ 'tag': tag,
+ 'filters': filters,
+ 'my': my,
+ 'sorting': sorting,
+ 'search': search,
+ })
+ return request.render("website_forum.forum_index", values)
+
+ @http.route(['''/forum/<model("forum.forum"):forum>/faq'''], type='http', auth="public", website=True, sitemap=True)
+ def forum_faq(self, forum, **post):
+ values = self._prepare_user_values(forum=forum, searches=dict(), header={'is_guidelines': True}, **post)
+ return request.render("website_forum.faq", values)
+
+ @http.route(['/forum/<model("forum.forum"):forum>/faq/karma'], type='http', auth="public", website=True, sitemap=False)
+ def forum_faq_karma(self, forum, **post):
+ values = self._prepare_user_values(forum=forum, header={'is_guidelines': True, 'is_karma': True}, **post)
+ return request.render("website_forum.faq_karma", values)
+
+ @http.route('/forum/get_tags', type='http', auth="public", methods=['GET'], website=True, sitemap=False)
+ def tag_read(self, query='', limit=25, **post):
+ data = request.env['forum.tag'].search_read(
+ domain=[('name', '=ilike', (query or '') + "%")],
+ fields=['id', 'name'],
+ limit=int(limit),
+ )
+ return json.dumps(data)
+
+ @http.route(['/forum/<model("forum.forum"):forum>/tag', '/forum/<model("forum.forum"):forum>/tag/<string:tag_char>'], type='http', auth="public", website=True, sitemap=False)
+ def tags(self, forum, tag_char=None, **post):
+ # build the list of tag first char, with their value as tag_char param Ex : [('All', 'all'), ('C', 'c'), ('G', 'g'), ('Z', z)]
+ first_char_tag = forum.get_tags_first_char()
+ first_char_list = [(t, t.lower()) for t in first_char_tag if t.isalnum()]
+ first_char_list.insert(0, (_('All'), 'all'))
+
+ active_char_tag = tag_char and tag_char.lower() or 'all'
+
+ # generate domain for searched tags
+ domain = [('forum_id', '=', forum.id), ('posts_count', '>', 0)]
+ order_by = 'name'
+ if active_char_tag and active_char_tag != 'all':
+ domain.append(('name', '=ilike', tools.escape_psql(active_char_tag) + '%'))
+ order_by = 'posts_count DESC'
+ tags = request.env['forum.tag'].search(domain, limit=None, order=order_by)
+ # prepare values and render template
+ values = self._prepare_user_values(forum=forum, searches={'tags': True}, **post)
+
+ values.update({
+ 'tags': tags,
+ 'pager_tag_chars': first_char_list,
+ 'active_char_tag': active_char_tag,
+ })
+ return request.render("website_forum.tag", values)
+
+ # Questions
+ # --------------------------------------------------
+
+ @http.route('/forum/get_url_title', type='json', auth="user", methods=['POST'], website=True)
+ def get_url_title(self, **kwargs):
+ try:
+ req = requests.get(kwargs.get('url'))
+ req.raise_for_status()
+ arch = lxml.html.fromstring(req.content)
+ return arch.find(".//title").text
+ except IOError:
+ return False
+
+ @http.route(['''/forum/<model("forum.forum"):forum>/question/<model("forum.post", "[('forum_id','=',forum.id),('parent_id','=',False),('can_view', '=', True)]"):question>'''],
+ type='http', auth="public", website=True, sitemap=False)
+ def old_question(self, forum, question, **post):
+ # Compatibility pre-v14
+ return request.redirect(_build_url_w_params("/forum/%s/%s" % (slug(forum), slug(question)), request.params), code=301)
+
+ @http.route(['''/forum/<model("forum.forum"):forum>/<model("forum.post", "[('forum_id','=',forum.id),('parent_id','=',False),('can_view', '=', True)]"):question>'''],
+ type='http', auth="public", website=True, sitemap=True)
+ def question(self, forum, question, **post):
+ if not forum.active:
+ return request.render("website_forum.header", {'forum': forum})
+
+ # Hide posts from abusers (negative karma), except for moderators
+ if not question.can_view:
+ raise werkzeug.exceptions.NotFound()
+
+ # Hide pending posts from non-moderators and non-creator
+ user = request.env.user
+ if question.state == 'pending' and user.karma < forum.karma_post and question.create_uid != user:
+ raise werkzeug.exceptions.NotFound()
+
+ if question.parent_id:
+ redirect_url = "/forum/%s/%s" % (slug(forum), slug(question.parent_id))
+ return werkzeug.utils.redirect(redirect_url, 301)
+ filters = 'question'
+ values = self._prepare_user_values(forum=forum, searches=post)
+ values.update({
+ 'main_object': question,
+ 'question': question,
+ 'can_bump': (question.forum_id.allow_bump and not question.child_count and (datetime.today() - question.write_date).days > 9),
+ 'header': {'question_data': True},
+ 'filters': filters,
+ 'reversed': reversed,
+ })
+ if (request.httprequest.referrer or "").startswith(request.httprequest.url_root):
+ values['back_button_url'] = request.httprequest.referrer
+
+ # increment view counter
+ question.sudo()._set_viewed()
+
+ return request.render("website_forum.post_description_full", values)
+
+ @http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/toggle_favourite', type='json', auth="user", methods=['POST'], website=True)
+ def question_toggle_favorite(self, forum, question, **post):
+ if not request.session.uid:
+ return {'error': 'anonymous_user'}
+ favourite = not question.user_favourite
+ question.sudo().favourite_ids = [(favourite and 4 or 3, request.uid)]
+ if favourite:
+ # Automatically add the user as follower of the posts that he
+ # favorites (on unfavorite we chose to keep him as a follower until
+ # he decides to not follow anymore).
+ question.sudo().message_subscribe(request.env.user.partner_id.ids)
+ return favourite
+
+ @http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/ask_for_close', type='http', auth="user", methods=['POST'], website=True)
+ def question_ask_for_close(self, forum, question, **post):
+ reasons = request.env['forum.post.reason'].search([('reason_type', '=', 'basic')])
+
+ values = self._prepare_user_values(**post)
+ values.update({
+ 'question': question,
+ 'forum': forum,
+ 'reasons': reasons,
+ })
+ return request.render("website_forum.close_post", values)
+
+ @http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/edit_answer', type='http', auth="user", website=True)
+ def question_edit_answer(self, forum, question, **kwargs):
+ for record in question.child_ids:
+ if record.create_uid.id == request.uid:
+ answer = record
+ break
+ return werkzeug.utils.redirect("/forum/%s/post/%s/edit" % (slug(forum), slug(answer)))
+
+ @http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/close', type='http', auth="user", methods=['POST'], website=True)
+ def question_close(self, forum, question, **post):
+ question.close(reason_id=int(post.get('reason_id', False)))
+ return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question)))
+
+ @http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/reopen', type='http', auth="user", methods=['POST'], website=True)
+ def question_reopen(self, forum, question, **kwarg):
+ question.reopen()
+ return werkzeug.utils.redirect("/forum/%s/%s" % (slug(forum), slug(question)))
+
+ @http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/delete', type='http', auth="user", methods=['POST'], website=True)
+ def question_delete(self, forum, question, **kwarg):
+ question.active = False
+ return werkzeug.utils.redirect("/forum/%s/%s" % (slug(forum), slug(question)))
+
+ @http.route('/forum/<model("forum.forum"):forum>/question/<model("forum.post"):question>/undelete', type='http', auth="user", methods=['POST'], website=True)
+ def question_undelete(self, forum, question, **kwarg):
+ question.active = True
+ return werkzeug.utils.redirect("/forum/%s/%s" % (slug(forum), slug(question)))
+
+ # Post
+ # --------------------------------------------------
+ @http.route(['/forum/<model("forum.forum"):forum>/ask'], type='http', auth="user", website=True)
+ def forum_post(self, forum, **post):
+ user = request.env.user
+ if not user.email or not tools.single_email_re.match(user.email):
+ return werkzeug.utils.redirect("/forum/%s/user/%s/edit?email_required=1" % (slug(forum), request.session.uid))
+ values = self._prepare_user_values(forum=forum, searches={}, header={'ask_hide': True}, new_question=True)
+ return request.render("website_forum.new_question", values)
+
+ @http.route(['/forum/<model("forum.forum"):forum>/new',
+ '/forum/<model("forum.forum"):forum>/<model("forum.post"):post_parent>/reply'],
+ type='http', auth="user", methods=['POST'], website=True)
+ def post_create(self, forum, post_parent=None, **post):
+ if post.get('content', '') == '<p><br></p>':
+ return request.render('http_routing.http_error', {
+ 'status_code': _('Bad Request'),
+ 'status_message': post_parent and _('Reply should not be empty.') or _('Question should not be empty.')
+ })
+
+ post_tag_ids = forum._tag_to_write_vals(post.get('post_tags', ''))
+
+ if request.env.user.forum_waiting_posts_count:
+ return werkzeug.utils.redirect("/forum/%s/ask" % slug(forum))
+
+ new_question = request.env['forum.post'].create({
+ 'forum_id': forum.id,
+ 'name': post.get('post_name') or (post_parent and 'Re: %s' % (post_parent.name or '')) or '',
+ 'content': post.get('content', False),
+ 'parent_id': post_parent and post_parent.id or False,
+ 'tag_ids': post_tag_ids
+ })
+ return werkzeug.utils.redirect("/forum/%s/%s" % (slug(forum), post_parent and slug(post_parent) or new_question.id))
+
+ @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/comment', type='http', auth="user", methods=['POST'], website=True)
+ def post_comment(self, forum, post, **kwargs):
+ question = post.parent_id if post.parent_id else post
+ if kwargs.get('comment') and post.forum_id.id == forum.id:
+ # TDE FIXME: check that post_id is the question or one of its answers
+ body = tools.mail.plaintext2html(kwargs['comment'])
+ post.with_context(mail_create_nosubscribe=True).message_post(
+ body=body,
+ message_type='comment',
+ subtype_xmlid='mail.mt_comment')
+ return werkzeug.utils.redirect("/forum/%s/%s" % (slug(forum), slug(question)))
+
+ @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/toggle_correct', type='json', auth="public", website=True)
+ def post_toggle_correct(self, forum, post, **kwargs):
+ if post.parent_id is False:
+ return request.redirect('/')
+ if not request.session.uid:
+ return {'error': 'anonymous_user'}
+
+ # set all answers to False, only one can be accepted
+ (post.parent_id.child_ids - post).write(dict(is_correct=False))
+ post.is_correct = not post.is_correct
+ return post.is_correct
+
+ @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/delete', type='http', auth="user", methods=['POST'], website=True)
+ def post_delete(self, forum, post, **kwargs):
+ question = post.parent_id
+ post.unlink()
+ if question:
+ werkzeug.utils.redirect("/forum/%s/%s" % (slug(forum), slug(question)))
+ return werkzeug.utils.redirect("/forum/%s" % slug(forum))
+
+ @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/edit', type='http', auth="user", website=True)
+ def post_edit(self, forum, post, **kwargs):
+ tags = [dict(id=tag.id, name=tag.name) for tag in post.tag_ids]
+ tags = json.dumps(tags)
+ values = self._prepare_user_values(forum=forum)
+ values.update({
+ 'tags': tags,
+ 'post': post,
+ 'is_edit': True,
+ 'is_answer': bool(post.parent_id),
+ 'searches': kwargs,
+ 'content': post.name,
+ })
+ return request.render("website_forum.edit_post", values)
+
+ @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/save', type='http', auth="user", methods=['POST'], website=True)
+ def post_save(self, forum, post, **kwargs):
+ vals = {
+ 'content': kwargs.get('content'),
+ }
+
+ if 'post_name' in kwargs:
+ if not kwargs.get('post_name').strip():
+ return request.render('http_routing.http_error', {
+ 'status_code': _('Bad Request'),
+ 'status_message': _('Title should not be empty.')
+ })
+
+ vals['name'] = kwargs.get('post_name')
+ vals['tag_ids'] = forum._tag_to_write_vals(kwargs.get('post_tags', ''))
+ post.write(vals)
+ question = post.parent_id if post.parent_id else post
+ return werkzeug.utils.redirect("/forum/%s/%s" % (slug(forum), slug(question)))
+
+ # JSON utilities
+ # --------------------------------------------------
+
+ @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/upvote', type='json', auth="public", website=True)
+ def post_upvote(self, forum, post, **kwargs):
+ if not request.session.uid:
+ return {'error': 'anonymous_user'}
+ if request.uid == post.create_uid.id:
+ return {'error': 'own_post'}
+ upvote = True if not post.user_vote > 0 else False
+ return post.vote(upvote=upvote)
+
+ @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/downvote', type='json', auth="public", website=True)
+ def post_downvote(self, forum, post, **kwargs):
+ if not request.session.uid:
+ return {'error': 'anonymous_user'}
+ if request.uid == post.create_uid.id:
+ return {'error': 'own_post'}
+ upvote = True if post.user_vote < 0 else False
+ return post.vote(upvote=upvote)
+
+ @http.route('/forum/post/bump', type='json', auth="public", website=True)
+ def post_bump(self, post_id, **kwarg):
+ post = request.env['forum.post'].browse(int(post_id))
+ if not post.exists() or post.parent_id:
+ return False
+ return post.bump()
+
+ # Moderation Tools
+ # --------------------------------------------------
+
+ @http.route('/forum/<model("forum.forum"):forum>/validation_queue', type='http', auth="user", website=True)
+ def validation_queue(self, forum, **kwargs):
+ user = request.env.user
+ if user.karma < forum.karma_moderate:
+ raise werkzeug.exceptions.NotFound()
+
+ Post = request.env['forum.post']
+ domain = [('forum_id', '=', forum.id), ('state', '=', 'pending')]
+ posts_to_validate_ids = Post.search(domain)
+
+ values = self._prepare_user_values(forum=forum)
+ values.update({
+ 'posts_ids': posts_to_validate_ids.sudo(),
+ 'queue_type': 'validation',
+ })
+
+ return request.render("website_forum.moderation_queue", values)
+
+ @http.route('/forum/<model("forum.forum"):forum>/flagged_queue', type='http', auth="user", website=True)
+ def flagged_queue(self, forum, **kwargs):
+ user = request.env.user
+ if user.karma < forum.karma_moderate:
+ raise werkzeug.exceptions.NotFound()
+
+ Post = request.env['forum.post']
+ domain = [('forum_id', '=', forum.id), ('state', '=', 'flagged')]
+ if kwargs.get('spam_post'):
+ domain += [('name', 'ilike', kwargs.get('spam_post'))]
+ flagged_posts_ids = Post.search(domain, order='write_date DESC')
+
+ values = self._prepare_user_values(forum=forum)
+ values.update({
+ 'posts_ids': flagged_posts_ids.sudo(),
+ 'queue_type': 'flagged',
+ 'flagged_queue_active': 1,
+ })
+
+ return request.render("website_forum.moderation_queue", values)
+
+ @http.route('/forum/<model("forum.forum"):forum>/offensive_posts', type='http', auth="user", website=True)
+ def offensive_posts(self, forum, **kwargs):
+ user = request.env.user
+ if user.karma < forum.karma_moderate:
+ raise werkzeug.exceptions.NotFound()
+
+ Post = request.env['forum.post']
+ domain = [('forum_id', '=', forum.id), ('state', '=', 'offensive'), ('active', '=', False)]
+ offensive_posts_ids = Post.search(domain, order='write_date DESC')
+
+ values = self._prepare_user_values(forum=forum)
+ values.update({
+ 'posts_ids': offensive_posts_ids.sudo(),
+ 'queue_type': 'offensive',
+ })
+
+ return request.render("website_forum.moderation_queue", values)
+
+ @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/validate', type='http', auth="user", website=True)
+ def post_accept(self, forum, post, **kwargs):
+ url = "/forum/%s/validation_queue" % (slug(forum))
+ if post.state == 'flagged':
+ url = "/forum/%s/flagged_queue" % (slug(forum))
+ elif post.state == 'offensive':
+ url = "/forum/%s/offensive_posts" % (slug(forum))
+ post.validate()
+ return werkzeug.utils.redirect(url)
+
+ @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/refuse', type='http', auth="user", website=True)
+ def post_refuse(self, forum, post, **kwargs):
+ post.refuse()
+ return self.question_ask_for_close(forum, post)
+
+ @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/flag', type='json', auth="public", website=True)
+ def post_flag(self, forum, post, **kwargs):
+ if not request.session.uid:
+ return {'error': 'anonymous_user'}
+ return post.flag()[0]
+
+ @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/ask_for_mark_as_offensive', type='http', auth="user", methods=['GET'], website=True)
+ def post_ask_for_mark_as_offensive(self, forum, post, **kwargs):
+ offensive_reasons = request.env['forum.post.reason'].search([('reason_type', '=', 'offensive')])
+
+ values = self._prepare_user_values(forum=forum)
+ values.update({
+ 'question': post,
+ 'forum': forum,
+ 'reasons': offensive_reasons,
+ 'offensive': True,
+ })
+ return request.render("website_forum.close_post", values)
+
+ @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/mark_as_offensive', type='http', auth="user", methods=["POST"], website=True)
+ def post_mark_as_offensive(self, forum, post, **kwargs):
+ post.mark_as_offensive(reason_id=int(kwargs.get('reason_id', False)))
+ url = ''
+ if post.parent_id:
+ url = "/forum/%s/%s/#answer-%s" % (slug(forum), post.parent_id.id, post.id)
+ else:
+ url = "/forum/%s/%s" % (slug(forum), slug(post))
+ return werkzeug.utils.redirect(url)
+
+ # User
+ # --------------------------------------------------
+ @http.route(['/forum/<model("forum.forum"):forum>/partner/<int:partner_id>'], type='http', auth="public", website=True)
+ def open_partner(self, forum, partner_id=0, **post):
+ if partner_id:
+ partner = request.env['res.partner'].sudo().search([('id', '=', partner_id)])
+ if partner and partner.user_ids:
+ return werkzeug.utils.redirect("/forum/%s/user/%d" % (slug(forum), partner.user_ids[0].id))
+ return werkzeug.utils.redirect("/forum/%s" % slug(forum))
+
+ # Profile
+ # -----------------------------------
+
+ @http.route(['/forum/<model("forum.forum"):forum>/user/<int:user_id>'], type='http', auth="public", website=True)
+ def view_user_forum_profile(self, forum, user_id, forum_origin, **post):
+ return werkzeug.utils.redirect('/profile/user/' + str(user_id) + '?forum_id=' + str(forum.id) + '&forum_origin=' + str(forum_origin))
+
+ def _prepare_user_profile_values(self, user, **post):
+ values = super(WebsiteForum, self)._prepare_user_profile_values(user, **post)
+ if not post.get('no_forum'):
+ if post.get('forum'):
+ forums = post['forum']
+ elif post.get('forum_id'):
+ forums = request.env['forum.forum'].browse(int(post['forum_id']))
+ values.update({
+ 'edit_button_url_param': 'forum_id=%s' % str(post['forum_id']),
+ 'forum_filtered': forums.name,
+ })
+ else:
+ forums = request.env['forum.forum'].search([])
+
+ values.update(self._prepare_user_values(forum=forums[0] if len(forums) == 1 else True, **post))
+ if forums:
+ values.update(self._prepare_open_forum_user(user, forums))
+ return values
+
+ def _prepare_open_forum_user(self, user, forums, **kwargs):
+ Post = request.env['forum.post']
+ Vote = request.env['forum.post.vote']
+ Activity = request.env['mail.message']
+ Followers = request.env['mail.followers']
+ Data = request.env["ir.model.data"]
+
+ # questions and answers by user
+ user_question_ids = Post.search([
+ ('parent_id', '=', False),
+ ('forum_id', 'in', forums.ids), ('create_uid', '=', user.id)],
+ order='create_date desc')
+ count_user_questions = len(user_question_ids)
+ min_karma_unlink = min(forums.mapped('karma_unlink_all'))
+
+ # limit length of visible posts by default for performance reasons, except for the high
+ # karma users (not many of them, and they need it to properly moderate the forum)
+ post_display_limit = None
+ if request.env.user.karma < min_karma_unlink:
+ post_display_limit = 20
+
+ user_questions = user_question_ids[:post_display_limit]
+ user_answer_ids = Post.search([
+ ('parent_id', '!=', False),
+ ('forum_id', 'in', forums.ids), ('create_uid', '=', user.id)],
+ order='create_date desc')
+ count_user_answers = len(user_answer_ids)
+ user_answers = user_answer_ids[:post_display_limit]
+
+ # showing questions which user following
+ post_ids = [follower.res_id for follower in Followers.sudo().search(
+ [('res_model', '=', 'forum.post'), ('partner_id', '=', user.partner_id.id)])]
+ followed = Post.search([('id', 'in', post_ids), ('forum_id', 'in', forums.ids), ('parent_id', '=', False)])
+
+ # showing Favourite questions of user.
+ favourite = Post.search(
+ [('favourite_ids', '=', user.id), ('forum_id', 'in', forums.ids), ('parent_id', '=', False)])
+
+ # votes which given on users questions and answers.
+ data = Vote.read_group([('forum_id', 'in', forums.ids), ('recipient_id', '=', user.id)], ["vote"],
+ groupby=["vote"])
+ up_votes, down_votes = 0, 0
+ for rec in data:
+ if rec['vote'] == '1':
+ up_votes = rec['vote_count']
+ elif rec['vote'] == '-1':
+ down_votes = rec['vote_count']
+
+ # Votes which given by users on others questions and answers.
+ vote_ids = Vote.search([('user_id', '=', user.id)])
+
+ # activity by user.
+ model, comment = Data.get_object_reference('mail', 'mt_comment')
+ activities = Activity.search(
+ [('res_id', 'in', (user_question_ids + user_answer_ids).ids), ('model', '=', 'forum.post'),
+ ('subtype_id', '!=', comment)],
+ order='date DESC', limit=100)
+
+ posts = {}
+ for act in activities:
+ posts[act.res_id] = True
+ posts_ids = Post.search([('id', 'in', list(posts))])
+ posts = {x.id: (x.parent_id or x, x.parent_id and x or False) for x in posts_ids}
+
+ # TDE CLEANME MASTER: couldn't it be rewritten using a 'menu' key instead of one key for each menu ?
+ if user == request.env.user:
+ kwargs['my_profile'] = True
+ else:
+ kwargs['users'] = True
+
+ values = {
+ 'uid': request.env.user.id,
+ 'user': user,
+ 'main_object': user,
+ 'searches': kwargs,
+ 'questions': user_questions,
+ 'count_questions': count_user_questions,
+ 'answers': user_answers,
+ 'count_answers': count_user_answers,
+ 'followed': followed,
+ 'favourite': favourite,
+ 'up_votes': up_votes,
+ 'down_votes': down_votes,
+ 'activities': activities,
+ 'posts': posts,
+ 'vote_post': vote_ids,
+ 'is_profile_page': True,
+ 'badge_category': 'forum',
+ }
+
+ return values
+
+ # Messaging
+ # --------------------------------------------------
+
+ @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)
+ def convert_comment_to_answer(self, forum, post, comment, **kwarg):
+ post = request.env['forum.post'].convert_comment_to_answer(comment.id)
+ if not post:
+ return werkzeug.utils.redirect("/forum/%s" % slug(forum))
+ question = post.parent_id if post.parent_id else post
+ return werkzeug.utils.redirect("/forum/%s/%s" % (slug(forum), slug(question)))
+
+ @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/convert_to_comment', type='http', auth="user", methods=['POST'], website=True)
+ def convert_answer_to_comment(self, forum, post, **kwarg):
+ question = post.parent_id
+ new_msg = post.convert_answer_to_comment()
+ if not new_msg:
+ return werkzeug.utils.redirect("/forum/%s" % slug(forum))
+ return werkzeug.utils.redirect("/forum/%s/%s" % (slug(forum), slug(question)))
+
+ @http.route('/forum/<model("forum.forum"):forum>/post/<model("forum.post"):post>/comment/<model("mail.message"):comment>/delete', type='json', auth="user", website=True)
+ def delete_comment(self, forum, post, comment, **kwarg):
+ if not request.session.uid:
+ return {'error': 'anonymous_user'}
+ return post.unlink_comment(comment.id)[0]