From 933cc193d33ce2910b51d5b1de4ad9bdf1d0d699 Mon Sep 17 00:00:00 2001 From: mariuccio Date: Thu, 12 May 2016 15:13:22 +0200 Subject: [PATCH 1/4] select_multiple_with_max plugin created --- .../select_multiple_with_max/README.rst | 78 +++++++++++++++ .../select_multiple_with_max/__init__.py | 9 ++ .../fields/select_multiple_with_max/apps.py | 15 +++ .../fields/select_multiple_with_max/conf.py | 29 ++++++ .../select_multiple_with_max/defaults.py | 9 ++ .../fields/select_multiple_with_max/fields.py | 21 ++++ .../fobi_form_elements.py | 97 +++++++++++++++++++ .../fields/select_multiple_with_max/forms.py | 84 ++++++++++++++++ .../select_multiple_with_max/settings.py | 13 +++ 9 files changed, 355 insertions(+) create mode 100644 src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/README.rst create mode 100644 src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/__init__.py create mode 100644 src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/apps.py create mode 100644 src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/conf.py create mode 100644 src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/defaults.py create mode 100644 src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/fields.py create mode 100644 src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/fobi_form_elements.py create mode 100644 src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/forms.py create mode 100644 src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/settings.py diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/README.rst b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/README.rst new file mode 100644 index 00000000..e8dd9141 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/README.rst @@ -0,0 +1,78 @@ +========================================================= +fobi.contrib.plugins.form_elements.fields.select_multiple_with_max +========================================================= +A ``Fobi`` Select Multiple form field plugin with max choices. Makes use of the +``django.forms.widgets.SelectMultiple``. + +Installation +=============================================== +1. Add ``fobi.contrib.plugins.form_elements.fields.select_multiple_with_max`` to the + ``INSTALLED_APPS`` in your ``settings.py``. + +.. code-block:: python + + INSTALLED_APPS = ( + # ... + 'fobi.contrib.plugins.form_elements.fields.select_multiple_with_max', + # ... + ) + +2. In the terminal type: + +.. code-block:: none + + $ ./manage.py fobi_sync_plugins + +3. Assign appropriate permissions to the target users/groups to be using + the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. + +4. By default, the submitted form value of `select_multiple_with_max` + elements is label (human readable representation of the value chosen). + However, that part of the bahaviour has been made configurable. You can + choose between the following options: + + Consider the following list of (value, label) choices (the first element in + the tuple is value, the second element is label): + + .. code-block:: python + + [ + ('alpha', 'Alpha'), + ('beta', 'Beta'), + ('gamma', 'Gamma'), + ] + + - "val": `value` (example: "alpha"). + - "repr" (default): `label` (example: "Alpha"). + - "mix": `value (label)` (examle: "Alpha (alpha)"). + + Simply set the + ``FOBI_FORM_ELEMENT_SELECT_MULTIPLE_WITH_MAX_SUBMIT_VALUE_AS`` assign one of the + following values: "val", "repr" or "mix" to get the desired behaviour. + +Usage +=============================================== +You should be entering a single choice per line. Choice might +consist of just a single value or value/label pair. + +For example: + +.. code-block:: none + + 1 + 2 + alpha, Alpha + beta, Beta + omega + +The following HTML would be made of: + +.. code-block:: html + + diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/__init__.py b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/__init__.py new file mode 100644 index 00000000..565f5a53 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/__init__.py @@ -0,0 +1,9 @@ +__title__ = 'fobi.contrib.plugins.form_elements.fields.select_multiple_with_max' +__author__ = 'Artur Barseghyan ' +__copyright__ = 'Copyright (c) 2014 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('default_app_config', 'UID',) + +default_app_config = 'fobi.contrib.plugins.form_elements.fields.select_multiple_with_max.apps.Config' + +UID = 'select_multiple_with_max' diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/apps.py b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/apps.py new file mode 100644 index 00000000..089dece7 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/apps.py @@ -0,0 +1,15 @@ +__title__ = 'fobi.contrib.plugins.form_elements.fields.select_multiple_with_max.apps' +__author__ = 'Artur Barseghyan ' +__copyright__ = 'Copyright (c) 2014 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('Config',) + +try: + from django.apps import AppConfig + + class Config(AppConfig): + name = 'fobi.contrib.plugins.form_elements.fields.select_multiple_with_max' + label = 'fobi_contrib_plugins_form_elements_fields_select_multiple_with_max' + +except ImportError: + pass diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/conf.py b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/conf.py new file mode 100644 index 00000000..c294c9a1 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/conf.py @@ -0,0 +1,29 @@ +__title__ = 'fobi.contrib.plugins.form_elements.fields.select_multiple_with_max.conf' +__author__ = 'Artur Barseghyan ' +__copyright__ = 'Copyright (c) 2014-2015 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('get_setting',) + +from django.conf import settings + +from . import defaults + +def get_setting(setting, override=None): + """ + Get a setting from + `fobi.contrib.plugins.form_elements.fields.select_multiple_with_max` conf module, + falling back to the default. + + If override is not None, it will be used instead of the setting. + + :param setting: String with setting name + :param override: Value to use when no setting is available. Defaults + to None. + :return: Setting value. + """ + if override is not None: + return override + if hasattr(settings, 'FOBI_FORM_ELEMENT_SELECT_MULTIPLE_WITH_MAX_{0}'.format(setting)): + return getattr(settings, 'FOBI_FORM_ELEMENT_SELECT_MULTIPLE_WITH_MAX_{0}'.format(setting)) + else: + return getattr(defaults, setting) diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/defaults.py b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/defaults.py new file mode 100644 index 00000000..9c474d26 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/defaults.py @@ -0,0 +1,9 @@ +__title__ = 'fobi.contrib.plugins.form_elements.fields.select_multiple_with_max.defaults' +__author__ = 'Artur Barseghyan ' +__copyright__ = 'Copyright (c) 2014-2015 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('SUBMIT_VALUE_AS',) + +from fobi.constants import SUBMIT_VALUE_AS_REPR + +SUBMIT_VALUE_AS = SUBMIT_VALUE_AS_REPR diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/fields.py b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/fields.py new file mode 100644 index 00000000..e8cf1c40 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/fields.py @@ -0,0 +1,21 @@ +from django.forms.fields import MultipleChoiceField +from django.utils.translation import ugettext_lazy as _ +from django.core.exceptions import ValidationError + + +class MultipleChoiceWithMaxField(MultipleChoiceField): + + def __init__(self, max_choices=None, choices=(), required=True, widget=None, + label=None, initial=None, help_text='', *args, **kwargs): + super(MultipleChoiceWithMaxField, self).__init__( + choices=choices, required=required, widget=widget, label=label, + initial=initial, help_text=help_text, *args, **kwargs + ) + self.max_choices = max_choices + + def validate(self, value): + super(MultipleChoiceWithMaxField, self).validate(value) + if self.max_choices: + if len(value) > self.max_choices: + raise ValidationError(_("You must choose no more than {0} " + "values.".format(self.max_choices))) diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/fobi_form_elements.py new file mode 100644 index 00000000..033de9b7 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/fobi_form_elements.py @@ -0,0 +1,97 @@ +__title__ = 'fobi.contrib.plugins.form_elements.fields.select_multiple_with_max.fobi_form_elements' +__author__ = 'Artur Barseghyan ' +__copyright__ = 'Copyright (c) 2014 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('SelectMultipleWithMaxInputPlugin',) + +from django.forms.widgets import SelectMultiple +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme +from fobi.constants import ( + SUBMIT_VALUE_AS_VAL, SUBMIT_VALUE_AS_REPR + ) +from fobi.helpers import get_select_field_choices, safe_text + +from . import UID +from .fields import MultipleChoiceWithMaxField +from .forms import SelectMultipleWithMaxInputForm +from .settings import SUBMIT_VALUE_AS + +theme = get_theme(request=None, as_instance=True) + +class SelectMultipleInputWithMaxPlugin(FormFieldPlugin): + """ + Select multiple field plugin with max. + """ + uid = UID + name = _("Select multiple") + group = _("Fields") + form = SelectMultipleWithMaxInputForm + + def get_form_field_instances(self, request=None): + """ + Get form field instances. + """ + choices = get_select_field_choices(self.data.choices) + + kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + 'required': self.data.required, + 'choices': choices, + 'widget': SelectMultiple(attrs={'class': theme.form_element_html_class}), + } + + if self.data.max_choices: + kwargs['max_choices'] = self.data.max_choices + + return [(self.data.name, MultipleChoiceWithMaxField, kwargs)] + + def submit_plugin_form_data(self, form_entry, request, form): + """ + Submit plugin form data/process. + + :param fobi.models.FormEntry form_entry: Instance of + ``fobi.models.FormEntry``. + :param django.http.HttpRequest request: + :param django.forms.Form form: + """ + # In case if we should submit value as is, we don't return anything. + # In other cases, we proceed further. + if SUBMIT_VALUE_AS != SUBMIT_VALUE_AS_VAL: + # Get the object + values = form.cleaned_data.get(self.data.name, None) + + # Get choices + choices = dict(get_select_field_choices(self.data.choices)) + + # Returned value + ret_values = [] + + for value in values: + # Handle the submitted form value + + if value in choices: + label = safe_text(choices.get(value)) + + # Should be returned as repr + if SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_REPR: + value = label + # Should be returned as mix + else: + value = "{0} ({1})".format(label, value) + + ret_values.append(value) + + # Overwrite ``cleaned_data`` of the ``form`` with object + # qualifier. + form.cleaned_data[self.data.name] = ret_values + + # It's critically important to return the ``form`` with updated + # ``cleaned_data`` + return form + + +form_element_plugin_registry.register(SelectMultipleWithMaxInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/forms.py b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/forms.py new file mode 100644 index 00000000..ff71c261 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/forms.py @@ -0,0 +1,84 @@ +__title__ = 'fobi.contrib.plugins.form_elements.fields.select_multiple.forms' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2015 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('SelectMultipleInputForm',) + +from django import forms +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import BaseFormFieldPluginForm, get_theme +from fobi.helpers import validate_initial_for_multiple_choices + +theme = get_theme(request=None, as_instance=True) + +class SelectMultipleInputForm(forms.Form, BaseFormFieldPluginForm): + """ + Form for ``SelectMultipleInputPlugin``. + """ + plugin_data_fields = [ + ("label", ""), + ("name", ""), + ("choices", ""), + ("help_text", ""), + ("initial", ""), + ("required", False) + ] + + label = forms.CharField( + label = _("Label"), + required = True, + widget = forms.widgets.TextInput(attrs={'class': theme.form_element_html_class}) + ) + name = forms.CharField( + label = _("Name"), + required = True, + widget = forms.widgets.TextInput(attrs={'class': theme.form_element_html_class}) + ) + choices = forms.CharField( + label = _("Choices"), + required = False, + help_text = _("Enter single values/pairs per line. Example:
" + "    1
" + "    2
" + "    alpha, Alpha
" + "    beta, Beta
" + "    omega" + "

" + "It finally transforms into the following HTML code:
" + '    <select id="id_NAME_OF_THE_ELEMENT" name="NAME_OF_THE_ELEMENT">
' + '        <option value="1">1</option>
' + '        <option value="2">2</option>
' + '        <option value="alpha">Alpha</option>
' + '        <option value="beta">Beta</option>
' + '        <option value="omega">omega</option>
' + '    </select>' + "
"), + widget = forms.widgets.Textarea(attrs={'class': theme.form_element_html_class}) + ) + help_text = forms.CharField( + label = _("Help text"), + required = False, + widget = forms.widgets.Textarea(attrs={'class': theme.form_element_html_class}) + ) + initial = forms.CharField( + label = _("Initial"), + required = False, + widget = forms.widgets.TextInput(attrs={'class': theme.form_element_html_class}) + ) + required = forms.BooleanField( + label = _("Required"), + required = False, + widget = forms.widgets.CheckboxInput(attrs={'class': theme.form_element_checkbox_html_class}) + ) + max_choices = forms.IntegerField( + label = _("Max choices"), + required = False, + widget=forms.widgets.NumberInput(ttrs={'class': theme.form_element_html_class}) + ) + + def clean_initial(self): + """ + Validating the initial value. + """ + return validate_initial_for_multiple_choices(self, 'choices', 'initial') diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/settings.py b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/settings.py new file mode 100644 index 00000000..54ffe179 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/settings.py @@ -0,0 +1,13 @@ +__title__ = 'fobi.contrib.plugins.form_elements.fields.select_multiple_with_max.settings' +__author__ = 'Artur Barseghyan ' +__copyright__ = 'Copyright (c) 2014-2015 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('SUBMIT_VALUE_AS',) + +from fobi.helpers import validate_submit_value_as + +from .conf import get_setting + +SUBMIT_VALUE_AS = get_setting('SUBMIT_VALUE_AS') + +validate_submit_value_as(SUBMIT_VALUE_AS) From 1a6ecbbbebbc71b4f6ca3e25747d849cd90add93 Mon Sep 17 00:00:00 2001 From: mariuccio Date: Thu, 12 May 2016 15:23:36 +0200 Subject: [PATCH 2/4] typing errors corrected --- .../fields/select_multiple_with_max/fobi_form_elements.py | 2 +- .../form_elements/fields/select_multiple_with_max/forms.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/fobi_form_elements.py index 033de9b7..d00c0b2d 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/fobi_form_elements.py @@ -20,7 +20,7 @@ from .settings import SUBMIT_VALUE_AS theme = get_theme(request=None, as_instance=True) -class SelectMultipleInputWithMaxPlugin(FormFieldPlugin): +class SelectMultipleWithMaxInputPlugin(FormFieldPlugin): """ Select multiple field plugin with max. """ diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/forms.py b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/forms.py index ff71c261..e9123e40 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/forms.py +++ b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/forms.py @@ -12,7 +12,7 @@ from fobi.helpers import validate_initial_for_multiple_choices theme = get_theme(request=None, as_instance=True) -class SelectMultipleInputForm(forms.Form, BaseFormFieldPluginForm): +class SelectMultipleWithMaxInputForm(forms.Form, BaseFormFieldPluginForm): """ Form for ``SelectMultipleInputPlugin``. """ @@ -22,7 +22,8 @@ class SelectMultipleInputForm(forms.Form, BaseFormFieldPluginForm): ("choices", ""), ("help_text", ""), ("initial", ""), - ("required", False) + ("required", False), + ("max_choices", "") ] label = forms.CharField( @@ -74,7 +75,7 @@ class SelectMultipleInputForm(forms.Form, BaseFormFieldPluginForm): max_choices = forms.IntegerField( label = _("Max choices"), required = False, - widget=forms.widgets.NumberInput(ttrs={'class': theme.form_element_html_class}) + widget=forms.widgets.NumberInput(attrs={'class': theme.form_element_html_class}) ) def clean_initial(self): From 15607b7e3862eca1c91807486e0ad7b985bf84c1 Mon Sep 17 00:00:00 2001 From: mariuccio Date: Thu, 12 May 2016 15:27:40 +0200 Subject: [PATCH 3/4] docs updated --- .../form_elements/fields/select_multiple_with_max/README.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/README.rst b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/README.rst index e8dd9141..abaa02c2 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/README.rst @@ -53,7 +53,8 @@ Installation Usage =============================================== You should be entering a single choice per line. Choice might -consist of just a single value or value/label pair. +consist of just a single value or value/label pair. If you enter an integer in +the 'max_choices' field, the user can choose only or less choices. For example: From d43c049667e1f86658da56990ab2b5005d7fb5ab Mon Sep 17 00:00:00 2001 From: mariuccio Date: Thu, 12 May 2016 15:41:30 +0200 Subject: [PATCH 4/4] plugin name corrected --- .../fields/select_multiple_with_max/fobi_form_elements.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/fobi_form_elements.py index d00c0b2d..f240277d 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/fobi_form_elements.py @@ -25,7 +25,7 @@ class SelectMultipleWithMaxInputPlugin(FormFieldPlugin): Select multiple field plugin with max. """ uid = UID - name = _("Select multiple") + name = _("Select multiple with max") group = _("Fields") form = SelectMultipleWithMaxInputForm