[MERGE] website_{rating, slides}: Manage attachments in review update
PURPOSE Allow to manage attachments when updating a frontend review. SPECIFICATIONS Currently when posting a review in eLearning or eCommerce one can attach files to its review thanks for portal composer. Rating popup composer allow to update its comment and rating. However this edit mode has two limitations. It does not display attachments previously added and does not allow to add attachments. This merge fixes that by * giving attachment values to the composer, allowing its display by the composer display at startup; * allowing to give new attachments and properly handle them in slides route; Uploaded and validated attachments cannot be removed by users. Indeed message with attachments has probably already be sent to people and removing attachments would lead to information loss. LINKS Task 2066600 (manage attachments) Task 2058595 (eLearning v13 testing) closes odoo/odoo#36409 Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
@@ -88,6 +88,15 @@ class PortalChatter(http.Controller):
|
||||
def _portal_post_filter_params(self):
|
||||
return ['token', 'hash', 'pid']
|
||||
|
||||
def _portal_post_check_attachments(self, attachment_ids, attachment_tokens):
|
||||
if len(attachment_tokens) != len(attachment_ids):
|
||||
raise UserError(_("An access token must be provided for each attachment."))
|
||||
for (attachment_id, access_token) in zip(attachment_ids, attachment_tokens):
|
||||
try:
|
||||
CustomerPortal._document_check_access(self, 'ir.attachment', attachment_id, access_token)
|
||||
except (AccessError, MissingError):
|
||||
raise UserError(_("The attachment %s does not exist or you do not have the rights to access it.") % attachment_id)
|
||||
|
||||
@http.route(['/mail/chatter_post'], type='http', methods=['POST'], auth='public', website=True)
|
||||
def portal_chatter_post(self, res_model, res_id, message, redirect=None, attachment_ids='', attachment_tokens='', **kw):
|
||||
"""Create a new `mail.message` with the given `message` and/or
|
||||
@@ -103,13 +112,7 @@ class PortalChatter(http.Controller):
|
||||
|
||||
attachment_ids = [int(attachment_id) for attachment_id in attachment_ids.split(',') if attachment_id]
|
||||
attachment_tokens = [attachment_token for attachment_token in attachment_tokens.split(',') if attachment_token]
|
||||
if len(attachment_tokens) != len(attachment_ids):
|
||||
raise UserError(_("An access token must be provided for each attachment."))
|
||||
for (attachment_id, access_token) in zip(attachment_ids, attachment_tokens):
|
||||
try:
|
||||
CustomerPortal._document_check_access(self, 'ir.attachment', attachment_id, access_token)
|
||||
except (AccessError, MissingError):
|
||||
raise UserError(_("The attachment %s does not exist or you do not have the rights to access it.") % attachment_id)
|
||||
self._portal_post_check_attachments(attachment_ids, attachment_tokens)
|
||||
|
||||
if message or attachment_ids:
|
||||
# message is received in plaintext and saved in html
|
||||
|
||||
@@ -1,163 +1,14 @@
|
||||
odoo.define('portal.chatter', function (require) {
|
||||
'use strict';
|
||||
|
||||
var ajax = require('web.ajax');
|
||||
var core = require('web.core');
|
||||
var publicWidget = require('web.public.widget');
|
||||
var time = require('web.time');
|
||||
var portalComposer = require('portal.composer');
|
||||
|
||||
var qweb = core.qweb;
|
||||
var _t = core._t;
|
||||
|
||||
/**
|
||||
* Widget PortalComposer
|
||||
*
|
||||
* Display the composer (according to access right)
|
||||
*
|
||||
*/
|
||||
var PortalComposer = publicWidget.Widget.extend({
|
||||
template: 'portal.Composer',
|
||||
xmlDependencies: ['/portal/static/src/xml/portal_chatter.xml'],
|
||||
events: {
|
||||
'change .o_portal_chatter_file_input': '_onFileInputChange',
|
||||
'click .o_portal_chatter_attachment_btn': '_onAttachmentButtonClick',
|
||||
'click .o_portal_chatter_attachment_delete': 'async _onAttachmentDeleteClick',
|
||||
'click .o_portal_chatter_composer_btn': 'async _onSubmitButtonClick',
|
||||
},
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
init: function (parent, options) {
|
||||
this._super.apply(this, arguments);
|
||||
this.options = _.defaults(options || {}, {
|
||||
'allow_composer': true,
|
||||
'display_composer': false,
|
||||
'csrf_token': odoo.csrf_token,
|
||||
'token': false,
|
||||
'res_model': false,
|
||||
'res_id': false,
|
||||
});
|
||||
this.attachments = [];
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
this.$attachmentButton = this.$('.o_portal_chatter_attachment_btn');
|
||||
this.$fileInput = this.$('.o_portal_chatter_file_input');
|
||||
this.$sendButton = this.$('.o_portal_chatter_composer_btn');
|
||||
this.$attachments = this.$('.o_portal_chatter_composer_form .o_portal_chatter_attachments');
|
||||
this.$attachmentIds = this.$('.o_portal_chatter_attachment_ids');
|
||||
this.$attachmentTokens = this.$('.o_portal_chatter_attachment_tokens');
|
||||
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onAttachmentButtonClick: function () {
|
||||
this.$fileInput.click();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} ev
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_onAttachmentDeleteClick: function (ev) {
|
||||
var self = this;
|
||||
var attachmentId = $(ev.currentTarget).closest('.o_portal_chatter_attachment').data('id');
|
||||
var accessToken = _.find(this.attachments, {'id': attachmentId}).access_token;
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
this.$sendButton.prop('disabled', true);
|
||||
|
||||
return this._rpc({
|
||||
route: '/portal/attachment/remove',
|
||||
params: {
|
||||
'attachment_id': attachmentId,
|
||||
'access_token': accessToken,
|
||||
},
|
||||
}).then(function () {
|
||||
self.attachments = _.reject(self.attachments, {'id': attachmentId});
|
||||
self._updateAttachments();
|
||||
self.$sendButton.prop('disabled', false);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_onFileInputChange: function () {
|
||||
var self = this;
|
||||
|
||||
this.$sendButton.prop('disabled', true);
|
||||
|
||||
return Promise.all(_.map(this.$fileInput[0].files, function (file) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var data = {
|
||||
'name': file.name,
|
||||
'file': file,
|
||||
'res_id': self.options.res_id,
|
||||
'res_model': self.options.res_model,
|
||||
'access_token': self.options.token,
|
||||
};
|
||||
ajax.post('/portal/attachment/add', data).then(function (attachment) {
|
||||
self.attachments.push(attachment);
|
||||
self._updateAttachments();
|
||||
resolve();
|
||||
}).guardedCatch(function (error) {
|
||||
self.displayNotification({
|
||||
title: _t("Something went wrong."),
|
||||
message: _.str.sprintf(_t("The file <strong>%s</strong> could not be saved."),
|
||||
_.escape(file.name)),
|
||||
type: 'warning',
|
||||
sticky: true,
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
})).then(function () {
|
||||
self.$sendButton.prop('disabled', false);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Returns a Promise that is never resolved to prevent sending the form
|
||||
* twice when clicking twice on the button, in combination with the `async`
|
||||
* in the event definition.
|
||||
*
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_onSubmitButtonClick: function () {
|
||||
return new Promise(function (resolve, reject) {});
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_updateAttachments: function () {
|
||||
this.$attachmentIds.val(_.pluck(this.attachments, 'id'));
|
||||
this.$attachmentTokens.val(_.pluck(this.attachments, 'access_token'));
|
||||
this.$attachments.html(qweb.render('portal.Chatter.Attachments', {
|
||||
attachments: this.attachments,
|
||||
showDelete: true,
|
||||
}));
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Widget PortalChatter
|
||||
*
|
||||
@@ -237,7 +88,7 @@ var PortalChatter = publicWidget.Widget.extend({
|
||||
|
||||
// instanciate and insert composer widget
|
||||
if (this.options['display_composer']) {
|
||||
this._composer = new PortalComposer(this, this.options);
|
||||
this._composer = new portalComposer.PortalComposer(this, this.options);
|
||||
defs.push(this._composer.replace(this.$('.o_portal_chatter_composer')));
|
||||
}
|
||||
|
||||
@@ -426,7 +277,6 @@ publicWidget.registry.portalChatter = publicWidget.Widget.extend({
|
||||
});
|
||||
|
||||
return {
|
||||
PortalComposer: PortalComposer,
|
||||
PortalChatter: PortalChatter,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
odoo.define('portal.composer', function (require) {
|
||||
'use strict';
|
||||
|
||||
var ajax = require('web.ajax');
|
||||
var core = require('web.core');
|
||||
var publicWidget = require('web.public.widget');
|
||||
|
||||
var qweb = core.qweb;
|
||||
var _t = core._t;
|
||||
|
||||
/**
|
||||
* Widget PortalComposer
|
||||
*
|
||||
* Display the composer (according to access right)
|
||||
*
|
||||
*/
|
||||
var PortalComposer = publicWidget.Widget.extend({
|
||||
template: 'portal.Composer',
|
||||
xmlDependencies: ['/portal/static/src/xml/portal_chatter.xml'],
|
||||
events: {
|
||||
'change .o_portal_chatter_file_input': '_onFileInputChange',
|
||||
'click .o_portal_chatter_attachment_btn': '_onAttachmentButtonClick',
|
||||
'click .o_portal_chatter_attachment_delete': 'async _onAttachmentDeleteClick',
|
||||
'click .o_portal_chatter_composer_btn': 'async _onSubmitButtonClick',
|
||||
},
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
init: function (parent, options) {
|
||||
this._super.apply(this, arguments);
|
||||
this.options = _.defaults(options || {}, {
|
||||
'allow_composer': true,
|
||||
'display_composer': false,
|
||||
'csrf_token': odoo.csrf_token,
|
||||
'token': false,
|
||||
'res_model': false,
|
||||
'res_id': false,
|
||||
});
|
||||
this.attachments = [];
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
var self = this;
|
||||
this.$attachmentButton = this.$('.o_portal_chatter_attachment_btn');
|
||||
this.$fileInput = this.$('.o_portal_chatter_file_input');
|
||||
this.$sendButton = this.$('.o_portal_chatter_composer_btn');
|
||||
this.$attachments = this.$('.o_portal_chatter_composer_form .o_portal_chatter_attachments');
|
||||
this.$attachmentIds = this.$('.o_portal_chatter_attachment_ids');
|
||||
this.$attachmentTokens = this.$('.o_portal_chatter_attachment_tokens');
|
||||
|
||||
return this._super.apply(this, arguments).then(function () {
|
||||
if (self.options.default_attachment_ids) {
|
||||
self.attachments = self.options.default_attachment_ids || [];
|
||||
_.each(self.attachments, function(attachment) {
|
||||
attachment.state = 'done';
|
||||
});
|
||||
self._updateAttachments();
|
||||
}
|
||||
return Promise.resolve();
|
||||
});
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_onAttachmentButtonClick: function () {
|
||||
this.$fileInput.click();
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} ev
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_onAttachmentDeleteClick: function (ev) {
|
||||
var self = this;
|
||||
var attachmentId = $(ev.currentTarget).closest('.o_portal_chatter_attachment').data('id');
|
||||
var accessToken = _.find(this.attachments, {'id': attachmentId}).access_token;
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
this.$sendButton.prop('disabled', true);
|
||||
|
||||
return this._rpc({
|
||||
route: '/portal/attachment/remove',
|
||||
params: {
|
||||
'attachment_id': attachmentId,
|
||||
'access_token': accessToken,
|
||||
},
|
||||
}).then(function () {
|
||||
self.attachments = _.reject(self.attachments, {'id': attachmentId});
|
||||
self._updateAttachments();
|
||||
self.$sendButton.prop('disabled', false);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_onFileInputChange: function () {
|
||||
var self = this;
|
||||
|
||||
this.$sendButton.prop('disabled', true);
|
||||
|
||||
return Promise.all(_.map(this.$fileInput[0].files, function (file) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var data = {
|
||||
'name': file.name,
|
||||
'file': file,
|
||||
'res_id': self.options.res_id,
|
||||
'res_model': self.options.res_model,
|
||||
'access_token': self.options.token,
|
||||
};
|
||||
ajax.post('/portal/attachment/add', data).then(function (attachment) {
|
||||
attachment.state = 'pending';
|
||||
self.attachments.push(attachment);
|
||||
self._updateAttachments();
|
||||
resolve();
|
||||
}).guardedCatch(function (error) {
|
||||
self.displayNotification({
|
||||
title: _t("Something went wrong."),
|
||||
message: _.str.sprintf(_t("The file <strong>%s</strong> could not be saved."),
|
||||
_.escape(file.name)),
|
||||
type: 'warning',
|
||||
sticky: true,
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
})).then(function () {
|
||||
self.$sendButton.prop('disabled', false);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Returns a Promise that is never resolved to prevent sending the form
|
||||
* twice when clicking twice on the button, in combination with the `async`
|
||||
* in the event definition.
|
||||
*
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_onSubmitButtonClick: function () {
|
||||
return new Promise(function (resolve, reject) {});
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_updateAttachments: function () {
|
||||
this.$attachmentIds.val(_.pluck(this.attachments, 'id'));
|
||||
this.$attachmentTokens.val(_.pluck(this.attachments, 'access_token'));
|
||||
this.$attachments.html(qweb.render('portal.Chatter.Attachments', {
|
||||
attachments: this.attachments,
|
||||
showDelete: true,
|
||||
}));
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
PortalComposer: PortalComposer,
|
||||
};
|
||||
});
|
||||
@@ -418,54 +418,6 @@ img.o_we_custom_image {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.o_portal_chatter {
|
||||
padding: 10px;
|
||||
|
||||
.o_portal_chatter_avatar{
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.o_portal_chatter_header {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.o_portal_chatter_composer {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.o_portal_chatter_messages {
|
||||
margin-bottom: 15px;
|
||||
|
||||
.o_portal_chatter_message_title {
|
||||
p {
|
||||
font-size:85%;
|
||||
color:$o-main-color-muted;
|
||||
margin: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.o_portal_chatter_pager {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.o_portal_chatter_attachment {
|
||||
.o_portal_chatter_attachment_name {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.o_portal_chatter_attachment_delete {
|
||||
@include o-position-absolute($top: 0, $right: 0);
|
||||
opacity: 0;
|
||||
}
|
||||
&:hover .o_portal_chatter_attachment_delete {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
form label {
|
||||
font-weight: $font-weight-bold;
|
||||
&.label-optional {
|
||||
@@ -512,3 +464,63 @@ form label {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Frontend Discuss widget
|
||||
// ------------------------------------------------------------
|
||||
|
||||
// Readonly display
|
||||
.o_portal_chatter {
|
||||
padding: 10px;
|
||||
|
||||
.o_portal_chatter_avatar {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.o_portal_chatter_header {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.o_portal_chatter_composer {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.o_portal_chatter_messages {
|
||||
margin-bottom: 15px;
|
||||
|
||||
.o_portal_chatter_message_title {
|
||||
p {
|
||||
font-size:85%;
|
||||
color:$o-main-color-muted;
|
||||
margin: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.o_portal_chatter_pager {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Readonly / Composer mix display
|
||||
.o_portal_chatter,
|
||||
.o_portal_chatter_composer {
|
||||
.o_portal_chatter_attachment {
|
||||
.o_portal_chatter_attachment_name {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.o_portal_chatter_attachment_delete {
|
||||
@include o-position-absolute($top: 0, $right: 0);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:hover .o_portal_chatter_attachment_delete {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
<div t-if="attachments.length" class="row">
|
||||
<div t-foreach="attachments" t-as="attachment" class="col-lg-2 col-md-3 col-sm-6">
|
||||
<div class="o_portal_chatter_attachment mb-2 position-relative text-center" t-att-data-id="attachment.id">
|
||||
<button t-if="showDelete" class="o_portal_chatter_attachment_delete btn btn-sm btn-outline-danger" title="Delete">
|
||||
<button t-if="showDelete and attachment.state == 'pending'" class="o_portal_chatter_attachment_delete btn btn-sm btn-outline-danger" title="Delete">
|
||||
<i class="fa fa-times"/>
|
||||
</button>
|
||||
<a t-attf-href="/web/content/#{attachment.id}?download=true&access_token=#{attachment.access_token}" target="_blank">
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
<xpath expr="//script[last()]" position="after">
|
||||
<script type="text/javascript" src="/portal/static/src/js/portal.js"></script>
|
||||
<script type="text/javascript" src="/portal/static/src/js/portal_chatter.js"></script>
|
||||
<script type="text/javascript" src="/portal/static/src/js/portal_composer.js"></script>
|
||||
<script type="text/javascript" src="/portal/static/src/js/portal_signature.js"></script>
|
||||
<script type="text/javascript" src="/portal/static/src/js/portal_sidebar.js"></script>
|
||||
</xpath>
|
||||
|
||||
@@ -2,11 +2,11 @@ odoo.define('rating.portal.composer', function (require) {
|
||||
'use strict';
|
||||
|
||||
var core = require('web.core');
|
||||
var portalChatter = require('portal.chatter');
|
||||
var portalComposer = require('portal.composer');
|
||||
|
||||
var _t = core._t;
|
||||
|
||||
var PortalComposer = portalChatter.PortalComposer;
|
||||
var PortalComposer = portalComposer.PortalComposer;
|
||||
|
||||
var STAR_RATING_RATIO = 2; // conversion factor from the star (1-5) to the db rating range (1-10)
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ odoo.define('portal.rating.composer', function (require) {
|
||||
|
||||
var publicWidget = require('web.public.widget');
|
||||
var session = require('web.session');
|
||||
var portalChatter = require('portal.chatter');
|
||||
var portalComposer = require('portal.composer');
|
||||
|
||||
var PortalComposer = portalChatter.PortalComposer;
|
||||
var PortalComposer = portalComposer.PortalComposer;
|
||||
|
||||
var STAR_RATING_RATIO = 2; // conversion factor from the star (1-5) to the db rating range (1-10)
|
||||
|
||||
@@ -61,7 +61,8 @@ publicWidget.registry.RatingPopupComposer = publicWidget.Widget.extend({
|
||||
* @override
|
||||
*/
|
||||
start: function () {
|
||||
var ratingPopup = new RatingPopupComposer(this, this.$el.data());
|
||||
var ratingPopupData = this.$el.data();
|
||||
var ratingPopup = new RatingPopupComposer(this, ratingPopupData);
|
||||
return Promise.all([
|
||||
this._super.apply(this, arguments),
|
||||
ratingPopup.appendTo(this.$el)
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
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-default_attachment_ids="default_attachment_ids"
|
||||
t-att-data-force_submit_url="force_submit_url"
|
||||
t-att-data-disable_composer="disable_composer">
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import mail
|
||||
from . import main
|
||||
from . import portal
|
||||
from . import mail
|
||||
|
||||
@@ -1,33 +1,51 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import logging
|
||||
from odoo.http import request
|
||||
import werkzeug
|
||||
|
||||
from werkzeug.exceptions import NotFound, Forbidden
|
||||
|
||||
from odoo import http
|
||||
from odoo.addons.portal.controllers.mail import _check_special_access
|
||||
from odoo.http import request
|
||||
from odoo.addons.portal.controllers.mail import _check_special_access, PortalChatter
|
||||
from odoo.tools import plaintext2html
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class SlidesPortalChatter(PortalChatter):
|
||||
|
||||
class SlidesMail(http.Controller):
|
||||
@http.route(['/mail/chatter_post'], type='http', 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':
|
||||
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')):
|
||||
# apply karma gain rule only once
|
||||
request.env.user.add_karma(slide_channel.karma_gen_channel_rank)
|
||||
return result
|
||||
|
||||
@http.route('/slides/mail/update_comment', type='http', auth="user")
|
||||
def mail_update_message(self, res_id, message, message_id, **post):
|
||||
|
||||
res_model = 'slide.channel' # keep this mecanism intern to slide
|
||||
message_body = plaintext2html(message)
|
||||
@http.route([
|
||||
'/slides/mail/update_comment',
|
||||
'/mail/chatter_update',
|
||||
], type='http', auth="user")
|
||||
def mail_update_message(self, res_model, res_id, message, message_id, redirect=None, attachment_ids='', attachment_tokens='', **post):
|
||||
# keep this mecanism intern to slide currently (saas 12.5) as it is
|
||||
# considered experimental
|
||||
if res_model != 'slide.channel':
|
||||
raise Forbidden()
|
||||
res_id = int(res_id)
|
||||
message_id = int(message_id)
|
||||
pid = int(post['pid']) if post.get('pid') else False
|
||||
|
||||
attachment_ids = [int(attachment_id) for attachment_id in attachment_ids.split(',') if attachment_id]
|
||||
attachment_tokens = [attachment_token for attachment_token in attachment_tokens.split(',') if attachment_token]
|
||||
self._portal_post_check_attachments(attachment_ids, attachment_tokens)
|
||||
|
||||
pid = int(post['pid']) if post.get('pid') else False
|
||||
if not _check_special_access(res_model, res_id, token=post.get('token'), _hash=post.get('hash'), pid=pid):
|
||||
raise Forbidden()
|
||||
|
||||
# update mail.message
|
||||
# fetch and update mail.message
|
||||
message_id = int(message_id)
|
||||
message_body = plaintext2html(message)
|
||||
domain = [
|
||||
('model', '=', res_model),
|
||||
('res_id', '=', res_id),
|
||||
@@ -40,7 +58,8 @@ class SlidesMail(http.Controller):
|
||||
if not message:
|
||||
raise NotFound()
|
||||
message.write({
|
||||
'body': message_body
|
||||
'body': message_body,
|
||||
'attachment_ids': [(4, aid) for aid in attachment_ids],
|
||||
})
|
||||
|
||||
# update rating
|
||||
@@ -51,4 +70,6 @@ class SlidesMail(http.Controller):
|
||||
'rating': float(post['rating_value'])
|
||||
})
|
||||
|
||||
return werkzeug.utils.redirect(request.httprequest.referrer, 302)
|
||||
# redirect to specified or referrer or simply channel page as fallback
|
||||
redirect_url = redirect or (request.httprequest.referrer and request.httprequest.referrer + '#review') or '/slides/%s' % res_id
|
||||
return werkzeug.utils.redirect(redirect_url, 302)
|
||||
|
||||
@@ -2,19 +2,18 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import base64
|
||||
import itertools
|
||||
import json
|
||||
import logging
|
||||
import werkzeug
|
||||
import math
|
||||
|
||||
from odoo import http, modules, tools, _
|
||||
from odoo.exceptions import AccessError, UserError
|
||||
from odoo.http import request
|
||||
from odoo.osv import expression
|
||||
|
||||
from odoo import http, tools, _
|
||||
from odoo.addons.http_routing.models.ir_http import slug
|
||||
from odoo.addons.website_profile.controllers.main import WebsiteProfile
|
||||
from odoo.addons.website.models.ir_http import sitemap_qs2dom
|
||||
from odoo.exceptions import AccessError, UserError
|
||||
from odoo.http import request
|
||||
from odoo.osv import expression
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -419,18 +418,28 @@ class WebsiteSlides(WebsiteProfile):
|
||||
'enable_slide_upload': 'enable_slide_upload' in kw,
|
||||
}
|
||||
if not request.env.user._is_public():
|
||||
last_message_values = request.env['mail.message'].search([
|
||||
last_message = request.env['mail.message'].search([
|
||||
('model', '=', channel._name),
|
||||
('res_id', '=', channel.id),
|
||||
('author_id', '=', request.env.user.partner_id.id),
|
||||
('message_type', '=', 'comment'),
|
||||
('website_published', '=', True)
|
||||
], order='write_date DESC', limit=1).read(['body', 'rating_value'])
|
||||
last_message_data = last_message_values[0] if last_message_values else {}
|
||||
], order='write_date DESC', limit=1)
|
||||
if last_message:
|
||||
last_message_values = last_message.read(['body', 'rating_value', 'attachment_ids'])[0]
|
||||
last_message_attachment_ids = last_message_values.pop('attachment_ids', [])
|
||||
if last_message_attachment_ids:
|
||||
last_message_attachment_ids = json.dumps(request.env['ir.attachment'].browse(last_message_attachment_ids).read(
|
||||
['id', 'name', 'mimetype', 'file_size', 'access_token']
|
||||
))
|
||||
else:
|
||||
last_message_values = {}
|
||||
last_message_attachment_ids = []
|
||||
values.update({
|
||||
'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'),
|
||||
'last_message_id': last_message_values.get('id'),
|
||||
'last_message': tools.html2plaintext(last_message_values.get('body', '')),
|
||||
'last_rating_value': last_message_values.get('rating_value'),
|
||||
'last_message_attachment_ids': last_message_attachment_ids,
|
||||
})
|
||||
if channel.can_review:
|
||||
values.update({
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
from odoo.addons.portal.controllers.mail import PortalChatter
|
||||
|
||||
|
||||
class SlidesPortalChatter(PortalChatter):
|
||||
|
||||
@http.route(['/mail/chatter_post'], type='http', 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':
|
||||
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')):
|
||||
# apply karma gain rule only once
|
||||
request.env.user.add_karma(slide_channel.karma_gen_channel_rank)
|
||||
return result
|
||||
@@ -147,9 +147,10 @@
|
||||
<t t-set="token" t-value="channel.access_token"/>
|
||||
<t t-set="hash" t-value="message_post_hash"/>
|
||||
<t t-set="pid" t-value="message_post_pid"/>
|
||||
<t t-set="default_message_id" t-value="last_message_id"/>
|
||||
<t t-set="default_message" t-value="last_message"/>
|
||||
<t t-set="default_message_id" t-value="last_message_id"/>
|
||||
<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="disable_composer" t-value="not channel.can_review"/>
|
||||
</t>
|
||||
|
||||
Reference in New Issue
Block a user