+--
+${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.