2013-05-31 06:49:24 +00:00
|
|
|
from __future__ import unicode_literals
|
2013-05-22 17:12:09 +00:00
|
|
|
from copy import deepcopy
|
|
|
|
|
|
2013-05-31 06:49:24 +00:00
|
|
|
from django.contrib.auth import authenticate
|
2013-06-01 16:02:01 +00:00
|
|
|
from django.contrib.auth.forms import (
|
|
|
|
|
AuthenticationForm, UserCreationForm, UserChangeForm
|
|
|
|
|
)
|
2013-05-22 17:12:09 +00:00
|
|
|
import django.forms
|
|
|
|
|
import django.forms.models
|
|
|
|
|
import django.forms.extras.widgets
|
2013-05-31 06:49:24 +00:00
|
|
|
from django.utils.translation import ugettext_lazy
|
|
|
|
|
|
|
|
|
|
import floppyforms
|
2013-05-22 17:12:09 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
_WIDGET_COMMON_ATTRIBUTES = (
|
|
|
|
|
'is_hidden',
|
|
|
|
|
'needs_multipart_form',
|
|
|
|
|
'is_localized',
|
|
|
|
|
'is_required')
|
|
|
|
|
|
|
|
|
|
_WIDGET_COMMON_ARGUMENTS = ('attrs',)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _copy_attributes(original, new_widget, attributes):
|
|
|
|
|
for attr in attributes:
|
|
|
|
|
original_value = getattr(original, attr)
|
|
|
|
|
original_value = deepcopy(original_value)
|
|
|
|
|
setattr(new_widget, attr, original_value)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _create_widget(widget_class, copy_attributes=(), init_arguments=()):
|
|
|
|
|
# attach defaults that apply for all widgets
|
|
|
|
|
copy_attributes = tuple(copy_attributes) + _WIDGET_COMMON_ATTRIBUTES
|
|
|
|
|
init_arguments = tuple(init_arguments) + _WIDGET_COMMON_ARGUMENTS
|
|
|
|
|
|
|
|
|
|
def create_new_widget(original):
|
|
|
|
|
kwargs = {}
|
|
|
|
|
for argname in init_arguments:
|
|
|
|
|
kwargs[argname] = getattr(original, argname)
|
|
|
|
|
new_widget = widget_class(**kwargs)
|
|
|
|
|
_copy_attributes(
|
|
|
|
|
original,
|
|
|
|
|
new_widget,
|
|
|
|
|
copy_attributes)
|
|
|
|
|
return new_widget
|
|
|
|
|
return create_new_widget
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _create_radioselect(original):
|
|
|
|
|
# return original widget if the renderer is something else than what
|
|
|
|
|
# django ships with by default. This means if this condition evaluates to
|
|
|
|
|
# true, then a custom renderer was specified. We cannot emulate its
|
|
|
|
|
# behaviour so we shouldn't guess and just return the original widget
|
|
|
|
|
if original.renderer is not django.forms.widgets.RadioFieldRenderer:
|
|
|
|
|
return original
|
|
|
|
|
create_new_widget = _create_widget(
|
|
|
|
|
floppyforms.widgets.RadioSelect,
|
|
|
|
|
('allow_multiple_selected',))
|
|
|
|
|
return create_new_widget(original)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _create_splitdatetimewidget(widget_class):
|
|
|
|
|
def create_new_widget(original):
|
|
|
|
|
new_widget = widget_class(
|
|
|
|
|
attrs=original.attrs,
|
|
|
|
|
date_format=original.widgets[0].format,
|
|
|
|
|
time_format=original.widgets[1].format)
|
|
|
|
|
_copy_attributes(original, new_widget, _WIDGET_COMMON_ARGUMENTS)
|
|
|
|
|
return new_widget
|
|
|
|
|
return create_new_widget
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _create_multiwidget(widget_class, copy_attributes=(), init_arguments=()):
|
|
|
|
|
create_new_widget = _create_widget(widget_class, copy_attributes,
|
|
|
|
|
init_arguments)
|
|
|
|
|
|
|
|
|
|
def create_new_multiwidget(original):
|
|
|
|
|
multiwidget = create_new_widget(original)
|
|
|
|
|
multiwidget.widgets = [
|
2013-05-22 20:16:03 +00:00
|
|
|
floppify_widget(widget)
|
2013-05-22 17:12:09 +00:00
|
|
|
for widget in multiwidget.widgets]
|
|
|
|
|
return multiwidget
|
|
|
|
|
return create_new_multiwidget
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# this dictionary keeps a mapping from django's widget classes to a callable
|
|
|
|
|
# that will accept an instance of this class. It will return a new instance of
|
|
|
|
|
# a corresponding floppyforms widget, with the same semantics -- all relevant
|
|
|
|
|
# attributes will be copied to the new widget.
|
|
|
|
|
_django_to_floppyforms_widget = {
|
|
|
|
|
django.forms.widgets.Input:
|
|
|
|
|
_create_widget(floppyforms.widgets.Input, ('input_type',)),
|
|
|
|
|
django.forms.widgets.TextInput:
|
|
|
|
|
_create_widget(floppyforms.widgets.TextInput, ('input_type',)),
|
|
|
|
|
django.forms.widgets.PasswordInput:
|
|
|
|
|
_create_widget(floppyforms.widgets.PasswordInput, ('input_type',)),
|
|
|
|
|
django.forms.widgets.HiddenInput:
|
|
|
|
|
_create_widget(floppyforms.widgets.HiddenInput, ('input_type',)),
|
|
|
|
|
django.forms.widgets.MultipleHiddenInput:
|
|
|
|
|
_create_widget(
|
|
|
|
|
floppyforms.widgets.MultipleHiddenInput,
|
|
|
|
|
('input_type',),
|
|
|
|
|
init_arguments=('choices',)),
|
|
|
|
|
django.forms.widgets.FileInput:
|
|
|
|
|
_create_widget(floppyforms.widgets.FileInput, ('input_type',)),
|
|
|
|
|
django.forms.widgets.ClearableFileInput:
|
|
|
|
|
_create_widget(
|
|
|
|
|
floppyforms.widgets.ClearableFileInput,
|
|
|
|
|
(
|
|
|
|
|
'input_type', 'initial_text', 'input_text',
|
|
|
|
|
'clear_checkbox_label', 'template_with_initial',
|
|
|
|
|
'template_with_clear')),
|
|
|
|
|
django.forms.widgets.Textarea:
|
|
|
|
|
_create_widget(floppyforms.widgets.Textarea),
|
|
|
|
|
django.forms.widgets.DateInput:
|
|
|
|
|
_create_widget(
|
|
|
|
|
floppyforms.widgets.DateInput,
|
|
|
|
|
init_arguments=('format',)),
|
|
|
|
|
django.forms.widgets.DateTimeInput:
|
|
|
|
|
_create_widget(
|
|
|
|
|
floppyforms.widgets.DateTimeInput,
|
|
|
|
|
init_arguments=('format',)),
|
|
|
|
|
django.forms.widgets.TimeInput:
|
|
|
|
|
_create_widget(
|
|
|
|
|
floppyforms.widgets.TimeInput,
|
|
|
|
|
init_arguments=('format',)),
|
|
|
|
|
django.forms.widgets.CheckboxInput:
|
|
|
|
|
_create_widget(floppyforms.widgets.CheckboxInput, ('check_test',)),
|
|
|
|
|
django.forms.widgets.Select:
|
|
|
|
|
_create_widget(
|
|
|
|
|
floppyforms.widgets.Select,
|
|
|
|
|
('allow_multiple_selected',)),
|
|
|
|
|
django.forms.widgets.NullBooleanSelect:
|
|
|
|
|
_create_widget(
|
|
|
|
|
floppyforms.widgets.NullBooleanSelect,
|
|
|
|
|
('allow_multiple_selected',)),
|
|
|
|
|
django.forms.widgets.SelectMultiple:
|
|
|
|
|
_create_widget(
|
|
|
|
|
floppyforms.widgets.SelectMultiple,
|
|
|
|
|
('allow_multiple_selected',)),
|
|
|
|
|
django.forms.widgets.RadioSelect:
|
|
|
|
|
_create_radioselect,
|
|
|
|
|
django.forms.widgets.CheckboxSelectMultiple:
|
|
|
|
|
_create_widget(floppyforms.widgets.CheckboxSelectMultiple),
|
|
|
|
|
django.forms.widgets.MultiWidget:
|
|
|
|
|
_create_widget(
|
|
|
|
|
floppyforms.widgets.MultiWidget,
|
|
|
|
|
init_arguments=('widgets',)),
|
|
|
|
|
django.forms.widgets.SplitDateTimeWidget:
|
|
|
|
|
_create_splitdatetimewidget(
|
|
|
|
|
floppyforms.widgets.SplitDateTimeWidget),
|
|
|
|
|
django.forms.widgets.SplitHiddenDateTimeWidget:
|
|
|
|
|
_create_splitdatetimewidget(
|
|
|
|
|
floppyforms.widgets.SplitHiddenDateTimeWidget),
|
|
|
|
|
django.forms.extras.widgets.SelectDateWidget:
|
|
|
|
|
_create_widget(
|
|
|
|
|
floppyforms.widgets.SelectDateWidget,
|
|
|
|
|
init_arguments=('years', 'required')),
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-22 20:16:03 +00:00
|
|
|
_django_field_to_floppyform_widget = {
|
|
|
|
|
django.forms.fields.FloatField:
|
|
|
|
|
_create_widget(floppyforms.widgets.NumberInput),
|
|
|
|
|
django.forms.fields.DecimalField:
|
|
|
|
|
_create_widget(floppyforms.widgets.NumberInput),
|
|
|
|
|
django.forms.fields.IntegerField:
|
|
|
|
|
_create_widget(floppyforms.widgets.NumberInput),
|
|
|
|
|
django.forms.fields.EmailField:
|
|
|
|
|
_create_widget(floppyforms.widgets.EmailInput),
|
|
|
|
|
django.forms.fields.URLField:
|
|
|
|
|
_create_widget(floppyforms.widgets.URLInput),
|
|
|
|
|
django.forms.fields.SlugField:
|
|
|
|
|
_create_widget(floppyforms.widgets.SlugInput),
|
|
|
|
|
django.forms.fields.IPAddressField:
|
|
|
|
|
_create_widget(floppyforms.widgets.IPAddressInput),
|
|
|
|
|
django.forms.fields.SplitDateTimeField:
|
|
|
|
|
_create_splitdatetimewidget(floppyforms.widgets.SplitDateTimeWidget),
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-22 17:12:09 +00:00
|
|
|
|
2013-05-22 20:16:03 +00:00
|
|
|
def floppify_widget(widget, field=None):
|
2013-05-22 17:12:09 +00:00
|
|
|
'''
|
|
|
|
|
Get an instance of django.forms.widgets.Widget and return a new widget
|
|
|
|
|
instance but using the corresponding floppyforms widget class.
|
|
|
|
|
|
|
|
|
|
Only original django widgets will be replaced with a floppyforms version.
|
|
|
|
|
The widget will be returned unaltered if it is not known, e.g. if it's a
|
|
|
|
|
custom widget from a third-party app.
|
2013-05-22 20:16:03 +00:00
|
|
|
|
|
|
|
|
The optional parameter ``field`` can be used to influence the widget
|
|
|
|
|
creation further. This is useful since floppyforms supports more widgets
|
|
|
|
|
than django does. For example is django using a ``TextInput`` for a
|
|
|
|
|
``EmailField``, but floppyforms has a better suiting widget called
|
|
|
|
|
``EmailInput``. If a widget is found specifically for the passed in
|
|
|
|
|
``field``, it will take precendence to the first parameter ``widget``
|
|
|
|
|
which will effectively be ignored.
|
2013-05-22 17:12:09 +00:00
|
|
|
'''
|
2013-05-22 20:16:03 +00:00
|
|
|
if field is not None:
|
|
|
|
|
create_widget = _django_field_to_floppyform_widget.get(
|
|
|
|
|
field.__class__)
|
|
|
|
|
if create_widget is not None:
|
|
|
|
|
# check if the default widget was replaced by a different one, in
|
|
|
|
|
# that case we cannot create the field specific floppyforms
|
|
|
|
|
# widget.
|
|
|
|
|
if field.widget.__class__ is field.__class__.widget:
|
|
|
|
|
return create_widget(widget)
|
|
|
|
|
create_widget = _django_to_floppyforms_widget.get(widget.__class__)
|
|
|
|
|
if create_widget is not None:
|
|
|
|
|
return create_widget(widget)
|
2013-05-22 17:12:09 +00:00
|
|
|
return widget
|
|
|
|
|
|
|
|
|
|
|
2013-05-22 20:16:03 +00:00
|
|
|
def floppify_form(form_class):
|
|
|
|
|
'''
|
|
|
|
|
Take a normal form and return a subclass of that form that replaces all
|
|
|
|
|
django widgets with the corresponding floppyforms widgets.
|
|
|
|
|
'''
|
|
|
|
|
new_form_class = type(form_class.__name__, (form_class,), {})
|
|
|
|
|
for field in new_form_class.base_fields.values():
|
|
|
|
|
field.widget = floppify_widget(field.widget, field=field)
|
|
|
|
|
return new_form_class
|
|
|
|
|
|
|
|
|
|
|
2013-05-22 17:12:09 +00:00
|
|
|
def modelform_factory(model, form=django.forms.models.ModelForm, fields=None,
|
|
|
|
|
exclude=None, formfield_callback=None, widgets=None):
|
|
|
|
|
form_class = django.forms.models.modelform_factory(
|
|
|
|
|
model=model,
|
|
|
|
|
form=form,
|
|
|
|
|
fields=fields,
|
|
|
|
|
exclude=exclude,
|
|
|
|
|
formfield_callback=formfield_callback,
|
|
|
|
|
widgets=widgets)
|
2013-05-22 20:16:03 +00:00
|
|
|
return floppify_form(form_class)
|
2013-05-31 06:49:24 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
ERROR_MESSAGE = ugettext_lazy("Please enter the correct %(username)s and password "
|
|
|
|
|
"for a staff account. Note that both fields may be case-sensitive.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AdminAuthenticationForm(AuthenticationForm):
|
|
|
|
|
"""
|
|
|
|
|
A custom authentication form used in the admin app.
|
|
|
|
|
Liberally copied from django.contrib.admin.forms.AdminAuthenticationForm
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
this_is_the_login_form = django.forms.BooleanField(widget=floppyforms.HiddenInput, initial=1,
|
|
|
|
|
error_messages={'required': ugettext_lazy("Please log in again, because your session has expired.")})
|
|
|
|
|
|
|
|
|
|
def clean(self):
|
|
|
|
|
username = self.cleaned_data.get('username')
|
|
|
|
|
password = self.cleaned_data.get('password')
|
|
|
|
|
message = ERROR_MESSAGE
|
|
|
|
|
|
|
|
|
|
if username and password:
|
|
|
|
|
self.user_cache = authenticate(username=username, password=password)
|
|
|
|
|
if self.user_cache is None:
|
|
|
|
|
raise floppyforms.ValidationError(message % {
|
|
|
|
|
'username': self.username_field.verbose_name
|
|
|
|
|
})
|
|
|
|
|
elif not self.user_cache.is_active or not self.user_cache.is_staff:
|
|
|
|
|
raise floppyforms.ValidationError(message % {
|
|
|
|
|
'username': self.username_field.verbose_name
|
|
|
|
|
})
|
|
|
|
|
return self.cleaned_data
|
2013-06-01 16:02:01 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
UserCreationForm = floppify_form(UserCreationForm)
|
|
|
|
|
UserChangeForm = floppify_form(UserChangeForm)
|