From 2bdf8344c4dfecc795eb319e084e1059bf15cc28 Mon Sep 17 00:00:00 2001 From: Serghei Mihai Date: Thu, 23 Feb 2017 14:56:53 +0100 Subject: [PATCH] use specialized widgets for datetimes (#15087) --- corbo/forms.py | 7 +- corbo/templates/corbo/announce_form.html | 5 - corbo/widgets.py | 174 +++++++++++++++++++++++++++++++ 3 files changed, 177 insertions(+), 9 deletions(-) create mode 100644 corbo/widgets.py diff --git a/corbo/forms.py b/corbo/forms.py index a7d610f..64aa5d6 100644 --- a/corbo/forms.py +++ b/corbo/forms.py @@ -8,6 +8,7 @@ from django.core import validators from django.core.exceptions import ValidationError from .models import Announce, Category, Broadcast, channel_choices +from . import widgets class AnnounceForm(forms.ModelForm): @@ -16,10 +17,8 @@ class AnnounceForm(forms.ModelForm): model = Announce exclude = ('identifier',) widgets = { - 'publication_time': forms.TextInput(attrs={'class': 'datetimepicker', - 'readonly': True}), - 'expiration_time': forms.TextInput(attrs={'class': 'datetimepicker', - 'readonly': True}), + 'publication_time': widgets.DateTimeWidget(), + 'expiration_time': widgets.DateTimeWidget(), 'category': forms.HiddenInput() } diff --git a/corbo/templates/corbo/announce_form.html b/corbo/templates/corbo/announce_form.html index 915b7a1..16baba8 100644 --- a/corbo/templates/corbo/announce_form.html +++ b/corbo/templates/corbo/announce_form.html @@ -28,10 +28,5 @@ {% trans 'Cancel' %} - {% endblock %} diff --git a/corbo/widgets.py b/corbo/widgets.py new file mode 100644 index 0000000..85ba234 --- /dev/null +++ b/corbo/widgets.py @@ -0,0 +1,174 @@ +# Bootstrap django-datetime-widget is a simple and clean widget for DateField, +# Timefiled and DateTimeField in Django framework. It is based on Bootstrap +# datetime picker, supports Bootstrap 2 +# +# https://github.com/asaglimbeni/django-datetime-widget +# +# License: BSD +# Initial Author: Alfredo Saglimbeni + +import json +import re +import uuid + +from django.forms.widgets import DateTimeInput, DateInput, TimeInput +from django.utils.formats import get_language +from django.utils.safestring import mark_safe + +DATE_FORMAT_JS_PY_MAPPING = { + 'P': '%p', + 'ss': '%S', + 'ii': '%M', + 'hh': '%H', + 'HH': '%I', + 'dd': '%d', + 'mm': '%m', + 'yy': '%y', + 'yyyy': '%Y', +} + +DATE_FORMAT_TO_PYTHON_REGEX = re.compile(r'\b(' + '|'.join(DATE_FORMAT_JS_PY_MAPPING.keys()) + r')\b') + + +DATE_FORMAT_PY_JS_MAPPING = { + '%M': 'ii', + '%m': 'mm', + '%I': 'HH', + '%H': 'hh', + '%d': 'dd', + '%Y': 'yyyy', + '%y': 'yy', + '%p': 'P', + '%S': 'ss' +} + +DATE_FORMAT_TO_JS_REGEX = re.compile(r'(? + %(rendered_widget)s + %(clear_button)s + + + + """ + +CLEAR_BTN_TEMPLATE = """""" + + +class PickerWidgetMixin(object): + + format_name = None + glyphicon = None + + def __init__(self, attrs=None, options=None, usel10n=None): + + if attrs is None: + attrs = {'readonly': ''} + + self.options = options + self.options['language'] = get_language().split('-')[0] + + # We're not doing localisation, get the Javascript date format provided by the user, + # with a default, and convert it to a Python data format for later string parsing + date_format = self.options['format'] + self.format = DATE_FORMAT_TO_PYTHON_REGEX.sub( + lambda x: DATE_FORMAT_JS_PY_MAPPING[x.group()], + date_format + ) + + super(PickerWidgetMixin, self).__init__(attrs, format=self.format) + + def render(self, name, value, attrs=None): + final_attrs = self.build_attrs(attrs) + rendered_widget = super(PickerWidgetMixin, self).render(name, value, final_attrs) + + #if not set, autoclose have to be true. + self.options.setdefault('autoclose', True) + + # Build javascript options out of python dictionary + options_list = [] + for key, value in iter(self.options.items()): + options_list.append("%s: %s" % (key, json.dumps(value))) + + js_options = ",\n".join(options_list) + + # Use provided id or generate hex to avoid collisions in document + id = final_attrs.get('id', uuid.uuid4().hex) + + return mark_safe(BOOTSTRAP_INPUT_TEMPLATE % dict( + id=id, + rendered_widget=rendered_widget, + clear_button=CLEAR_BTN_TEMPLATE if self.options.get('clearBtn') else '', + glyphicon=self.glyphicon, + options=js_options + ) + ) + + +class DateTimeWidget(PickerWidgetMixin, DateTimeInput): + """ + DateTimeWidget is the corresponding widget for Datetime field, it renders both the date and time + sections of the datetime picker. + """ + + format_name = 'DATETIME_INPUT_FORMATS' + glyphicon = 'glyphicon-th' + + def __init__(self, attrs=None, options=None, usel10n=None): + + if options is None: + options = {} + + # Set the default options to show only the datepicker object + options['format'] = options.get('format', 'dd/mm/yyyy hh:ii') + + super(DateTimeWidget, self).__init__(attrs, options, usel10n) + + +class DateWidget(PickerWidgetMixin, DateInput): + """ + DateWidget is the corresponding widget for Date field, it renders only the date section of + datetime picker. + """ + + format_name = 'DATE_INPUT_FORMATS' + glyphicon = 'glyphicon-calendar' + + def __init__(self, attrs=None, options=None, usel10n=None): + + if options is None: + options = {} + + # Set the default options to show only the datepicker object + options['startView'] = options.get('startView', 2) + options['minView'] = options.get('minView', 2) + options['format'] = options.get('format', 'dd/mm/yyyy') + + super(DateWidget, self).__init__(attrs, options, usel10n) + + +class TimeWidget(PickerWidgetMixin, TimeInput): + """ + TimeWidget is the corresponding widget for Time field, it renders only the time section of + datetime picker. + """ + + format_name = 'TIME_INPUT_FORMATS' + glyphicon = 'glyphicon-time' + + def __init__(self, attrs=None, options=None, usel10n=None): + + if options is None: + options = {} + + # Set the default options to show only the timepicker object + options['startView'] = options.get('startView', 1) + options['minView'] = options.get('minView', 0) + options['maxView'] = options.get('maxView', 1) + options['format'] = options.get('format', 'hh:ii') + + super(TimeWidget, self).__init__(attrs, options, usel10n) -- 2.11.0