backoffice: add button to download all files in a zip (#44395)

This commit is contained in:
Frédéric Péters 2020-06-30 11:54:39 +02:00
parent aca20bf41a
commit c81b3b1fb2
5 changed files with 107 additions and 8 deletions

View File

@ -304,6 +304,17 @@ def test_forms_edit(pub):
assert_option_display(resp, 'Limit to one form', 'Enabled')
assert FormDef.get(1).only_allow_one is True
# Misc management
assert_option_display(resp, 'Management', 'Default')
resp = resp.click('Management', href='options/management')
assert resp.forms[0]['include_download_all_button'].checked is False
resp.forms[0]['include_download_all_button'].checked = True
resp = resp.forms[0].submit()
assert resp.location == 'http://example.net/backoffice/forms/1/'
resp = resp.follow()
assert_option_display(resp, 'Management', 'Custom')
assert FormDef.get(1).include_download_all_button is True
# Tracking code
assert_option_display(resp, 'Tracking Code', 'Disabled')
resp = resp.click('Tracking Code')

View File

@ -1913,6 +1913,40 @@ def test_backoffice_submission_context(pub):
assert 'by %s' % user.get_display_name() in resp.text
def test_backoffice_download_as_zip(pub):
create_user(pub)
create_environment(pub)
formdef = FormDef.get_by_urlname('form-title')
formdef.fields.append(fields.FileField(id='4', label='file1 field', type='file'))
formdef.fields.append(fields.FileField(id='5', label='file2 field', type='file2'))
formdef.store()
number31 = [x for x in formdef.data_class().select() if x.data['1'] == 'FOO BAR 30'][0]
number31.data['4'] = PicklableUpload('/foo/bar', content_type='text/plain')
number31.data['4'].receive([b'hello world'])
number31.data['5'] = PicklableUpload('/foo/bar', content_type='text/plain')
number31.data['5'].receive([b'hello world2'])
number31.store()
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
assert 'Download all files as .zip' not in resp
formdef.include_download_all_button = True
formdef.store()
resp = app.get('/backoffice/management/form-title/%s/' % number31.id)
resp = resp.click('Download all files as .zip')
zip_content = BytesIO(resp.body)
zipf = zipfile.ZipFile(zip_content, 'a')
filelist = zipf.namelist()
assert set(filelist) == {'1_bar', '2_bar'}
for zipinfo in zipf.infolist():
content = zipf.read(zipinfo)
if zipinfo.filename == '1_bar':
assert content == b'hello world'
elif zipinfo.filename == '2_bar':
assert content == b'hello world2'
else:
assert False # unknown zip part
def test_backoffice_sidebar_user_template(pub):
user = create_user(pub)
create_environment(pub)

View File

@ -155,7 +155,7 @@ class FieldsDirectory(FieldsDirectory):
class OptionsDirectory(Directory):
_q_exports = ['confirmation', 'only_allow_one',
'always_advertise', 'tracking_code', 'online_status', 'captcha',
'description', 'keywords', 'category', ('360_view', 'p_360_view'),
'description', 'keywords', 'category', 'management',
'geolocations', 'appearance', 'templates']
def __init__(self, formdef):
@ -196,12 +196,15 @@ class OptionsDirectory(Directory):
value=self.formdef.has_captcha)
return self.handle(form, _('CAPTCHA'))
def p_360_view(self):
def management(self):
form = Form(enctype='multipart/form-data')
form.add(CheckboxWidget, 'include_download_all_button',
title=_('Include button to download all files'),
value=self.formdef.include_download_all_button)
form.add(CheckboxWidget, 'skip_from_360_view',
title=_('Skip from per user view'),
value=self.formdef.skip_from_360_view)
return self.handle(form, _('Skip from per user view'))
return self.handle(form, _('Management'))
def online_status(self):
form = Form(enctype='multipart/form-data')
@ -286,6 +289,7 @@ class OptionsDirectory(Directory):
'publication_date', 'expiration_date', 'has_captcha',
'description', 'keywords', 'category_id',
'skip_from_360_view', 'geoloc_label', 'appearance_keywords',
'include_download_all_button',
'digest_template']
for attr in attrs:
widget = form.get_widget(attr)
@ -515,10 +519,10 @@ class FormDefPage(Directory):
self.formdef.always_advertise and
C_('display to unlogged|Enabled') or C_('display to unlogged|Disabled'))
r += add_option_line('options/360_view',
_('Skip from per user view'),
self.formdef.skip_from_360_view and
C_('skip from per user view|Enabled') or C_('skip from per user view|Disabled'))
r += add_option_line('options/management',
_('Management'),
_('Custom') if (self.formdef.skip_from_360_view or
self.formdef.include_download_all_button) else _('Default'))
r += add_option_line('options/tracking_code',
_('Tracking Code'),

View File

@ -21,6 +21,7 @@ import re
import time
import types
import vobject
import zipfile
try:
import xlwt
@ -51,6 +52,7 @@ from ..qommon import sms
from ..qommon import errors
from ..qommon import ods
from ..qommon.form import *
from ..qommon.form import PicklableUpload
from ..qommon.storage import (Equal, NotEqual, LessOrEqual, GreaterOrEqual, Or,
Intersects, ILike, FtsMatch, Contains, Null, NotNull)
from ..qommon.template import Template
@ -2515,6 +2517,7 @@ class FormPage(Directory):
class FormBackOfficeStatusPage(FormStatusPage):
_q_exports_orig = ['', 'download', 'json', 'action', 'live',
'inspect', ('inspect-tool', 'inspect_tool'),
('download-as-zip', 'download_as_zip'),
('user-pending-forms', 'user_pending_forms')]
form_page_class = FormFillPage
@ -2641,6 +2644,24 @@ class FormBackOfficeStatusPage(FormStatusPage):
'date': formdata.anonymised.strftime(misc.date_format())}
r += htmltext('</div>')
if formdata.formdef.include_download_all_button:
has_attached_files = False
for value in (formdata.data or {}).values():
if isinstance(value, PicklableUpload):
has_attached_files = True
if isinstance(value, dict) and isinstance(value.get('data'), list):
# block fields
for subvalue in value.get('data'):
for subvalue_elem in subvalue.values():
if isinstance(subvalue_elem, PicklableUpload):
has_attached_files = True
break
if has_attached_files:
break
if has_attached_files:
r += htmltext('<p><a class="button" href="download-as-zip">%s</a></p>') % _('Download all files as .zip')
r += htmltext('</div>')
if formdata.submission_context or formdata.submission_channel:
@ -2717,6 +2738,34 @@ class FormBackOfficeStatusPage(FormStatusPage):
return r.getvalue()
def download_as_zip(self):
formdata = self.filled
zip_content = BytesIO()
zip_file = zipfile.ZipFile(zip_content, 'w')
counter = {'value': 0}
def add_zip_file(upload):
counter['value'] += 1
filename = '%s_%s' % (counter['value'], upload.base_filename)
zip_file.writestr(filename, upload.get_content())
for value in formdata.data.values():
if isinstance(value, PicklableUpload):
add_zip_file(value)
if isinstance(value, dict) and isinstance(value.get('data'), list):
for subvalue in value.get('data'):
for subvalue_elem in subvalue.values():
if isinstance(subvalue_elem, PicklableUpload):
add_zip_file(subvalue_elem)
zip_file.close()
response = get_response()
response.set_content_type('application/zip')
response.set_header('content-disposition',
'attachment; filename=files-%s.zip' % formdata.get_display_id())
return zip_content.getvalue()
def get_user_pending_forms(self):
from wcs import sql
formdata = self.filled

View File

@ -111,6 +111,7 @@ class FormDef(StorableObject):
expiration_date = None
has_captcha = False
skip_from_360_view = False
include_download_all_button = False
appearance_keywords = None
digest_template = None
@ -134,7 +135,7 @@ class FormDef(StorableObject):
'digest_template']
BOOLEAN_ATTRIBUTES = ['discussion', 'detailed_emails', 'disabled',
'only_allow_one', 'enable_tracking_codes', 'confirmation',
'always_advertise',
'always_advertise', 'include_download_all_button',
'has_captcha', 'skip_from_360_view']
def __init__(self, *args, **kwargs):