Skip to content

Commit

Permalink
Add support for the "accept" attribute on file inputs.
Browse files Browse the repository at this point in the history
If the widget's field - if there is one - has the "accept" attribute set (the
`NamedImage` field has `image/*` set by default) then this is rendered as an
`accept` attribute on the file input.

This would restrict the allowed file types before uploading while still being
checked on the server side.

Fixes: #66
Depends on: plone/plone.namedfile#158
[thet]
  • Loading branch information
thet committed Mar 14, 2024
1 parent 74e5ab2 commit 96db8cc
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 5 deletions.
12 changes: 12 additions & 0 deletions news/67.feature
@@ -0,0 +1,12 @@
Add support for the "accept" attribute on file inputs.

If the widget's field - if there is one - has the "accept" attribute set (the
`NamedImage` field has `image/*` set by default) then this is rendered as an
`accept` attribute on the file input.

This would restrict the allowed file types before uploading while still being
checked on the server side.

Fixes: https://github.com/plone/plone.formwidget.namedfile/issues/66
Depends on: https://github.com/plone/plone.namedfile/pull/158
[thet]
3 changes: 2 additions & 1 deletion plone/formwidget/namedfile/file_input.pt
Expand Up @@ -130,7 +130,8 @@
<div style="padding-left: 1.5em; padding-top: 0.5em;"
tal:omit-tag="not:allow_nochange"
>
<input type="file"
<input accept="${view/accept}"
type="file"
tal:attributes="
id string:${view/id}-input;
name view/name;
Expand Down
3 changes: 2 additions & 1 deletion plone/formwidget/namedfile/image_input.pt
Expand Up @@ -135,7 +135,8 @@
<div style="padding-left: 1.5em; padding-top: 0.5em;"
tal:omit-tag="not:allow_nochange"
>
<input type="file"
<input accept="${view/accept}"
type="file"
tal:attributes="
id string:${view/id}-input;
name view/name;
Expand Down
6 changes: 6 additions & 0 deletions plone/formwidget/namedfile/widget.py
Expand Up @@ -82,6 +82,12 @@ class NamedFileWidget(Explicit, FileWidget):
value = None # don't default to a string
_file_upload_id = None

@property
def accept(self):
accept = getattr(self.field, "accept", None)
if accept:
return ", ".join(accept)

@property
def is_uploaded(self):
return utils.is_file_upload(self.value) or INamed.providedBy(self.value)
Expand Down
65 changes: 63 additions & 2 deletions plone/formwidget/namedfile/widget.rst
Expand Up @@ -151,7 +151,7 @@ First use a GET request::
>>> image_widget.extract()
<ZPublisher.HTTPRequest.FileUpload ...>

The rendering is unchanged:
The rendering is unchanged::

>>> print(file_widget.render())
<span id="widget.id.file" class="named-file-widget" >
Expand Down Expand Up @@ -284,7 +284,7 @@ At first, there is no value, so the behaviour is much like before::
>>> image_widget.update()
>>> print(image_widget.render())
<span id="widget.id.image" class="named-image-widget required namedimage-field" >
<input type="file" id="widget.id.image-input" name="widget.name.image" />
<input accept="image/*" type="file" id="widget.id.image-input" name="widget.name.image" />
</span>

However, if we now set a value, we will have the option of keeping it,
Expand Down Expand Up @@ -388,6 +388,67 @@ stored in the field::
True


Rendering field widgets with constraints on allowed media types
-----------------------------------------------------------------

The NamedImage already has a constraint on `image/*` mime types for files and
this is also rendered for the input element. See above.
You can also customize the allowed media types with the `accept` attribute,
like shown here::

>>> class IContentConstrained(Interface):
... file_field = field.NamedFile(
... title=u"File",
... accept=("audio/mp3", "audio/flac", ".wav")
... )
... image_field = field.NamedImage(
... title=u"Image",
... accept=("image/webp", "image/png", ".jpg")
... )

>>> @implementer(IContentConstrained, IImageScaleTraversable, IAttributeAnnotatable)
... class ContentConstrained(object):
... def __init__(self, file, image):
... self.file_field = file
... self.image_field = image
... self._p_mtime = DateTime()
... self.path = '/content_constrained'
...
... def absolute_url(self):
... return root_url + self.path
...
... def Title(self):
... return 'A content item'

>>> content_constrained = ContentConstrained(None, None)

>>> file_widget_constrained = NamedFileFieldWidget(IContentConstrained['file_field'], make_request())
>>> image_widget_constrained = NamedImageFieldWidget(IContentConstrained['image_field'], make_request())

>>> file_widget_constrained.context = content_constrained
>>> image_widget_constrained.context = content_constrained

>>> file_widget_constrained.id = 'widget.id.file'
>>> file_widget_constrained.name = 'widget.name.file'

>>> image_widget_constrained.id = 'widget.id.image'
>>> image_widget_constrained.name = 'widget.name.image'

At first, there is no value, so the behaviour is much like before::

>>> file_widget_constrained.update()
>>> print(file_widget_constrained.render())
<span id="widget.id.file" class="named-file-widget required namedfile-field" >
<input accept="audio/mp3, audio/flac, .wav" type="file" id="widget.id.file-input" name="widget.name.file" />
</span>

>>> image_widget_constrained.update()
>>> print(image_widget_constrained.render())
<span id="widget.id.image" class="named-image-widget required namedimage-field" >
<input accept="image/webp, image/png, .jpg" type="file" id="widget.id.image-input" name="widget.name.image" />
</span>


Download view
-------------

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -35,7 +35,7 @@
"BTrees",
"setuptools",
"plone.base",
"plone.namedfile",
"plone.namedfile>6.2.3",
"Products.MimetypesRegistry",
"persistent",
"plone.registry",
Expand Down

0 comments on commit 96db8cc

Please sign in to comment.