Relational data in server formatter are: - for one relation: None/false or object - for many relations: list of objects or list of commands The commands were: - 'insert': to add a new item in a relational field - 'unlink': to remove an item from a relational field - 'insert-and-unlink': to remove an item from a relational field - 'clear': to remove all items from relational field There was a slight nuance between 'unlink' and 'insert-and-unlink' at some point, but it becomes irrelevant with current code of model. The name of the commands were hard to grasp what they actually mean for the many relations. This commit improves it by renaming 'insert' by 'ADD' and the 2 'unlink' commands by 'DELETE'. This makes it more apparent that the data in 'ADD' refers to data of record to add in the relation, while 'DELETE' refers to data of record to delete from the relation. The 'clear' has been replaced by `False` value instead of a command. Part-of: odoo/odoo#136308
95 lines
4.3 KiB
Python
95 lines
4.3 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import contextlib
|
|
|
|
from odoo import _, api, models, SUPERUSER_ID
|
|
from odoo.exceptions import AccessError, MissingError, UserError
|
|
from odoo.http import request
|
|
from odoo.tools import consteq
|
|
|
|
|
|
class IrAttachment(models.Model):
|
|
_inherit = 'ir.attachment'
|
|
|
|
def _check_attachments_access(self, attachment_tokens):
|
|
"""This method relies on access rules/rights and therefore it should not be called from a sudo env."""
|
|
self = self.sudo(False)
|
|
attachment_tokens = attachment_tokens or ([None] * len(self))
|
|
if len(attachment_tokens) != len(self):
|
|
raise UserError(_("An access token must be provided for each attachment."))
|
|
for attachment, access_token in zip(self, attachment_tokens):
|
|
try:
|
|
attachment_sudo = attachment.with_user(SUPERUSER_ID).exists()
|
|
if not attachment_sudo:
|
|
raise MissingError(_("The attachment %s does not exist.", attachment.id))
|
|
try:
|
|
attachment.check('write')
|
|
except AccessError:
|
|
if not access_token or not attachment_sudo.access_token or not consteq(attachment_sudo.access_token, access_token):
|
|
message_sudo = self.env['mail.message'].sudo().search([('attachment_ids', 'in', attachment_sudo.ids)], limit=1)
|
|
if not message_sudo or not message_sudo.is_current_user_or_guest_author:
|
|
raise
|
|
except (AccessError, MissingError):
|
|
raise UserError(_("The attachment %s does not exist or you do not have the rights to access it.", attachment.id))
|
|
|
|
def _post_add_create(self, **kwargs):
|
|
""" Overrides behaviour when the attachment is created through the controller
|
|
"""
|
|
super(IrAttachment, self)._post_add_create(**kwargs)
|
|
for record in self:
|
|
record.register_as_main_attachment(force=False)
|
|
|
|
def register_as_main_attachment(self, force=True):
|
|
""" Registers this attachment as the main one of the model it is
|
|
attached to.
|
|
|
|
:param bool force: if set, the method always updates the existing main attachment
|
|
otherwise it only sets the main attachment if there is none.
|
|
"""
|
|
self.ensure_one()
|
|
if not self.res_model or not self.res_id:
|
|
return
|
|
related_record = self.env[self.res_model].browse(self.res_id)
|
|
if not related_record or \
|
|
not related_record.check_access_rights('write', raise_exception=False) or \
|
|
not hasattr(related_record, 'message_main_attachment_id'):
|
|
return
|
|
|
|
if force or not related_record.message_main_attachment_id:
|
|
with contextlib.suppress(AccessError):
|
|
related_record.message_main_attachment_id = self
|
|
|
|
def _delete_and_notify(self, message=None):
|
|
if message:
|
|
message.write({}) # to make sure write_date on the message is updated
|
|
self.env['bus.bus']._sendmany((attachment._bus_notification_target(), 'ir.attachment/delete', {
|
|
'id': attachment.id, 'message': {'id': message.id, 'write_date': message.write_date} if message else None
|
|
}) for attachment in self)
|
|
self.unlink()
|
|
|
|
def _bus_notification_target(self):
|
|
self.ensure_one()
|
|
return self.env.user.partner_id
|
|
|
|
def _attachment_format(self):
|
|
safari = request and request.httprequest.user_agent and request.httprequest.user_agent.browser == 'safari'
|
|
return [{
|
|
'checksum': attachment.checksum,
|
|
'create_date': attachment.create_date,
|
|
'id': attachment.id,
|
|
'filename': attachment.name,
|
|
'name': attachment.name,
|
|
"size": attachment.file_size,
|
|
'res_name': attachment.res_name,
|
|
'mimetype': 'application/octet-stream' if safari and attachment.mimetype and 'video' in attachment.mimetype else attachment.mimetype,
|
|
'originThread': [('ADD', {
|
|
'id': attachment.res_id,
|
|
'model': attachment.res_model,
|
|
})],
|
|
} for attachment in self]
|
|
|
|
@api.model
|
|
def _get_upload_env(self, thread_model, thread_id):
|
|
return request.env
|