From cce80f4c73bfa6c5f28b84cf54ae2d23dc66110c Mon Sep 17 00:00:00 2001 From: Rodolpho Lima Date: Wed, 30 Aug 2023 15:59:42 +0000 Subject: [PATCH] [FIX] web_editor: duplicate attachments in html field Before this commit, when an image was cropped in a record on which multiple users were collaborating, multiple attachments would be created for the same image (one per user). Multiple attachments for the same image would also be created if the user uploaded the same image multiple times in the same record. This commit makes sure that, when about to create an attachment via the 'add_data', 'add_url' and 'modify_image' routes, if an attachment already exists for the same field values, the existing attachment is returned instead of creating a new one. task-3524601 closes odoo/odoo#148556 X-original-commit: 0e2508388fc03b2ea7a4033e571d289efa4ac479 Signed-off-by: Nicolas Bayet (nby) --- addons/web_editor/controllers/main.py | 29 +++++++++++++++++-- .../components/media_dialog/file_selector.js | 2 +- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/addons/web_editor/controllers/main.py b/addons/web_editor/controllers/main.py index df43054847d..0b0806b3f39 100644 --- a/addons/web_editor/controllers/main.py +++ b/addons/web_editor/controllers/main.py @@ -34,6 +34,26 @@ DEFAULT_LIBRARY_ENDPOINT = 'https://media-api.odoo.com' DEFAULT_OLG_ENDPOINT = 'https://olg.api.odoo.com' +def get_existing_attachment(IrAttachment, vals): + """ + Check if an attachment already exists for the same vals. Return it if + so, None otherwise. + """ + fields = dict(vals) + # Falsy res_id defaults to 0 on attachment creation. + fields['res_id'] = fields.get('res_id') or 0 + raw, datas = fields.pop('raw', None), fields.pop('datas', None) + domain = [(field, '=', value) for field, value in fields.items()] + if fields.get('type') == 'url': + if 'url' not in fields: + return None + domain.append(('checksum', '=', False)) + else: + if not (raw or datas): + return None + domain.append(('checksum', '=', IrAttachment._compute_checksum(raw or b64decode(datas)))) + return IrAttachment.search(domain, limit=1) or None + class Web_Editor(http.Controller): #------------------------------------------------------ # convert font into picture @@ -355,7 +375,8 @@ class Web_Editor(http.Controller): if not attachment_data['public']: attachment.sudo().generate_access_token() else: - attachment = IrAttachment.create(attachment_data) + attachment = get_existing_attachment(IrAttachment, attachment_data) \ + or IrAttachment.create(attachment_data) return attachment @@ -530,7 +551,11 @@ class Web_Editor(http.Controller): fields['res_id'] = res_id if fields['mimetype'] == 'image/webp': fields['name'] = re.sub(r'\.(jpe?g|png)$', '.webp', fields['name'], flags=re.I) - attachment = attachment.copy(fields) + existing_attachment = get_existing_attachment(request.env['ir.attachment'], fields) + if existing_attachment and not existing_attachment.url: + attachment = existing_attachment + else: + attachment = attachment.copy(fields) if alt_data: for size, per_type in alt_data.items(): reference_id = attachment.id diff --git a/addons/web_editor/static/src/components/media_dialog/file_selector.js b/addons/web_editor/static/src/components/media_dialog/file_selector.js index 169173622ab..01465547da5 100644 --- a/addons/web_editor/static/src/components/media_dialog/file_selector.js +++ b/addons/web_editor/static/src/components/media_dialog/file_selector.js @@ -315,7 +315,7 @@ export class FileSelector extends Component { } async onUploaded(attachment) { - this.state.attachments = [attachment, ...this.state.attachments]; + this.state.attachments = [attachment, ...this.state.attachments.filter(attach => attach.id !== attachment.id)]; this.selectAttachment(attachment); if (!this.props.multiSelect) { await this.props.save();