Introduction
============
This package contains fields and wrapper objects for storing:
* A file with a filename
* An image with a filename
Blob-based and non-blob-based types are provided. The blob-based types
require the ZODB3 package to be at version 3.8.1 or later,
and BLOBs to be configured in zope.conf.
plone.supermodel handlers are registered.
See the ``usage.rst`` doctest for more details.
Source Code
===========
Note: This packages is licensed under a *BSD license*.
Please do not add dependencies on GPL code!
Contributors please read the document `Process for Plone core's development <https://docs.plone.org/develop/coredev/docs/index.html>`_
Sources are at the `Plone code repository hosted at Github <https://github.com/plone/plone.namedfile>`_.
Changelog
=========
.. You should *NOT* be adding new change log entries to this file.
You should create a file in the news directory instead.
For helpful instructions, please see:
https://github.com/plone/plone.releaser/blob/master/ADD-A-NEWS-ITEM.rst
.. towncrier release notes start
6.4.0 (2024-11-25)
------------------
New features:
- Set `Link` header with `rel="canonical"` for file downloads. @mamico (#163)
6.3.1 (2024-07-30)
------------------
Bug fixes:
- Fix: Upload a svg without width and height set @dobri1408 (#161)
6.3.0 (2024-03-15)
------------------
New features:
- Improve contenttype detection logic for unregistered but common types.
Change get_contenttype to support common types which are or were not registered
with IANA, like image/webp or audio/midi.
Note: image/webp is already a IANA registered type and also added by
Products.MimetypesRegistry.
[thet] (157-2)
- Support for allowed media types.
Support to constrain files to specific media types with a "accept" attribute on
file and image fields, just like the "accept" attribute of the HTML file input.
Fixes: #157
[thet] (#157)
6.2.3 (2023-11-03)
------------------
Bug fixes:
- Be more strict when checking if mimetype is allowed to be displayed inline.
[maurits] (#1167)
6.2.2 (2023-10-18)
------------------
Bug fixes:
- Fix calculation of file modification time. @davisagli (#153)
6.2.1 (2023-09-21)
------------------
Bug fixes:
- Fix stored XSS (Cross Site Scripting) for SVG images.
Done by forcing a download instead of displaying inline.
See `security advisory <https://github.com/plone/plone.namedfile/security/advisories/GHSA-jj7c-jrv4-c65x>`_.
[maurits] (#1)
6.2.0 (2023-09-14)
------------------
New features:
- Add internal modification timestamp with fallback to _p_mtime.
[mathias.leimgruber] (#149)
- Use new internal modification timestamp as part of the hash key for scales.
[mathias.leimgruber] (#150)
6.1.2 (2023-08-31)
------------------
Bug fixes:
- Fixed the issue where SVG images containing extensive metadata were not being displayed
correctly (resulting in a width/height of 1px). This problem could occur when the
<svg> tag exceeded the MAX_INFO_BYTES limit.
Fixes `issue 147 <https://github.com/plone/plone.namedfile/issues/147>`_.
[mliebischer] (#147)
6.1.1 (2023-06-22)
------------------
Bug fixes:
- Return a 400 Bad Request response if the `@@images` view is published without a subpath. @davisagli (#144)
Tests
- Fix tests to work with various ``beautifulsoup4`` versions.
[maurits] (#867)
6.1.0 (2023-05-22)
------------------
New features:
- Move ``Zope2FileUploadStorable`` code from plone.app.z3cform to here to break a cyclic dependency.
[gforcada] (#3764)
6.0.2 (2023-05-08)
------------------
Bug fixes:
- Fix picture tag when original image is used instead of a scale.
[maurits] (#142)
6.0.1 (2023-03-14)
------------------
Tests
- Tox: explicitly test only the ``plone.namedfile`` package. [maurits] (#50)
6.0.0 (2022-11-22)
------------------
Bug fixes:
- Log a warning when a scale for a picture variant is not found.
Until now this gave an exception.
[maurits] (#134)
- Prevent exception when an anonymous user is the first to load a page with a private image.
Anonymous still gets an Unauthorized when loading the image, but that is to be expected.
The html at least shows normally.
Fixes `issue 135 <https://github.com/plone/plone.namedfile/issues/135>`_.
[maurits] (#135)
- Fixed writing to the database each time an original is requested.
This happens when requesting the original under a unique id.
[maurits] (#3678)
6.0.0b5 (2022-10-03)
--------------------
Breaking changes:
- No longer test Plone 5.2 on 3.6 and Plone 6.0 on 3.7.
[maurits] (#3637)
Bug fixes:
- Use ``mode`` parameter instead of deprecated ``direction`` and warn user about it.
[petschki, maurits] (#102)
6.0.0b4 (2022-09-07)
--------------------
Bug fixes:
- Move getAllowedSizes + getQuality from CMFPlone.utils to this package.
[jensens] (#132)
6.0.0b3 (2022-07-20)
--------------------
Bug fixes:
- Get title from ImageScale class.
Prevents a traceback when the context is a tile.
When no title can we found, fall back to an empty string.
[maurits] (#128)
6.0.0b2 (2022-06-27)
--------------------
Bug fixes:
- Do not use full url in `image_scales` dictionary.
[petschki] (#123)
6.0.0b1 (2022-06-23)
--------------------
New features:
- Creating a tag no longer generates the actual scale.
[maurits] (#113)
- Add picture method to to ImageScaling and include it in @@image-test.
Picture tags only work on Plone 6, with several other branches for picture variants merged.
See `plip-image-srcsets.cfg <https://github.com/plone/buildout.coredev/blob/6.0/plips/plip-image-srcsets.cfg>`_.
If not available (like on Plone 5.2), an ordinary image tag is created.
[MrTango] (#113)
- Add ``@@images-test`` page for Editors.
This shows various variants from the image field of the current context.
It shows a list of stored scales.
It allows purging the stored scales.
[maurits] (#113)
- removed marking request for disable CSRF protection (need plone.scale with https://github.com/plone/plone.scale/pull/58)
[mamico] (#177)
- use the attribute _sizes for local caching correctly available_sizes
[mamico] (#177)
- add additional infos in scale storage only if missing
[mamico] (#177)
- Register adapter for image fields to the new image_scales metadata.
Use this in the image_scale view to get images from a list a brains.
This code is not active on Plone 5, only Plone 6.
[cekk, maurits] (#3521)
Bug fixes:
- Cleanup: isort, black, pyupgrade, remove use of six, etc.
[maurits] (#120)
6.0.0a4 (2022-05-26)
--------------------
Bug fixes:
- Only look at the width when checking if a HiDPI image would be larger than the original.
Otherwise HiDPI srcsets are never included when the scale is defined with a height of 65536.
[maurits] (#114)
- Fix Unauthorized when accessing ``@@images/image`` of private image, even as Manager.
Fixes problem introduced in previous release.
[maurits] (#118)
6.0.0a3 (2022-02-28)
--------------------
Bug fixes:
- ``ImageScaling`` view: use ``guarded_orig_image`` to get field from a url.
Mostly, this makes the view easier to customize.
[maurits] (#104)
6.0.0a2 (2022-02-23)
--------------------
Breaking changes:
- Removed deprecated extras from setup.py.
These ones are gone now: ``blobs``, ``editor``, ``marshaler``, ``scales``, ``supermodel``.
See `issue 106 <https://github.com/plone/plone.namedfile/issues/106>`_.
[maurits] (#106)
New features:
- Register ``AnnotationStorage`` as ``IImageScaleStorage`` multi adapter.
Both from ``plone.scale``.
Use this adapter in our scaling functions when we store or get an image scale.
[maurits] (#44)
6.0.0a1 (2022-01-28)
--------------------
Breaking changes:
- Drop support for Python 2.7.
Main target is now Plone 6, but we try to keep it running on Plone 5.2 with Python 3.
See discussion in `plone.scale issue 44 <https://github.com/plone/plone.scale/issues/44>`_.
[maurits] (#44)
Bug fixes:
- Fixed NameError `file` on Python 3. Use `io.IOBase` instead. (#3)
5.6.0 (2021-12-29)
------------------
New features:
- Make DefaultImageScalingFactory more flexible, with methods you can override.
[maurits] (#104)
5.5.1 (2021-07-28)
------------------
Bug fixes:
- Cache stable image scales strongly.
When plone.app.imaging is available, this is already done.
Otherwise, we should do this ourselves.
Fixes `issue 100 <https://github.com/plone/plone.namedfile/issues/100>`_.
[maurits] (#100)
5.5.0 (2021-06-30)
------------------
New features:
- Prevent stored XSS from file upload (svg, html).
Do this by implementing an allowlist of trusted mimetypes.
You can turn this around by using a denylist of just svg, html and javascript.
Do this by setting OS environment variable ``NAMEDFILE_USE_DENYLIST=1``.
From `Products.PloneHotfix20210518 <https://plone.org/security/hotfix/20210518/reflected-xss-in-various-spots>`_.
[maurits] (#3274)
5.4.0 (2020-06-23)
------------------
New features:
- Range support (https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests)
[mamico] (#86)
5.3.1 (2020-04-30)
------------------
Bug fixes:
- Fix image scaling to re-use the original image when scaling is not required to allow Plone REST API to use cacheable scale URL for the original image without performance penalty [datakurre] (#92)
5.3.0 (2020-04-21)
------------------
New features:
- Change to use field value _p_mtime instead of context object _p_mtime as image scale invalidation timestamp to fix issue where context object (e.g. a document with lead image) modification invalidated all its image field scales even the images itself were not modified. [datakurre] (#91)
5.2.2 (2020-04-14)
------------------
Bug fixes:
- Close BlobFile in DefaultImageScalingFactory. [timo] (#89)
- Implement the handling of SVG files before passing it to Pillow, fixes #3063
[sneridagh] (#3063)
5.2.1 (2019-12-11)
------------------
Bug fixes:
- Fix tiff support. Remove process_tiff and let the PIL do the work.
[mamico] (#85)
- Fix content_type in getImageInfo when using PIL.
[mamicp] (#85)
5.2.0 (2019-11-25)
------------------
New features:
- Load SVG files.
[balavec] (#63)
5.1.0 (2019-10-21)
------------------
New features:
- Add new interface ``plone.namedfile.interfaces.IPluggableFileFieldValidation`` and ``plone.namedfile.interfaces.IPluggableImageFieldValidation``.
Refactored: the fields validation now looks for adapters with this interfaces adapting field and value.
All found adapters are called.
The image content type checker (existed before) is by now the only adapter implemented and registered so far.
Other adapters can be registered in related or custom code.
[jensens] (#81)
5.0.5 (2019-10-12)
------------------
Bug fixes:
- fix ResourceWarnings for unclosed files
[mamico] (#80)
5.0.4 (2019-06-27)
------------------
Bug fixes:
- It is now possible to customize in an easier way the ``@@images`` view [ale-rt] (#65)
5.0.3 (2019-04-29)
------------------
Bug fixes:
- Increase static MAX_INFO_BYTES to fix an issue where the filesize was not extracted properly from an image with lots of metadata. [elioschmutz] (#74)
5.0.2 (2018-11-13)
------------------
Bug fixes:
- Do not fail image upload when Exif data is bad. [maurits] (#68)
5.0.1 (2018-11-08)
------------------
Bug fixes:
- Fix a forgotten change to BytesIO.
[pbauer]
5.0 (2018-11-02)
----------------
New features:
- Target Zope 4 (test changes only).
- Python 3 compatibility
[pbauer, matthewwilkes, fgrcon, jensens]
Bug fixes:
- Prepare for Python 2 / 3 compatibility
[ale-rt, pbauer, MatthewWilkes, jensens]
- remove mention of "retina" (https://github.com/plone/Products.CMFPlone/issues/2123)
[tkimnguyen]
- Fix test to use new zope testbrowser internals.
[davisagli]
4.2.3 (2017-09-08)
------------------
Bug fixes:
- Fix bug #56 where ``srcset`` generation failed on no given width or height if there was no sclae given.
https://github.com/plone/plone.namedfile/pull/56
[jensens]
4.2.2 (2017-07-03)
------------------
Bug fixes:
- Dont't break DefaultImageScalingFactory, if for any reason the fieldname isn't available on the context.
[thet]
- Different caching keys for different domains
[mamico]
4.2.1 (2017-05-30)
------------------
Bug fixes:
- Fix #46, when ``process_png``, ``process_jpeg`` and ``process_tiff`` could fail with a ``width referenced before assignment`` error.
[thet]
- Fix contentType attribute should be str type, what leads to validation errors (fixes `#38`_).
[rodfersou]
- Fix bug on Image rotation if ImageIFD.XResolution or ImageIFD.YResolution are not set.
[loechel]
- Fix: Do not log failing PIL image regognition as error, but as warning.
[jensens]
- Fix: compatibility for Plone 4 re-added.
[loechel]
4.2.0 (2017-03-26)
------------------
New features:
- Add retina image scales using srcset attribute.
[didrix]
4.1.2 (2017-02-12)
------------------
Bug fixes:
- BrowserViews have no Acquisition.
[pbauer]
4.1.1 (2017-01-20)
------------------
New features:
- Add automatic image rotation based on EXIF data for all images.
Based on piexif library and ideas of maartenkling and ezvirtual.
Choosen piexif as it allow read and write of exif data for future enhancements.
http://piexif.readthedocs.org/en/latest/
For Orientation examples and description see http://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/ test data https://github.com/recurser/exif-orientation-examples
Additional Test Images with different MIME-Types (JPEG and TIFF) and possible problems: https://github.com/ianare/exif-samples.git
[loechel]
- Support SVG images
[tomgross]
Bug fixes:
- Added handler for Tiff Images in getImageInfo.
[loechel]
- Restructured packages.
Moved image meta data detection in an own subfolder
[loechel]
4.1 (2016-09-14)
----------------
New features:
- Add Pdata storage
[vangheem]
4.0 (2016-08-12)
----------------
Incompatibilities:
- Targets Plone 5.1 only, coredev 5.0 and 4.3 are on 3.0.x branch [jensens]:
- ``plone.supermodel``, ``plone.scale`` and ``plone.schemaeditor`` are now hard depedencies.
The extras in setup.py are kept for bbb reasons, but are empty.
Conditional code is now no longer conditional.
This simplifies the code a lot.
- ``zope.app.file`` is no longer hard dependency.
If it is there, its FileChunk implementation is still checked for, otherwise not.
New:
- uses adapter as factory for scales as in plone.scale>=1.5
[jensens]
Fixes:
- Several tests were failing on Windows 10 due to binary files being opened in text mode. Fixed.
[smcmahon]
- Prevent attempt to create a filestream_iterator from a temporary file associated with an
uncommited blob.
Fixes an error on Windows 10 "WindowsError 32" by attempting to delete or access a file in use
by another process.
[smcmahon]
- Fix tests to work with latest plone.scale changes, where gif images are no longer converted to jpeg.
[thet]
- Fixed test setup to use layers properly.
[jensens]
- Fixed test isolation problem in ``test_blobfile.py``.
[jensens]
- Fix warning on testing.zcml missing an i18n:domain.
[gforcada]
- Fix some code analysis warnings.
[gforcada]
3.0.8 (2016-02-26)
------------------
Fixes:
- PEP 8, UTF-8 headers, implements/adapts to decorators, doctest formating.
[thet, jensens]
- Workarround for method getImageSize.
Prevent returning (-1, -1) as the size of the image.
[andreesg]
3.0.7 (2016-02-12)
------------------
Fixes:
- Make plone.protect a soft dependency. This allows to use this package in
setups without the Plone stack. Fixes plone/Products.CMFPlone#1311
[thet]
3.0.6 (2016-01-08)
------------------
Fixes:
- Stabilised tests. [gotcha]
3.0.5 (2015-11-26)
------------------
New:
- Added webdav support to image scales.
https://github.com/plone/Products.CMFPlone/issues/1251
[maurits]
3.0.4 (2015-10-28)
------------------
Fixes:
- No longer rely on deprecated ``bobobase_modification_time`` from
``Persistence.Persistent``.
[thet]
3.0.3 (2015-08-14)
------------------
- Don't fail, when accessing the ``tag`` method of the ``@@images`` view, if
``scale`` returns ``None``.
[thet]
3.0.2 (2015-03-13)
------------------
- Cache image scales using the plone.stableResource ruleset when they are
accessed via UID-based URLs. (Requires plone.app.imaging >= 1.1.0)
[davisagli]
3.0.1 (2014-10-23)
------------------
- Fixed inserting filename in Content-Disposition header.
[kroman0]
- Respect field level security in download views also for primary fields.
[jensens]
- Internationalize field factory label.
[thomasdesvenain]
3.0.0 (2014-04-13)
------------------
- Disable CSRF protection when creating a scale so we can write to the database
[vangheem]
2.0.5 (2014-02-19)
------------------
- Ensure zope.app.file.file module alias is created before its use in
file package.
[thomasdesvenain]
2.0.4 (2014-01-27)
------------------
- Disable CSRF protection when creating a scale so we can write to the database
[vangheem]
- Validate image field : check if content is actually an image using mimetype.
[thomasdesvenain]
- Fix: get_contenttype works when empty string is given as contentType.
- Backward compatibility of NamedFile with zope.app.file FileChunk.
Avoids NamedFile validation unexpected failures.
[thomasdesvenain]
2.0.5 (2014-02-19)
------------------
- Ensure zope.app.file.file module alias is created before its use in
file package.
[thomasdesvenain]
2.0.4 (2014-01-27)
------------------
- Backward compatibility of NamedFile with zope.app.file FileChunk.
Avoids NamedFile validation unexpected failures.
[thomasdesvenain]
- Validate image field : check if content is actually an image using mimetype.
[thomasdesvenain]
- Fix: get_contenttype works when empty string is given as contentType.
[thomasdesvenain]
2.0.3 (2013-12-07)
------------------
- Scaling Traverser now does not try to traverse two steps in one.
This is impossible in chameleon.
[do3cc]
2.0.2 (2013-05-23)
------------------
* Use plone.app.imaging's (>=1.0.8) quality setting if it exists.
https://dev.plone.org/ticket/13337
[khink]
* fix invalidation on contexts that do not implement dublin core; Notably
portlet assignments. Fallback is bobo_modification_time. Maybe portlet
assignments should implement modified() instead?
[tmog]
* Fixed handling of TTW Dexterity content type image field
data when image data is large and stored as
zope.app.file.file.FileChunk in ZODB instead of raw string data.
Issue appearated after Plone 4.3 migration [miohtama]
2.0.1 (2013-01-17)
------------------
* Add direction parameter support in scaling (was ignored in tag and scale
functions).
Now calling tag function with parameter direction='down' crops the image.
direction='thumbnail' by default so default behaviour remains the same.
[jriboux]
2.0 (2012-08-29)
----------------
* Move file and image value implementations here instead of extending
the ones from zope.app.file and z3c.blobfile. This helps tame a mess
of dependencies.
[davisagli]
* The blob-based file and image implementations are now always available.
(But they will only work if Zope is using a storage with blob support.)
[davisagli]
* Add support for HEAD requests to @@images view
[anthonygerrard]
* Add hook to override headers in subclasses of file download view
[anthonygerrard]
* Don't set filename in header if filename contains non ascii chars.
[do3cc]
* Adding Dexterity Image caused TypeError if jpeg file contained
corrupt metadata. Closes http://dev.plone.org/ticket/12753.
[patch by joka, applied by kleist]
1.0.6 - 2011-10-18
------------------
* Fix test failure.
[davisagli]
* Fix bug in producing tag for a scale on an item with a unicode title
[tomster]
1.0.5 - 2011-09-24
------------------
* Make the ``download`` view respect custom read permissions for the field
being downloaded, rather than only checking the view permission for the
object as a whole.
[davisagli]
1.0.4 - 2011-08-21
------------------
* Fix bug in producing tag for a scale on an item whose title has non-ASCII
characters.
[davisagli]
* Make sure image scales of allowed attributes can be accessed on disallowed
containers.
[davisagli]
* Add unit tests for safe_filename, since not exercised within this module.
(should be moved to plone.formwidget.namedfile?)
[lentinj]
1.0.3 - 2011-05-20
------------------
* Relicense under BSD license.
See http://plone.org/foundation/materials/foundation-resolutions/plone-framework-components-relicensing-policy
[davisagli]
1.0.2 - 2011-05-19
------------------
* Don't omit empty string attributes from ImageScale tag.
[elro]
1.0.1 - 2011-05-19
------------------
* In the tag method of ImageScale to allow height/width/alt/title to be
omitted when they are supplied as a None argument.
[elro]
* In marshalled file fields, encode the filename parameter of the
Content-Disposition header in accordance with RFC 2231. This ensures that
filenames with non-ASCII characters can be successfully demarshalled.
[davisagli]
* Make the various file classes be strict about only accepting unicode
filenames.
[davisagli]
1.0 - 2011-04-30
----------------
* Use unique urls for accessing the original scale.
[elro]
* Avoid Content-Disposition for image scales.
[elro]
1.0b8 - 2011-04-12
------------------
* Declare dependency on plone.rfc822 >= 1.0b2 (for IPrimaryField).
[davisagli]
* Add a @@display-file view which doesn't set Content-Disposition, so we don't
force download of images, for example.
[lentinj]
1.0b7 - 2011-03-22
------------------
* Support getting the original size as a scale.
[elro]
* Add tag() method to scaling view.
[elro]
* Scaling: quote values of extra tag attributes.
[elro]
1.0b6 - 2011-02-11
------------------
* Add primary field support to @@download and @@images views.
[elro]
* Add getAvailableSizes and getImageSize to the @@images view.
[elro]
1.0b5 - 2010-04-19
------------------
* Add support for scaled images. See usage.txt for details.
[davisagli]
* Fix the field schemata so they can be used as the form schema when
adding the field using plone.schemaeditor.
[rossp]
1.0b4 - 2009-11-17
------------------
* Avoid using the internal _current_filename() helper, which disappeared in
ZODB 3.9.
[optilude]
* Add field factories for plone.schemaeditor (only installed if
plone.schemaeditor is available)
[davisagli]
1.0b3 - 2009-10-08
------------------
* Add plone.rfc822 field marshaler (only installed if plone.rfc822 is
available)
[optilude]
1.0b2 - 2009-09-17
------------------
* Add plone.supermodel import/export handlers (only installed if
plone.supermodel is available).
[optilude]
1.0b1 - 2009-05-30
------------------
* Make z3c.blobfile (and blobs in general) a soft dependency. You'll need to
separately depend on z3c.blobfile (and probably pin it to versio 0.1.2) to
get the NamedBlobFile and NamedBlobImage fields. This means that
plone.namedfile can be used with ZODB versions that do not support BLOBs.
This policy will probably be revisited for a 2.0 release.
[optilude]
1.0a1 - 2009-04-17
------------------
* Initial release
.. _`#38`: https://github.com/plone/plone.namedfile/issues/38
Usage
=====
This demonstrates how to use the package.
Schema fields
-------------
The following schema fields can be used to describe file data. We'll only
test the BLOB versions of the fields if z3c.blobfile is installed::
>>> from zope.interface import Interface
>>> from plone.namedfile import field
>>> class IFileContainer(Interface):
... simple = field.NamedFile(title=u"Named file")
... image = field.NamedImage(title=u"Named image file")
... blob = field.NamedBlobFile(title=u"Named blob file")
... blobimage = field.NamedBlobImage(title=u"Named blob image file")
These store data with the following types::
>>> from zope.interface import implementer
>>> from plone import namedfile
>>> @implementer(IFileContainer)
... class FileContainer(object):
... __allow_access_to_unprotected_subobjects__ = 1
... def __init__(self):
... self.simple = namedfile.NamedFile()
... self.image = namedfile.NamedImage()
... self.blob = namedfile.NamedBlobFile()
... self.blobimage = namedfile.NamedBlobImage()
...
... def absolute_url(self):
... return "http://foo/bar"
File data and content type
--------------------------
Let's now show how to get and set file data.
The FileContainer class creates empty objects to start with::
>>> container = FileContainer()
>>> bytearray(container.simple.data)
bytearray(b'')
>>> container.simple.contentType
''
>>> container.simple.filename is None
True
>>> len(container.image.data)
0
>>> container.image.contentType
''
>>> container.image.filename is None
True
>>> len(container.blob.data)
0
>>> container.blob.contentType
''
>>> container.blob.filename is None
True
>>> len(container.blobimage.data)
0
>>> container.blobimage.contentType
''
>>> container.blobimage.filename is None
True
Let's now set some actual data in these files. Notice how the constructor
will attempt to guess the filename from the file extension::
>>> container.simple = namedfile.NamedFile('dummy test data', filename=u"test.txt")
>>> bytearray(container.simple.data)
bytearray(b'dummy test data')
>>> container.simple.contentType
'text/plain'
>>> print(container.simple.filename)
test.txt
>>> container.blob = namedfile.NamedBlobFile('dummy test data', filename=u"test.txt")
>>> bytearray(container.blob.data)
bytearray(b'dummy test data')
>>> container.blob.contentType
'text/plain'
>>> print(container.blob.filename)
test.txt
Let's also try to read a GIF, courtesy of the zope.app.file tests::
>>> zptlogo = (
... b'GIF89a\x10\x00\x10\x00\xd5\x00\x00\xff\xff\xff\xff\xff\xfe\xfc\xfd\xfd'
... b'\xfa\xfb\xfc\xf7\xf9\xfa\xf5\xf8\xf9\xf3\xf6\xf8\xf2\xf5\xf7\xf0\xf4\xf6'
... b'\xeb\xf1\xf3\xe5\xed\xef\xde\xe8\xeb\xdc\xe6\xea\xd9\xe4\xe8\xd7\xe2\xe6'
... b'\xd2\xdf\xe3\xd0\xdd\xe3\xcd\xdc\xe1\xcb\xda\xdf\xc9\xd9\xdf\xc8\xd8\xdd'
... b'\xc6\xd7\xdc\xc4\xd6\xdc\xc3\xd4\xda\xc2\xd3\xd9\xc1\xd3\xd9\xc0\xd2\xd9'
... b'\xbd\xd1\xd8\xbd\xd0\xd7\xbc\xcf\xd7\xbb\xcf\xd6\xbb\xce\xd5\xb9\xcd\xd4'
... b'\xb6\xcc\xd4\xb6\xcb\xd3\xb5\xcb\xd2\xb4\xca\xd1\xb2\xc8\xd0\xb1\xc7\xd0'
... b'\xb0\xc7\xcf\xaf\xc6\xce\xae\xc4\xce\xad\xc4\xcd\xab\xc3\xcc\xa9\xc2\xcb'
... b'\xa8\xc1\xca\xa6\xc0\xc9\xa4\xbe\xc8\xa2\xbd\xc7\xa0\xbb\xc5\x9e\xba\xc4'
... b'\x9b\xbf\xcc\x98\xb6\xc1\x8d\xae\xbaFgs\x00\x00\x00\x00\x00\x00\x00\x00'
... b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
... b'\x00,\x00\x00\x00\x00\x10\x00\x10\x00\x00\x06z@\x80pH,\x12k\xc8$\xd2f\x04'
... b'\xd4\x84\x01\x01\xe1\xf0d\x16\x9f\x80A\x01\x91\xc0ZmL\xb0\xcd\x00V\xd4'
... b'\xc4a\x87z\xed\xb0-\x1a\xb3\xb8\x95\xbdf8\x1e\x11\xca,MoC$\x15\x18{'
... b'\x006}m\x13\x16\x1a\x1f\x83\x85}6\x17\x1b $\x83\x00\x86\x19\x1d!%)\x8c'
... b'\x866#\'+.\x8ca`\x1c`(,/1\x94B5\x19\x1e"&*-024\xacNq\xba\xbb\xb8h\xbeb'
... b'\x00A\x00;'
... )
>>> container.image = namedfile.NamedImage(zptlogo, filename=u"zpt.gif")
>>> container.image.data == zptlogo
True
>>> container.image.contentType
'image/gif'
>>> print(container.image.filename)
zpt.gif
>>> container.blobimage = namedfile.NamedBlobImage(zptlogo, filename=u"zpt.gif")
>>> container.blobimage.data == zptlogo
True
>>> container.blobimage.contentType
'image/gif'
>>> print(container.blobimage.filename)
zpt.gif
Note that is possible for force the mimetype::
>>> container.image = namedfile.NamedImage(zptlogo, contentType='image/foo', filename=u"zpt.gif")
>>> container.image.data == zptlogo
True
>>> container.image.contentType
'image/foo'
>>> print(container.image.filename)
zpt.gif
>>> container.blobimage = namedfile.NamedBlobImage(zptlogo, contentType='image/foo', filename=u"zpt.gif")
>>> container.blobimage.data == zptlogo
True
>>> container.blobimage.contentType
'image/foo'
>>> print(container.blobimage.filename)
zpt.gif
The filename must be set to a unicode string, not a bytestring::
>>> container.image.filename = b'foo'
Traceback (most recent call last):
...
zope.schema._bootstrapinterfaces.WrongType: ...
Restricting media types
-----------------------
It is possible to define accepted media types, just like with the "accept"
attribute of HTML file inputs. You can pass a tuple of file extensions or media
type values::
>>> class IFileContainerConstrained(Interface):
... file = field.NamedFile(title=u"File", accept=("text/plain", ".pdf"))
>>> @implementer(IFileContainerConstrained)
... class FileContainerConstrained:
... __allow_access_to_unprotected_subobjects__ = 1
... def __init__(self):
... self.file = namedfile.NamedFile()
>>> container_constrained = FileContainerConstrained()
Adding valid file types and checking passes. Note, that the validation logic is
called by the framework and does not need to be called manualle, like in this
test.
::
>>> container_constrained.file = namedfile.NamedFile(
... 'dummy test data',
... filename=u"test.txt"
... )
>>> IFileContainerConstrained["file"].validate(container_constrained.file)
>>> container_constrained.file = namedfile.NamedFile(
... 'dummy test data',
... filename=u"test.pdf"
... )
>>> IFileContainerConstrained["file"].validate(container_constrained.file)
Adding invalid file types and checking fails with a ValidationError::
>>> container_constrained.file = namedfile.NamedFile(
... 'dummy test data',
... filename=u"test.wav"
... )
>>> IFileContainerConstrained["file"].validate(container_constrained.file)
Traceback (most recent call last):
...
plone.namedfile.field.InvalidFile: ('audio/x-wav', 'file')
Download view
-------------
This package also comes with a view that can be used to download files. This
will set Content-Disposition to ensure the browser downloads the file rather
than displaying it. To use it, link to ../context-object/@@download/fieldname,
where `fieldname` is the name of the attribute on the context-object where the
named file is stored.
We will test this with a dummy request, faking traversal::
>>> from plone.namedfile.browser import Download
>>> from zope.publisher.browser import TestRequest
>>> request = TestRequest()
>>> download = Download(container, request).publishTraverse(request, 'simple')
>>> bytearray(download())
bytearray(b'dummy test data')
>>> request.response.getHeader('Content-Length')
'15'
>>> request.response.getHeader('Content-Type')
'text/plain'
>>> request.response.getHeader('Content-Disposition')
"attachment; filename*=UTF-8''test.txt"
>>> request.response.getHeader('Link')
'<http://foo/bar/@@download/simple/test.txt>; rel="canonical"'
>>> request = TestRequest()
>>> download = Download(container, request).publishTraverse(request, 'blob')
>>> data = download()
>>> bytearray(hasattr(data, 'read') and data.read() or data)
bytearray(b'dummy test data')
>>> request.response.getHeader('Content-Length')
'15'
>>> request.response.getHeader('Content-Type')
'text/plain'
>>> request.response.getHeader('Content-Disposition')
"attachment; filename*=UTF-8''test.txt"
>>> request.response.getHeader('Link')
'<http://foo/bar/@@download/blob/test.txt>; rel="canonical"'
>>> request = TestRequest()
>>> download = Download(container, request).publishTraverse(request, 'image')
>>> download() == zptlogo
True
>>> request.response.getHeader('Content-Length')
'341'
>>> request.response.getHeader('Content-Type')
'image/foo'
>>> request.response.getHeader('Content-Disposition')
"attachment; filename*=UTF-8''zpt.gif"
>>> request.response.getHeader('Link')
'<http://foo/bar/@@download/image/zpt.gif>; rel="canonical"'
>>> request = TestRequest()
>>> download = Download(container, request).publishTraverse(request, 'blobimage')
>>> data = download()
>>> (hasattr(data, 'read') and data.read() or data) == zptlogo
True
>>> request.response.getHeader('Content-Length')
'341'
>>> request.response.getHeader('Content-Type')
'image/foo'
>>> request.response.getHeader('Content-Disposition')
"attachment; filename*=UTF-8''zpt.gif"
>>> request.response.getHeader('Link')
'<http://foo/bar/@@download/blobimage/zpt.gif>; rel="canonical"'
Range support
-------------
Checking for partial requests support::
>>> request = TestRequest()
>>> download = Download(container, request).publishTraverse(request, 'blobimage')
>>> data = download()
>>> request.response.getHeader('Content-Length')
'341'
>>> request.response.getHeader('Accept-Ranges')
'bytes'
Request a specific range::
>>> request = TestRequest(environ={'HTTP_RANGE': 'bytes=0-99'})
>>> download = Download(container, request).publishTraverse(request, 'blobimage')
>>> data = download()
>>> request.response.getStatus()
206
>>> len(hasattr(data, 'read') and data.read() or data)
100
The Content-Length header now indicates the size of the requested range (and not the full size of the image).
The Content-Range response header indicates where in the full resource this partial message belongs.::
>>> request.response.getHeader('Content-Length')
'100'
>>> request.response.getHeader('Content-Range')
'bytes 0-99/341'
Display-file view
-----------------
This package also comes with a view that can be used to display files in the
browser. To use it, link to ../context-object/@@display-file/fieldname, where
`fieldname` is the name of the attribute on the context-object where the named
file is stored.
We will test this with a dummy request, faking traversal::
>>> from plone.namedfile.browser import DisplayFile
>>> from zope.publisher.browser import TestRequest
>>> request = TestRequest()
>>> display_file = DisplayFile(container, request).publishTraverse(request, 'simple')
>>> bytearray(display_file())
bytearray(b'dummy test data')
>>> request.response.getHeader('Content-Length')
'15'
>>> request.response.getHeader('Content-Type')
'text/plain'
>>> request.response.getHeader('Content-Disposition')
>>> request = TestRequest()
>>> display_file = DisplayFile(container, request).publishTraverse(request, 'blob')
>>> data = display_file()
>>> bytearray(hasattr(data, 'read') and data.read() or data)
bytearray(b'dummy test data')
>>> request.response.getHeader('Content-Length')
'15'
>>> request.response.getHeader('Content-Type')
'text/plain'
>>> request.response.getHeader('Content-Disposition')
>>> request = TestRequest()
>>> display_file = DisplayFile(container, request).publishTraverse(request, 'image')
>>> display_file() == zptlogo
True
>>> request.response.getHeader('Content-Length')
'341'
>>> request.response.getHeader('Content-Type')
'image/foo'
Since the Content-Type is unknown, we do not trust it, and refuse to display inline.
We download instead.
>>> request.response.getHeader('Content-Disposition')
"attachment; filename*=UTF-8''zpt.gif"
>>> request = TestRequest()
>>> display_file = DisplayFile(container, request).publishTraverse(request, 'blobimage')
>>> data = display_file()
>>> (hasattr(data, 'read') and data.read() or data) == zptlogo
True
>>> request.response.getHeader('Content-Length')
'341'
>>> request.response.getHeader('Content-Type')
'image/foo'
>>> request.response.getHeader('Content-Disposition')
"attachment; filename*=UTF-8''zpt.gif"
Specifying the primary field
----------------------------
To use the @@download view without specifying the field in the URL, the
primary field information must be registered with an adapter. (Frameworks such
as plone.dexterity may already have done this for you.)::
>>> from plone.rfc822.interfaces import IPrimaryFieldInfo
>>> from zope.component import adapter
>>> @implementer(IPrimaryFieldInfo)
... @adapter(IFileContainer)
... class FieldContainerPrimaryFieldInfo(object):
... fieldname = 'simple'
... field = IFileContainer['simple']
... def __init__(self, context):
... self.value = context.simple
>>> from zope.component import getSiteManager
>>> components = getSiteManager()
>>> components.registerAdapter(FieldContainerPrimaryFieldInfo)
We will test this with a dummy request, faking traversal::
>>> request = TestRequest()
>>> download = Download(container, request)
>>> bytearray(download())
bytearray(b'dummy test data')
>>> request.response.getHeader('Content-Length')
'15'
>>> request.response.getHeader('Content-Type')
'text/plain'
>>> request.response.getHeader('Content-Disposition')
"attachment; filename*=UTF-8''test.txt"
Image scales
------------
This package can handle the creation, storage, and retrieval of arbitrarily
sized scaled versions of images stored in NamedImage or NamedBlobImage fields.
Image scales are accessed via an @@images view that is available for any item
providing ``plone.namedfile.interfaces.IImageScaleTraversable``. There are
several ways that you may reference scales from page templates.
1. for full control you may do the tag generation explicitly::
<img tal:define="images context/@@images;
thumbnail python: images.scale('image', width=64, height=64);"
tal:condition="thumbnail"
tal:attributes="src thumbnail/url;
width thumbnail/width;
height thumbnail/height" />
This would create an up to 64 by 64 pixel scaled down version of the image
stored in the "image" field. It also allows for passing in additional
parameters supported by the ``scaleImage`` function from ``plone.scale``,
e.g. ``mode`` or ``quality``.
.. _`plone.scale`: https://pypi.org/project/plone.scale/
2. for automatic tag generation with extra parameters you would use::
<img tal:define="images context/@@images"
tal:replace="structure python: images.tag('image',
width=1200, height=800, mode='contain')" />
3. It is possible to access scales via predefined named scale sizes, rather
than hardcoding the dimensions every time you access a scale. The scale
sizes are found via calling a utility providing
``plone.namedfile.interfaces.IAvailableSizes``, which should return a dict of
scale name => (width, height). A scale called 'mini' could then be accessed
like this::
<img tal:define="images context/@@images"
tal:replace="structure python: images.tag('image', scale='mini')" />
This would use the predefined scale size "mini" to determine the desired
image dimensions, but still allow to pass in extra parameters.
4. a convenience short-cut for option 3 can be used::
<img tal:replace="structure context/@@images/image/mini" />
5. and lastly, the short-cut can also be used to render the unscaled image::
<img tal:replace="structure context/@@images/image" />
Raw data
{
"_id": null,
"home_page": "https://pypi.org/project/plone.namedfile",
"name": "plone.namedfile",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.7",
"maintainer_email": null,
"keywords": "plone named file image blob",
"author": "Laurence Rowe, Martin Aspeli",
"author_email": "plone-developers@lists.sourceforge.net",
"download_url": "https://files.pythonhosted.org/packages/bd/86/1c1e95231dd02d8757f5d7da6aa4f3530dc59831209e5d7b14f91509eb03/plone_namedfile-6.4.0.tar.gz",
"platform": null,
"description": "Introduction\n============\n\nThis package contains fields and wrapper objects for storing:\n\n* A file with a filename\n* An image with a filename\n\nBlob-based and non-blob-based types are provided. The blob-based types\nrequire the ZODB3 package to be at version 3.8.1 or later,\nand BLOBs to be configured in zope.conf.\n\nplone.supermodel handlers are registered.\n\nSee the ``usage.rst`` doctest for more details.\n\n\nSource Code\n===========\n\n Note: This packages is licensed under a *BSD license*. \n Please do not add dependencies on GPL code!\n\nContributors please read the document `Process for Plone core's development <https://docs.plone.org/develop/coredev/docs/index.html>`_\n\nSources are at the `Plone code repository hosted at Github <https://github.com/plone/plone.namedfile>`_.\n\n\nChangelog\n=========\n\n.. You should *NOT* be adding new change log entries to this file.\n You should create a file in the news directory instead.\n For helpful instructions, please see:\n https://github.com/plone/plone.releaser/blob/master/ADD-A-NEWS-ITEM.rst\n\n.. towncrier release notes start\n\n6.4.0 (2024-11-25)\n------------------\n\nNew features:\n\n\n- Set `Link` header with `rel=\"canonical\"` for file downloads. @mamico (#163)\n\n\n6.3.1 (2024-07-30)\n------------------\n\nBug fixes:\n\n\n- Fix: Upload a svg without width and height set @dobri1408 (#161)\n\n\n6.3.0 (2024-03-15)\n------------------\n\nNew features:\n\n\n- Improve contenttype detection logic for unregistered but common types.\n\n Change get_contenttype to support common types which are or were not registered\n with IANA, like image/webp or audio/midi.\n\n Note: image/webp is already a IANA registered type and also added by\n Products.MimetypesRegistry.\n [thet] (157-2)\n- Support for allowed media types.\n\n Support to constrain files to specific media types with a \"accept\" attribute on\n file and image fields, just like the \"accept\" attribute of the HTML file input.\n\n Fixes: #157\n [thet] (#157)\n\n\n6.2.3 (2023-11-03)\n------------------\n\nBug fixes:\n\n\n- Be more strict when checking if mimetype is allowed to be displayed inline.\n [maurits] (#1167)\n\n\n6.2.2 (2023-10-18)\n------------------\n\nBug fixes:\n\n\n- Fix calculation of file modification time. @davisagli (#153)\n\n\n6.2.1 (2023-09-21)\n------------------\n\nBug fixes:\n\n\n- Fix stored XSS (Cross Site Scripting) for SVG images.\n Done by forcing a download instead of displaying inline.\n See `security advisory <https://github.com/plone/plone.namedfile/security/advisories/GHSA-jj7c-jrv4-c65x>`_.\n [maurits] (#1)\n\n\n6.2.0 (2023-09-14)\n------------------\n\nNew features:\n\n\n- Add internal modification timestamp with fallback to _p_mtime.\n [mathias.leimgruber] (#149)\n- Use new internal modification timestamp as part of the hash key for scales.\n [mathias.leimgruber] (#150)\n\n\n6.1.2 (2023-08-31)\n------------------\n\nBug fixes:\n\n\n- Fixed the issue where SVG images containing extensive metadata were not being displayed\n correctly (resulting in a width/height of 1px). This problem could occur when the\n <svg> tag exceeded the MAX_INFO_BYTES limit.\n\n Fixes `issue 147 <https://github.com/plone/plone.namedfile/issues/147>`_.\n [mliebischer] (#147)\n\n\n6.1.1 (2023-06-22)\n------------------\n\nBug fixes:\n\n\n- Return a 400 Bad Request response if the `@@images` view is published without a subpath. @davisagli (#144)\n\n\nTests\n\n\n- Fix tests to work with various ``beautifulsoup4`` versions.\n [maurits] (#867)\n\n\n6.1.0 (2023-05-22)\n------------------\n\nNew features:\n\n\n- Move ``Zope2FileUploadStorable`` code from plone.app.z3cform to here to break a cyclic dependency.\n [gforcada] (#3764)\n\n\n6.0.2 (2023-05-08)\n------------------\n\nBug fixes:\n\n\n- Fix picture tag when original image is used instead of a scale.\n [maurits] (#142)\n\n\n6.0.1 (2023-03-14)\n------------------\n\nTests\n\n\n- Tox: explicitly test only the ``plone.namedfile`` package. [maurits] (#50)\n\n\n6.0.0 (2022-11-22)\n------------------\n\nBug fixes:\n\n\n- Log a warning when a scale for a picture variant is not found.\n Until now this gave an exception.\n [maurits] (#134)\n- Prevent exception when an anonymous user is the first to load a page with a private image.\n Anonymous still gets an Unauthorized when loading the image, but that is to be expected.\n The html at least shows normally.\n Fixes `issue 135 <https://github.com/plone/plone.namedfile/issues/135>`_.\n [maurits] (#135)\n- Fixed writing to the database each time an original is requested.\n This happens when requesting the original under a unique id.\n [maurits] (#3678)\n\n\n6.0.0b5 (2022-10-03)\n--------------------\n\nBreaking changes:\n\n\n- No longer test Plone 5.2 on 3.6 and Plone 6.0 on 3.7.\n [maurits] (#3637)\n\n\nBug fixes:\n\n\n- Use ``mode`` parameter instead of deprecated ``direction`` and warn user about it.\n [petschki, maurits] (#102)\n\n\n6.0.0b4 (2022-09-07)\n--------------------\n\nBug fixes:\n\n\n- Move getAllowedSizes + getQuality from CMFPlone.utils to this package.\n [jensens] (#132)\n\n\n6.0.0b3 (2022-07-20)\n--------------------\n\nBug fixes:\n\n\n- Get title from ImageScale class.\n Prevents a traceback when the context is a tile.\n When no title can we found, fall back to an empty string.\n [maurits] (#128)\n\n\n6.0.0b2 (2022-06-27)\n--------------------\n\nBug fixes:\n\n\n- Do not use full url in `image_scales` dictionary.\n [petschki] (#123)\n\n\n6.0.0b1 (2022-06-23)\n--------------------\n\nNew features:\n\n\n- Creating a tag no longer generates the actual scale.\n [maurits] (#113)\n- Add picture method to to ImageScaling and include it in @@image-test.\n Picture tags only work on Plone 6, with several other branches for picture variants merged.\n See `plip-image-srcsets.cfg <https://github.com/plone/buildout.coredev/blob/6.0/plips/plip-image-srcsets.cfg>`_.\n If not available (like on Plone 5.2), an ordinary image tag is created.\n [MrTango] (#113)\n- Add ``@@images-test`` page for Editors.\n This shows various variants from the image field of the current context.\n It shows a list of stored scales.\n It allows purging the stored scales.\n [maurits] (#113)\n- removed marking request for disable CSRF protection (need plone.scale with https://github.com/plone/plone.scale/pull/58)\n [mamico] (#177)\n- use the attribute _sizes for local caching correctly available_sizes\n [mamico] (#177)\n- add additional infos in scale storage only if missing\n [mamico] (#177)\n- Register adapter for image fields to the new image_scales metadata.\n Use this in the image_scale view to get images from a list a brains.\n This code is not active on Plone 5, only Plone 6.\n [cekk, maurits] (#3521)\n\n\nBug fixes:\n\n\n- Cleanup: isort, black, pyupgrade, remove use of six, etc.\n [maurits] (#120)\n\n\n6.0.0a4 (2022-05-26)\n--------------------\n\nBug fixes:\n\n\n- Only look at the width when checking if a HiDPI image would be larger than the original.\n Otherwise HiDPI srcsets are never included when the scale is defined with a height of 65536.\n [maurits] (#114)\n- Fix Unauthorized when accessing ``@@images/image`` of private image, even as Manager.\n Fixes problem introduced in previous release.\n [maurits] (#118)\n\n\n6.0.0a3 (2022-02-28)\n--------------------\n\nBug fixes:\n\n\n- ``ImageScaling`` view: use ``guarded_orig_image`` to get field from a url.\n Mostly, this makes the view easier to customize.\n [maurits] (#104)\n\n\n6.0.0a2 (2022-02-23)\n--------------------\n\nBreaking changes:\n\n\n- Removed deprecated extras from setup.py.\n These ones are gone now: ``blobs``, ``editor``, ``marshaler``, ``scales``, ``supermodel``.\n See `issue 106 <https://github.com/plone/plone.namedfile/issues/106>`_.\n [maurits] (#106)\n\n\nNew features:\n\n\n- Register ``AnnotationStorage`` as ``IImageScaleStorage`` multi adapter.\n Both from ``plone.scale``.\n Use this adapter in our scaling functions when we store or get an image scale.\n [maurits] (#44)\n\n\n6.0.0a1 (2022-01-28)\n--------------------\n\nBreaking changes:\n\n\n- Drop support for Python 2.7.\n Main target is now Plone 6, but we try to keep it running on Plone 5.2 with Python 3.\n See discussion in `plone.scale issue 44 <https://github.com/plone/plone.scale/issues/44>`_.\n [maurits] (#44)\n\n\nBug fixes:\n\n\n- Fixed NameError `file` on Python 3. Use `io.IOBase` instead. (#3)\n\n\n5.6.0 (2021-12-29)\n------------------\n\nNew features:\n\n\n- Make DefaultImageScalingFactory more flexible, with methods you can override.\n [maurits] (#104)\n\n\n5.5.1 (2021-07-28)\n------------------\n\nBug fixes:\n\n\n- Cache stable image scales strongly.\n When plone.app.imaging is available, this is already done.\n Otherwise, we should do this ourselves.\n Fixes `issue 100 <https://github.com/plone/plone.namedfile/issues/100>`_.\n [maurits] (#100)\n\n\n5.5.0 (2021-06-30)\n------------------\n\nNew features:\n\n\n- Prevent stored XSS from file upload (svg, html).\n Do this by implementing an allowlist of trusted mimetypes.\n You can turn this around by using a denylist of just svg, html and javascript.\n Do this by setting OS environment variable ``NAMEDFILE_USE_DENYLIST=1``.\n From `Products.PloneHotfix20210518 <https://plone.org/security/hotfix/20210518/reflected-xss-in-various-spots>`_.\n [maurits] (#3274)\n\n\n5.4.0 (2020-06-23)\n------------------\n\nNew features:\n\n\n- Range support (https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests)\n [mamico] (#86)\n\n\n5.3.1 (2020-04-30)\n------------------\n\nBug fixes:\n\n\n- Fix image scaling to re-use the original image when scaling is not required to allow Plone REST API to use cacheable scale URL for the original image without performance penalty [datakurre] (#92)\n\n\n5.3.0 (2020-04-21)\n------------------\n\nNew features:\n\n\n- Change to use field value _p_mtime instead of context object _p_mtime as image scale invalidation timestamp to fix issue where context object (e.g. a document with lead image) modification invalidated all its image field scales even the images itself were not modified. [datakurre] (#91)\n\n\n5.2.2 (2020-04-14)\n------------------\n\nBug fixes:\n\n\n- Close BlobFile in DefaultImageScalingFactory. [timo] (#89)\n- Implement the handling of SVG files before passing it to Pillow, fixes #3063\n [sneridagh] (#3063)\n\n\n5.2.1 (2019-12-11)\n------------------\n\nBug fixes:\n\n\n- Fix tiff support. Remove process_tiff and let the PIL do the work.\n [mamico] (#85)\n- Fix content_type in getImageInfo when using PIL.\n [mamicp] (#85)\n\n\n5.2.0 (2019-11-25)\n------------------\n\nNew features:\n\n\n- Load SVG files.\n [balavec] (#63)\n\n\n5.1.0 (2019-10-21)\n------------------\n\nNew features:\n\n\n- Add new interface ``plone.namedfile.interfaces.IPluggableFileFieldValidation`` and ``plone.namedfile.interfaces.IPluggableImageFieldValidation``.\n Refactored: the fields validation now looks for adapters with this interfaces adapting field and value.\n All found adapters are called.\n The image content type checker (existed before) is by now the only adapter implemented and registered so far.\n Other adapters can be registered in related or custom code.\n [jensens] (#81)\n\n\n5.0.5 (2019-10-12)\n------------------\n\nBug fixes:\n\n\n- fix ResourceWarnings for unclosed files\n [mamico] (#80)\n\n\n5.0.4 (2019-06-27)\n------------------\n\nBug fixes:\n\n\n- It is now possible to customize in an easier way the ``@@images`` view [ale-rt] (#65)\n\n\n5.0.3 (2019-04-29)\n------------------\n\nBug fixes:\n\n\n- Increase static MAX_INFO_BYTES to fix an issue where the filesize was not extracted properly from an image with lots of metadata. [elioschmutz] (#74)\n\n\n5.0.2 (2018-11-13)\n------------------\n\nBug fixes:\n\n\n- Do not fail image upload when Exif data is bad. [maurits] (#68)\n\n\n5.0.1 (2018-11-08)\n------------------\n\nBug fixes:\n\n- Fix a forgotten change to BytesIO.\n [pbauer]\n\n\n5.0 (2018-11-02)\n----------------\n\nNew features:\n\n- Target Zope 4 (test changes only).\n\n- Python 3 compatibility\n [pbauer, matthewwilkes, fgrcon, jensens]\n\nBug fixes:\n\n- Prepare for Python 2 / 3 compatibility\n [ale-rt, pbauer, MatthewWilkes, jensens]\n\n- remove mention of \"retina\" (https://github.com/plone/Products.CMFPlone/issues/2123)\n [tkimnguyen]\n\n- Fix test to use new zope testbrowser internals.\n [davisagli]\n\n\n4.2.3 (2017-09-08)\n------------------\n\nBug fixes:\n\n- Fix bug #56 where ``srcset`` generation failed on no given width or height if there was no sclae given.\n https://github.com/plone/plone.namedfile/pull/56\n [jensens]\n\n\n4.2.2 (2017-07-03)\n------------------\n\nBug fixes:\n\n- Dont't break DefaultImageScalingFactory, if for any reason the fieldname isn't available on the context.\n [thet]\n\n- Different caching keys for different domains\n [mamico]\n\n\n4.2.1 (2017-05-30)\n------------------\n\nBug fixes:\n\n- Fix #46, when ``process_png``, ``process_jpeg`` and ``process_tiff`` could fail with a ``width referenced before assignment`` error.\n [thet]\n\n- Fix contentType attribute should be str type, what leads to validation errors (fixes `#38`_).\n [rodfersou]\n\n- Fix bug on Image rotation if ImageIFD.XResolution or ImageIFD.YResolution are not set.\n [loechel]\n\n- Fix: Do not log failing PIL image regognition as error, but as warning.\n [jensens]\n\n- Fix: compatibility for Plone 4 re-added.\n [loechel]\n\n\n4.2.0 (2017-03-26)\n------------------\n\nNew features:\n\n- Add retina image scales using srcset attribute.\n [didrix]\n\n\n4.1.2 (2017-02-12)\n------------------\n\nBug fixes:\n\n- BrowserViews have no Acquisition.\n [pbauer]\n\n\n4.1.1 (2017-01-20)\n------------------\n\nNew features:\n\n- Add automatic image rotation based on EXIF data for all images.\n Based on piexif library and ideas of maartenkling and ezvirtual.\n Choosen piexif as it allow read and write of exif data for future enhancements.\n http://piexif.readthedocs.org/en/latest/\n For Orientation examples and description see http://www.daveperrett.com/articles/2012/07/28/exif-orientation-handling-is-a-ghetto/ test data https://github.com/recurser/exif-orientation-examples\n Additional Test Images with different MIME-Types (JPEG and TIFF) and possible problems: https://github.com/ianare/exif-samples.git\n [loechel]\n\n- Support SVG images\n [tomgross]\n\n\nBug fixes:\n\n- Added handler for Tiff Images in getImageInfo.\n [loechel]\n\n- Restructured packages.\n Moved image meta data detection in an own subfolder\n [loechel]\n\n\n4.1 (2016-09-14)\n----------------\n\nNew features:\n\n- Add Pdata storage\n [vangheem]\n\n\n4.0 (2016-08-12)\n----------------\n\nIncompatibilities:\n\n- Targets Plone 5.1 only, coredev 5.0 and 4.3 are on 3.0.x branch [jensens]:\n\n - ``plone.supermodel``, ``plone.scale`` and ``plone.schemaeditor`` are now hard depedencies.\n The extras in setup.py are kept for bbb reasons, but are empty.\n Conditional code is now no longer conditional.\n This simplifies the code a lot.\n\n - ``zope.app.file`` is no longer hard dependency.\n If it is there, its FileChunk implementation is still checked for, otherwise not.\n\n\nNew:\n\n- uses adapter as factory for scales as in plone.scale>=1.5\n [jensens]\n\nFixes:\n\n- Several tests were failing on Windows 10 due to binary files being opened in text mode. Fixed.\n [smcmahon]\n\n- Prevent attempt to create a filestream_iterator from a temporary file associated with an\n uncommited blob.\n Fixes an error on Windows 10 \"WindowsError 32\" by attempting to delete or access a file in use\n by another process.\n [smcmahon]\n\n- Fix tests to work with latest plone.scale changes, where gif images are no longer converted to jpeg.\n [thet]\n\n- Fixed test setup to use layers properly.\n [jensens]\n\n- Fixed test isolation problem in ``test_blobfile.py``.\n [jensens]\n\n- Fix warning on testing.zcml missing an i18n:domain.\n [gforcada]\n\n- Fix some code analysis warnings.\n [gforcada]\n\n3.0.8 (2016-02-26)\n------------------\n\nFixes:\n\n- PEP 8, UTF-8 headers, implements/adapts to decorators, doctest formating.\n [thet, jensens]\n\n- Workarround for method getImageSize.\n Prevent returning (-1, -1) as the size of the image.\n [andreesg]\n\n\n3.0.7 (2016-02-12)\n------------------\n\nFixes:\n\n- Make plone.protect a soft dependency. This allows to use this package in\n setups without the Plone stack. Fixes plone/Products.CMFPlone#1311\n [thet]\n\n3.0.6 (2016-01-08)\n------------------\n\nFixes:\n\n- Stabilised tests. [gotcha]\n\n\n3.0.5 (2015-11-26)\n------------------\n\nNew:\n\n- Added webdav support to image scales.\n https://github.com/plone/Products.CMFPlone/issues/1251\n [maurits]\n\n\n3.0.4 (2015-10-28)\n------------------\n\nFixes:\n\n- No longer rely on deprecated ``bobobase_modification_time`` from\n ``Persistence.Persistent``.\n [thet]\n\n\n3.0.3 (2015-08-14)\n------------------\n\n- Don't fail, when accessing the ``tag`` method of the ``@@images`` view, if\n ``scale`` returns ``None``.\n [thet]\n\n\n3.0.2 (2015-03-13)\n------------------\n\n- Cache image scales using the plone.stableResource ruleset when they are\n accessed via UID-based URLs. (Requires plone.app.imaging >= 1.1.0)\n [davisagli]\n\n\n3.0.1 (2014-10-23)\n------------------\n\n- Fixed inserting filename in Content-Disposition header.\n [kroman0]\n\n- Respect field level security in download views also for primary fields.\n [jensens]\n\n- Internationalize field factory label.\n [thomasdesvenain]\n\n\n3.0.0 (2014-04-13)\n------------------\n\n- Disable CSRF protection when creating a scale so we can write to the database\n [vangheem]\n\n\n2.0.5 (2014-02-19)\n------------------\n\n- Ensure zope.app.file.file module alias is created before its use in\n file package.\n [thomasdesvenain]\n\n\n2.0.4 (2014-01-27)\n------------------\n\n- Disable CSRF protection when creating a scale so we can write to the database\n [vangheem]\n\n- Validate image field : check if content is actually an image using mimetype.\n [thomasdesvenain]\n\n- Fix: get_contenttype works when empty string is given as contentType.\n\n- Backward compatibility of NamedFile with zope.app.file FileChunk.\n Avoids NamedFile validation unexpected failures.\n [thomasdesvenain]\n\n\n2.0.5 (2014-02-19)\n------------------\n\n- Ensure zope.app.file.file module alias is created before its use in\n file package.\n [thomasdesvenain]\n\n\n2.0.4 (2014-01-27)\n------------------\n\n- Backward compatibility of NamedFile with zope.app.file FileChunk.\n Avoids NamedFile validation unexpected failures.\n [thomasdesvenain]\n\n- Validate image field : check if content is actually an image using mimetype.\n [thomasdesvenain]\n\n- Fix: get_contenttype works when empty string is given as contentType.\n [thomasdesvenain]\n\n\n2.0.3 (2013-12-07)\n------------------\n\n- Scaling Traverser now does not try to traverse two steps in one.\n This is impossible in chameleon.\n [do3cc]\n\n\n2.0.2 (2013-05-23)\n------------------\n\n* Use plone.app.imaging's (>=1.0.8) quality setting if it exists.\n https://dev.plone.org/ticket/13337\n [khink]\n\n* fix invalidation on contexts that do not implement dublin core; Notably\n portlet assignments. Fallback is bobo_modification_time. Maybe portlet\n assignments should implement modified() instead?\n [tmog]\n\n* Fixed handling of TTW Dexterity content type image field\n data when image data is large and stored as\n zope.app.file.file.FileChunk in ZODB instead of raw string data.\n Issue appearated after Plone 4.3 migration [miohtama]\n\n\n2.0.1 (2013-01-17)\n------------------\n\n* Add direction parameter support in scaling (was ignored in tag and scale\n functions).\n Now calling tag function with parameter direction='down' crops the image.\n direction='thumbnail' by default so default behaviour remains the same.\n [jriboux]\n\n2.0 (2012-08-29)\n----------------\n\n* Move file and image value implementations here instead of extending\n the ones from zope.app.file and z3c.blobfile. This helps tame a mess\n of dependencies.\n [davisagli]\n\n* The blob-based file and image implementations are now always available.\n (But they will only work if Zope is using a storage with blob support.)\n [davisagli]\n\n* Add support for HEAD requests to @@images view\n [anthonygerrard]\n\n* Add hook to override headers in subclasses of file download view\n [anthonygerrard]\n\n* Don't set filename in header if filename contains non ascii chars.\n [do3cc]\n\n* Adding Dexterity Image caused TypeError if jpeg file contained\n corrupt metadata. Closes http://dev.plone.org/ticket/12753.\n [patch by joka, applied by kleist]\n\n1.0.6 - 2011-10-18\n------------------\n\n* Fix test failure.\n [davisagli]\n\n* Fix bug in producing tag for a scale on an item with a unicode title\n [tomster]\n\n1.0.5 - 2011-09-24\n------------------\n\n* Make the ``download`` view respect custom read permissions for the field\n being downloaded, rather than only checking the view permission for the\n object as a whole.\n [davisagli]\n\n1.0.4 - 2011-08-21\n------------------\n\n* Fix bug in producing tag for a scale on an item whose title has non-ASCII\n characters.\n [davisagli]\n\n* Make sure image scales of allowed attributes can be accessed on disallowed\n containers.\n [davisagli]\n\n* Add unit tests for safe_filename, since not exercised within this module.\n (should be moved to plone.formwidget.namedfile?)\n [lentinj]\n\n1.0.3 - 2011-05-20\n------------------\n\n* Relicense under BSD license.\n See http://plone.org/foundation/materials/foundation-resolutions/plone-framework-components-relicensing-policy\n [davisagli]\n\n1.0.2 - 2011-05-19\n------------------\n\n* Don't omit empty string attributes from ImageScale tag.\n [elro]\n\n1.0.1 - 2011-05-19\n------------------\n\n* In the tag method of ImageScale to allow height/width/alt/title to be\n omitted when they are supplied as a None argument.\n [elro]\n\n* In marshalled file fields, encode the filename parameter of the\n Content-Disposition header in accordance with RFC 2231. This ensures that\n filenames with non-ASCII characters can be successfully demarshalled.\n [davisagli]\n\n* Make the various file classes be strict about only accepting unicode\n filenames.\n [davisagli]\n\n1.0 - 2011-04-30\n----------------\n\n* Use unique urls for accessing the original scale.\n [elro]\n\n* Avoid Content-Disposition for image scales.\n [elro]\n\n1.0b8 - 2011-04-12\n------------------\n\n* Declare dependency on plone.rfc822 >= 1.0b2 (for IPrimaryField).\n [davisagli]\n\n* Add a @@display-file view which doesn't set Content-Disposition, so we don't\n force download of images, for example.\n [lentinj]\n\n1.0b7 - 2011-03-22\n------------------\n\n* Support getting the original size as a scale.\n [elro]\n\n* Add tag() method to scaling view.\n [elro]\n\n* Scaling: quote values of extra tag attributes.\n [elro]\n\n1.0b6 - 2011-02-11\n------------------\n\n* Add primary field support to @@download and @@images views.\n [elro]\n\n* Add getAvailableSizes and getImageSize to the @@images view.\n [elro]\n\n1.0b5 - 2010-04-19\n------------------\n\n* Add support for scaled images. See usage.txt for details.\n [davisagli]\n\n* Fix the field schemata so they can be used as the form schema when\n adding the field using plone.schemaeditor.\n [rossp]\n\n1.0b4 - 2009-11-17\n------------------\n\n* Avoid using the internal _current_filename() helper, which disappeared in\n ZODB 3.9.\n [optilude]\n\n* Add field factories for plone.schemaeditor (only installed if\n plone.schemaeditor is available)\n [davisagli]\n\n1.0b3 - 2009-10-08\n------------------\n\n* Add plone.rfc822 field marshaler (only installed if plone.rfc822 is\n available)\n [optilude]\n\n1.0b2 - 2009-09-17\n------------------\n\n* Add plone.supermodel import/export handlers (only installed if\n plone.supermodel is available).\n [optilude]\n\n1.0b1 - 2009-05-30\n------------------\n\n* Make z3c.blobfile (and blobs in general) a soft dependency. You'll need to\n separately depend on z3c.blobfile (and probably pin it to versio 0.1.2) to\n get the NamedBlobFile and NamedBlobImage fields. This means that\n plone.namedfile can be used with ZODB versions that do not support BLOBs.\n This policy will probably be revisited for a 2.0 release.\n [optilude]\n\n1.0a1 - 2009-04-17\n------------------\n\n* Initial release\n\n\n.. _`#38`: https://github.com/plone/plone.namedfile/issues/38\n\n\nUsage\n=====\n\nThis demonstrates how to use the package.\n\nSchema fields\n-------------\n\nThe following schema fields can be used to describe file data. We'll only\ntest the BLOB versions of the fields if z3c.blobfile is installed::\n\n >>> from zope.interface import Interface\n >>> from plone.namedfile import field\n\n >>> class IFileContainer(Interface):\n ... simple = field.NamedFile(title=u\"Named file\")\n ... image = field.NamedImage(title=u\"Named image file\")\n ... blob = field.NamedBlobFile(title=u\"Named blob file\")\n ... blobimage = field.NamedBlobImage(title=u\"Named blob image file\")\n\nThese store data with the following types::\n\n >>> from zope.interface import implementer\n >>> from plone import namedfile\n\n\n >>> @implementer(IFileContainer)\n ... class FileContainer(object):\n ... __allow_access_to_unprotected_subobjects__ = 1\n ... def __init__(self):\n ... self.simple = namedfile.NamedFile()\n ... self.image = namedfile.NamedImage()\n ... self.blob = namedfile.NamedBlobFile()\n ... self.blobimage = namedfile.NamedBlobImage()\n ...\n ... def absolute_url(self):\n ... return \"http://foo/bar\"\n\n\nFile data and content type\n--------------------------\n\nLet's now show how to get and set file data.\n\nThe FileContainer class creates empty objects to start with::\n\n >>> container = FileContainer()\n\n >>> bytearray(container.simple.data)\n bytearray(b'')\n >>> container.simple.contentType\n ''\n >>> container.simple.filename is None\n True\n\n >>> len(container.image.data)\n 0\n >>> container.image.contentType\n ''\n >>> container.image.filename is None\n True\n\n >>> len(container.blob.data)\n 0\n >>> container.blob.contentType\n ''\n >>> container.blob.filename is None\n True\n >>> len(container.blobimage.data)\n 0\n >>> container.blobimage.contentType\n ''\n >>> container.blobimage.filename is None\n True\n\nLet's now set some actual data in these files. Notice how the constructor\nwill attempt to guess the filename from the file extension::\n\n >>> container.simple = namedfile.NamedFile('dummy test data', filename=u\"test.txt\")\n >>> bytearray(container.simple.data)\n bytearray(b'dummy test data')\n >>> container.simple.contentType\n 'text/plain'\n >>> print(container.simple.filename)\n test.txt\n\n >>> container.blob = namedfile.NamedBlobFile('dummy test data', filename=u\"test.txt\")\n >>> bytearray(container.blob.data)\n bytearray(b'dummy test data')\n >>> container.blob.contentType\n 'text/plain'\n >>> print(container.blob.filename)\n test.txt\n\nLet's also try to read a GIF, courtesy of the zope.app.file tests::\n\n >>> zptlogo = (\n ... b'GIF89a\\x10\\x00\\x10\\x00\\xd5\\x00\\x00\\xff\\xff\\xff\\xff\\xff\\xfe\\xfc\\xfd\\xfd'\n ... b'\\xfa\\xfb\\xfc\\xf7\\xf9\\xfa\\xf5\\xf8\\xf9\\xf3\\xf6\\xf8\\xf2\\xf5\\xf7\\xf0\\xf4\\xf6'\n ... b'\\xeb\\xf1\\xf3\\xe5\\xed\\xef\\xde\\xe8\\xeb\\xdc\\xe6\\xea\\xd9\\xe4\\xe8\\xd7\\xe2\\xe6'\n ... b'\\xd2\\xdf\\xe3\\xd0\\xdd\\xe3\\xcd\\xdc\\xe1\\xcb\\xda\\xdf\\xc9\\xd9\\xdf\\xc8\\xd8\\xdd'\n ... b'\\xc6\\xd7\\xdc\\xc4\\xd6\\xdc\\xc3\\xd4\\xda\\xc2\\xd3\\xd9\\xc1\\xd3\\xd9\\xc0\\xd2\\xd9'\n ... b'\\xbd\\xd1\\xd8\\xbd\\xd0\\xd7\\xbc\\xcf\\xd7\\xbb\\xcf\\xd6\\xbb\\xce\\xd5\\xb9\\xcd\\xd4'\n ... b'\\xb6\\xcc\\xd4\\xb6\\xcb\\xd3\\xb5\\xcb\\xd2\\xb4\\xca\\xd1\\xb2\\xc8\\xd0\\xb1\\xc7\\xd0'\n ... b'\\xb0\\xc7\\xcf\\xaf\\xc6\\xce\\xae\\xc4\\xce\\xad\\xc4\\xcd\\xab\\xc3\\xcc\\xa9\\xc2\\xcb'\n ... b'\\xa8\\xc1\\xca\\xa6\\xc0\\xc9\\xa4\\xbe\\xc8\\xa2\\xbd\\xc7\\xa0\\xbb\\xc5\\x9e\\xba\\xc4'\n ... b'\\x9b\\xbf\\xcc\\x98\\xb6\\xc1\\x8d\\xae\\xbaFgs\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n ... b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n ... b'\\x00,\\x00\\x00\\x00\\x00\\x10\\x00\\x10\\x00\\x00\\x06z@\\x80pH,\\x12k\\xc8$\\xd2f\\x04'\n ... b'\\xd4\\x84\\x01\\x01\\xe1\\xf0d\\x16\\x9f\\x80A\\x01\\x91\\xc0ZmL\\xb0\\xcd\\x00V\\xd4'\n ... b'\\xc4a\\x87z\\xed\\xb0-\\x1a\\xb3\\xb8\\x95\\xbdf8\\x1e\\x11\\xca,MoC$\\x15\\x18{'\n ... b'\\x006}m\\x13\\x16\\x1a\\x1f\\x83\\x85}6\\x17\\x1b $\\x83\\x00\\x86\\x19\\x1d!%)\\x8c'\n ... b'\\x866#\\'+.\\x8ca`\\x1c`(,/1\\x94B5\\x19\\x1e\"&*-024\\xacNq\\xba\\xbb\\xb8h\\xbeb'\n ... b'\\x00A\\x00;'\n ... )\n\n >>> container.image = namedfile.NamedImage(zptlogo, filename=u\"zpt.gif\")\n >>> container.image.data == zptlogo\n True\n >>> container.image.contentType\n 'image/gif'\n >>> print(container.image.filename)\n zpt.gif\n\n >>> container.blobimage = namedfile.NamedBlobImage(zptlogo, filename=u\"zpt.gif\")\n >>> container.blobimage.data == zptlogo\n True\n >>> container.blobimage.contentType\n 'image/gif'\n >>> print(container.blobimage.filename)\n zpt.gif\n\nNote that is possible for force the mimetype::\n\n >>> container.image = namedfile.NamedImage(zptlogo, contentType='image/foo', filename=u\"zpt.gif\")\n >>> container.image.data == zptlogo\n True\n >>> container.image.contentType\n 'image/foo'\n >>> print(container.image.filename)\n zpt.gif\n\n >>> container.blobimage = namedfile.NamedBlobImage(zptlogo, contentType='image/foo', filename=u\"zpt.gif\")\n >>> container.blobimage.data == zptlogo\n True\n >>> container.blobimage.contentType\n 'image/foo'\n >>> print(container.blobimage.filename)\n zpt.gif\n\nThe filename must be set to a unicode string, not a bytestring::\n\n >>> container.image.filename = b'foo'\n Traceback (most recent call last):\n ...\n zope.schema._bootstrapinterfaces.WrongType: ...\n\n\nRestricting media types\n-----------------------\n\nIt is possible to define accepted media types, just like with the \"accept\"\nattribute of HTML file inputs. You can pass a tuple of file extensions or media\ntype values::\n\n\n >>> class IFileContainerConstrained(Interface):\n ... file = field.NamedFile(title=u\"File\", accept=(\"text/plain\", \".pdf\"))\n\n >>> @implementer(IFileContainerConstrained)\n ... class FileContainerConstrained:\n ... __allow_access_to_unprotected_subobjects__ = 1\n ... def __init__(self):\n ... self.file = namedfile.NamedFile()\n\n >>> container_constrained = FileContainerConstrained()\n\n\nAdding valid file types and checking passes. Note, that the validation logic is\ncalled by the framework and does not need to be called manualle, like in this\ntest.\n::\n\n >>> container_constrained.file = namedfile.NamedFile(\n ... 'dummy test data',\n ... filename=u\"test.txt\"\n ... )\n >>> IFileContainerConstrained[\"file\"].validate(container_constrained.file)\n\n >>> container_constrained.file = namedfile.NamedFile(\n ... 'dummy test data',\n ... filename=u\"test.pdf\"\n ... )\n >>> IFileContainerConstrained[\"file\"].validate(container_constrained.file)\n\nAdding invalid file types and checking fails with a ValidationError::\n\n >>> container_constrained.file = namedfile.NamedFile(\n ... 'dummy test data',\n ... filename=u\"test.wav\"\n ... )\n >>> IFileContainerConstrained[\"file\"].validate(container_constrained.file)\n Traceback (most recent call last):\n ...\n plone.namedfile.field.InvalidFile: ('audio/x-wav', 'file')\n\n\nDownload view\n-------------\n\nThis package also comes with a view that can be used to download files. This\nwill set Content-Disposition to ensure the browser downloads the file rather\nthan displaying it. To use it, link to ../context-object/@@download/fieldname,\nwhere `fieldname` is the name of the attribute on the context-object where the\nnamed file is stored.\n\nWe will test this with a dummy request, faking traversal::\n\n >>> from plone.namedfile.browser import Download\n >>> from zope.publisher.browser import TestRequest\n\n >>> request = TestRequest()\n >>> download = Download(container, request).publishTraverse(request, 'simple')\n >>> bytearray(download())\n bytearray(b'dummy test data')\n >>> request.response.getHeader('Content-Length')\n '15'\n >>> request.response.getHeader('Content-Type')\n 'text/plain'\n >>> request.response.getHeader('Content-Disposition')\n \"attachment; filename*=UTF-8''test.txt\"\n >>> request.response.getHeader('Link')\n '<http://foo/bar/@@download/simple/test.txt>; rel=\"canonical\"'\n\n >>> request = TestRequest()\n >>> download = Download(container, request).publishTraverse(request, 'blob')\n >>> data = download()\n >>> bytearray(hasattr(data, 'read') and data.read() or data)\n bytearray(b'dummy test data')\n >>> request.response.getHeader('Content-Length')\n '15'\n >>> request.response.getHeader('Content-Type')\n 'text/plain'\n >>> request.response.getHeader('Content-Disposition')\n \"attachment; filename*=UTF-8''test.txt\"\n >>> request.response.getHeader('Link')\n '<http://foo/bar/@@download/blob/test.txt>; rel=\"canonical\"'\n\n >>> request = TestRequest()\n >>> download = Download(container, request).publishTraverse(request, 'image')\n >>> download() == zptlogo\n True\n\n >>> request.response.getHeader('Content-Length')\n '341'\n >>> request.response.getHeader('Content-Type')\n 'image/foo'\n >>> request.response.getHeader('Content-Disposition')\n \"attachment; filename*=UTF-8''zpt.gif\"\n >>> request.response.getHeader('Link')\n '<http://foo/bar/@@download/image/zpt.gif>; rel=\"canonical\"'\n\n >>> request = TestRequest()\n >>> download = Download(container, request).publishTraverse(request, 'blobimage')\n >>> data = download()\n >>> (hasattr(data, 'read') and data.read() or data) == zptlogo\n True\n >>> request.response.getHeader('Content-Length')\n '341'\n >>> request.response.getHeader('Content-Type')\n 'image/foo'\n >>> request.response.getHeader('Content-Disposition')\n \"attachment; filename*=UTF-8''zpt.gif\"\n >>> request.response.getHeader('Link')\n '<http://foo/bar/@@download/blobimage/zpt.gif>; rel=\"canonical\"'\n\nRange support\n-------------\n\nChecking for partial requests support::\n\n >>> request = TestRequest()\n >>> download = Download(container, request).publishTraverse(request, 'blobimage')\n >>> data = download()\n >>> request.response.getHeader('Content-Length')\n '341'\n >>> request.response.getHeader('Accept-Ranges')\n 'bytes'\n\nRequest a specific range::\n\n >>> request = TestRequest(environ={'HTTP_RANGE': 'bytes=0-99'})\n >>> download = Download(container, request).publishTraverse(request, 'blobimage')\n >>> data = download()\n >>> request.response.getStatus()\n 206\n >>> len(hasattr(data, 'read') and data.read() or data)\n 100\n\nThe Content-Length header now indicates the size of the requested range (and not the full size of the image).\nThe Content-Range response header indicates where in the full resource this partial message belongs.::\n\n >>> request.response.getHeader('Content-Length')\n '100'\n >>> request.response.getHeader('Content-Range')\n 'bytes 0-99/341'\n\n\nDisplay-file view\n-----------------\n\nThis package also comes with a view that can be used to display files in the\nbrowser. To use it, link to ../context-object/@@display-file/fieldname, where\n`fieldname` is the name of the attribute on the context-object where the named\nfile is stored.\n\nWe will test this with a dummy request, faking traversal::\n\n >>> from plone.namedfile.browser import DisplayFile\n >>> from zope.publisher.browser import TestRequest\n\n >>> request = TestRequest()\n >>> display_file = DisplayFile(container, request).publishTraverse(request, 'simple')\n >>> bytearray(display_file())\n bytearray(b'dummy test data')\n >>> request.response.getHeader('Content-Length')\n '15'\n >>> request.response.getHeader('Content-Type')\n 'text/plain'\n >>> request.response.getHeader('Content-Disposition')\n\n >>> request = TestRequest()\n >>> display_file = DisplayFile(container, request).publishTraverse(request, 'blob')\n >>> data = display_file()\n >>> bytearray(hasattr(data, 'read') and data.read() or data)\n bytearray(b'dummy test data')\n >>> request.response.getHeader('Content-Length')\n '15'\n >>> request.response.getHeader('Content-Type')\n 'text/plain'\n >>> request.response.getHeader('Content-Disposition')\n\n >>> request = TestRequest()\n >>> display_file = DisplayFile(container, request).publishTraverse(request, 'image')\n >>> display_file() == zptlogo\n True\n\n >>> request.response.getHeader('Content-Length')\n '341'\n >>> request.response.getHeader('Content-Type')\n 'image/foo'\n\nSince the Content-Type is unknown, we do not trust it, and refuse to display inline.\nWe download instead.\n\n >>> request.response.getHeader('Content-Disposition')\n \"attachment; filename*=UTF-8''zpt.gif\"\n\n >>> request = TestRequest()\n >>> display_file = DisplayFile(container, request).publishTraverse(request, 'blobimage')\n >>> data = display_file()\n >>> (hasattr(data, 'read') and data.read() or data) == zptlogo\n True\n >>> request.response.getHeader('Content-Length')\n '341'\n >>> request.response.getHeader('Content-Type')\n 'image/foo'\n >>> request.response.getHeader('Content-Disposition')\n \"attachment; filename*=UTF-8''zpt.gif\"\n\n\nSpecifying the primary field\n----------------------------\n\nTo use the @@download view without specifying the field in the URL, the\nprimary field information must be registered with an adapter. (Frameworks such\nas plone.dexterity may already have done this for you.)::\n\n >>> from plone.rfc822.interfaces import IPrimaryFieldInfo\n >>> from zope.component import adapter\n\n >>> @implementer(IPrimaryFieldInfo)\n ... @adapter(IFileContainer)\n ... class FieldContainerPrimaryFieldInfo(object):\n ... fieldname = 'simple'\n ... field = IFileContainer['simple']\n ... def __init__(self, context):\n ... self.value = context.simple\n\n >>> from zope.component import getSiteManager\n >>> components = getSiteManager()\n >>> components.registerAdapter(FieldContainerPrimaryFieldInfo)\n\nWe will test this with a dummy request, faking traversal::\n\n >>> request = TestRequest()\n >>> download = Download(container, request)\n >>> bytearray(download())\n bytearray(b'dummy test data')\n >>> request.response.getHeader('Content-Length')\n '15'\n >>> request.response.getHeader('Content-Type')\n 'text/plain'\n >>> request.response.getHeader('Content-Disposition')\n \"attachment; filename*=UTF-8''test.txt\"\n\n\nImage scales\n------------\n\nThis package can handle the creation, storage, and retrieval of arbitrarily\nsized scaled versions of images stored in NamedImage or NamedBlobImage fields.\n\nImage scales are accessed via an @@images view that is available for any item\nproviding ``plone.namedfile.interfaces.IImageScaleTraversable``. There are\nseveral ways that you may reference scales from page templates.\n\n1. for full control you may do the tag generation explicitly::\n\n <img tal:define=\"images context/@@images;\n thumbnail python: images.scale('image', width=64, height=64);\"\n tal:condition=\"thumbnail\"\n tal:attributes=\"src thumbnail/url;\n width thumbnail/width;\n height thumbnail/height\" />\n\n This would create an up to 64 by 64 pixel scaled down version of the image\n stored in the \"image\" field. It also allows for passing in additional\n parameters supported by the ``scaleImage`` function from ``plone.scale``,\n e.g. ``mode`` or ``quality``.\n\n .. _`plone.scale`: https://pypi.org/project/plone.scale/\n\n2. for automatic tag generation with extra parameters you would use::\n\n <img tal:define=\"images context/@@images\"\n tal:replace=\"structure python: images.tag('image',\n width=1200, height=800, mode='contain')\" />\n\n3. It is possible to access scales via predefined named scale sizes, rather\n than hardcoding the dimensions every time you access a scale. The scale\n sizes are found via calling a utility providing\n ``plone.namedfile.interfaces.IAvailableSizes``, which should return a dict of\n scale name => (width, height). A scale called 'mini' could then be accessed\n like this::\n\n <img tal:define=\"images context/@@images\"\n tal:replace=\"structure python: images.tag('image', scale='mini')\" />\n\n This would use the predefined scale size \"mini\" to determine the desired\n image dimensions, but still allow to pass in extra parameters.\n\n4. a convenience short-cut for option 3 can be used::\n\n <img tal:replace=\"structure context/@@images/image/mini\" />\n\n5. and lastly, the short-cut can also be used to render the unscaled image::\n\n <img tal:replace=\"structure context/@@images/image\" />\n",
"bugtrack_url": null,
"license": "BSD",
"summary": "File types and fields for images, files and blob files with filenames",
"version": "6.4.0",
"project_urls": {
"Homepage": "https://pypi.org/project/plone.namedfile"
},
"split_keywords": [
"plone",
"named",
"file",
"image",
"blob"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "c70a3568ac7dfa72442d5b8c8f6774ead5ce32eaccb1eb05ce16be2ba22d6ee7",
"md5": "962cc19fdd158e9f885bb114f9b4a522",
"sha256": "435991c090c831a3a38b561f5e66d88de7b117b6d0ae2c2b8e9341a108328663"
},
"downloads": -1,
"filename": "plone.namedfile-6.4.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "962cc19fdd158e9f885bb114f9b4a522",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.7",
"size": 326126,
"upload_time": "2024-11-25T15:11:59",
"upload_time_iso_8601": "2024-11-25T15:11:59.708824Z",
"url": "https://files.pythonhosted.org/packages/c7/0a/3568ac7dfa72442d5b8c8f6774ead5ce32eaccb1eb05ce16be2ba22d6ee7/plone.namedfile-6.4.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "bd861c1e95231dd02d8757f5d7da6aa4f3530dc59831209e5d7b14f91509eb03",
"md5": "6c80dbe24852ac28bf3e5896e99ee76f",
"sha256": "060d75ea063f8ed70e5889e2eae1b1cd64d51647cd5f19167340b9c766fad5cc"
},
"downloads": -1,
"filename": "plone_namedfile-6.4.0.tar.gz",
"has_sig": false,
"md5_digest": "6c80dbe24852ac28bf3e5896e99ee76f",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.7",
"size": 329666,
"upload_time": "2024-11-25T15:12:04",
"upload_time_iso_8601": "2024-11-25T15:12:04.087590Z",
"url": "https://files.pythonhosted.org/packages/bd/86/1c1e95231dd02d8757f5d7da6aa4f3530dc59831209e5d7b14f91509eb03/plone_namedfile-6.4.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-11-25 15:12:04",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "plone.namedfile"
}