Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FIX] ODOO-SA-2020-12-02 #26

Open
wants to merge 2 commits into
base: osi-acc/12.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion addons/mail/i18n/mail.pot
Original file line number Diff line number Diff line change
Expand Up @@ -2547,6 +2547,12 @@ msgstr ""
msgid "Is Read"
msgstr ""

#. module: mail
#: code:addons/mail/models/mail_notification.py:0
#, python-format
msgid "Can not update the message or recipient of a notification."
msgstr ""

#. module: mail
#: model:ir.model.fields,field_description:mail.field_mail_channel__is_subscribed
msgid "Is Subscribed"
Expand Down Expand Up @@ -4829,6 +4835,12 @@ msgstr ""
msgid "The owner of records created upon receiving emails on this alias. If this field is not set the system will attempt to find the right owner based on the sender (From) address, or will use the Administrator account if no system user is found for that address."
msgstr ""

#. module: mail
#: code:addons/mail/models/mail_channel.py:0
#, python-format
msgid "The partner can not join this channel"
msgstr ""

#. module: mail
#: code:addons/mail/models/mail_activity.py:243
#: code:addons/mail/models/mail_message.py:704
Expand Down Expand Up @@ -5427,6 +5439,24 @@ msgstr ""
msgid "You can mark any message as 'starred', and it shows up in this mailbox."
msgstr ""

#. module: mail
#: cod:addons/mail/models/mail_channel.py:0
#, python-format
msgid "You can not remove this partner from this channel"
msgstr ""

#. module: mail
#: code:addons/mail/models/mail_channel.py:0
#, python-format
msgid "You can not write on the record of other users"
msgstr ""

#. module: mail
#: code:addons/mail/models/mail_channel.py:0
#, python-format
msgid "You can not write on this field"
msgstr ""

#. module: mail
#: code:addons/mail/models/res_users.py:87
#, python-format
Expand Down Expand Up @@ -5455,7 +5485,7 @@ msgstr ""
#. openerp-web
#: code:addons/mail/static/src/js/services/mail_notification_manager.js:198
#, python-format
msgid "You have been invited to: "
msgid "You have been invited to: %s"
msgstr ""

#. module: mail
Expand Down
46 changes: 43 additions & 3 deletions addons/mail/models/mail_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from uuid import uuid4

from odoo import _, api, fields, models, modules, tools
from odoo.exceptions import UserError, ValidationError
from odoo.exceptions import AccessError, UserError, ValidationError
from odoo.osv import expression
from odoo.tools import ormcache, pycompat
from odoo.tools.safe_eval import safe_eval
Expand All @@ -34,6 +34,27 @@ class ChannelPartner(models.Model):
is_minimized = fields.Boolean("Conversation is minimized")
is_pinned = fields.Boolean("Is pinned on the interface", default=True)

@api.model
def create(self, vals):
if 'channel_id' in vals and not self.env.user._is_admin():
channel_id = self.env['mail.channel'].browse(vals['channel_id'])
if not channel_id._can_invite(vals.get('partner_id')):
raise AccessError(_('The partner can not join this channel'))
return super(ChannelPartner, self).create(vals)

def write(self, vals):
if not self.env.user._is_admin():
if {'channel_id', 'partner_id', 'partner_email'} & set(vals):
raise AccessError(_('You can not write on this field'))
elif self.mapped('partner_id') != self.env.user.partner_id:
raise AccessError(_('You can not write on the record of other users'))
return super(ChannelPartner, self).write(vals)

def unlink(self):
if not self.env.user._is_admin() and not all(record.channel_id.is_member for record in self):
raise AccessError(_('You can not remove this partner from this channel'))
return super(ChannelPartner, self).unlink()


class Moderation(models.Model):
_name = 'mail.moderation'
Expand Down Expand Up @@ -213,6 +234,10 @@ def create(self, vals):
vals['image'] = defaults['image']

tools.image_resize_images(vals)

