diff --git a/sao/CHANGELOG b/sao/CHANGELOG index 52242a4e5f..082b682668 100644 --- a/sao/CHANGELOG +++ b/sao/CHANGELOG @@ -1,3 +1,4 @@ +* Support filters attribute for binary and image widgets * Use search window to delete/remove records from xxx2Many widgets Version 8.0.0 - 2026-04-20 diff --git a/sao/src/view/form.js b/sao/src/view/form.js index 9d5eba5bbd..da96ae44a0 100644 --- a/sao/src/view/form.js +++ b/sao/src/view/form.js @@ -4521,6 +4521,10 @@ function eval_pyson(value){ this, view, attributes); this.filename = attributes.filename || null; }, + default_filters: '', + get filters() { + return this.attributes.filters || this.default_filters; + }, toolbar: function(class_) { var group = jQuery('
', { 'class': class_, @@ -4539,6 +4543,7 @@ function eval_pyson(value){ this.input_select = jQuery('', { 'type': 'file', }).change(this.select.bind(this)); + this.input_select.attr('accept', this.filters); this.but_select = jQuery('', { 'class': 'btn btn-default input-file', 'type': 'button', @@ -4856,9 +4861,6 @@ function eval_pyson(value){ break; } var group = this.toolbar('btn-group'); - this.input_select.attr( - 'accept', - 'image/png,image/jpeg,image/gif,.png,.jpg,.gif,.tif,.xpm'); if (!attributes.readonly) { jQuery('', { 'class': 'text-center caption', @@ -4866,6 +4868,8 @@ function eval_pyson(value){ } this._readonly = false; }, + default_filters: ( + 'image/png,image/jpeg,image/gif,.png,.jpg,.gif,.tif,.xpm'), set_readonly: function(readonly) { Sao.View.Form.Image._super.set_readonly.call(this, readonly); var record = this.record; diff --git a/tryton/CHANGELOG b/tryton/CHANGELOG index 38824dac69..6ca07b4c93 100644 --- a/tryton/CHANGELOG +++ b/tryton/CHANGELOG @@ -1,3 +1,4 @@ +* Support filters attribute for binary and image widgets * Use search window to delete/remove records from xxx2Many widgets Version 8.0.0 - 2026-04-20 diff --git a/tryton/tryton/gui/window/view_form/view/form_gtk/binary.py b/tryton/tryton/gui/window/view_form/view/form_gtk/binary.py index cd2ed83e4e..7c228934b2 100644 --- a/tryton/tryton/gui/window/view_form/view/form_gtk/binary.py +++ b/tryton/tryton/gui/window/view_form/view/form_gtk/binary.py @@ -64,12 +64,22 @@ class BinaryMixin(Widget): def filename_field(self): return self.record.group.fields.get(self.filename) + default_filters = None + @property def filters(self): - filter_all = Gtk.FileFilter() - filter_all.set_name(_('All files')) - filter_all.add_pattern("*") - return [filter_all] + file_filter = Gtk.FileFilter() + if filters := self.attrs.get('filters', self.default_filters): + file_filter.set_name(_("All Supported Types")) + for type in filters.split(','): + if type.startswith('.'): + file_filter.add_pattern(type) + else: + file_filter.add_mime_type(type) + else: + file_filter.set_name(_("All files")) + file_filter.add_pattern("*") + return [file_filter] @property def preview(self): diff --git a/tryton/tryton/gui/window/view_form/view/form_gtk/image.py b/tryton/tryton/gui/window/view_form/view/form_gtk/image.py index f456aa8077..9197a48246 100644 --- a/tryton/tryton/gui/window/view_form/view/form_gtk/image.py +++ b/tryton/tryton/gui/window/view_form/view/form_gtk/image.py @@ -47,17 +47,7 @@ class Image(BinaryMixin, Widget): self._readonly = False - @property - def filters(self): - filters = super().filters - filter_image = Gtk.FileFilter() - filter_image.set_name(_('Images')) - for mime in ("image/png", "image/jpeg", "image/gif"): - filter_image.add_mime_type(mime) - for pat in ("*.png", "*.jpg", "*.gif", "*.tif", "*.xpm"): - filter_image.add_pattern(pat) - filters.insert(0, filter_image) - return filters + default_filters = 'image/png,image/jpeg,image/gif,.png,.jpg,.gif,.tif,.xpm' def _readonly_set(self, value): self._readonly = value diff --git a/tryton/tryton/gui/window/view_form/view/list_gtk/widget.py b/tryton/tryton/gui/window/view_form/view/list_gtk/widget.py index 973258562a..d132eade03 100644 --- a/tryton/tryton/gui/window/view_form/view/list_gtk/widget.py +++ b/tryton/tryton/gui/window/view_form/view/list_gtk/widget.py @@ -699,6 +699,24 @@ class _BinarySave(_BinaryIcon): class _BinarySelect(_BinaryIcon): + + default_filters = None + + @property + def filters(self): + file_filter = Gtk.FileFilter() + if filters := self.attrs.get('filters', self.default_filters): + file_filter.set_name(_("All Supported Types")) + for type in filters.split(','): + if type.startswith('.'): + file_filter.add_pattern(type) + else: + file_filter.add_mime_type(type) + else: + file_filter.set_name(_("All files")) + file_filter.add_pattern("*") + return [file_filter] + def clicked(self, renderer, path): record, field = self._get_record_field_from_path(path) if hasattr(field, 'get_size'): @@ -715,7 +733,7 @@ class _BinarySelect(_BinaryIcon): field.set_client(record, None) else: def _select(): - filename = file_selection(_('Open...')) + filename = file_selection(_("Select"), filters=self.filters) if filename: with open(filename, 'rb') as fp: field.set_client(record, fp.read()) diff --git a/trytond/CHANGELOG b/trytond/CHANGELOG index 2cbc4e8d6a..42c361fd41 100644 --- a/trytond/CHANGELOG +++ b/trytond/CHANGELOG @@ -1,3 +1,4 @@ +* Add filters attribute for binary and image widgets * Move last_user and last_modification to ResourceAccessMixin * Add check deliverability option to validate email tool * Add support for AGE to SQLite backend diff --git a/trytond/doc/topics/views/index.rst b/trytond/doc/topics/views/index.rst index 8eae170139..9e1f4cf727 100644 --- a/trytond/doc/topics/views/index.rst +++ b/trytond/doc/topics/views/index.rst @@ -365,6 +365,14 @@ attributes: ``rounded`` or ``circle``. The default value is ``square``. +``filters`` + A comma separated list of mime type and file extension (starting with a + period ``.``) to restrict the files being shown for selection of binary and + image widgets. + The default value for binary widget is no filter. + The default value for image widget is: + ``image/png,image/jpeg,image/gif,.png,.jpg,.gif,.tif,.xpm``. + ``visible`` When set to ``1`` for :class:`~trytond.model.fields.One2Many` and :class:`~trytond.model.fields.Many2Many` the client may try to read the diff --git a/trytond/trytond/ir/ui/form.rnc b/trytond/trytond/ir/ui/form.rnc index 330c2fea43..db451de316 100644 --- a/trytond/trytond/ir/ui/form.rnc +++ b/trytond/trytond/ir/ui/form.rnc @@ -122,6 +122,7 @@ attlist.field &= attlist.field &= attribute symbol { text }? attlist.field &= [a:defaultValue = "1"] attribute grouping { "0" | "1" }? attlist.field &= [a:defaultValue = "square"] attribute border { "square" | "circle" | "rounded" }? +attlist.field &= attribute filters { text }? attlist.field &= [a:defaultValue = "0"] attribute visible { "0" | "1" }? image = element image { attlist.image, empty } attlist.image &= attribute name { text } diff --git a/trytond/trytond/ir/ui/tree.rnc b/trytond/trytond/ir/ui/tree.rnc index ffefba09f0..6895a0f4ff 100644 --- a/trytond/trytond/ir/ui/tree.rnc +++ b/trytond/trytond/ir/ui/tree.rnc @@ -75,6 +75,7 @@ attlist.field &= attribute help_field { text }? attlist.field &= attribute view_ids { text }? attlist.field &= attribute symbol { text }? attlist.field &= [a:defaultValue = "1"] attribute grouping { "0" | "1" }? +attlist.field &= attribute filters { text }? prefix = element prefix { attlist.affix, empty } suffix = element suffix { attlist.affix, empty } attlist.affix &= attribute string { text }?