[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:
@@ -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>
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user