[IMP] website_slides : allow review, comment and vote only if enough karma

To avoid eLearning to be spammed, the comment, review and vote behaviours
are now allowed only if the user has enough karma to do it.

Here is the new behaviour on courses and slides rating / comment / vote
    -If allow_comment is checked on Course :
        - Review (rating) is allowed on Course only if enough karma
        - Comment is allowed on slides within the course
            only if enough karma and course type is 'training'
        - Vote is allowed on slides within the course
            only if enough karma and course type is 'documentation'
    -If allow_comment is not checked on Course :
        - Review (rating) is not allowed on Course
        - Comment is not allowed on slides within the course
        - Vote is not allowed on slides within the course
    - Rating is not allowed on slides within the course anymore

Task ID : 1943788
PR #31321
This commit is contained in:
David Beguin
2019-03-15 15:05:54 +00:00
parent 81cebf3485
commit d4db21a9cc
10 changed files with 81 additions and 23 deletions
+2 -2
View File
@@ -580,7 +580,7 @@ class Post(models.Model):
@api.one
def refuse(self):
if not self.can_moderate:
raise KarmaError('Not enough karma to refuse a post')
raise KarmaError(_('Not enough karma to refuse a post'))
self.moderator_id = self.env.user
return True
@@ -799,7 +799,7 @@ class Post(models.Model):
self.ensure_one()
if not self.can_comment:
raise KarmaError('Not enough karma to comment')
raise KarmaError(_('Not enough karma to comment'))
if not kwargs.get('record_name') and self.parent_id:
kwargs['record_name'] = self.parent_id.name
return super(Post, self).message_post(message_type=message_type, **kwargs)
@@ -301,7 +301,7 @@ odoo.define('website_rating.thread', function(require) {
'res_model': false,
'res_id': false,
'pid': 0,
'display_composer': !session.is_website_user,
'display_composer': options['disable_composer'] ? false : !session.is_website_user,
'display_rating': true,
'csrf_token': odoo.csrf_token,
'user_id': session.user_id,
@@ -61,7 +61,8 @@
t-att-data-default_message="default_message"
t-att-data-default_message_id="default_message_id"
t-att-data-default_rating_value="default_rating_value"
t-att-data-force_submit_url="force_submit_url">
t-att-data-force_submit_url="force_submit_url"
t-att-data-disable_composer="disable_composer">
</div>
</template>
+9 -2
View File
@@ -382,12 +382,15 @@ class WebsiteSlides(WebsiteProfile):
], order='write_date DESC', limit=1).read(['body', 'rating_value'])
last_message_data = last_message_values[0] if last_message_values else {}
values.update({
'message_post_hash': channel._sign_token(request.env.user.partner_id.id),
'message_post_pid': request.env.user.partner_id.id,
'last_message_id': last_message_data.get('id'),
'last_message': tools.html2plaintext(last_message_data.get('body', '')),
'last_rating_value': last_message_data.get('rating_value'),
})
if channel.can_review:
values.update({
'message_post_hash': channel._sign_token(request.env.user.partner_id.id),
'message_post_pid': request.env.user.partner_id.id,
})
# fetch slides and handle uncategorized slides; done as sudo because we want to display all
# of them but unreachable ones won't be clickable (+ slide controller will crash anyway)
@@ -574,6 +577,10 @@ class WebsiteSlides(WebsiteProfile):
if (upvote and slide_partners.vote == 1) or (not upvote and slide_partners.vote == -1):
return {'error': 'vote_done'}
slide = request.env['slide.slide'].browse(int(slide_id))
if not slide.channel_id.allow_comment:
return {'error': 'comment_disabled'}
if not slide.can_vote:
return {'error': 'missing_karma'}
if upvote:
slide.action_like()
else:
+14 -2
View File
@@ -6,6 +6,7 @@ import uuid
from odoo import api, fields, models, tools, _
from odoo.addons.http_routing.models.ir_http import slug
from odoo.addons.gamification.models.gamification_karma_rank import KarmaError
from odoo.exceptions import UserError
from odoo.osv import expression
@@ -114,7 +115,7 @@ class Channel(models.Model):
total_time = fields.Float('# Hours', compute='_compute_slides_statistics', digits=(10, 4), store=True)
# configuration
allow_comment = fields.Boolean(
"Allow comment on Content", default=False,
"Allow rating on Course", default=False,
help="If checked it allows members to either:\n"
" * like content and post comments on documentation course;\n"
" * post comment and review on training course;")
@@ -155,7 +156,11 @@ class Channel(models.Model):
karma_gen_slide_vote = fields.Integer(string='Lesson voted', default=1)
karma_gen_channel_rank = fields.Integer(string='Course ranked', default=5)
karma_gen_channel_finish = fields.Integer(string='Course finished', default=10)
# TODO DBE : Add karma based action rules (like in forum)
# Karma based actions
karma_review = fields.Integer('Add a review', default=10, help="Karma needed to add a review on the course")
karma_slide_comment = fields.Integer('Add a comment', default=3, help="Karma needed to add a comment on a slide of this course")
karma_slide_vote = fields.Integer('Vote on slide', default=3, help="Karma needed to like/dislike a slide of this course.")
can_review = fields.Boolean('Can Review', compute='_compute_karma_rights')
@api.depends('slide_ids.is_published')
def _compute_slide_last_update(self):
@@ -263,6 +268,11 @@ class Channel(models.Model):
if channel.id: # avoid to perform a slug on a not yet saved record in case of an onchange.
channel.website_url = '%s/slides/%s' % (base_url, slug(channel))
@api.multi
def _compute_karma_rights(self):
for channel in self:
channel.can_review = self.env.user.karma >= channel.karma_review
# ---------------------------------------------------------
# ORM Overrides
# ---------------------------------------------------------
@@ -318,6 +328,8 @@ class Channel(models.Model):
through the 'Presentation Published' email, it should be considered as a
note as we don't want all channel followers to be notified of this answer. """
self.ensure_one()
if kwargs.get('message_type') == 'comment' and not self.can_review:
raise KarmaError(_('Not enough karma to review'))
if parent_id:
parent_message = self.env['mail.message'].sudo().browse(parent_id)
if parent_message.subtype_id and parent_message.subtype_id == self.env.ref('website_slides.mt_channel_slide_published'):
@@ -13,6 +13,7 @@ from werkzeug import urls
from odoo import api, fields, models, _
from odoo.addons.http_routing.models.ir_http import slug
from odoo.addons.gamification.models.gamification_karma_rank import KarmaError
from odoo.exceptions import Warning, UserError
from odoo.http import request
from odoo.addons.http_routing.models.ir_http import url_for
@@ -155,6 +156,9 @@ class Slide(models.Model):
slide_views = fields.Integer('# of Website Views', store=True, compute="_compute_slide_views")
public_views = fields.Integer('# of Public Views')
total_views = fields.Integer("Total # Views", default="0", compute='_compute_total', store=True)
# Karma Based action
can_comment = fields.Boolean('Can Comment', compute='_compute_karma_rights')
can_vote = fields.Boolean('Can Vote', compute='_compute_karma_rights')
_sql_constraints = [
('exclusion_html_content_and_url', "CHECK(html_content IS NULL OR url IS NULL)", "A slide is either filled with a document url or HTML content. Not both.")
@@ -267,6 +271,12 @@ class Slide(models.Model):
def _get_can_publish_error_message(self):
return _("Publishing is restricted to the responsible of training courses or members of the publisher group for documentation courses")
@api.multi
def _compute_karma_rights(self):
for slide in self:
slide.can_comment = self.env.user.karma >= slide.channel_id.karma_slide_comment
slide.can_vote = self.env.user.karma >= slide.channel_id.karma_slide_vote
# ---------------------------------------------------------
# ORM Overrides
# ---------------------------------------------------------
@@ -313,6 +323,14 @@ class Slide(models.Model):
# Mail/Rating
# ---------------------------------------------------------
@api.multi
@api.returns('mail.message', lambda value: value.id)
def message_post(self, message_type='notification', **kwargs):
self.ensure_one()
if message_type == 'comment' and not self.can_comment: # user comments have a restriction on karma
raise KarmaError(_('Not enough karma to comment'))
return super(Slide, self).message_post(message_type=message_type, **kwargs)
@api.multi
def get_access_action(self, access_uid=None):
""" Instead of the classic form view, redirect to website if it is published. """
@@ -59,6 +59,10 @@ var SlideLikeWidget = Widget.extend({
self._popoverAlert(self.$el, _.str.sprintf(_t('Please <a href="/web/login?redirect=%s">login</a> to vote this slide'), (document.URL)));
} else if (data.error === 'vote_done') {
self._popoverAlert(self.$el, _t('You have already voted for this slide'));
} else if (data.error === 'comment_disabled') {
self._popoverAlert(self.$el, _t('Votes and comments are disabled for this channel'));
} else if (data.error === 'missing_karma') {
self._popoverAlert(self.$el, _t('You don\'t have enough karma to vote'));
} else {
self._popoverAlert(self.$el, _t('Unknown error'));
}
@@ -111,6 +111,20 @@
<field name="allow_comment"/>
</group>
</page>
<page string="Karma Rules" name="karma_rules">
<group>
<group string="Karma generation">
<field name="karma_gen_slide_vote"/>
<field name="karma_gen_channel_rank"/>
<field name="karma_gen_channel_finish"/>
</group>
<group string="Karma based actions">
<field name="karma_review"/>
<field name="karma_slide_comment"/>
<field name="karma_slide_vote"/>
</group>
</group>
</page>
<page string="Statistics" groups="base.group_no_one">
<group>
<group>
@@ -104,6 +104,7 @@
<t t-set="default_message" t-value="last_message"/>
<t t-set="default_rating_value" t-value="last_rating_value"/>
<t t-set="force_submit_url" t-value="'/slides/mail/update_comment' if last_message_id else False"/>
<t t-set="disable_composer" t-value="not channel.allow_comment or not channel.can_review"/>
</t>
</div>
</div>
@@ -115,7 +116,7 @@
<i class="fa fa-home"/> Course
</a>
</li>
<li class="nav-item">
<li class="nav-item" t-if="channel.allow_comment">
<a class="nav-link" id="review-tab" data-toggle="pill" href="#review" role="tab" aria-controls="review" aria-selected="false">Review</a>
</li>
</ul>
@@ -237,19 +237,21 @@
</t>
</div>
<span class="text-muted font-weight-bold mr-3">Rating</span>
<div class="text-muted border-left pl-3">
<div class="o_wslides_js_slide_like mr-2">
<span class="o_wslides_js_slide_like_up" tabindex="0" data-toggle="popover" t-att-data-slide-id="slide.id">
<i class="fa fa-thumbs-up fa-1x" role="img" aria-label="Likes" title="Likes"></i>
<span t-esc="slide.likes"/>
</span>
<span class="o_wslides_js_slide_like_down ml-3" tabindex="0" data-toggle="popover" t-att-data-slide-id="slide.id">
<i class="fa fa-thumbs-down fa-1x" role="img" aria-label="Dislikes" title="Dislikes"></i>
<span t-esc="slide.dislikes"/>
</span>
<t t-if="slide.channel_id.allow_comment and slide.channel_id.channel_type == 'documentation'">
<span class="text-muted font-weight-bold mr-3">Rating</span>
<div class="text-muted border-left pl-3">
<div class="o_wslides_js_slide_like mr-2">
<span class="o_wslides_js_slide_like_up" tabindex="0" data-toggle="popover" t-att-data-slide-id="slide.id">
<i class="fa fa-thumbs-up fa-1x" role="img" aria-label="Likes" title="Likes"></i>
<span t-esc="slide.likes"/>
</span>
<span class="o_wslides_js_slide_like_down ml-3" tabindex="0" data-toggle="popover" t-att-data-slide-id="slide.id">
<i class="fa fa-thumbs-down fa-1x" role="img" aria-label="Dislikes" title="Dislikes"></i>
<span t-esc="slide.dislikes"/>
</span>
</div>
</div>
</div>
</t>
</div>
</div>
<div class="row mb-5">
@@ -287,8 +289,7 @@
<div role="tabpanel" t-att-class="comments and 'tab-pane fade in show active' or 'tab-pane fade'" id="discuss">
<t t-call="portal.message_thread">
<t t-set="object" t-value="slide"/>
<t t-set="hash" t-value="message_post_hash"/>
<t t-set="pid" t-value="message_post_pid"/>
<t t-set="disable_composer" t-value="not (slide.can_comment and slide.channel_id.allow_comment and slide.channel_id.channel_type == 'training')"/>
<t t-set="display_rating" t-value="False"/>
</t>
</div>