settings: add a file types section (#6134)
This commit is contained in:
parent
78c0dad11f
commit
476aa415d4
|
@ -48,6 +48,9 @@ def create_superuser():
|
|||
account1.user_id = user1.id
|
||||
account1.store()
|
||||
|
||||
pub.cfg['identification'] = {'methods': ['password']}
|
||||
pub.write_cfg()
|
||||
|
||||
def create_role():
|
||||
Role.wipe()
|
||||
role = Role(name='foobar')
|
||||
|
@ -1362,3 +1365,68 @@ def test_settings_idp():
|
|||
resp = resp.click(href='http-sso.example.net-saml2-metadata/delete')
|
||||
resp = resp.forms[0].submit() # confirm delete
|
||||
assert len(pub.cfg['idp']) == 0
|
||||
|
||||
def test_settings_filetypes():
|
||||
create_superuser()
|
||||
app = login(get_app(pub))
|
||||
|
||||
resp = app.get('/admin/settings/filetypes/')
|
||||
assert 'There are no file type defined at the moment.' in resp.body
|
||||
resp.forms[0]['label'] = 'Text files'
|
||||
resp.forms[0]['mimetypes'] = '.odt'
|
||||
resp = resp.forms[0].submit('submit')
|
||||
assert resp.location == 'http://example.net/admin/settings/filetypes/'
|
||||
resp = resp.follow()
|
||||
assert pub.cfg['filetypes'][1]['label'] == 'Text files'
|
||||
|
||||
resp = resp.click('Text files')
|
||||
assert resp.forms[0]['mimetypes'].value == 'application/vnd.oasis.opendocument.text'
|
||||
resp.forms[0]['mimetypes'] = 'application/vnd.oasis.opendocument.text, .doc, .docx, .pdf'
|
||||
resp = resp.forms[0].submit('submit')
|
||||
assert resp.location == 'http://example.net/admin/settings/filetypes/'
|
||||
resp = resp.follow()
|
||||
assert 'application/msword (.doc)' in resp.body
|
||||
assert 'application/pdf' in pub.cfg['filetypes'][1]['mimetypes']
|
||||
|
||||
resp.forms[0]['label'] = 'HTML files'
|
||||
resp.forms[0]['mimetypes'] = '.html'
|
||||
resp = resp.forms[0].submit('submit')
|
||||
resp = resp.follow()
|
||||
resp = resp.click('HTML files') # go to form
|
||||
resp = resp.forms[0].submit('cancel') # and cancel
|
||||
assert resp.location == 'http://example.net/admin/settings/filetypes/'
|
||||
resp = resp.follow()
|
||||
assert 'HTML files' in resp.body
|
||||
|
||||
resp = resp.click('HTML files') # go to form
|
||||
resp = resp.forms[0].submit('delete') # and delete
|
||||
assert resp.location == 'http://example.net/admin/settings/filetypes/'
|
||||
resp = resp.follow()
|
||||
assert 'HTML files' not in resp.body
|
||||
|
||||
def test_settings_filetypes_update():
|
||||
create_superuser()
|
||||
app = login(get_app(pub))
|
||||
|
||||
pub.cfg['filetypes'] = {
|
||||
1: {'mimetypes': ['application/pdf', 'application/msword'],
|
||||
'label': 'Text files'}
|
||||
}
|
||||
pub.write_cfg()
|
||||
resp = app.get('/admin/settings/filetypes/')
|
||||
assert 'Text files' in resp.body
|
||||
|
||||
FormDef.wipe()
|
||||
formdef = FormDef()
|
||||
formdef.name = 'form title'
|
||||
formdef.fields = [fields.FileField(
|
||||
id='1', label='1st field', type='file',
|
||||
file_type=['application/pdf,application/msword'])]
|
||||
formdef.store()
|
||||
assert FormDef.get(formdef.id).fields[0].file_type == ['application/pdf,application/msword']
|
||||
|
||||
resp = resp.click('Text files')
|
||||
resp.forms[0]['mimetypes'] = 'application/vnd.oasis.opendocument.text, .doc, .docx, .pdf'
|
||||
resp = resp.forms[0].submit('submit')
|
||||
assert 'application/pdf' in pub.cfg['filetypes'][1]['mimetypes']
|
||||
assert FormDef.get(formdef.id).fields[0].file_type == ['application/vnd.oasis.opendocument.text,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/pdf']
|
||||
|
|
|
@ -2,6 +2,7 @@ import pytest
|
|||
|
||||
from wcs.qommon.form import FileSizeWidget
|
||||
from wcs.qommon.humantime import humanduration2seconds, seconds2humanduration
|
||||
from wcs.admin.settings import FileTypesDirectory
|
||||
|
||||
def test_parse_file_size():
|
||||
assert FileSizeWidget.parse_file_size('17') == 17
|
||||
|
@ -24,3 +25,23 @@ def test_parse_invalid_file_size():
|
|||
def test_humantime():
|
||||
for x in range(3, 100000, 13):
|
||||
assert humanduration2seconds(seconds2humanduration(x)) == x
|
||||
|
||||
def test_parse_mimetypes():
|
||||
assert FileTypesDirectory.parse_mimetypes('application/pdf') == ['application/pdf']
|
||||
assert FileTypesDirectory.parse_mimetypes('.pdf') == ['application/pdf']
|
||||
assert FileTypesDirectory.parse_mimetypes('.pdf, .odt') == [
|
||||
'application/pdf', 'application/vnd.oasis.opendocument.text']
|
||||
|
||||
def test_format_mimetypes():
|
||||
assert FileTypesDirectory.format_mimetypes(['application/pdf']) == \
|
||||
'application/pdf (.pdf)'
|
||||
assert FileTypesDirectory.format_mimetypes(['application/pdf', 'text/rtf']) == \
|
||||
'application/pdf (.pdf), text/rtf'
|
||||
assert FileTypesDirectory.format_mimetypes(['application/pdf', 'application/msword']) == \
|
||||
'application/pdf (.pdf), application/msword (.doc)'
|
||||
assert FileTypesDirectory.format_mimetypes(['application/pdf',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'application/msword']) == \
|
||||
'application/pdf (.pdf), '\
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document (.docx)'\
|
||||
'...'
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
import copy
|
||||
import cStringIO
|
||||
import mimetypes
|
||||
import random
|
||||
import os
|
||||
import urllib2
|
||||
|
@ -209,6 +210,131 @@ class UsersDirectory(Directory):
|
|||
return Directory._q_traverse(self, path)
|
||||
|
||||
|
||||
class FileTypesDirectory(Directory):
|
||||
_q_exports = ['', 'add']
|
||||
|
||||
def get_form(self, filetype={}):
|
||||
form = Form(enctype='multipart/form-data')
|
||||
form.add(StringWidget, 'label', title=_('Label'), required=True,
|
||||
value=filetype.get('label'))
|
||||
form.add(StringWidget, 'mimetypes', title=_('Mime types'),
|
||||
required=True, size=70,
|
||||
value=', '.join(filetype.get('mimetypes', [])))
|
||||
form.add_submit('submit', _('Save'))
|
||||
return form
|
||||
|
||||
@classmethod
|
||||
def parse_mimetypes(cls, value):
|
||||
def ensure_mimetype(x):
|
||||
x = x.strip()
|
||||
if x.startswith('.'):
|
||||
mime_type, encoding = mimetypes.guess_type('foobar' + x)
|
||||
if mime_type:
|
||||
return mime_type
|
||||
return x
|
||||
return [ensure_mimetype(x) for x in value.split(',')]
|
||||
|
||||
@classmethod
|
||||
def format_mimetypes(cls, types):
|
||||
if not types:
|
||||
return ''
|
||||
l = []
|
||||
ellipsis = '...'
|
||||
for mimetype in types:
|
||||
if sum([len(x) for x in l]) > 80:
|
||||
# string got too long already, stop this now and we'll get an
|
||||
# ellipsis
|
||||
break
|
||||
ext = mimetypes.guess_extension(mimetype)
|
||||
if ext:
|
||||
l.append('%s (%s)' % (mimetype, ext))
|
||||
else:
|
||||
l.append(mimetype)
|
||||
else:
|
||||
# we got to the end of the list, we won't need an ellipsis
|
||||
ellipsis = ''
|
||||
return ', '.join(l) + ellipsis
|
||||
|
||||
def _q_index(self):
|
||||
html_top('settings', title=_('File Types'))
|
||||
filetypes_cfg = get_cfg('filetypes', {})
|
||||
|
||||
form = self.get_form()
|
||||
if form.get_submit() == 'submit':
|
||||
if filetypes_cfg:
|
||||
new_filetype_id = max(filetypes_cfg.keys()) + 1
|
||||
else:
|
||||
new_filetype_id = 1
|
||||
new_filetype = {
|
||||
'label': form.get_widget('label').parse(),
|
||||
'mimetypes': self.parse_mimetypes(form.get_widget('mimetypes').parse())
|
||||
}
|
||||
filetypes_cfg[new_filetype_id] = new_filetype
|
||||
get_publisher().cfg['filetypes'] = filetypes_cfg
|
||||
get_publisher().write_cfg()
|
||||
return redirect('.')
|
||||
|
||||
r = TemplateIO(html=True)
|
||||
r += htmltext('<h2>%s</h2>') % _('File Types')
|
||||
if filetypes_cfg:
|
||||
r += htmltext('<ul class="biglist filetypes">')
|
||||
for filetype_id, filetype in filetypes_cfg.items():
|
||||
r += htmltext('<li>')
|
||||
r += htmltext(' <strong class="label"><a href="%s">%s</a></strong>') % (
|
||||
filetype_id, filetype.get('label'))
|
||||
r += htmltext(' <p class="details">%s</p>') % \
|
||||
self.format_mimetypes(filetype.get('mimetypes'))
|
||||
r += htmltext('</li>')
|
||||
r += htmltext('</ul>')
|
||||
else:
|
||||
r += htmltext('<div class="infonotice"><p>')
|
||||
r += _('There are no file type defined at the moment.')
|
||||
r += htmltext('</p></div>')
|
||||
|
||||
r += htmltext('<h3>%s</h3>') % _('New file type')
|
||||
r += form.render()
|
||||
|
||||
return r.getvalue()
|
||||
|
||||
def _q_traverse(self, path):
|
||||
get_response().breadcrumb.append(('filetypes/', _('File Types')))
|
||||
return Directory._q_traverse(self, path)
|
||||
|
||||
def _q_lookup(self, component):
|
||||
filetypes_cfg = get_cfg('filetypes', {})
|
||||
try:
|
||||
filetype_id = int(component)
|
||||
filetype = filetypes_cfg[filetype_id]
|
||||
except (ValueError, KeyError):
|
||||
raise errors.TraversalError()
|
||||
|
||||
form = self.get_form(filetype)
|
||||
form.add_submit('cancel', _('Cancel'))
|
||||
form.add_submit('delete', _('Delete'))
|
||||
|
||||
if form.get_widget('cancel').parse():
|
||||
return redirect('.')
|
||||
|
||||
if form.get_submit() == 'submit':
|
||||
filetype['label'] = form.get_widget('label').parse()
|
||||
previous_mimetypes = filetype['mimetypes']
|
||||
filetype['mimetypes'] = self.parse_mimetypes(form.get_widget('mimetypes').parse())
|
||||
FormDef.update_mimetypes(previous_mimetypes, filetype['mimetypes'])
|
||||
get_publisher().write_cfg()
|
||||
return redirect('.')
|
||||
|
||||
if form.get_submit() == 'delete':
|
||||
del filetypes_cfg[filetype_id]
|
||||
get_publisher().write_cfg()
|
||||
return redirect('.')
|
||||
|
||||
html_top('settings', title=_('File Types'))
|
||||
r = TemplateIO(html=True)
|
||||
r += htmltext('<h2>%s - %s</h2>') % (_('File Type'), filetype['label'])
|
||||
r += form.render()
|
||||
return r.getvalue()
|
||||
|
||||
|
||||
class ThemePreviewDirectory(Directory):
|
||||
def _q_traverse(self, path):
|
||||
if len(path) < 2:
|
||||
|
@ -252,13 +378,14 @@ class SettingsDirectory(QommonSettingsDirectory):
|
|||
'sms', 'certificates', 'texts', 'utf8switch', 'install_theme',
|
||||
'session', 'download_theme', 'smstest', 'postgresql',
|
||||
('admin-permissions', 'admin_permissions'),
|
||||
'theme_preview']
|
||||
'theme_preview', 'filetypes']
|
||||
|
||||
emails = EmailsDirectory()
|
||||
identification = IdentificationDirectory()
|
||||
users = UsersDirectory()
|
||||
texts = TextsDirectory()
|
||||
theme_preview = ThemePreviewDirectory()
|
||||
filetypes = FileTypesDirectory()
|
||||
|
||||
def _q_index(self):
|
||||
html_top('settings', title = _('Settings'))
|
||||
|
@ -357,6 +484,8 @@ class SettingsDirectory(QommonSettingsDirectory):
|
|||
if self.texts.texts_dict:
|
||||
r += htmltext('<dt><a href="texts/">%s</a></dt> <dd>%s</dd>') % (
|
||||
_('Texts'), _('Configure text that appears on some pages'))
|
||||
r += htmltext('<dt><a href="filetypes/">%s</a></dt> <dd>%s</dd>') % (
|
||||
_('File Types'), _('Configure known file types'))
|
||||
r += htmltext('</dl>')
|
||||
r += htmltext('</div>')
|
||||
|
||||
|
|
|
@ -663,6 +663,16 @@ class FileField(WidgetField):
|
|||
('audio/*', _('Sound files')),
|
||||
('video/*', _('Video files')),
|
||||
('image/*', _('Image files'))]
|
||||
filetypes_cfg = get_cfg('filetypes', {})
|
||||
if filetypes_cfg:
|
||||
for file_type in filetypes_cfg.values():
|
||||
file_types.append((
|
||||
','.join(file_type['mimetypes']), file_type['label']))
|
||||
if self.file_type:
|
||||
known_file_types = [x[0] for x in file_types]
|
||||
for file_type in self.file_type:
|
||||
if not file_type in known_file_types:
|
||||
file_types.append((file_type, file_type))
|
||||
form.add(CheckboxesWidget, 'file_type', title=_('File type suggestion'),
|
||||
value=self.file_type, elements=file_types, inline=True,
|
||||
advanced=not(self.file_type))
|
||||
|
|
|
@ -851,6 +851,28 @@ class FormDef(StorableObject):
|
|||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def update_mimetypes(cls, previous_mimetypes, new_mimetypes):
|
||||
# look for file fields in all formdefs, to update them with the
|
||||
# new mimetypes.
|
||||
if previous_mimetypes == new_mimetypes:
|
||||
return
|
||||
previous_mimetypes = ','.join(previous_mimetypes)
|
||||
new_mimetypes = ','.join(new_mimetypes)
|
||||
for formdef in cls.select():
|
||||
changed = False
|
||||
for field in formdef.fields:
|
||||
if not hasattr(field, 'file_type'):
|
||||
continue
|
||||
if not field.file_type:
|
||||
continue
|
||||
if previous_mimetypes in field.file_type:
|
||||
field.file_type.remove(previous_mimetypes)
|
||||
field.file_type.append(new_mimetypes)
|
||||
changed = True
|
||||
if changed:
|
||||
formdef.store()
|
||||
|
||||
# don't pickle _workflow cache
|
||||
def __getstate__(self):
|
||||
odict = self.__dict__
|
||||
|
|
Loading…
Reference in New Issue