details: https://code.tryton.org/tryton/commit/16057e0c1c93
branch: default
user: Cédric Krier <[email protected]>
date: Thu Jun 18 23:24:06 2026 +0200
description:
Prevent to modify attachments on model without write access
When the user has no write access to the module, the "Add" button of the
attachment menu is not displayed and the manage button opens a screen in
readonly mode.
Closes #14863
diffstat:
sao/src/tab.js | 41 ++++++++++++++++++---------------
sao/src/window.js | 2 +
tryton/tryton/gui/window/attachment.py | 7 ++++-
tryton/tryton/gui/window/form.py | 8 ++++--
4 files changed, 34 insertions(+), 24 deletions(-)
diffs (126 lines):
diff -r 9a7e061acd01 -r 16057e0c1c93 sao/src/tab.js
--- a/sao/src/tab.js Thu Jan 08 01:06:16 2026 +0100
+++ b/sao/src/tab.js Thu Jun 18 23:24:06 2026 +0200
@@ -1189,6 +1189,7 @@
var record = this.screen.current_record;
var menu = dropdown.find('.dropdown-menu');
menu.empty();
+ let access = Sao.common.MODELACCESS.get(this.screen.model_name);
return Sao.Window.Attachment.get_attachments(record)
.then(function(attachments) {
attachments.forEach(function(value) {
@@ -1215,25 +1216,27 @@
menu.append(jQuery('<li/>', {
'class': 'divider',
}));
- menu.append(jQuery('<li/>', {
- 'role': 'presentation',
- 'class': 'input-file',
- }).append(jQuery('<input/>', {
- 'type': 'file',
- 'role': 'menuitem',
- 'multiple': true,
- 'tabindex': -1,
- }).change(function() {
- var attachment = window_();
- Sao.common.get_input_data(
- jQuery(this), function(data, filename) {
- attachment.add_data(data, filename);
- });
- })).append(jQuery('<a/>', {
- 'role': 'menuitem',
- 'href': '#',
- 'tabindex': -1,
- }).text(Sao.i18n.gettext('Add...'))));
+ if (access.write) {
+ menu.append(jQuery('<li/>', {
+ 'role': 'presentation',
+ 'class': 'input-file',
+ }).append(jQuery('<input/>', {
+ 'type': 'file',
+ 'role': 'menuitem',
+ 'multiple': true,
+ 'tabindex': -1,
+ }).change(function() {
+ var attachment = window_();
+ Sao.common.get_input_data(
+ jQuery(this), function(data, filename) {
+ attachment.add_data(data, filename);
+ });
+ })).append(jQuery('<a/>', {
+ 'role': 'menuitem',
+ 'href': '#',
+ 'tabindex': -1,
+ }).text(Sao.i18n.gettext('Add...'))));
+ }
menu.append(jQuery('<li/>', {
'role': 'presentation',
}).append(jQuery('<a/>', {
diff -r 9a7e061acd01 -r 16057e0c1c93 sao/src/window.js
--- a/sao/src/window.js Thu Jan 08 01:06:16 2026 +0100
+++ b/sao/src/window.js Thu Jun 18 23:24:06 2026 +0200
@@ -668,10 +668,12 @@
this.resource = record.model.name + ',' + record.id;
this.attachment_callback = callback;
var context = jQuery.extend({}, record.get_context());
+ let access = Sao.common.MODELACCESS.get(record.model.name);
var screen = new Sao.Screen('ir.attachment', {
domain: [['resource', '=', this.resource]],
mode: ['tree', 'form'],
context: context,
+ readonly: !access.write,
});
var title = record.rec_name().then(function(rec_name) {
return Sao.i18n.gettext('Attachments (%1)', rec_name);
diff -r 9a7e061acd01 -r 16057e0c1c93 tryton/tryton/gui/window/attachment.py
--- a/tryton/tryton/gui/window/attachment.py Thu Jan 08 01:06:16 2026 +0100
+++ b/tryton/tryton/gui/window/attachment.py Thu Jun 18 23:24:06 2026 +0200
@@ -8,7 +8,7 @@
from urllib.parse import unquote, urlparse
from tryton.common import (
- RPCException, RPCExecute, file_open, file_write, url_open)
+ MODELACCESS, RPCException, RPCExecute, file_open, file_write, url_open)
from tryton.gui.window.view_form.screen import Screen
from tryton.gui.window.win_form import WinForm
@@ -21,10 +21,13 @@
def __init__(self, record, callback=None):
self.resource = '%s,%s' % (record.model_name, record.id)
self.attachment_callback = callback
+ access = MODELACCESS[record.model_name]
title = _('Attachments (%s)') % (record.rec_name())
screen = Screen('ir.attachment', domain=[
('resource', '=', self.resource),
- ], mode=['tree', 'form'])
+ ],
+ mode=['tree', 'form'],
+ readonly=not access['write'])
super().__init__(screen, self.callback,
view_type='tree', title=title)
screen.search_filter()
diff -r 9a7e061acd01 -r 16057e0c1c93 tryton/tryton/gui/window/form.py
--- a/tryton/tryton/gui/window/form.py Thu Jan 08 01:06:16 2026 +0100
+++ b/tryton/tryton/gui/window/form.py Thu Jun 18 23:24:06 2026 +0200
@@ -166,6 +166,7 @@
def activate(widget, callback):
callback()
+ access = common.MODELACCESS[self.model]
button = self.buttons['attach']
if widget != button:
if button.props.sensitive:
@@ -178,9 +179,10 @@
item.connect('activate', activate, callback)
menu.add(item)
menu.add(Gtk.SeparatorMenuItem())
- add_item = Gtk.MenuItem(label=_("Add..."))
- add_item.connect('activate', add_file)
- menu.add(add_item)
+ if access['write']:
+ add_item = Gtk.MenuItem(label=_("Add..."))
+ add_item.connect('activate', add_file)
+ menu.add(add_item)
preview_item = Gtk.CheckMenuItem(label=_("Preview"))
preview_item.set_active(bool(
self.attachment_preview.get_children()))