Skip to content

Commit

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

This already restricts the allowed file types before uploading while still being checked on the server side too.

Fixes: #66
Depends on: plone/plone.namedfile#158
  • Loading branch information
thet committed Mar 14, 2024
1 parent 74e5ab2 commit 865daeb
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 4 deletions.
8 changes: 8 additions & 0 deletions news/67.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Add support for accept attribute on file input.

If the widget's field - if there is one - has `allowedContentTypes` set (the `NamedImage` field has `image/*` set by default) the allowed content types are rendered as `accept` attribute on the file input.

This already restricts the allowed file types before uploading while still being checked on the server side too.

Fixes: https://github.com/plone/plone.formwidget.namedfile/issues/66
Depends on: https://github.com/plone/plone.namedfile/pull/158
3 changes: 2 additions & 1 deletion plone/formwidget/namedfile/file_input.pt
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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):
allowed = getattr(self.field, "allowedContentTypes", None)
if allowed:
return ", ".join(allowed)

@property
def is_uploaded(self):
return utils.is_file_upload(self.value) or INamed.providedBy(self.value)
Expand Down
64 changes: 62 additions & 2 deletions plone/formwidget/namedfile/widget.rst
Original file line number Diff line number Diff line change
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 type="file" accept="image/*" 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,66 @@ stored in the field::
True


Rendering field widgets with constraints on allowed content 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 content types, like shown here::

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

>>> @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 type="file" accept="audio/mp3, audio/flac" 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 type="file" accept="image/webp, image/png" id="widget.id.image-input" name="widget.name.image" />
</span>


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

Expand Down

0 comments on commit 865daeb

Please sign in to comment.