[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:
committed by
Thibault Delavallée
parent
9355631e89
commit
aead9a884e
@@ -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):
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user