# always add the current user to the channel
vals['channel_partner_ids'] = vals.get('channel_partner_ids', []) + [(4, self.env.user.partner_id.id)]

# Create channel and alias
channel = super(Channel, self.with_context(
alias_model_name=self._name, alias_parent_model_name=self._name, mail_create_nolog=True, mail_create_nosubscribe=True)
Expand Down Expand Up @@ -384,7 +409,7 @@ def message_post(self, message_type='notification', **kwargs):
if moderation_status == 'rejected':
return self.env['mail.message']

self.filtered(lambda channel: channel.channel_type == 'chat').mapped('channel_last_seen_partner_ids').write({'is_pinned': True})
self.filtered(lambda channel: channel.channel_type == 'chat').mapped('channel_last_seen_partner_ids').sudo().write({'is_pinned': True})

message = super(Channel, self.with_context(mail_create_nosubscribe=True)).message_post(message_type=message_type, moderation_status=moderation_status, **kwargs)

Expand Down Expand Up @@ -732,6 +757,22 @@ def channel_invite(self, partner_ids):
# broadcast the channel header to the added partner
self._broadcast(partner_ids)

def _can_invite(self, partner_id):
"""Return True if the current user can invite the partner to the channel."""
self.ensure_one()
sudo_self = self.sudo()
if sudo_self.public == 'public':
return True
if sudo_self.public == 'private':
return self.is_member

# get the user related to the invited partner
partner = self.env['res.partner'].browse(partner_id).exists()
invited_user_id = partner.user_ids[:1]
if invited_user_id:
return (self.env.user | invited_user_id) <= sudo_self.group_public_id.users
return False

@api.multi
def notify_typing(self, is_typing, is_website_user=False):
""" Broadcast the typing notification to channel members
Expand Down Expand Up @@ -824,7 +865,6 @@ def channel_create(self, name, privacy='public'):
'name': name,
'public': privacy,
'email_send': False,
'channel_partner_ids': [(4, self.env.user.partner_id.id)]
})
notification = _('<div class="o_mail_notification">created <a href="#" class="o_channel_redirect" data-oe-id="%s">#%s</a></div>') % (new_channel.id, new_channel.name,)
new_channel.message_post(body=notification, message_type="notification", subtype="mail.mt_comment")
Expand Down
18 changes: 17 additions & 1 deletion addons/mail/models/mail_followers.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def _invalidate_documents(self):

@api.model_create_multi
def create(self, vals_list):
res = super(Followers, self).create(vals_list)
res = super(Followers, self).create(vals_list)._check_rights()
res._invalidate_documents()
return res

Expand All @@ -57,6 +57,7 @@ def write(self, vals):
if 'res_model' in vals or 'res_id' in vals:
self._invalidate_documents()
res = super(Followers, self).write(vals)
self._check_rights()
if any(x in vals for x in ['res_model', 'res_id', 'partner_id']):
self._invalidate_documents()
return res
Expand All @@ -66,6 +67,21 @@ def unlink(self):
self._invalidate_documents()
return super(Followers, self).unlink()

def _check_rights(self):
user_partner = self.env.user.partner_id
for record in self:
obj = self.env[record.res_model].browse(record.res_id)
if record.channel_id or record.partner_id != user_partner:
obj.check_access_rights('write')
obj.check_access_rule('write')
subject = record.channel_id or record.partner_id
subject.check_access_rights('read')
subject.check_access_rule('read')
else:
obj.check_access_rights('read')
obj.check_access_rule('read')
return self

_sql_constraints = [
('mail_followers_res_partner_res_model_id_uniq', 'unique(res_model,res_id,partner_id)', 'Error, a partner cannot follow twice the same object.'),
('mail_followers_res_channel_res_model_id_uniq', 'unique(res_model,res_id,channel_id)', 'Error, a channel cannot follow twice the same object.'),
Expand Down
16 changes: 14 additions & 2 deletions addons/mail/models/mail_notification.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-

from odoo import api, fields, models
from odoo.exceptions import AccessError
from odoo.tools.translate import _


Expand Down Expand Up @@ -43,12 +44,23 @@ def init(self):
if not self._cr.fetchone():
self._cr.execute('CREATE INDEX mail_notification_res_partner_id_is_read_email_status_mail_message_id ON mail_message_res_partner_needaction_rel (res_partner_id, is_read, email_status, mail_message_id)')

@api.model
def create(self, vals):
msg = self.env['mail.message'].browse(vals['mail_message_id'])
msg.check_access_rights('read')
msg.check_access_rule('read')
return super(Notification, self).create(vals)

@api.multi
def write(self, vals):
if ('mail_message_id' in vals or 'res_partner_id' in vals) and not self.env.user._is_admin():
raise AccessError(_("Can not update the message or recipient of a notification."))
return super(Notification, self).write(vals)

@api.multi
def format_failure_reason(self):
self.ensure_one()
if self.failure_type != 'UNKNOWN':
return dict(type(self).failure_type.selection).get(self.failure_type, _('No Error'))
else:
return _("Unknown error") + ": %s" % (self.failure_reason or '')


2 changes: 1 addition & 1 deletion addons/mail/models/mail_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def format_amount(env, amount, currency):
'str': str,
'quote': urls.url_quote,
'urlencode': urls.url_encode,
'datetime': datetime,
'datetime': tools.wrap_module(datetime, []),
'len': len,
'abs': abs,
'min': min,
Expand Down
2 changes: 1 addition & 1 deletion addons/mail/models/mail_thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -1316,7 +1316,7 @@ def message_route_process(self, message, message_dict, routes):

# disabled subscriptions during message_new/update to avoid having the system user running the
# email gateway become a follower of all inbound messages
MessageModel = Model.sudo(user_id).with_context(mail_create_nosubscribe=True, mail_create_nolog=True)
MessageModel = Model.sudo(self.env.uid == 1 and user_id or None).with_context(mail_create_nosubscribe=True, mail_create_nolog=True)
if thread_id and hasattr(MessageModel, 'message_update'):
thread = MessageModel.browse(thread_id)
thread.message_update(message_dict)
Expand Down
10 changes: 5 additions & 5 deletions addons/mail/static/src/js/discuss.js
Original file line number Diff line number Diff line change
Expand Up @@ -559,12 +559,12 @@ var Discuss = AbstractAction.extend(ControlPanelMixin, {
if (type === 'public') {
$input.autocomplete({
source: function (request, response) {
self._lastSearchVal = _.escape(request.term);
self._lastSearchVal = request.term;
self._searchChannel(self._lastSearchVal).done(function (result){
result.push({
label: _.str.sprintf(
'<strong>' + _t("Create %s") + '</strong>',
'<em>"#' + self._lastSearchVal + '"</em>'
'<em>"#' + _.escape(self._lastSearchVal) + '"</em>'
),
value: '_create',
});
Expand All @@ -587,15 +587,15 @@ var Discuss = AbstractAction.extend(ControlPanelMixin, {
});
} else if (type === 'private') {
$input.on('keyup', this, function (ev) {
var name = _.escape($(ev.target).val());
var name = $(ev.target).val();
if (ev.which === $.ui.keyCode.ENTER && name) {
self.call('mail_service', 'createChannel', name, 'private');
}
});
} else if (type === 'dm_chat') {
$input.autocomplete({
source: function (request, response) {
self._lastSearchVal = _.escape(request.term);
self._lastSearchVal = request.term;
self.call('mail_service', 'searchPartner', self._lastSearchVal, 10).done(response);
},
select: function (ev, ui) {
Expand Down Expand Up @@ -1186,7 +1186,7 @@ var Discuss = AbstractAction.extend(ControlPanelMixin, {
* @private
*/
_onInviteButtonClicked: function () {
var title = _.str.sprintf(_t("Invite people to #%s"), this._thread.getName());
var title = _.str.sprintf(_t("Invite people to #%s"), _.escape(this._thread.getName()));
new PartnerInviteDialog(this, title, this._thread.getID()).open();
},
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ var AbstractThread = Class.extend(Mixins.EventDispatcherMixin, {
* @param {Object} params
* @param {Object} params.data
* @param {integer|string} params.data.id the ID of this thread
* @param {string} params.data.name the name of this thread
* @param {string} params.data.name the server name of this thread
* @param {string} [params.data.status=''] the status of this thread
* @param {Object} params.parent Object with the event-dispatcher mixin
* (@see {web.mixins.EventDispatcherMixin})
Expand Down
2 changes: 1 addition & 1 deletion addons/mail/static/src/js/models/threads/dm_chat.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ var DMChat = TwoUserChannel.extend({
* @param {Object[]} params.data.direct_partner
* @param {integer} params.data.direct_partner[0].id
* @param {string} params.data.direct_partner[0].im_status
* @param {string} params.data.direct_partner[0].name
* @param {string} params.data.direct_partner[0].name server name of partner
*/
init: function (params) {
this._super.apply(this, arguments);
Expand Down
2 changes: 1 addition & 1 deletion addons/mail/static/src/js/models/threads/livechat.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ var Livechat = TwoUserChannel.extend({
* @override
* @param {Object} params
* @param {Object} params.data
* @param {string} params.data.anonymous_name name of the website user
* @param {string} params.data.anonymous_name server name of the website user
*/
init: function (params) {
this._super.apply(this, arguments);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,11 @@ MailManager.include({
*
* @private
* @param {Object} channelData
* @param {string} channelData.channel_type
* @param {integer} channelData.id
* @param {string} [channelData.info]
* @param {boolean} channelData.is_minimized
* @param {string} channelData.name server name of channel
* @param {string} channelData.state
*/
_handlePartnerChannelNotification: function (channelData) {
Expand All @@ -196,7 +198,7 @@ MailManager.include({
) {
this.do_notify(
_t("Invitation"),
_t("You have been invited to: ") + channelData.name);
_.str.sprintf(_t("You have been invited to: %s"), _.escape(channelData.name)));
}
}
var channel = this.getChannel(channelData.id);
Expand Down Expand Up @@ -438,12 +440,12 @@ MailManager.include({
if (_.contains(['public', 'private'], channel.getType())) {
message = _.str.sprintf(
_t("You unsubscribed from <b>%s</b>."),
channel.getName()
_.escape(channel.getName())
);
} else {
message = _.str.sprintf(
_t("You unpinned your conversation with <b>%s</b>."),
channel.getName()
_.escape(channel.getName())
);
}
this._removeChannel(channel);
Expand Down
2 changes: 1 addition & 1 deletion addons/mail/static/src/xml/discuss.xml
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@
</t>
</span>
<span t-if="displayHash" class="o_mail_hash">#</span>
<t t-esc="_.unescape(thread.getName())"/>
<t t-esc="thread.getName()"/>
<i t-if="thread.isMassMailing()" class="fa fa-envelope-o" title="Sends messages by email" role="img" aria-label="Send by mail"/>
</span>
<span t-attf-class="fa fa-cog o_mail_channel_settings"
Expand Down
2 changes: 1 addition & 1 deletion addons/mail/static/tests/discuss_tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ QUnit.test('unescape channel name in the sidebar', function (assert) {
channel_channel: [{
id: 1,
channel_type: "channel",
name: "R&amp;D",
name: "R&D",
}],
},
};
Expand Down
2 changes: 1 addition & 1 deletion addons/portal/controllers/portal.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def account(self, redirect=None, **post):
'error_message': [],
})

if post:
if post and request.httprequest.method == 'POST':
error, error_message = self.details_form_validate(post)
values.update({'error': error, 'error_message': error_message})
values.update(post)
Expand Down
Loading