From de0fdb6eef3a2ed0a55963c6137011a1ea727ff5 Mon Sep 17 00:00:00 2001 From: Artur Barseghyan Date: Fri, 21 Oct 2016 23:22:41 +0200 Subject: [PATCH] prepare 0.8.9; better debugging; slider plugin major improvements --- .gitignore | 3 +- CHANGELOG.rst | 7 ++ setup.py | 2 +- src/fobi/__init__.py | 4 +- src/fobi/base.py | 12 ++- .../form_elements/fields/slider/constants.py | 16 ++++ .../fields/slider/fobi_form_elements.py | 95 ++++++++++++++----- .../form_elements/fields/slider/forms.py | 57 +++++++---- .../form_elements/fields/slider/widgets.py | 2 +- src/fobi/views.py | 4 + src/fobi/widgets.py | 42 +++++++- 11 files changed, 191 insertions(+), 53 deletions(-) diff --git a/.gitignore b/.gitignore index 962392fc..4bd301da 100644 --- a/.gitignore +++ b/.gitignore @@ -43,7 +43,8 @@ fobi/fobi.migrations.rst /examples/simple/lund/ /examples/simple/settings/local_settings.py /examples/simple/settings/local_settings_foundation5.py -/examples/simple/settings/lund.py +/examples/simple/settings/bootstrap3_theme_django_1_9_lund.py +/examples/simple/runserver/bootstrap3-theme-django-1-9-lund.sh /examples/quickstart/local_settings.py /examples/quick_start/db.sqlite3 /examples/quickstart/ diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 50ec891c..33a38b93 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -15,6 +15,13 @@ are used for versioning (schema follows below): 0.3.4 to 0.4). - All backwards incompatible changes are mentioned in this document. +0.8.9 +----- +2016-10-22 + +- Simplified debugging (never set `FOBI_DEBUG` to True in production!). +- Major `slider` plugin improvements. + 0.8.8 ----- 2016-10-21 diff --git a/setup.py b/setup.py index 0d24bfbd..e12029e1 100644 --- a/setup.py +++ b/setup.py @@ -204,7 +204,7 @@ for locale_dir in locale_dirs: for f in os.listdir(locale_dir)] -version = '0.8.8' +version = '0.8.9' install_requires = [] # If certain version of Django is already installed, choose version agnostic diff --git a/src/fobi/__init__.py b/src/fobi/__init__.py index 61408dea..50ede0da 100644 --- a/src/fobi/__init__.py +++ b/src/fobi/__init__.py @@ -1,6 +1,6 @@ __title__ = 'django-fobi' -__version__ = '0.8.8' -__build__ = 0x00005f +__version__ = '0.8.9' +__build__ = 0x000060 __author__ = 'Artur Barseghyan ' __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' diff --git a/src/fobi/base.py b/src/fobi/base.py index 7710be71..c0527338 100644 --- a/src/fobi/base.py +++ b/src/fobi/base.py @@ -1378,14 +1378,16 @@ class FormElementPlugin(BasePlugin): # Get form field instances (as defined by ``get_form_field_instances`` # methods in plugins). In DEBUG mode raise an exception if something # goes wrong. Otherwise - skip the element. - try: + if DEBUG: form_field_instances = self.get_form_field_instances( request=request ) - except AttributeError as e: - if DEBUG: - raise e - else: + else: + try: + form_field_instances = self.get_form_field_instances( + request=request + ) + except AttributeError as e: return [] processed_field_instances = [] diff --git a/src/fobi/contrib/plugins/form_elements/fields/slider/constants.py b/src/fobi/contrib/plugins/form_elements/fields/slider/constants.py index dd277cd2..8110629f 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/slider/constants.py +++ b/src/fobi/contrib/plugins/form_elements/fields/slider/constants.py @@ -16,6 +16,12 @@ __all__ = ( 'SLIDER_HANDLE_TRIANGLE', 'SLIDER_HANDLE_CUSTOM', 'SLIDER_HANDLE_CHOICES', + + 'SLIDER_SHOW_ENDPOINTS_AS_LABELED_TICKS', + 'SLIDER_SHOW_ENDPOINTS_AS_LABELS', + 'SLIDER_SHOW_ENDPOINTS_AS_TICKS', + 'SLIDER_DEFAULT_SHOW_ENDPOINTS_AS', + 'SLIDER_SHOW_ENDPOINTS_AS_CHOICES', ) SLIDER_TOOLTIP_SHOW = 'show' @@ -40,3 +46,13 @@ SLIDER_HANDLE_CHOICES = ( (SLIDER_HANDLE_TRIANGLE, _("Triangle")), (SLIDER_HANDLE_CUSTOM, _("Custom")), ) + +SLIDER_SHOW_ENDPOINTS_AS_LABELED_TICKS = 'labeled_ticks' +SLIDER_SHOW_ENDPOINTS_AS_LABELS = 'labels' +SLIDER_SHOW_ENDPOINTS_AS_TICKS = 'ticks' +SLIDER_DEFAULT_SHOW_ENDPOINTS_AS = SLIDER_SHOW_ENDPOINTS_AS_LABELS +SLIDER_SHOW_ENDPOINTS_AS_CHOICES = ( + (SLIDER_SHOW_ENDPOINTS_AS_LABELS, _("Labels")), + (SLIDER_SHOW_ENDPOINTS_AS_TICKS, _("Ticks without labels")), + (SLIDER_SHOW_ENDPOINTS_AS_LABELED_TICKS, _("Labeled ticks")), +) diff --git a/src/fobi/contrib/plugins/form_elements/fields/slider/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/slider/fobi_form_elements.py index d666268e..eac624d6 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/slider/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/slider/fobi_form_elements.py @@ -1,12 +1,19 @@ from django.forms.fields import ChoiceField -from django.forms.widgets import Select +from django.utils.html import format_html +from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _ from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme -# from fobi.helpers import get_select_field_choices +from fobi.widgets import RichSelect from . import UID -from .constants import SLIDER_DEFAULT_TOOLTIP, SLIDER_DEFAULT_HANDLE +from .constants import ( + SLIDER_DEFAULT_TOOLTIP, + SLIDER_DEFAULT_HANDLE, + SLIDER_SHOW_ENDPOINTS_AS_LABELED_TICKS, + SLIDER_SHOW_ENDPOINTS_AS_TICKS, + SLIDER_DEFAULT_SHOW_ENDPOINTS_AS +) from .forms import SliderInputForm from .settings import INITIAL, MAX_VALUE, MIN_VALUE, STEP @@ -67,26 +74,70 @@ class SliderInputPlugin(FormFieldPlugin): 'data-slider-handle': handle, } - if self.data.enable_ticks: - # if custom_ticks: - # pass - # else: - tick_label_start = self.data.tick_label_start \ - if self.data.tick_label_start \ - else min_value + show_endpoints_as = self.data.show_endpoints_as \ + if self.data.show_endpoints_as \ + else SLIDER_DEFAULT_SHOW_ENDPOINTS_AS - tick_label_end = self.data.tick_label_end \ - if self.data.tick_label_end \ - else max_value + prepend_html_list = [] + append_html_list = [] - widget_attrs.update({ - 'data-slider-ticks': "[{0}, {1}]".format( - min_value, max_value - ), - 'data-slider-ticks-labels': '["{0}", "{1}"]'.format( - tick_label_start, tick_label_end - ), - }) + # Show endpoints as labeled ticks + if SLIDER_SHOW_ENDPOINTS_AS_LABELED_TICKS == show_endpoints_as: + + label_start = self.data.label_start \ + if self.data.label_start \ + else min_value + + label_end = self.data.label_end \ + if self.data.label_end \ + else max_value + + widget_attrs.update({ + 'data-slider-ticks': "[{0}, {1}]".format( + min_value, max_value + ), + 'data-slider-ticks-labels': '["{0!s}", "{1!s}"]'.format( + label_start.encode('utf8'), label_end.encode('utf8') + ), + }) + + # Show endpoints as ticks + elif SLIDER_SHOW_ENDPOINTS_AS_TICKS == show_endpoints_as: + + widget_attrs.update({ + 'data-slider-ticks': "[{0}, {1}]".format( + min_value, max_value + ), + 'data-slider-ticks-labels': '["{0}", "{1}"]'.format( + "", "" + ), + }) + + # Show endpoints as labels + else: + + if self.data.label_start: + prepend_html_list.append(format_html(" ")) + prepend_html_list.append(format_html(self.data.label_start)) + prepend_html_list.append(format_html(" ")) + + if self.data.label_end: + append_html_list.append(format_html(" ")) + append_html_list.append(format_html(self.data.label_end)) + append_html_list.append(format_html(" ")) + + widget_kwargs = {'attrs': widget_attrs} + + # For showing endpoints as labels + if prepend_html_list: + widget_kwargs.update({ + 'prepend_html': mark_safe(''.join(prepend_html_list)), + }) + + if append_html_list: + widget_kwargs.update({ + 'append_html': mark_safe(''.join(append_html_list)), + }) kwargs = { 'label': self.data.label, @@ -94,7 +145,7 @@ class SliderInputPlugin(FormFieldPlugin): 'initial': initial, 'required': self.data.required, 'choices': choices, - 'widget': Select(attrs=widget_attrs), + 'widget': RichSelect(**widget_kwargs), } return [(self.data.name, ChoiceField, kwargs)] diff --git a/src/fobi/contrib/plugins/form_elements/fields/slider/forms.py b/src/fobi/contrib/plugins/form_elements/fields/slider/forms.py index c72b874e..bd227e71 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/slider/forms.py +++ b/src/fobi/contrib/plugins/form_elements/fields/slider/forms.py @@ -3,7 +3,18 @@ from django.utils.translation import ugettext_lazy as _ from fobi.base import BaseFormFieldPluginForm, get_theme -from . import constants +from .constants import ( + SLIDER_DEFAULT_TOOLTIP, + SLIDER_DEFAULT_HANDLE, + SLIDER_DEFAULT_SHOW_ENDPOINTS_AS, + SLIDER_TOOLTIP_CHOICES, + SLIDER_HANDLE_CHOICES, + SLIDER_SHOW_ENDPOINTS_AS_CHOICES, + SLIDER_HANDLE_TRIANGLE, + SLIDER_HANDLE_CUSTOM, + SLIDER_SHOW_ENDPOINTS_AS_LABELED_TICKS, + SLIDER_SHOW_ENDPOINTS_AS_TICKS +) from .settings import INITIAL, MAX_VALUE, MIN_VALUE, STEP @@ -25,12 +36,12 @@ class SliderInputForm(forms.Form, BaseFormFieldPluginForm): ("min_value", MIN_VALUE), ("max_value", MAX_VALUE), ("step", STEP), - ("tooltip", constants.SLIDER_DEFAULT_TOOLTIP), - ("handle", constants.SLIDER_DEFAULT_HANDLE), + ("tooltip", SLIDER_DEFAULT_TOOLTIP), + ("handle", SLIDER_DEFAULT_HANDLE), # ("disable_slider_background", False), - ("enable_ticks", False), - ("tick_label_start", ""), - ("tick_label_end", ""), + ("show_endpoints_as", SLIDER_DEFAULT_SHOW_ENDPOINTS_AS), + ("label_start", ""), + ("label_end", ""), # ("custom_ticks", ""), ("help_text", ""), ("initial", INITIAL), @@ -81,7 +92,7 @@ class SliderInputForm(forms.Form, BaseFormFieldPluginForm): ) tooltip = forms.ChoiceField( label=_("Tooltip"), - choices=constants.SLIDER_TOOLTIP_CHOICES, + choices=SLIDER_TOOLTIP_CHOICES, required=False, widget=forms.widgets.Select( attrs={'class': theme.form_element_html_class} @@ -89,7 +100,7 @@ class SliderInputForm(forms.Form, BaseFormFieldPluginForm): ) handle = forms.ChoiceField( label=_("Handle"), - choices=constants.SLIDER_HANDLE_CHOICES, + choices=SLIDER_HANDLE_CHOICES, required=False, widget=forms.widgets.Select( attrs={'class': theme.form_element_html_class} @@ -102,23 +113,25 @@ class SliderInputForm(forms.Form, BaseFormFieldPluginForm): # attrs={'class': theme.form_element_checkbox_html_class} # ) # ) - enable_ticks = forms.BooleanField( - label=_("Enable ticks"), - help_text=_("Adds ticks (endpoints) at start/end"), + show_endpoints_as = forms.ChoiceField( + label=_("Show endpoints as"), + choices=SLIDER_SHOW_ENDPOINTS_AS_CHOICES, required=False, - widget=forms.widgets.CheckboxInput( - attrs={'class': theme.form_element_checkbox_html_class} + widget=forms.widgets.Select( + attrs={'class': theme.form_element_html_class} ) ) - tick_label_start = forms.CharField( - label=_("Tick start label"), + label_start = forms.CharField( + label=_("Start label"), + help_text=_("Start endpoint label"), required=False, widget=forms.widgets.TextInput( attrs={'class': theme.form_element_html_class} ) ) - tick_label_end = forms.CharField( - label=_("Tick end label"), + label_end = forms.CharField( + label=_("End label"), + help_text=_("End endpoint label"), required=False, widget=forms.widgets.TextInput( attrs={'class': theme.form_element_html_class} @@ -178,7 +191,7 @@ class SliderInputForm(forms.Form, BaseFormFieldPluginForm): min_value = self.cleaned_data['min_value'] initial = self.cleaned_data['initial'] step = self.cleaned_data['step'] - enable_ticks = self.cleaned_data['enable_ticks'] + show_endpoints_as = self.cleaned_data['show_endpoints_as'] handle = self.cleaned_data['handle'] if max_value < min_value: @@ -205,8 +218,12 @@ class SliderInputForm(forms.Form, BaseFormFieldPluginForm): _("`initial` should be >= than `min_value`.") ) - if handle in (constants.SLIDER_HANDLE_TRIANGLE, - constants.SLIDER_HANDLE_CUSTOM) and enable_ticks: + label_handles = (SLIDER_HANDLE_TRIANGLE, SLIDER_HANDLE_CUSTOM) + tick_endpoints = ( + SLIDER_SHOW_ENDPOINTS_AS_LABELED_TICKS, + SLIDER_SHOW_ENDPOINTS_AS_TICKS + ) + if handle in label_handles and show_endpoints_as in tick_endpoints: self.add_error( 'handle', _("You are not allowed to use Triangle or Custom handles " diff --git a/src/fobi/contrib/plugins/form_elements/fields/slider/widgets.py b/src/fobi/contrib/plugins/form_elements/fields/slider/widgets.py index b0ad6c72..0c5a7eeb 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/slider/widgets.py +++ b/src/fobi/contrib/plugins/form_elements/fields/slider/widgets.py @@ -9,7 +9,7 @@ __title__ = 'fobi.contrib.plugins.form_elements.fields.slider.' \ __author__ = 'Artur Barseghyan ' __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' -__all__ = ('BaseSliderPluginWidget',) +__all__ = ('BaseSliderPluginWidget', ) class BaseSliderPluginWidget(FormElementPluginWidget): diff --git a/src/fobi/views.py b/src/fobi/views.py index 5e51ba47..c4730f4c 100644 --- a/src/fobi/views.py +++ b/src/fobi/views.py @@ -521,6 +521,8 @@ def edit_form_entry(request, form_entry_id, theme=None, template_name=None): # In debug mode, try to identify possible problems. if DEBUG: + assembled_form.as_p() + else: try: assembled_form.as_p() except Exception as err: @@ -2082,6 +2084,8 @@ def view_form_entry(request, form_entry_slug, theme=None, template_name=None): # In debug mode, try to identify possible problems. if DEBUG: + form.as_p() + else: try: form.as_p() except Exception as err: diff --git a/src/fobi/widgets.py b/src/fobi/widgets.py index 6f99fd08..3796c74e 100644 --- a/src/fobi/widgets.py +++ b/src/fobi/widgets.py @@ -1,6 +1,9 @@ -from django.forms.widgets import RadioSelect +from django.forms.widgets import RadioSelect, Select +from django.utils.html import format_html +from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _ + # Safe import of ``NumberInput`` try: from django.forms.widgets import NumberInput @@ -19,6 +22,7 @@ __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ( 'NumberInput', 'BooleanRadioSelect', + 'RichSelect', ) @@ -50,3 +54,39 @@ class BooleanRadioSelect(RadioSelect): kwargs['choices'] = BOOLEAN_CHOICES super(BooleanRadioSelect, self).__init__(*args, **kwargs) + + +class RichSelect(Select): + """Rich select widget with some rich enhancements. + + Based on original Select widget and intended to be a drop-off replacement. + """ + + def __init__(self, attrs=None, choices=(), prepend_html=None, + append_html=None): + """Constructor. + + :param dict attrs: + :param tuple choices: + :param str prepend_html: + :param str append_html: + """ + self.prepend_html = prepend_html if prepend_html else "" + self.append_html = append_html if append_html else "" + super(RichSelect, self).__init__(attrs=attrs, choices=choices) + + def render(self, name, value, attrs=None): + """Renders the element, having prepended and appended extra parts.""" + rendered_select = super(RichSelect, self).render( + name=name, + value=value, + attrs=attrs + ) + + return mark_safe( + '\n'.join([ + format_html(self.prepend_html), + rendered_select, + format_html(self.append_html) + ]) + )