diff --git a/docs/HISTORY.txt b/docs/HISTORY.txt index 9bd93ee4..3fb925b7 100644 --- a/docs/HISTORY.txt +++ b/docs/HISTORY.txt @@ -7,6 +7,10 @@ Changelog * 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 ------------------ diff --git a/plone/namedfile/browser.py b/plone/namedfile/browser.py index f10e1246..9c3e40bb 100644 --- a/plone/namedfile/browser.py +++ b/plone/namedfile/browser.py @@ -40,6 +40,14 @@ def publishTraverse(self, request, name): return self def __call__(self): + file = self._getFile() + + if not self.filename: + self.filename = getattr(file, 'filename', self.fieldname) + set_headers(file, self.request.response, filename=self.filename) + return stream_data(file) + + def _getFile(self): if not self.fieldname: info = IPrimaryFieldInfo(self.context, None) if info is None: @@ -53,9 +61,15 @@ def __call__(self): if file is None: raise NotFound(self, self.fieldname, self.request) - if not self.filename: - self.filename = getattr(file, 'filename', self.fieldname) - - set_headers(file, self.request.response, filename=self.filename) + return file +class DisplayFile(Download): + """Display a file, via ../context/@@display-file/fieldname/filename + + Same as Download, however in this case we don't set the filename so the + browser can decide to display the file instead. + """ + def __call__(self): + file = self._getFile() + set_headers(file, self.request.response) return stream_data(file) diff --git a/plone/namedfile/configure.zcml b/plone/namedfile/configure.zcml index fd299d31..4798e387 100644 --- a/plone/namedfile/configure.zcml +++ b/plone/namedfile/configure.zcml @@ -11,6 +11,13 @@ permission="zope2.View" /> + + diff --git a/plone/namedfile/usage.txt b/plone/namedfile/usage.txt index 8ebf39dc..22d50b51 100644 --- a/plone/namedfile/usage.txt +++ b/plone/namedfile/usage.txt @@ -191,10 +191,11 @@ Note that is possible for force the mimetype: Download view ------------- -This package also comes with a view that can be used to download files. 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. +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. @@ -266,6 +267,82 @@ We will test this with a dummy request, faking traversal. 'image/foo' 'attachment; filename="zpt.gif"' +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') + >>> display_file() + 'dummy test data' + >>> request.response.getHeader('Content-Length') + '15' + >>> request.response.getHeader('Content-Type') + 'text/plain' + >>> request.response.getHeader('Content-Disposition') + + >>> if HAVE_BLOBS: + ... request = TestRequest() + ... display_file = DisplayFile(container, request).publishTraverse(request, 'blob') + ... data = display_file() + ... print repr(isinstance(data, file)) + ... print repr(data.read()) + ... print repr(request.response.getHeader('Content-Length')) + ... print repr(request.response.getHeader('Content-Type')) + ... print repr(request.response.getHeader('Content-Disposition')) + ... else: + ... print repr(True) + ... print repr('dummy test data') + ... print repr('15') + ... print repr('text/plain') + ... print repr(None) + True + 'dummy test data' + '15' + 'text/plain' + None + + >>> 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' + >>> request.response.getHeader('Content-Disposition') + + >>> request = TestRequest() + >>> if HAVE_BLOBS: + ... display_file = DisplayFile(container, request).publishTraverse(request, 'blobimage') + ... data = display_file() + ... print repr(isinstance(data, file)) + ... print repr(data.read() == zptlogo) + ... print repr(request.response.getHeader('Content-Length')) + ... print repr(request.response.getHeader('Content-Type')) + ... print repr(request.response.getHeader('Content-Disposition')) + ... else: + ... print repr(True) + ... print repr(True) + ... print repr('341') + ... print repr('image/foo') + ... print repr(None) + True + True + '341' + 'image/foo' + None + Specifying the primary field ----------------------------