settings: add a file types section (#6134)

This commit is contained in:
Frédéric Péters 2014-12-12 13:55:25 +01:00
parent 78c0dad11f
commit 476aa415d4
5 changed files with 251 additions and 1 deletions

View File

@ -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']

View File

@ -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)'\
'...'

View File

@ -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>')

View File

@ -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))

View File

@ -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__