From aead9a884e9007e8e19a7471608a5a3417fb3d52 Mon Sep 17 00:00:00 2001 From: Noe Antoine Date: Fri, 14 Jan 2022 10:33:33 +0000 Subject: [PATCH] [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 --- addons/portal/controllers/mail.py | 58 +++++++++++-------- .../portal/static/src/js/portal_composer.js | 13 ++++- .../static/src/js/portal_composer.js | 21 ++++++- .../portal_rating/views/rating_templates.xml | 1 + addons/website_slides/controllers/mail.py | 9 ++- .../views/website_slides_templates_course.xml | 1 + 6 files changed, 74 insertions(+), 29 deletions(-) diff --git a/addons/portal/controllers/mail.py b/addons/portal/controllers/mail.py index e2bb31c961b..44bc004cd34 100644 --- a/addons/portal/controllers/mail.py +++ b/addons/portal/controllers/mail.py @@ -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): diff --git a/addons/portal/static/src/js/portal_composer.js b/addons/portal/static/src/js/portal_composer.js index 645210c0116..1622f34bda0 100644 --- a/addons/portal/static/src/js/portal_composer.js +++ b/addons/portal/static/src/js/portal_composer.js @@ -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 //-------------------------------------------------------------------------- diff --git a/addons/portal_rating/static/src/js/portal_composer.js b/addons/portal_rating/static/src/js/portal_composer.js index 3d1bb0ea7fc..7833de0b86c 100644 --- a/addons/portal_rating/static/src/js/portal_composer.js +++ b/addons/portal_rating/static/src/js/portal_composer.js @@ -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); + }, }); }); diff --git a/addons/portal_rating/views/rating_templates.xml b/addons/portal_rating/views/rating_templates.xml index 2fcc31e5894..d5f6b9b7451 100644 --- a/addons/portal_rating/views/rating_templates.xml +++ b/addons/portal_rating/views/rating_templates.xml @@ -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" diff --git a/addons/website_slides/controllers/mail.py b/addons/website_slides/controllers/mail.py index 8c72322e290..7c52fa46b35 100644 --- a/addons/website_slides/controllers/mail.py +++ b/addons/website_slides/controllers/mail.py @@ -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')): diff --git a/addons/website_slides/views/website_slides_templates_course.xml b/addons/website_slides/views/website_slides_templates_course.xml index e753b91f439..b5e5f72bce6 100644 --- a/addons/website_slides/views/website_slides_templates_course.xml +++ b/addons/website_slides/views/website_slides_templates_course.xml @@ -153,6 +153,7 @@ +