misc: include image thumbnails in formdata view page (#20045)

This commit is contained in:
Frédéric Péters 2017-11-11 13:57:23 +01:00
parent b89e466a72
commit 61d37fa4b0
6 changed files with 87 additions and 29 deletions

View File

@ -1853,11 +1853,22 @@ def test_form_file_field_image_submit(pub):
assert '<img alt="" src="tempfile?' in resp.body
tempfile_id = resp.body[resp.body.index('tempfile?'):].split('&', 1)[0].split('=')[1]
resp = app.get('/test/tempfile?t=%s' % tempfile_id)
assert resp.body == image_content
resp_tempfile = app.get('/test/tempfile?t=%s' % tempfile_id)
assert resp_tempfile.body == image_content
if Image: # check thumbnailing
resp = app.get('/test/tempfile?t=%s&thumbnail=1' % tempfile_id)
if Image:
# check thumbnailing of image in validation page
resp_thumbnail = app.get('/test/tempfile?t=%s&thumbnail=1' % tempfile_id)
assert resp_thumbnail.content_type == 'image/png'
resp = resp.form.submit('submit').follow()
assert '<img ' in resp.body
assert 'download?f=0&thumbnail=1' in resp.body
resp = resp.goto('download?f=0&thumbnail=1')
assert '/thumbnail/' in resp.location
resp = resp.follow()
if Image:
# check thumbnailing of image in submitted form
assert resp.content_type == 'image/png'
# check a fake image is not sent back

View File

@ -803,9 +803,18 @@ class FileField(WidgetField):
return upload
raise ValueError('invalid data for file type (%r)' % value)
def get_view_value(self, value):
return htmltext('<a download="%s" href="[download]?f=%s">%s</a>') % (
value.base_filename, self.id, value)
def get_view_short_value(self, value, max_len=30):
return self.get_view_value(value, include_image_thumbnail=False)
def get_view_value(self, value, include_image_thumbnail=True):
t = TemplateIO(html=True)
t += htmltext('<div class="file-field">')
t += htmltext('<a download="%s" href="[download]?f=%s">') % (value.base_filename, self.id)
if value.content_type and value.content_type.startswith('image/'):
t += htmltext('<img alt="" src="[download]?f=%s&thumbnail=1"/>') % self.id
t += htmltext('<span>%s</span>') % value
t += htmltext('</a></div>')
return t.getvalue()
def get_csv_value(self, value, **kwargs):
return [str(value) if value else '']

View File

@ -28,6 +28,7 @@ from wcs.workflows import EditableWorkflowStatusItem
from django.template import RequestContext
from qommon import _
from qommon import misc
from qommon import template
from qommon import get_logger
from qommon.form import *
@ -40,6 +41,7 @@ from qommon import errors
class FileDirectory(Directory):
_q_exports = []
_lookup_methods = ['lookup_file_field']
thumbnails = False
def __init__(self, formdata, reference):
self.formdata = formdata
@ -50,6 +52,9 @@ class FileDirectory(Directory):
return self.formdata.data[self.reference]
def _q_lookup(self, component):
if component == 'thumbnail':
self.thumbnails = True
return self
upload = None
for lookup_method_name in self._lookup_methods:
lookup_method = getattr(self, lookup_method_name)
@ -78,8 +83,17 @@ class FileDirectory(Directory):
response.set_header(
'content-disposition', 'attachment; filename="%s"' % file.base_filename)
return file.get_file_pointer().read()
fp = file.get_file_pointer()
if self.thumbnails and file.content_type.startswith('image/'):
try:
thumbnail = misc.get_thumbnail(fp)
except misc.ThumbnailError:
pass
else:
response.set_content_type('image/png')
return thumbnail
return fp.read()
class FilesDirectory(Directory):
@ -557,10 +571,12 @@ class FormStatusPage(Directory):
if not hasattr(file, 'content_type'):
raise errors.TraversalError()
if hasattr(file, 'base_filename') and file.base_filename:
return redirect('files/%s/%s' % (fn, file.base_filename))
else:
return redirect('files/%s/' % fn)
file_url = 'files/%s/' % fn
if get_request().form.get('thumbnail') == '1':
file_url += 'thumbnail/'
if getattr(file, 'base_filename'):
file_url += file.base_filename
return redirect(file_url)
def display_file_field(self, form_url, field, value):
r = TemplateIO(html=True)

View File

@ -19,11 +19,6 @@ import time
from StringIO import StringIO
import urllib2
try:
from PIL import Image
except ImportError:
Image = None
try:
import qrcode
except ImportError:
@ -1085,19 +1080,15 @@ class FormPage(Directory):
response.set_charset(tempfile['charset'])
file_pointer = get_session().get_tempfile_content(t).get_file_pointer()
if Image is not None and get_request().form.get('thumbnail') == '1':
if get_request().form.get('thumbnail') == '1':
try:
image = Image.open(file_pointer)
image.thumbnail((500, 300))
image_thumb_fp = StringIO()
image.save(image_thumb_fp, "PNG")
except IOError:
# too bad we couldn't load the image, return the whole file :/
return file_pointer.read()
response.set_content_type('image/png')
return image_thumb_fp.getvalue()
else:
return file_pointer.read()
thumbnail = misc.get_thumbnail(file_pointer)
except misc.ThumbnailError:
pass
else:
response.set_content_type('image/png')
return thumbnail
return file_pointer.read()
def validating(self, data):
self.html_top(self.formdef.name)

View File

@ -31,6 +31,11 @@ import tempfile
import unicodedata
import hashlib
try:
from PIL import Image
except ImportError:
Image = None
from django.utils import datetime_safe
from quixote import get_publisher, get_response, get_request
@ -45,6 +50,10 @@ from urllib2 import urlparse
from cStringIO import StringIO
class ThumbnailError(Exception):
pass
def get_abs_path(s):
if not s:
return s
@ -530,3 +539,19 @@ def file_digest(content, chunk_size=100000):
for chunk in iter(read_chunk, ''):
digest.update(chunk)
return digest.hexdigest()
def get_thumbnail(fp):
if Image is None:
raise ThumbnailError()
try:
image = Image.open(fp)
image.thumbnail((500, 300))
image_thumb_fp = StringIO()
image.save(image_thumb_fp, "PNG")
except IOError:
# failed to create thumbnail; restore file pointer state and raise
# exception.
fp.rewind()
raise ThumbnailError()
return image_thumb_fp.getvalue()

View File

@ -608,3 +608,9 @@ div.halfwidth input,
div.fullwidth input {
width: 100%;
}
div.file-field img {
max-width: 100%;
max-height: 25vh;
display: block;
}