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 @@ +