docbow/docbow_project/docbow/widgets.py

234 lines
8.4 KiB
Python

import uuid
import re
from django.forms import Textarea, MultiWidget, Select, HiddenInput, FileInput, SelectMultiple
from django.utils.safestring import mark_safe
from django.conf import settings
from django.template.loader import render_to_string
from django.forms import CheckboxSelectMultiple
from .upload_views import get_files_for_id
from docbow_project.docbow import app_settings
class TextInpuWithPredefinedValues(MultiWidget):
CLIENT_CODE = '''
<script type="text/javascript">
$(document).ready(function () {
$('#id_%(name)s_0').change(function () {
$('#id_%(name)s_1').val($(this).val());
});
$('#id_%(name)s_1').keypress(function () {
$('#id_%(name)s_0').val('---');
});
});
</script>'''
def __init__(self, attrs=None, choices=[]):
widget_list = (Select(attrs=attrs, choices=choices), Textarea(attrs=attrs))
super(TextInpuWithPredefinedValues, self).__init__(widget_list, attrs)
def decompress(self, value):
return ['---', value]
def value_from_datadict(self, data, files, name):
select, text_input = super(TextInpuWithPredefinedValues, self).value_from_datadict(data, files, name)
if text_input or select == '---':
return text_input
else:
return select
def render(self, name, value, attrs=None, renderer=None):
output = super(TextInpuWithPredefinedValues, self).render(name, value, attrs)
return output + mark_safe(self.CLIENT_CODE % {'name': name})
class MultiFileInput(FileInput):
'''
FileInput field supporting the multiple attribute
'''
def __init__(self, attrs=None):
super(MultiFileInput, self).__init__(attrs)
self.attrs['multiple'] = 'true'
def value_from_datadict(self, data, files, name):
if name in files:
return files.getlist(name)
else:
return []
class JqueryFileUploadFileInput(MultiFileInput):
template_name = 'docbow/upload-multiwidget.html'
class Media:
js = (
'jquery-ui/js/jquery-ui-1.8.4.min.js',
'jquery-plugin/js/jquery.tmpl.min-beta1.js',
'jquery-plugin/js/jquery.iframe-transport.js',
'jquery-plugin/js/jquery.fileupload.js',
'jquery-plugin/js/jquery.fileupload-ui.js',
)
css = {'all': ('jquery-ui/css/jquery-ui-1.8.4.css', 'jquery-plugin/css/jquery.fileupload-ui.css',)}
def __init__(self, extensions=[], attached_file_kind=None, *args, **kwargs):
self.extensions = extensions
self.attached_file_kind = attached_file_kind
super(JqueryFileUploadFileInput, self).__init__(*args, **kwargs)
def _get_context(self, name):
return {
'open_tv': '{{',
'close_tv': '}}',
'upload_url': self.url,
'max_file_size': app_settings.MAX_FILE_SIZE,
'extensions': self.extensions,
'attached_file_kind': self.attached_file_kind,
'files': self.files,
'name': name,
'STATIC_URL': settings.STATIC_URL,
}
def get_context(self, name, value, attrs):
context = super(JqueryFileUploadFileInput, self).get_context(name, value, attrs)
context['widget'].update(self._get_context(name))
return context
def render(self, name, value, attrs=None):
# Dead code in django 1.11
output = render_to_string('docbow/upload.html', self._get_context(name))
return mark_safe(output)
class JqueryFileUploadInput(MultiWidget):
needs_multipart_form = True
CLIENT_CODE = '''
<script type="text/javascript">
</script>'''
upload_id_re = re.compile(r'^[a-z0-9A-Z-]+$')
upload_id = None
template_name = 'docbow/multiwidget.html'
def __init__(
self, attrs=None, choices=[], max_filename_length=None, extensions=[], attached_file_kind=None
):
self.extensions = extensions
self.max_filename_length = max_filename_length
self.attached_file_kind = attached_file_kind
widget_list = (
HiddenInput(attrs=attrs),
JqueryFileUploadFileInput(
attrs=attrs, extensions=extensions, attached_file_kind=attached_file_kind
),
)
super(JqueryFileUploadInput, self).__init__(widget_list, attrs)
def decompress(self, value):
# map python value to widget contents
if self.upload_id:
pass
elif isinstance(value, (list, tuple)) and value and value[0] is not None:
self.upload_id = str(value[0])
else:
self.upload_id = str(uuid.uuid4())
return [self.upload_id, None]
def value_from_datadict(self, data, files, name):
'''
If some file was submitted, that's the value,
If a regular hidden_id is present, use it to find uploaded files,
otherwise return an empty list
'''
upload_id, file_input = super(JqueryFileUploadInput, self).value_from_datadict(data, files, name)
if file_input:
pass
elif JqueryFileUploadInput.upload_id_re.match(upload_id):
file_input = list(get_files_for_id(upload_id))
else:
file_input = []
return [upload_id, file_input]
def render(self, name, value, attrs=None, renderer=None):
self.decompress(value)
url = '/upload/%s/' % self.upload_id
if self.attached_file_kind and self.attached_file_kind.id:
url += '%s/' % self.attached_file_kind.id
if self.attached_file_kind.cardinality:
url += '?cardinality=%s' % self.attached_file_kind.cardinality
if self.max_filename_length:
url += '&' if '?' in url else '?'
url += 'max_filename_length=%d' % self.max_filename_length
self.widgets[1].url = url
self.widgets[1].files = '/upload/%s/' % get_files_for_id(self.upload_id)
output = super(JqueryFileUploadInput, self).render(name, value, attrs)
fileinput_id = '%s_%s' % (attrs['id'], '1')
return output + mark_safe(
self.CLIENT_CODE % {'upload_id': self.upload_id, 'fileinput_id': fileinput_id}
)
class ForcedValueWidget(SelectMultiple):
def __init__(self, attrs=None, format=None, value=None, display_value=""):
super(ForcedValueWidget, self).__init__(attrs)
self.display_value = display_value
self.value = value
def value_from_datadict(self, data, files, name):
return self.value
def render(self, name, value, attrs=None, renderer=None):
return mark_safe(
u'<div class="selector"><span class="display-value">%s</span></div>' % self.display_value
)
class FilteredSelectMultiple(SelectMultiple):
"""
Imported from django.contrib.admin.widget, to improve on javascript files.
A SelectMultiple with a JavaScript filter interface.
Note that the resulting JavaScript assumes that the jsi18n
catalog has been loaded in the page
"""
class Media:
js = (
"docbow/filter-widget/js/core.js",
"docbow/filter-widget/js/SelectBox.js",
"docbow/filter-widget/js/SelectFilter2.js",
"js/i18n.js",
)
css = {'all': ('docbow/filter-widget/css/filter-widget.css',)}
def __init__(self, verbose_name, is_stacked, attrs=None, choices=()):
self.verbose_name = verbose_name
self.is_stacked = is_stacked
super(FilteredSelectMultiple, self).__init__(attrs, choices)
def render(self, name, value, attrs=None, *args, **kwargs):
if attrs is None:
attrs = {}
attrs['class'] = 'selectfilter'
if self.is_stacked:
attrs['class'] += 'stacked'
# disable html5 validation
if 'required' in attrs:
del attrs['required']
output = [super(FilteredSelectMultiple, self).render(name, value, attrs, *args, **kwargs)]
output.append(u'<script type="text/javascript">addEvent(window, "load", function(e) {')
# TODO: "id_" is hard-coded here. This should instead use the correct
# API to determine the ID dynamically.
output.append(
u'SelectFilter.init("id_%s", "%s", %s, "%s"); });</script>\n'
% (name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), settings.STATIC_URL)
)
return mark_safe(u''.join(output))
CheckboxMultipleSelect = CheckboxSelectMultiple