diff --git a/addons/website_forum/__init__.py b/addons/website_forum/__init__.py index bde83af3aea..dc8bc3228a8 100644 --- a/addons/website_forum/__init__.py +++ b/addons/website_forum/__init__.py @@ -2,3 +2,4 @@ import controllers import models +import tests diff --git a/addons/website_forum/controllers/main.py b/addons/website_forum/controllers/main.py index 08e03d9d6bf..a17dc6bdbc1 100644 --- a/addons/website_forum/controllers/main.py +++ b/addons/website_forum/controllers/main.py @@ -1,9 +1,7 @@ # -*- coding: utf-8 -*- -from datetime import datetime import werkzeug.urls import werkzeug.wrappers -import re import simplejson from openerp import tools @@ -13,7 +11,6 @@ from openerp.addons.web.controllers.main import login_redirect from openerp.addons.web.http import request from openerp.addons.website.controllers.main import Website as controllers from openerp.addons.website.models.website import slug -from openerp.tools.translate import _ controllers = controllers() @@ -35,12 +32,15 @@ class WebsiteForum(http.Controller): def _prepare_forum_values(self, forum=None, **kwargs): user = request.registry['res.users'].browse(request.cr, request.uid, request.uid, context=request.context) - values = {'user': user, - 'is_public_user': user.id == request.website.user_id.id, - 'notifications': self._get_notifications(), - 'header': kwargs.get('header', dict()), - 'searches': kwargs.get('searches', dict()), - } + values = { + 'user': user, + 'is_public_user': user.id == request.website.user_id.id, + 'notifications': self._get_notifications(), + 'header': kwargs.get('header', dict()), + 'searches': kwargs.get('searches', dict()), + 'validation_email_sent': request.session.get('validation_email_sent', False), + 'validation_email_done': request.session.get('validation_email_done', False), + } if forum: values['forum'] = forum elif kwargs.get('forum_id'): @@ -48,6 +48,34 @@ class WebsiteForum(http.Controller): values.update(kwargs) return values + # User and validation + # -------------------------------------------------- + + @http.route('/forum/send_validation_email', type='json', auth='user', website=True) + def send_validation_email(self, forum_id=None, **kwargs): + request.registry['res.users'].send_forum_validation_email(request.cr, request.uid, request.uid, forum_id=forum_id, context=request.context) + request.session['validation_email_sent'] = True + return True + + @http.route('/forum/validate_email', type='http', auth='public', website=True) + def validate_email(self, token, id, email, forum_id=None, **kwargs): + if forum_id: + try: + forum_id = int(forum_id) + except ValueError: + forum_id = None + done = request.registry['res.users'].process_forum_validation_token(request.cr, request.uid, token, int(id), email, forum_id=forum_id, context=request.context) + if done: + request.session['validation_email_done'] = True + if forum_id: + return request.redirect("/forum/%s" % int(forum_id)) + return request.redirect('/forum') + + @http.route('/forum/validate_email/close', type='json', auth='public', website=True) + def validate_email_done(self): + request.session['validation_email_done'] = False + return True + # Forum # -------------------------------------------------- @@ -298,10 +326,12 @@ class WebsiteForum(http.Controller): cr, uid, context = request.cr, request.uid, request.context 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 - request.registry['forum.post']._post_comment( - cr, uid, post, + request.registry['forum.post'].message_post( + cr, uid, post.id, body=kwargs.get('comment'), - context=context) + type='comment', + subtype='mt_comment', + context=dict(context, mail_create_nosubcribe=True)) return werkzeug.utils.redirect("/forum/%s/question/%s" % (slug(forum), slug(question))) @http.route('/forum//post//toggle_correct', type='json', auth="public", website=True) diff --git a/addons/website_forum/data/forum_data.xml b/addons/website_forum/data/forum_data.xml index 61bb7ee0fb4..379e746be4f 100644 --- a/addons/website_forum/data/forum_data.xml +++ b/addons/website_forum/data/forum_data.xml @@ -100,5 +100,36 @@ too localized + + + Email Verification + + ]]> + ${object.email|safe} + + + Hello ${object.name}, +

+

+ You have been invited to validate your email in order to get access to "${object.company_id.name}" Q/A Forums. +

