backoffice: add button to download all files in a zip (#44395)
This commit is contained in:
parent
aca20bf41a
commit
c81b3b1fb2
|
@ -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')
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue