[IMP] website_slides: allow empty messages when reviewing courses

In order to reduce friction and ease the rating process for attendees,
we do not force the constraint stating that the message must contain a
message or an attachment onto the users. Instead, we allow ratings and
replace the empty message by a blank space to avoid triggering the error
message. If no stars are selected, an error message is now displayed to
the user, asking to select a rating before submission, preventing them
to post their review until then.

Since we do not really support 0 stars ratings, and do not want the user
to see that error message in case of the course, we set the default rating
to 4.0 instead of 0.0.
-> Therefore, we also remove the grey star contrast coloring on first review
   since we do not want the user to have the impression a choice has already
   be made beforehand. In that case, everything will be yellow.

Void content detection is done by extracting it in both python (portal
chatter post controller) and frontend (submission check) and relaxing it
in slides modules.

Task-2728564

Part-of: odoo/odoo#82792
This commit is contained in:
Noe Antoine
2022-02-22 19:47:53 +00:00
committed by Thibault Delavallée
parent 9355631e89
commit aead9a884e
6 changed files with 74 additions and 29 deletions
+33 -25
View File
@@ -112,6 +112,10 @@ class PortalChatter(http.Controller):
except (AccessError, MissingError):
raise UserError(_("The attachment %s does not exist or you do not have the rights to access it.", attachment_id))
def _portal_post_has_content(self, res_model, res_id, message, attachment_ids=None, **kw):
""" Tells if we can effectively post on the model based on content. """
return bool(message) or bool(attachment_ids)
@http.route(['/mail/chatter_post'], type='json', methods=['POST'], auth='public', website=True)
def portal_chatter_post(self, res_model, res_id, message, attachment_ids=None, attachment_tokens=None, **kw):
"""Create a new `mail.message` with the given `message` and/or `attachment_ids` and return new message values.
@@ -120,38 +124,42 @@ class PortalChatter(http.Controller):
`res_model`. The user must have access rights on this target document or
must provide valid identifiers through `kw`. See `_message_post_helper`.
"""
if not self._portal_post_has_content(res_model, res_id, message,
attachment_ids=attachment_ids, attachment_tokens=attachment_tokens,
**kw):
return
res_id = int(res_id)
self._portal_post_check_attachments(attachment_ids, attachment_tokens)
if message or attachment_ids:
result = {'default_message': message}
# message is received in plaintext and saved in html
if message:
message = plaintext2html(message)
post_values = {
'res_model': res_model,
'res_id': res_id,
'message': message,
'send_after_commit': False,
'attachment_ids': False, # will be added afterward
}
post_values.update((fname, kw.get(fname)) for fname in self._portal_post_filter_params())
message = _message_post_helper(**post_values)
result.update({'default_message_id': message.id})
result = {'default_message': message}
# message is received in plaintext and saved in html
if message:
message = plaintext2html(message)
post_values = {
'res_model': res_model,
'res_id': res_id,
'message': message,
'send_after_commit': False,
'attachment_ids': False, # will be added afterward
}
post_values.update((fname, kw.get(fname)) for fname in self._portal_post_filter_params())
message = _message_post_helper(**post_values)
result.update({'default_message_id': message.id})
if attachment_ids:
# sudo write the attachment to bypass the read access
# verification in mail message
record = request.env[res_model].browse(res_id)
message_values = {'res_id': res_id, 'model': res_model}
attachments = record._message_post_process_attachments([], attachment_ids, message_values)
if attachment_ids:
# sudo write the attachment to bypass the read access
# verification in mail message
record = request.env[res_model].browse(res_id)
message_values = {'res_id': res_id, 'model': res_model}
attachments = record._message_post_process_attachments([], attachment_ids, message_values)
if attachments.get('attachment_ids'):
message.sudo().write(attachments)
if attachments.get('attachment_ids'):
message.sudo().write(attachments)
result.update({'default_attachment_ids': message.attachment_ids.sudo().read(['id', 'name', 'mimetype', 'file_size', 'access_token'])})
return result
result.update({'default_attachment_ids': message.attachment_ids.sudo().read(['id', 'name', 'mimetype', 'file_size', 'access_token'])})
return result
@http.route('/mail/chatter_init', type='json', auth='public', website=True)
def portal_chatter_init(self, res_model, res_id, domain=False, limit=False, **kwargs):
+11 -2
View File
@@ -158,9 +158,9 @@ var PortalComposer = publicWidget.Widget.extend({
*/
_onSubmitButtonClick: function (ev) {
ev.preventDefault();
if (!this.$inputTextarea.val().trim() && !this.attachments.length) {
const error = this._onSubmitCheckContent();
if (error) {
this.$inputTextarea.addClass('border-danger');
const error = _t('Some fields are required. Please make sure to write a message or attach a document');
this.$(".o_portal_chatter_composer_error").text(error).removeClass('d-none');
return Promise.reject();
} else {
@@ -168,6 +168,15 @@ var PortalComposer = publicWidget.Widget.extend({
}
},
/**
* @private
*/
_onSubmitCheckContent: function () {
if (!this.$inputTextarea.val().trim() && !this.attachments.length) {
return _t('Some fields are required. Please make sure to write a message or attach a document');
};
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
@@ -34,9 +34,10 @@ PortalComposer.include({
// default options
this.options = _.defaults(this.options, {
'rate_with_void_content': false,
'default_message': false,
'default_message_id': false,
'default_rating_value': 0.0,
'default_rating_value': 4.0,
'force_submit_url': false,
});
// star input widget
@@ -61,6 +62,10 @@ PortalComposer.include({
// rating stars
self.$input = self.$('input[name="rating_value"]');
self.$star_list = self.$('.stars').find('i');
// if this is the first review, we do not use grey color contrast, even with default rating value.
if (!self.options.default_message_id) {
self.$star_list.removeClass('text-black-25');
}
// set the default value to trigger the display of star widget and update the hidden input value.
self.set("star_value", self.options.default_rating_value);
@@ -149,5 +154,19 @@ PortalComposer.include({
$modal.modal('hide');
});
},
/**
* @override
* @private
*/
_onSubmitCheckContent: function (ev) {
if (this.options.rate_with_void_content) {
if (this.$input.val() === 0) {
return _t('The rating is required. Please make sure to select one before sending your review.')
}
return false;
}
return this._super.apply(this, arguments);
},
});
});
@@ -67,6 +67,7 @@
t-att-data-default_rating_value="default_rating_value"
t-att-data-default_attachment_ids="default_attachment_ids"
t-att-data-force_submit_url="force_submit_url"
t-att-data-rate_with_void_content="rate_with_void_content"
t-att-data-disable_composer="disable_composer"
t-att-data-display_composer="display_composer"
t-att-data-link_btn_classes="_link_btn_classes"
+8 -1
View File
@@ -13,10 +13,17 @@ from odoo.tools import plaintext2html, html2plaintext
class SlidesPortalChatter(PortalChatter):
def _portal_post_has_content(self, res_model, res_id, message, attachment_ids=None, **kw):
""" Relax constraint on slide model: having a rating value is sufficient
to consider we have a content. """
if res_model == 'slide.channel' and kw.get('rating_value'):
return True
return super()._portal_post_has_content(res_model, res_id, message, attachment_ids=attachment_ids, **kw)
@http.route(['/mail/chatter_post'], type='json', methods=['POST'], auth='public', website=True)
def portal_chatter_post(self, res_model, res_id, message, **kw):
result = super(SlidesPortalChatter, self).portal_chatter_post(res_model, res_id, message, **kw)
if res_model == 'slide.channel':
if result and res_model == 'slide.channel':
rating_value = kw.get('rating_value', False)
slide_channel = request.env[res_model].sudo().browse(int(res_id))
if rating_value and slide_channel and request.env.user.partner_id.id == int(kw.get('pid')):
@@ -153,6 +153,7 @@
<t t-set="default_rating_value" t-value="last_rating_value"/>
<t t-set="default_attachment_ids" t-value="last_message_attachment_ids"/>
<t t-set="force_submit_url" t-value="'/slides/mail/update_comment' if last_message_id else False"/>
<t t-set="rate_with_void_content" t-value="True"/>
<t t-set="disable_composer" t-value="not channel.can_review"/>
<t t-set="_link_btn_classes" t-value="'btn-sm btn-link text-white mx-3'"/>
</t>