+

+ To validate your email, please click on the following link: +

+ +

+ Thanks, +

+
+--
+${object.company_id.name or ''}
+${object.company_id.email or ''}
+${object.company_id.phone or ''}
+
]]>
+
+ diff --git a/addons/website_forum/models/forum.py b/addons/website_forum/models/forum.py index 1bc9b69b37a..a98b3ef63ca 100644 --- a/addons/website_forum/models/forum.py +++ b/addons/website_forum/models/forum.py @@ -1,16 +1,18 @@ # -*- coding: utf-8 -*- from datetime import datetime +import uuid +from werkzeug.exceptions import Forbidden import openerp -from openerp import tools +from openerp import api, tools from openerp import SUPERUSER_ID from openerp.addons.website.models.website import slug +from openerp.exceptions import Warning from openerp.osv import osv, fields from openerp.tools import html2plaintext from openerp.tools.translate import _ -from werkzeug.exceptions import Forbidden class KarmaError(Forbidden): """ Karma-related error, used for forum and posts. """ @@ -23,42 +25,48 @@ class Forum(osv.Model): _description = 'Forums' _inherit = ['mail.thread', 'website.seo.metadata'] + def init(self, cr): + """ Add forum uuid for user email validation. """ + forum_uuids = self.pool['ir.config_parameter'].search(cr, SUPERUSER_ID, [('key', '=', 'website_forum.uuid')]) + if not forum_uuids: + self.pool['ir.config_parameter'].set_param(cr, SUPERUSER_ID, 'website_forum.uuid', str(uuid.uuid4()), ['base.group_system']) + _columns = { 'name': fields.char('Name', required=True, translate=True), 'faq': fields.html('Guidelines'), 'description': fields.html('Description'), # karma generation - 'karma_gen_question_new': fields.integer('Karma earned for new questions'), - 'karma_gen_question_upvote': fields.integer('Karma earned for upvoting a question'), - 'karma_gen_question_downvote': fields.integer('Karma earned for downvoting a question'), - 'karma_gen_answer_upvote': fields.integer('Karma earned for upvoting an answer'), - 'karma_gen_answer_downvote': fields.integer('Karma earned for downvoting an answer'), - 'karma_gen_answer_accept': fields.integer('Karma earned for accepting an anwer'), - 'karma_gen_answer_accepted': fields.integer('Karma earned for having an answer accepted'), - 'karma_gen_answer_flagged': fields.integer('Karma earned for having an answer flagged'), + 'karma_gen_question_new': fields.integer('Asking a question'), + 'karma_gen_question_upvote': fields.integer('Question upvoted'), + 'karma_gen_question_downvote': fields.integer('Question downvoted'), + 'karma_gen_answer_upvote': fields.integer('Answer upvoted'), + 'karma_gen_answer_downvote': fields.integer('Answer downvoted'), + 'karma_gen_answer_accept': fields.integer('Accepting an answer'), + 'karma_gen_answer_accepted': fields.integer('Answer accepted'), + 'karma_gen_answer_flagged': fields.integer('Answer flagged'), # karma-based actions - 'karma_ask': fields.integer('Karma to ask a new question'), - 'karma_answer': fields.integer('Karma to answer a question'), - 'karma_edit_own': fields.integer('Karma to edit its own posts'), - 'karma_edit_all': fields.integer('Karma to edit all posts'), - 'karma_close_own': fields.integer('Karma to close its own posts'), - 'karma_close_all': fields.integer('Karma to close all posts'), - 'karma_unlink_own': fields.integer('Karma to delete its own posts'), - 'karma_unlink_all': fields.integer('Karma to delete all posts'), - 'karma_upvote': fields.integer('Karma to upvote'), - 'karma_downvote': fields.integer('Karma to downvote'), - 'karma_answer_accept_own': fields.integer('Karma to accept an answer on its own questions'), - 'karma_answer_accept_all': fields.integer('Karma to accept an answers to all questions'), - 'karma_editor_link_files': fields.integer('Karma for linking files (Editor)'), - 'karma_editor_clickable_link': fields.integer('Karma for clickable links (Editor)'), - 'karma_comment_own': fields.integer('Karma to comment its own posts'), - 'karma_comment_all': fields.integer('Karma to comment all posts'), - 'karma_comment_convert_own': fields.integer('Karma to convert its own answers to comments and vice versa'), - 'karma_comment_convert_all': fields.integer('Karma to convert all answers to answers and vice versa'), - 'karma_comment_unlink_own': fields.integer('Karma to unlink its own comments'), - 'karma_comment_unlink_all': fields.integer('Karma to unlinnk all comments'), - 'karma_retag': fields.integer('Karma to change question tags'), - 'karma_flag': fields.integer('Karma to flag a post as offensive'), + 'karma_ask': fields.integer('Ask a question'), + 'karma_answer': fields.integer('Answer a question'), + 'karma_edit_own': fields.integer('Edit its own posts'), + 'karma_edit_all': fields.integer('Edit all posts'), + 'karma_close_own': fields.integer('Close its own posts'), + 'karma_close_all': fields.integer('Close all posts'), + 'karma_unlink_own': fields.integer('Delete its own posts'), + 'karma_unlink_all': fields.integer('Delete all posts'), + 'karma_upvote': fields.integer('Upvote'), + 'karma_downvote': fields.integer('Downvote'), + 'karma_answer_accept_own': fields.integer('Accept an answer on its own questions'), + 'karma_answer_accept_all': fields.integer('Accept an answer to all questions'), + 'karma_editor_link_files': fields.integer('Linking files (Editor)'), + 'karma_editor_clickable_link': fields.integer('Clickable links (Editor)'), + 'karma_comment_own': fields.integer('Comment its own posts'), + 'karma_comment_all': fields.integer('Comment all posts'), + 'karma_comment_convert_own': fields.integer('Convert its own answers to comments and vice versa'), + 'karma_comment_convert_all': fields.integer('Convert all answers to comments and vice versa'), + 'karma_comment_unlink_own': fields.integer('Unlink its own comments'), + 'karma_comment_unlink_all': fields.integer('Unlink all comments'), + 'karma_retag': fields.integer('Change question tags'), + 'karma_flag': fields.integer('Flag a post as offensive'), } def _get_default_faq(self, cr, uid, context=None): @@ -70,7 +78,7 @@ class Forum(osv.Model): _defaults = { 'description': 'This community is for professionals and enthusiasts of our products and services.', 'faq': _get_default_faq, - 'karma_gen_question_new': 2, + 'karma_gen_question_new': 0, # set to null for anti spam protection 'karma_gen_question_upvote': 5, 'karma_gen_question_downvote': -2, 'karma_gen_answer_upvote': 10, @@ -78,8 +86,8 @@ class Forum(osv.Model): 'karma_gen_answer_accept': 2, 'karma_gen_answer_accepted': 15, 'karma_gen_answer_flagged': -100, - 'karma_ask': 0, - 'karma_answer': 0, + 'karma_ask': 3, # set to not null for anti spam protection + 'karma_answer': 3, # set to not null for anti spam protection 'karma_edit_own': 1, 'karma_edit_all': 300, 'karma_close_own': 100, @@ -92,8 +100,8 @@ class Forum(osv.Model): 'karma_answer_accept_all': 500, 'karma_editor_link_files': 20, 'karma_editor_clickable_link': 20, - 'karma_comment_own': 1, - 'karma_comment_all': 1, + 'karma_comment_own': 3, + 'karma_comment_all': 5, 'karma_comment_convert_own': 50, 'karma_comment_convert_all': 500, 'karma_comment_unlink_own': 50, @@ -393,13 +401,6 @@ class Post(osv.Model): return super(Post, self).unlink(cr, uid, ids, context=context) def vote(self, cr, uid, ids, upvote=True, context=None): - posts = self.browse(cr, uid, ids, context=context) - - if upvote and any(not post.can_upvote for post in posts): - raise KarmaError('Not enough karma to upvote.') - elif not upvote and any(not post.can_downvote for post in posts): - raise KarmaError('Not enough karma to downvote.') - Vote = self.pool['forum.post.vote'] vote_ids = Vote.search(cr, uid, [('post_id', 'in', ids), ('user_id', '=', uid)], context=context) new_vote = '1' if upvote else '-1' @@ -509,15 +510,18 @@ class Post(osv.Model): res_id = post.parent_id and "%s#answer-%s" % (post.parent_id.id, post.id) or post.id return "/forum/%s/question/%s" % (post.forum_id.id, res_id) - def _post_comment(self, cr, uid, post, body, context=None): - context = dict(context or {}, mail_create_nosubcribe=True) - if not post.can_comment: - raise KarmaError('Not enough karma to comment') - return self.message_post(cr, uid, post.id, - body=body, - type='comment', - subtype='mt_comment', - context=context) + @api.cr_uid_ids_context + def message_post(self, cr, uid, thread_id, type='notification', subtype=None, context=None, **kwargs): + if thread_id and type == 'comment': # user comments have a restriction on karma + if isinstance(thread_id, (list, tuple)): + post_id = thread_id[0] + else: + post_id = thread_id + post = self.browse(cr, uid, post_id, context=context) + if not post.can_comment: + raise KarmaError('Not enough karma to comment') + return super(Post, self).message_post(cr, uid, thread_id, type=type, subtype=subtype, context=context, **kwargs) + class PostReason(osv.Model): _name = "forum.post.reason" @@ -557,6 +561,17 @@ class Vote(osv.Model): def create(self, cr, uid, vals, context=None): vote_id = super(Vote, self).create(cr, uid, vals, context=context) vote = self.browse(cr, uid, vote_id, context=context) + + # own post check + if vote.user_id.id == vote.post_id.create_uid.id: + raise Warning('Not allowed to vote for its own post') + # karma check + if vote.vote == '1' and not vote.post_id.can_upvote: + raise KarmaError('Not enough karma to upvote.') + elif vote.vote == '-1' and not vote.post_id.can_downvote: + raise KarmaError('Not enough karma to downvote.') + + # karma update if vote.post_id.parent_id: karma_value = self._get_karma_value('0', vote.vote, vote.forum_id.karma_gen_answer_upvote, vote.forum_id.karma_gen_answer_downvote) else: @@ -567,6 +582,16 @@ class Vote(osv.Model): def write(self, cr, uid, ids, values, context=None): if 'vote' in values: for vote in self.browse(cr, uid, ids, context=context): + # own post check + if vote.user_id.id == vote.post_id.create_uid.id: + raise Warning('Not allowed to vote for its own post') + # karma check + if (values['vote'] == '1' or vote.vote == '-1' and values['vote'] == '0') and not vote.post_id.can_upvote: + raise KarmaError('Not enough karma to upvote.') + elif (values['vote'] == '-1' or vote.vote == '1' and values['vote'] == '0') and not vote.post_id.can_downvote: + raise KarmaError('Not enough karma to downvote.') + + # karma update if vote.post_id.parent_id: karma_value = self._get_karma_value(vote.vote, values['vote'], vote.forum_id.karma_gen_answer_upvote, vote.forum_id.karma_gen_answer_downvote) else: diff --git a/addons/website_forum/models/res_users.py b/addons/website_forum/models/res_users.py index fa59b8155d7..651be986c89 100644 --- a/addons/website_forum/models/res_users.py +++ b/addons/website_forum/models/res_users.py @@ -1,14 +1,22 @@ # -*- coding: utf-8 -*- +from datetime import datetime +from urllib import urlencode + +import hashlib + +from openerp import SUPERUSER_ID from openerp.osv import osv, fields + class Users(osv.Model): _inherit = 'res.users' def __init__(self, pool, cr): - init_res = super(Users, self).__init__(pool, cr) - self.SELF_WRITEABLE_FIELDS = list(set( - self.SELF_WRITEABLE_FIELDS + \ + init_res = super(Users, self).__init__(pool, cr) + self.SELF_WRITEABLE_FIELDS = list( + set( + self.SELF_WRITEABLE_FIELDS + ['country_id', 'city', 'website', 'website_description', 'website_published'])) return init_res @@ -37,6 +45,50 @@ class Users(osv.Model): 'karma': 0, } + def _generate_forum_token(self, cr, uid, user_id, email): + """Return a token for email validation. This token is valid for the day + and is a hash based on a (secret) uuid generated by the forum module, + the user_id, the email and currently the day (to be updated if necessary). """ + forum_uuid = self.pool.get('ir.config_parameter').get_param(cr, SUPERUSER_ID, 'website_forum.uuid') + return hashlib.sha256('%s-%s-%s-%s' % ( + datetime.now().replace(hour=0, minute=0, second=0, microsecond=0), + forum_uuid, + user_id, + email)).hexdigest() + + def send_forum_validation_email(self, cr, uid, user_id, forum_id=None, context=None): + user = self.pool['res.users'].browse(cr, uid, user_id, context=context) + token = self._generate_forum_token(cr, uid, user_id, user.email) + activation_template_id = self.pool['ir.model.data'].xmlid_to_res_id(cr, uid, 'website_forum.validation_email') + if activation_template_id: + params = { + 'token': token, + 'id': user_id, + 'email': user.email} + if forum_id: + params['forum_id'] = forum_id + base_url = self.pool['ir.config_parameter'].get_param(cr, uid, 'web.base.url') + token_url = base_url + '/forum/validate_email?%s' % urlencode(params) + tpl_ctx = dict(context, token_url=token_url) + self.pool['email.template'].send_mail(cr, SUPERUSER_ID, activation_template_id, user_id, force_send=True, context=tpl_ctx) + return True + + def process_forum_validation_token(self, cr, uid, token, user_id, email, forum_id=None, context=None): + validation_token = self.pool['res.users']._generate_forum_token(cr, uid, user_id, email) + user = self.pool['res.users'].browse(cr, SUPERUSER_ID, user_id, context=context) + if token == validation_token and user.karma == 0: + karma = 3 + if not forum_id: + forum_ids = self.pool['forum.forum'].search(cr, uid, [], limit=1, context=context) + if forum_ids: + forum_id = forum_ids[0] + if forum_id: + forum = self.pool['forum.forum'].browse(cr, uid, forum_id, context=context) + # karma gained: karma to ask a question and have 2 downvotes + karma = forum.karma_ask + (-2 * forum.karma_gen_question_downvote) + return user.write({'karma': karma}) + return False + def add_karma(self, cr, uid, ids, karma, context=None): for user in self.browse(cr, uid, ids, context=context): self.write(cr, uid, [user.id], {'karma': user.karma + karma}, context=context) diff --git a/addons/website_forum/static/src/js/website_forum.js b/addons/website_forum/static/src/js/website_forum.js index 9a00eb7de23..75cf37c05a7 100644 --- a/addons/website_forum/static/src/js/website_forum.js +++ b/addons/website_forum/static/src/js/website_forum.js @@ -6,8 +6,8 @@ $(document).ready(function () { ev.preventDefault(); var $warning = $('
'+ ''+ - karma + ' karma is required to perform this action. You can earn karma by answering questions or having '+ - 'your answers upvoted by the community.
'); + karma + ' karma is required to perform this action. You can earn karma by having '+ + 'your answers upvoted by the community.'); var vote_alert = $(ev.currentTarget).parent().find("#vote_alert"); if (vote_alert.length == 0) { $(ev.currentTarget).parent().append($warning); @@ -50,7 +50,6 @@ $(document).ready(function () { } } }); - return true; }); $('.accept_answer').not('.karma_required').on('click', function (ev) { @@ -76,7 +75,6 @@ $(document).ready(function () { } } }); - return true; }); $('.favourite_question').on('click', function (ev) { @@ -89,7 +87,6 @@ $(document).ready(function () { $link.removeClass("forum_favourite_question") } }); - return true; }); $('.comment_delete').on('click', function (ev) { @@ -98,15 +95,29 @@ $(document).ready(function () { openerp.jsonRpc($link.data('href'), 'call', {}).then(function (data) { $link.parents('.comment').first().remove(); }); - return true; }); $('.notification_close').on('click', function (ev) { ev.preventDefault(); var $link = $(ev.currentTarget); openerp.jsonRpc("/forum/notification_read", 'call', { - 'notification_id': $link.attr("id")}) - return true; + 'notification_id': $link.attr("id")}); + }); + + $('.send_validation_email').on('click', function (ev) { + ev.preventDefault(); + var $link = $(ev.currentTarget); + openerp.jsonRpc("/forum/send_validation_email", 'call', { + 'forum_id': $link.attr('forum-id'), + }).then(function (data) { + if (data) { + $('button.validation_email_close').click(); + } + }); + }); + + $('.validated_email_close').on('click', function (ev) { + openerp.jsonRpc("/forum/validate_email/close", 'call', {}); }); if($('input.load_tags').length){ diff --git a/addons/website_forum/tests/__init__.py b/addons/website_forum/tests/__init__.py new file mode 100644 index 00000000000..d1c5a474498 --- /dev/null +++ b/addons/website_forum/tests/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +import common +import test_forum diff --git a/addons/website_forum/tests/common.py b/addons/website_forum/tests/common.py new file mode 100644 index 00000000000..324d676fcb3 --- /dev/null +++ b/addons/website_forum/tests/common.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- + +from openerp.tests import common + +KARMA = { + 'ask': 5, 'ans': 10, + 'com_own': 5, 'com_all': 10, + 'com_conv_all': 50, + 'upv': 5, 'dwv': 10, + 'edit_own': 10, 'edit_all': 20, + 'close_own': 10, 'close_all': 20, + 'unlink_own': 10, 'unlink_all': 20, + 'gen_que_new': 1, 'gen_que_upv': 5, 'gen_que_dwv': -10, + 'gen_ans_upv': 10, 'gen_ans_dwv': -20, +} + + +class TestForumCommon(common.TransactionCase): + + def setUp(self): + super(TestForumCommon, self).setUp() + + Forum = self.env['forum.forum'] + Post = self.env['forum.post'] + + # Test users + TestUsersEnv = self.env['res.users'].with_context({'no_reset_password': True}) + group_employee_id = self.ref('base.group_user') + group_portal_id = self.ref('base.group_portal') + group_public_id = self.ref('base.group_public') + self.user_employee = TestUsersEnv.create({ + 'name': 'Armande Employee', + 'login': 'Armande', + 'alias_name': 'armande', + 'email': 'armande.employee@example.com', + 'karma': 0, + 'groups_id': [(6, 0, [group_employee_id])] + }) + self.user_portal = TestUsersEnv.create({ + 'name': 'Beatrice Portal', + 'login': 'Beatrice', + 'alias_name': 'beatrice', + 'email': 'beatrice.employee@example.com', + 'karma': 0, + 'groups_id': [(6, 0, [group_portal_id])] + }) + self.user_public = TestUsersEnv.create({ + 'name': 'Cedric Public', + 'login': 'Cedric', + 'alias_name': 'cedric', + 'email': 'cedric.employee@example.com', + 'karma': 0, + 'groups_id': [(6, 0, [group_public_id])] + }) + + # Test forum + self.forum = Forum.create({ + 'name': 'TestForum', + 'karma_ask': KARMA['ask'], + 'karma_answer': KARMA['ans'], + 'karma_comment_own': KARMA['com_own'], + 'karma_comment_all': KARMA['com_all'], + 'karma_answer_accept_own': 9999, + 'karma_answer_accept_all': 9999, + 'karma_upvote': KARMA['upv'], + 'karma_downvote': KARMA['dwv'], + 'karma_edit_own': KARMA['edit_own'], + 'karma_edit_all': KARMA['edit_all'], + 'karma_close_own': KARMA['close_own'], + 'karma_close_all': KARMA['close_all'], + 'karma_unlink_own': KARMA['unlink_own'], + 'karma_unlink_all': KARMA['unlink_all'], + 'karma_comment_convert_all': KARMA['com_conv_all'], + 'karma_gen_question_new': KARMA['gen_que_new'], + 'karma_gen_question_upvote': KARMA['gen_que_upv'], + 'karma_gen_question_downvote': KARMA['gen_que_dwv'], + 'karma_gen_answer_upvote': KARMA['gen_ans_upv'], + 'karma_gen_answer_downvote': KARMA['gen_ans_dwv'], + 'karma_gen_answer_accept': 9999, + 'karma_gen_answer_accepted': 9999, + }) + self.post = Post.create({ + 'name': 'TestQuestion', + 'content': 'I am not a bird.', + 'forum_id': self.forum.id, + 'tag_ids': [(0, 0, {'name': 'Tag0', 'forum_id': self.forum.id})] + }) + self.answer = Post.create({ + 'name': 'TestAnswer', + 'content': 'I am an anteater.', + 'forum_id': self.forum.id, + 'parent_id': self.post.id, + }) diff --git a/addons/website_forum/tests/test_forum.py b/addons/website_forum/tests/test_forum.py new file mode 100644 index 00000000000..7ca4c2a69ab --- /dev/null +++ b/addons/website_forum/tests/test_forum.py @@ -0,0 +1,180 @@ +# -*- coding: utf-8 -*- + +from openerp.addons.website_forum.tests.common import KARMA, TestForumCommon +from openerp.addons.website_forum.models.forum import KarmaError +from openerp.exceptions import Warning, AccessError +from openerp.tools import mute_logger + + +class TestForum(TestForumCommon): + + @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.models') + def test_ask(self): + Post = self.env['forum.post'] + + # Public user asks a question: not allowed + with self.assertRaises(AccessError): + Post.sudo(self.user_public).create({ + 'name': " Question ?", + 'forum_id': self.forum.id, + }) + + # Portal user asks a question with tags: not allowed, unsufficient karma + with self.assertRaises(KarmaError): + Post.sudo(self.user_portal).create({ + 'name': " Q_0", + 'forum_id': self.forum.id, + 'tag_ids': [(0, 0, {'name': 'Tag0', 'forum_id': self.forum.id})] + }) + + # Portal user asks a question with tags: ok if enough karma + self.user_portal.karma = KARMA['ask'] + Post.sudo(self.user_portal).create({ + 'name': " Q0", + 'forum_id': self.forum.id, + 'tag_ids': [(0, 0, {'name': 'Tag0', 'forum_id': self.forum.id})] + }) + self.assertEqual(self.user_portal.karma, KARMA['ask'] + KARMA['gen_que_new'], 'website_forum: wrong karma generation when asking question') + + @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.models') + def test_answer(self): + Post = self.env['forum.post'] + + # Answers its own question: not allowed, unsufficient karma + with self.assertRaises(KarmaError): + Post.sudo(self.user_employee).create({ + 'name': " A0", + 'forum_id': self.forum.id, + 'parent_id': self.post.id, + }) + + # Answers on question: ok if enough karma + self.user_employee.karma = KARMA['ans'] + Post.sudo(self.user_employee).create({ + 'name': " A0", + 'forum_id': self.forum.id, + 'parent_id': self.post.id, + }) + self.assertEqual(self.user_employee.karma, KARMA['ans'], 'website_forum: wrong karma generation when answering question') + + @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.models') + def test_vote_crash(self): + Post = self.env['forum.post'] + self.user_employee.karma = KARMA['ans'] + emp_answer = Post.sudo(self.user_employee).create({ + 'name': 'TestAnswer', + 'forum_id': self.forum.id, + 'parent_id': self.post.id}) + + # upvote its own post + with self.assertRaises(Warning): + emp_answer.vote(upvote=True) + + # not enough karma + with self.assertRaises(KarmaError): + self.post.sudo(self.user_portal).vote(upvote=True) + + def test_vote(self): + self.post.create_uid.karma = KARMA['ask'] + self.user_portal.karma = KARMA['upv'] + self.post.sudo(self.user_portal).vote(upvote=True) + self.assertEqual(self.post.create_uid.karma, KARMA['ask'] + KARMA['gen_que_upv'], 'website_forum: wrong karma generation of upvoted question author') + + @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.models') + def test_downvote_crash(self): + Post = self.env['forum.post'] + self.user_employee.karma = KARMA['ans'] + emp_answer = Post.sudo(self.user_employee).create({ + 'name': 'TestAnswer', + 'forum_id': self.forum.id, + 'parent_id': self.post.id}) + + # downvote its own post + with self.assertRaises(Warning): + emp_answer.vote(upvote=False) + + # not enough karma + with self.assertRaises(KarmaError): + self.post.sudo(self.user_portal).vote(upvote=False) + + def test_downvote(self): + self.post.create_uid.karma = 50 + self.user_portal.karma = KARMA['dwv'] + self.post.sudo(self.user_portal).vote(upvote=False) + self.assertEqual(self.post.create_uid.karma, 50 + KARMA['gen_que_dwv'], 'website_forum: wrong karma generation of downvoted question author') + + def test_comment_crash(self): + with self.assertRaises(KarmaError): + self.post.sudo(self.user_portal).message_post(body='Should crash', type='comment') + + def test_comment(self): + self.post.sudo(self.user_employee).message_post(body='Test0', type='notification') + self.user_employee.karma = KARMA['com_all'] + self.post.sudo(self.user_employee).message_post(body='Test1', type='comment') + self.assertEqual(len(self.post.message_ids), 4, 'website_forum: wrong behavior of message_post') + + def test_convert_answer_to_comment_crash(self): + Post = self.env['forum.post'] + + # converting a question does nothing + msg_ids = self.post.sudo(self.user_portal).convert_answer_to_comment() + self.assertEqual(msg_ids[0], False, 'website_forum: question to comment conversion failed') + self.assertEqual(Post.search([('name', '=', 'TestQuestion')])[0].forum_id.name, 'TestForum', 'website_forum: question to comment conversion failed') + + with self.assertRaises(KarmaError): + self.answer.sudo(self.user_portal).convert_answer_to_comment() + + def test_convert_answer_to_comment(self): + self.user_portal.karma = KARMA['com_conv_all'] + post_author = self.answer.create_uid.partner_id + msg_ids = self.answer.sudo(self.user_portal).convert_answer_to_comment() + self.assertEqual(len(msg_ids), 1, 'website_forum: wrong answer to comment conversion') + msg = self.env['mail.message'].browse(msg_ids[0]) + self.assertEqual(msg.author_id, post_author, 'website_forum: wrong answer to comment conversion') + self.assertIn('I am an anteater', msg.body, 'website_forum: wrong answer to comment conversion') + + def test_edit_post_crash(self): + with self.assertRaises(KarmaError): + self.post.sudo(self.user_portal).write({'name': 'I am not your father.'}) + + def test_edit_post(self): + self.post.create_uid.karma = KARMA['edit_own'] + self.post.write({'name': 'Actually I am your dog.'}) + self.user_portal.karma = KARMA['edit_all'] + self.post.sudo(self.user_portal).write({'name': 'Actually I am your cat.'}) + + def test_close_post_crash(self): + with self.assertRaises(KarmaError): + self.post.sudo(self.user_portal).close(None) + + def test_close_post_own(self): + self.post.create_uid.karma = KARMA['close_own'] + self.post.close(None) + + def test_close_post_all(self): + self.user_portal.karma = KARMA['close_all'] + self.post.sudo(self.user_portal).close(None) + + def test_deactivate_post_crash(self): + with self.assertRaises(KarmaError): + self.post.sudo(self.user_portal).write({'active': False}) + + def test_deactivate_post_own(self): + self.post.create_uid.karma = KARMA['unlink_own'] + self.post.write({'active': False}) + + def test_deactivate_post_all(self): + self.user_portal.karma = KARMA['unlink_all'] + self.post.sudo(self.user_portal).write({'active': False}) + + def test_unlink_post_crash(self): + with self.assertRaises(KarmaError): + self.post.sudo(self.user_portal).unlink() + + def test_unlink_post_own(self): + self.post.create_uid.karma = KARMA['unlink_own'] + self.post.unlink() + + def test_unlink_post_all(self): + self.user_portal.karma = KARMA['unlink_all'] + self.post.sudo(self.user_portal).unlink() diff --git a/addons/website_forum/views/forum.xml b/addons/website_forum/views/forum.xml index a2d7c093a7f..d0bb3e13e3d 100644 --- a/addons/website_forum/views/forum.xml +++ b/addons/website_forum/views/forum.xml @@ -24,28 +24,45 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/addons/website_forum/views/website_forum.xml b/addons/website_forum/views/website_forum.xml index f4f5591b612..565d3489b88 100644 --- a/addons/website_forum/views/website_forum.xml +++ b/addons/website_forum/views/website_forum.xml @@ -96,6 +96,21 @@
View Your Badges
+
+ +
+

+ It appears your email has not been verified. + Click here to send a verification email allowing you to participate to the forum. +

+
+
+
+ +
+

Congratulations! Your email has just been validated. You may now participate to our forums.

+
+