prepare 0.8.6; functional improvements of the slider plugin;

This commit is contained in:
Artur Barseghyan 2016-10-21 00:19:59 +02:00
parent 518d23d7e6
commit a11d9cdfa0
33 changed files with 534 additions and 221 deletions

View file

@ -15,6 +15,12 @@ are used for versioning (schema follows below):
0.3.4 to 0.4).
- All backwards incompatible changes are mentioned in this document.
0.8.6
-----
2016-10-21
- Functional improvements of `slider` plugin.
0.8.5
-----
2016-10-20

View file

@ -30,12 +30,14 @@ Regarding the form wizards
+ Make sure to include django-formtools package in the requirements.
+ Add navigation buttons to the form wizards.
- Make sure plugin media is collected in wizards.
- Add data-slider-tooltip="hide" option to the `slider`.
+ Add data-slider-tooltip="hide" option to the `slider`.
- Ideally, it would be great to support data-slider-handle="square" (or
"round", "triangle") options of the bootstrap-slider plugin. See the first
issue in "Uncategorised".
- Rethink the new navigation of forms and form wizards.
- Add support for form wizard conditions.
- Fixed broken dependencies for docs.
- Fixed non-collected media in the form wizard.
Roadmap
-------

View file

@ -13,8 +13,9 @@ fobi_plugins = [
]
fobi_forms = [
'fobi.models.FormWizardEntry', 'fobi.models.FormEntry', 'fobi.models.FormElementEntry',
'fobi.models.FormFieldsetEntry', 'fobi.models.FormHandlerEntry',
'fobi.models.FormWizardEntry', 'fobi.models.FormEntry',
'fobi.models.FormElementEntry', 'fobi.models.FormFieldsetEntry',
'fobi.models.FormHandlerEntry',
]
fobi_data = [

View file

@ -2,9 +2,14 @@ from django.core.management import BaseCommand
from admin_tools.dashboard.models import DashboardPreferences
__all__ = ('Command',)
class Command(BaseCommand):
help = """
Clears dashboard preferences.
"""
"""Clears dashboard preferences."""
help = """Clears dashboard preferences."""
def handle(self, *args, **options):
"""Handle."""
DashboardPreferences._default_manager.all().delete()

View file

@ -12,12 +12,14 @@ from django.utils.translation import ugettext_lazy as _
from admin_tools.menu import items, Menu
from admin_tools_dashboard import conf
from . import conf
__all__ = ('CustomMenu',)
class CustomMenu(Menu):
"""
Custom Menu.
"""
"""Custom Menu."""
def __init__(self, **kwargs):
Menu.__init__(self, **kwargs)
self.children += [
@ -26,13 +28,13 @@ class CustomMenu(Menu):
# Foo
self.children.append(items.ModelList(_('Foo'),
models = conf.foo_apps
models=conf.foo_apps
))
# Fobi
self.children.append(items.MenuItem(
_('Fobi'),
children = [
children=[
items.ModelList(_('Plugins'), models=conf.fobi_plugins),
items.ModelList(_('Forms'), models=conf.fobi_forms),
items.ModelList(_('Data'), models=conf.fobi_data),
@ -43,24 +45,22 @@ class CustomMenu(Menu):
# FeinCMS pages integration
self.children.append(items.AppList(
_('FeinCMS Pages'),
models = conf.feincms_pages
models=conf.feincms_pages
))
if 'cms' in settings.INSTALLED_APPS:
# DjangoCMS pages integration
self.children.append(items.AppList(
_('DjangoCMS Pages'),
models = conf.djangocms_pages
models=conf.djangocms_pages
))
# append an app list module for "Administration"
self.children.append(items.AppList(
_('Administration'),
models = ['django.contrib.*',]
models=['django.contrib.*',]
))
def init_with_context(self, context):
"""
Use this method if you need to access the request context.
"""
"""Use this method if you need to access the request context."""
return super(CustomMenu, self).init_with_context(context)

View file

@ -1,5 +1,7 @@
from django.contrib import admin
from mptt.admin import MPTTModelAdmin
from .models import Genre
admin.site.register(Genre, MPTTModelAdmin)

View file

@ -1,14 +1,25 @@
from six import python_2_unicode_compatible
from django.db import models
from mptt.models import MPTTModel, TreeForeignKey
__all__ = ('Genre',)
@python_2_unicode_compatible
class Genre(MPTTModel):
"""Genre."""
name = models.CharField(max_length=50, unique=True)
parent = TreeForeignKey('self', null=True, blank=True,
related_name='children', db_index=True)
class MPTTMeta:
#level_attr = 'mptt_level'
"""MPTT meta."""
# level_attr = 'mptt_level'
order_insertion_by=['name']
def __unicode__(self):
def __str__(self):
return self.name

View file

@ -1,5 +1,6 @@
__all__ = ('disable_admin_tools',)
def disable_admin_tools(request):
"""
Disable admin tools.
"""
"""Disable admin tools."""
return {'ADMIN_TOOLS_DISABLED': True}

View file

@ -4,18 +4,32 @@ from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from customauth.models import MyUser
from .models import MyUser
__all__ = (
'UserCreationForm',
'UserChangeForm',
'MyUserAdmin',
)
class UserCreationForm(forms.ModelForm):
"""A form for creating new users. Includes all the required
fields, plus a repeated password."""
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
"""A form for creating new users.
Includes all the required fields, plus a repeated password.
"""
password1 = forms.CharField(label='Password',
widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation',
widget=forms.PasswordInput)
class Meta:
"""Meta."""
model = MyUser
fields = ('username', 'email', 'first_name', 'last_name', 'date_of_birth')
fields = ('username', 'email', 'first_name', 'last_name',
'date_of_birth',)
def clean_password2(self):
# Check that the two password entries match
@ -35,15 +49,20 @@ class UserCreationForm(forms.ModelForm):
class UserChangeForm(forms.ModelForm):
"""A form for updating users. Includes all the fields on
the user, but replaces the password field with admin's
password hash display field.
"""A form for updating users.
Includes all the fields on the user, but replaces the password field
with admin's password hash display field.
"""
password = ReadOnlyPasswordHashField()
class Meta:
"""Meta."""
model = MyUser
fields = ('username', 'email', 'first_name', 'last_name', 'password', 'date_of_birth', 'is_active',)
fields = ('username', 'email', 'first_name', 'last_name', 'password',
'date_of_birth', 'is_active',)
def clean_password(self):
# Regardless of what the user provides, return the initial value.
@ -53,6 +72,8 @@ class UserChangeForm(forms.ModelForm):
class MyUserAdmin(UserAdmin):
"""MyUser admin."""
# The forms to add and change user instances
form = UserChangeForm
add_form = UserCreationForm
@ -60,10 +81,12 @@ class MyUserAdmin(UserAdmin):
# The fields to be used in displaying the User model.
# These override the definitions on the base UserAdmin
# that reference specific fields on auth.User.
list_display = ('username', 'email', 'first_name', 'last_name', 'date_of_birth',)
list_display = ('username', 'email', 'first_name', 'last_name',
'date_of_birth',)
fieldsets = (
(None, {'fields': ('username', 'email', 'password')}),
('Personal info', {'fields': ('first_name', 'last_name', 'date_of_birth',)}),
('Personal info', {'fields': ('first_name', 'last_name',
'date_of_birth',)}),
('Permissions', {'fields': ('is_active', 'is_staff', 'is_superuser',
'groups', 'user_permissions')}),
)
@ -72,16 +95,18 @@ class MyUserAdmin(UserAdmin):
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('username', 'email', 'first_name', 'last_name', 'date_of_birth',
'password1', 'password2')}
),
'fields': ('username', 'email', 'first_name', 'last_name',
'date_of_birth', 'password1', 'password2')}
)
)
search_fields = ('email',)
ordering = ('email',)
filter_horizontal = ()
filter_horizontal = []
# Now register the new UserAdmin...
admin.site.register(MyUser, MyUserAdmin)
# ... and, since we're not using Django's built-in permissions,
# unregister the Group model from admin.
# ...and, since we're not using Django's built-in permissions,
# un-register the Group model from admin.
admin.site.unregister(Group)

View file

@ -1,5 +1,10 @@
from django.db import models
from django.contrib.auth.models import AbstractUser
__all__ = ('MyUser',)
class MyUser(AbstractUser):
"""My user."""
date_of_birth = models.DateField(null=True, blank=True)

View file

@ -1,7 +1,7 @@
=====================================
Example app
=====================================
Gemeral purpose example app that:
===
foo
===
A general purpose example app that:
- Shows how to make a form callback.
- Contains a sample endpoint for testing the HTTP repost form handler plugin.

View file

@ -2,28 +2,34 @@ from __future__ import print_function
import logging
from fobi.base import FormCallback, form_callback_registry
from fobi.constants import (
CALLBACK_BEFORE_FORM_VALIDATION,
CALLBACK_FORM_VALID_BEFORE_SUBMIT_PLUGIN_FORM_DATA,
CALLBACK_FORM_VALID,
CALLBACK_FORM_VALID_AFTER_FORM_HANDLERS,
CALLBACK_FORM_INVALID
)
logger = logging.getLogger('fobi')
from fobi.constants import (
CALLBACK_BEFORE_FORM_VALIDATION, CALLBACK_FORM_VALID_BEFORE_SUBMIT_PLUGIN_FORM_DATA,
CALLBACK_FORM_VALID, CALLBACK_FORM_VALID_AFTER_FORM_HANDLERS, CALLBACK_FORM_INVALID
)
from fobi.base import FormCallback, form_callback_registry
__all__= (
'SaveAsFooItem',
'DummyInvalidCallback',
)
# *************************************************************
# **************** Save as foo callback ***********************
# *************************************************************
class SaveAsFooItem(FormCallback):
"""
Saves the form as a foo item, if certain conditions are met.
"""
"""Save the form as a foo item, if certain conditions are met."""
stage = CALLBACK_FORM_VALID
def callback(self, form_entry, request, form):
"""
Custom callback login comes here.
"""
"""Custom callback login comes here."""
logger.debug("Great! Your form is valid!")
@ -33,16 +39,14 @@ form_callback_registry.register(SaveAsFooItem)
# **************** Save as foo callback ***********************
# *************************************************************
class DummyInvalidCallback(FormCallback):
"""
Saves the form as a foo item, if certain conditions are met.
"""
"""Saves the form as a foo item, if certain conditions are met."""
stage = CALLBACK_FORM_INVALID
def callback(self, form_entry, request, form):
"""
Custom callback login comes here.
"""
"""Custom callback login comes here."""
logger.debug("Damn! You've made a mistake, boy!")

View file

@ -1,16 +1,20 @@
from django.core.management.base import BaseCommand
from fobi.tests.helpers import (
get_or_create_admin_user, create_form_with_entries
get_or_create_admin_user,
create_form_with_entries
)
__all__ = ('Command',)
class Command(BaseCommand):
"""Creates test data to fill the dashboard with."""
def handle(self, *args, **options):
"""
Creates test data to fill the dashboard with.
"""
"""Handle."""
try:
user = get_or_create_admin_user()
create_form_with_entries(user, create_entries_if_form_exist=False)
except Exception as e:
except Exception as err:
pass

View file

@ -1,4 +1,9 @@
from django.db import models
__all__ = ('FileTest',)
class FileTest(models.Model):
file = models.FileField(upload_to='foo/')
"""File test."""
file = models.FileField(upload_to='foo/')

View file

@ -1,9 +1,13 @@
from django.conf.urls import url
from foo.views import (
endpoint as foo_views_endpoint, forms_list as foo_forms_list,
from .views import (
endpoint as foo_views_endpoint,
forms_list as foo_forms_list
)
__all__ = ('urlpatterns',)
urlpatterns = [
url(r'^endpoint/$', view=foo_views_endpoint, name='foo.endpoint'),
url(r'^forms-list/$', view=foo_forms_list, name='foo.forms_list'),

View file

@ -1,24 +1,34 @@
import uuid
import logging
import uuid
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.template import RequestContext
from django.shortcuts import render_to_response
from django.views.decorators.csrf import csrf_exempt
from fobi.models import FormEntry
from fobi.helpers import handle_uploaded_file
from fobi.base import get_theme
from fobi.helpers import handle_uploaded_file
from fobi.models import FormEntry
from nine import versions
if versions.DJANGO_GTE_1_10:
from django.shortcuts import render
else:
from django.shortcuts import render_to_response
logger = logging.getLogger('fobi')
__all__ = (
'endpoint',
'forms_list',
)
@csrf_exempt
def endpoint(request):
"""
Endpoint.
"""Endpoint.
:param django.http.HttpRequest request:
:param string template_name:
:return django.http.HttpResponse:
"""
logger.debug("POST: {0}\nFILES: {1}".format(request.POST, request.FILES))
@ -32,8 +42,11 @@ def endpoint(request):
def forms_list(request, template_name='foo/forms_list.html'):
"""
Fobi forms list.
"""Fobi forms list.
:param django.http.HttpRequest request:
:param string template_name:
:return django.http.HttpResponse:
"""
form_entries = FormEntry._default_manager.filter(is_public=True) \
.select_related('user')
@ -46,6 +59,10 @@ def forms_list(request, template_name='foo/forms_list.html'):
'show_delete_link': False,
'show_export_link': False,
}
return render_to_response(
template_name, context, context_instance=RequestContext(request)
)
if versions.DJANGO_GTE_1_10:
return render(request, template_name, context)
else:
return render_to_response(
template_name, context, context_instance=RequestContext(request)
)

View file

@ -1,11 +1,10 @@
__all__ = ('SavedFormDataEntryReferenceAdmin',)
from django.contrib import admin
from django.utils.translation import ugettext_lazy as _
from foreign_key_to_saved_form_data_entry.models import (
SavedFormDataEntryReference
)
from .models import SavedFormDataEntryReference
__all__ = ('SavedFormDataEntryReferenceAdmin',)
class SavedFormDataEntryReferenceAdmin(admin.ModelAdmin):
list_display = ('form',)
@ -14,5 +13,6 @@ class SavedFormDataEntryReferenceAdmin(admin.ModelAdmin):
app_label = _('ForeignKey to db_store.SavedFormDataEntry')
admin.site.register(SavedFormDataEntryReference,
SavedFormDataEntryReferenceAdmin)
admin.site.register(
SavedFormDataEntryReference, SavedFormDataEntryReferenceAdmin
)

View file

@ -1,15 +1,21 @@
__all__ = ('SavedFormDataEntryReference',)
from six import python_2_unicode_compatible
from django.db import models
#from fobi.contrib.plugins.form_handlers.db_store.models import (
# SavedFormDataEntry
# )
__all__ = ('SavedFormDataEntryReference',)
@python_2_unicode_compatible
class SavedFormDataEntryReference(models.Model):
"""
Model which references the
"""SavedFormDataEntryReference model.
References the
`fobi.contrib.plugins.form_handlers.db_store.models.SavedFormDataEntry`.
"""
#form = models.ForeignKey(SavedFormDataEntry)
form = models.ForeignKey('fobi_contrib_plugins_form_handlers_db_store.SavedFormDataEntry')
form = models.ForeignKey(
'fobi_contrib_plugins_form_handlers_db_store.SavedFormDataEntry'
)
def __str__(self):
return self.form.name

View file

@ -1,30 +1,28 @@
__all__ = ('RadioInputPlugin',)
from django.forms.fields import ChoiceField
from django.forms.widgets import RadioSelect
from django.utils.translation import ugettext_lazy as _
from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme
from fobi.helpers import safe_text, get_select_field_choices
from fobi.contrib.plugins.form_elements.fields.radio import UID
from fobi.helpers import safe_text, get_select_field_choices
from override_radio_plugin.forms import RadioInputForm
from .forms import RadioInputForm
theme = get_theme(request=None, as_instance=True)
__all__ = ('RadioInputPlugin',)
class RadioInputPlugin(FormFieldPlugin):
"""
Radio field plugin.
"""
"""Radio field plugin."""
uid = UID
name = _("Radio")
group = _("Fields")
form = RadioInputForm
def get_form_field_instances(self, request=None):
"""
Get form field instances.
"""
"""Get form field instances."""
choices = get_select_field_choices(self.data.choices)
widget_attrs = {'class': theme.form_radio_element_html_class}
@ -40,8 +38,7 @@ class RadioInputPlugin(FormFieldPlugin):
return [(self.data.name, ChoiceField, kwargs)]
def submit_plugin_form_data(self, form_entry, request, form):
"""
Submit plugin form data/process.
"""Submit plugin form data/process.
:param fobi.models.FormEntry form_entry: Instance of
``fobi.models.FormEntry``.
@ -63,4 +60,5 @@ class RadioInputPlugin(FormFieldPlugin):
# ``cleaned_data``
return form
form_element_plugin_registry.register(RadioInputPlugin, force=True)

View file

@ -1,5 +1,3 @@
__all__ = ('RadioInputForm',)
from django import forms
from django.utils.translation import ugettext_lazy as _
@ -8,10 +6,12 @@ from fobi.helpers import validate_initial_for_choices
theme = get_theme(request=None, as_instance=True)
__all__ = ('RadioInputForm',)
class RadioInputForm(forms.Form, BaseFormFieldPluginForm):
"""
Form for ``RadioInputPlugin``.
"""
"""Form for ``RadioInputPlugin``."""
plugin_data_fields = [
("label", ""),
("name", ""),
@ -22,54 +22,72 @@ class RadioInputForm(forms.Form, BaseFormFieldPluginForm):
]
label = forms.CharField(
label = _("Label"),
required = True,
widget = forms.widgets.TextInput(attrs={'class': theme.form_element_html_class})
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})
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:<code><br/>"
"&nbsp;&nbsp;&nbsp;&nbsp;1<br/>"
"&nbsp;&nbsp;&nbsp;&nbsp;2<br/>"
"&nbsp;&nbsp;&nbsp;&nbsp;alpha, Alpha<br/>"
"&nbsp;&nbsp;&nbsp;&nbsp;beta, Beta<br/>"
"&nbsp;&nbsp;&nbsp;&nbsp;omega"
"</code><br/>"
"It finally transforms into the following HTML code:<code><br/>"
'&nbsp;&nbsp;&nbsp;&nbsp;&lt;select id="id_NAME_OF_THE_ELEMENT" name="NAME_OF_THE_ELEMENT"&gt;<br/>'
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;option value="1"&gt;1&lt;/option&gt;<br/>'
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;option value="2"&gt;2&lt;/option&gt;<br/>'
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;option value="alpha"&gt;Alpha&lt;/option&gt;<br/>'
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;option value="beta"&gt;Beta&lt;/option&gt;<br/>'
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;option value="omega"&gt;omega&lt;/option&gt;<br/>'
'&nbsp;&nbsp;&nbsp;&nbsp;&lt;/select&gt;'
"</code>"),
widget = forms.widgets.Textarea(attrs={'class': theme.form_element_html_class})
label=_("Choices"),
required=False,
help_text=_("Enter single values/pairs per line. Example:<code><br/>"
"&nbsp;&nbsp;&nbsp;&nbsp;1<br/>"
"&nbsp;&nbsp;&nbsp;&nbsp;2<br/>"
"&nbsp;&nbsp;&nbsp;&nbsp;alpha, Alpha<br/>"
"&nbsp;&nbsp;&nbsp;&nbsp;beta, Beta<br/>"
"&nbsp;&nbsp;&nbsp;&nbsp;omega"
"</code><br/>"
"It finally transforms into the following HTML code:"
"<code><br/>"
'&nbsp;&nbsp;&nbsp;&nbsp;&lt;select '
'id="id_NAME_OF_THE_ELEMENT" '
'name="NAME_OF_THE_ELEMENT"&gt;<br/>'
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'
'&lt;option value="1"&gt;1&lt;/option&gt;<br/>'
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'
'&lt;option value="2"&gt;2&lt;/option&gt;<br/>'
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'
'&lt;option value="alpha"&gt;Alpha&lt;/option&gt;<br/>'
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'
'&lt;option value="beta"&gt;Beta&lt;/option&gt;<br/>'
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'
'&lt;option value="omega"&gt;omega&lt;/option&gt;<br/>'
'&nbsp;&nbsp;&nbsp;&nbsp;&lt;/select&gt;'
"</code>"),
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})
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})
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})
label=_("Required"),
required=False,
widget=forms.widgets.CheckboxInput(
attrs={'class': theme.form_element_checkbox_html_class}
)
)
def clean_initial(self):
"""
Validating the initial value.
"""
"""Validating the initial value."""
return validate_initial_for_choices(self, 'choices', 'initial')

View file

@ -4,26 +4,26 @@ from django.forms.widgets import Select
from django.utils.translation import ugettext_lazy as _
from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme
from fobi.helpers import safe_text
from fobi.contrib.plugins.form_elements.fields.select_model_object import UID
from override_select_model_object_plugin.forms \
import SelectModelObjectInputForm
from fobi.helpers import safe_text
from .forms import SelectModelObjectInputForm
theme = get_theme(request=None, as_instance=True)
__all__ = ('SelectModelObjectInputPlugin',)
class SelectModelObjectInputPlugin(FormFieldPlugin):
"""
Select model object field plugin.
"""
"""Select model object field plugin."""
uid = UID
name = _("Select model object")
group = _("Fields")
form = SelectModelObjectInputForm
def get_form_field_instances(self, request=None):
"""
Get form field instances.
"""
"""Get form field instances."""
app_label, model_name = self.data.model.split('.')
model = models.get_model(app_label, model_name)
queryset = model._default_manager.all()
@ -40,8 +40,7 @@ class SelectModelObjectInputPlugin(FormFieldPlugin):
return [(self.data.name, ModelChoiceField, kwargs)]
def submit_plugin_form_data(self, form_entry, request, form):
"""
Submit plugin form data/process.
"""Submit plugin form data/process.
:param fobi.models.FormEntry form_entry: Instance of
``fobi.models.FormEntry``.
@ -52,9 +51,7 @@ class SelectModelObjectInputPlugin(FormFieldPlugin):
obj = form.cleaned_data.get(self.data.name, None)
if obj:
# Handle the submitted form value
value = '{0}'.format(
safe_text(obj)
)
value = '{0}'.format(safe_text(obj))
# Overwrite ``cleaned_data`` of the ``form`` with object qualifier.
form.cleaned_data[self.data.name] = value

View file

@ -1,5 +1,3 @@
__all__ = ('SelectModelObjectInputForm',)
from django import forms
from django.utils.translation import ugettext_lazy as _
@ -10,10 +8,12 @@ from fobi.contrib.plugins.form_elements.fields.select_model_object.settings \
theme = get_theme(request=None, as_instance=True)
__all__ = ('SelectModelObjectInputForm',)
class SelectModelObjectInputForm(forms.Form, BaseFormFieldPluginForm):
"""
Form for ``SelectModelObjectPlugin``.
"""
"""Form for ``SelectModelObjectPlugin``."""
plugin_data_fields = [
("label", ""),
("name", ""),
@ -24,42 +24,52 @@ class SelectModelObjectInputForm(forms.Form, BaseFormFieldPluginForm):
]
label = forms.CharField(
label = _("Label"),
required = True,
widget = forms.widgets.TextInput(attrs={'class': theme.form_element_html_class})
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})
label=_("Name"),
required=True,
widget=forms.widgets.TextInput(
attrs={'class': theme.form_element_html_class}
)
)
model = forms.ChoiceField(
label = _("Model"),
choices = [],
required = False,
widget = forms.widgets.Select(attrs={'class': theme.form_element_html_class})
label=_("Model"),
choices=[],
required=False,
widget=forms.widgets.Select(
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})
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})
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})
label=_("Required"),
required=False,
widget=forms.widgets.CheckboxInput(
attrs={'class': theme.form_element_checkbox_html_class}
)
)
def __init__(self, *args, **kwargs):
"""
In order to avoid static calls to `get_registered_models`.
"""
"""In order to avoid static calls to `get_registered_models`."""
super(SelectModelObjectInputForm, self).__init__(*args, **kwargs)
self.fields['model'].choices = get_registered_models(
ignore = IGNORED_MODELS
)
ignore=IGNORED_MODELS
)

View file

@ -1,17 +1,18 @@
__all__ = ('MySimpleTheme',)
from fobi.base import theme_registry
from fobi.contrib.themes.simple.fobi_themes import SimpleTheme
__all__ = ('MySimpleTheme',)
class MySimpleTheme(SimpleTheme):
"""
Overriding the "simple" theme.
"""
"""Overriding the "simple" theme."""
html_classes = ['my-simple-theme',]
base_view_template = 'override_simple_theme/base_view.html'
form_ajax = 'override_simple_theme/snippets/form_ajax.html'
form_snippet_template_name = 'override_simple_theme/snippets/form_snippet.html'
form_snippet_template_name = \
'override_simple_theme/snippets/form_snippet.html'
# It's important to set the `force` argument to True, in

View file

@ -1,8 +1,9 @@
from django.utils.translation import ugettext_lazy as _
from feincms.module.page.models import Page
from feincms.content.raw.models import RawContent
from feincms.content.richtext.models import RichTextContent
from feincms.module.page.models import Page
# Import the ``django-fobi`` widget.
from fobi.contrib.apps.feincms_integration.widgets import FobiFormWidget
@ -19,7 +20,7 @@ Page.register_templates(
('sidebar', _(u"Sidebar")),
)
},
)
)
# Standard content types
Page.create_content_type(RawContent)

View file

@ -21,7 +21,7 @@ fobi_theme_home_template_mapping = {
fobi_home_template = fobi_theme_home_template_mapping.get(
DEFAULT_THEME,
'home/base.html'
)
)
FOBI_EDIT_URLS_PREFIX = ''
if DEFAULT_THEME in ('simple', 'djangocms_admin_style_theme'):

View file

@ -199,7 +199,7 @@ for locale_dir in locale_dirs:
for f
in os.listdir(locale_dir)]
version = '0.8.5'
version = '0.8.6'
install_requires = []
# If certain version of Django is already installed, choose version agnostic

View file

@ -1,6 +1,6 @@
__title__ = 'django-fobi'
__version__ = '0.8.5'
__build__ = 0x00005c
__version__ = '0.8.6'
__build__ = 0x00005d
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = '2014-2016 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'

View file

@ -1411,7 +1411,7 @@ class FormElementPlugin(BasePlugin):
# fobi specific context processor. By automatically
# force-prefixing all dynamic value definitions with
# "fobi_dynamic_values." string. See the docs for
# more ("Dyamic initial values" section).
# more ("Dynamic initial values" section).
initial = field_kwargs['initial']
# For the moment, only string types are dynamic

View file

@ -0,0 +1,38 @@
from django.utils.translation import ugettext_lazy as _
__all__ = (
'SLIDER_TOOLTIP_SHOW',
'SLIDER_TOOLTIP_HIDE',
'SLIDER_TOOLTIP_ALWAYS',
'SLIDER_DEFAULT_TOOLTIP',
'SLIDER_TOOLTIP_CHOICES',
'SLIDER_HANDLE_ROUND',
'SLIDER_HANDLE_SQUARE',
'SLIDER_HANDLE_TRIANGLE',
'SLIDER_HANDLE_CUSTOM',
'SLIDER_HANDLE_CHOICES',
)
SLIDER_TOOLTIP_SHOW = 'show'
SLIDER_TOOLTIP_HIDE = 'hide'
SLIDER_TOOLTIP_ALWAYS = 'always'
SLIDER_DEFAULT_TOOLTIP = SLIDER_TOOLTIP_HIDE
SLIDER_TOOLTIP_CHOICES = (
(SLIDER_TOOLTIP_SHOW, _("Show")),
(SLIDER_TOOLTIP_HIDE, _("Hide")),
(SLIDER_TOOLTIP_ALWAYS, _("Always")),
)
SLIDER_HANDLE_ROUND = 'round'
SLIDER_HANDLE_SQUARE = 'square'
SLIDER_HANDLE_TRIANGLE = 'triangle'
SLIDER_HANDLE_CUSTOM = 'custom'
SLIDER_DEFAULT_HANDLE = SLIDER_HANDLE_ROUND
SLIDER_HANDLE_CHOICES = (
(SLIDER_HANDLE_ROUND, _("Round")),
(SLIDER_HANDLE_SQUARE, _("Square")),
(SLIDER_HANDLE_TRIANGLE, _("Triangle")),
(SLIDER_HANDLE_CUSTOM, _("Custom")),
)

View file

@ -1,10 +1,12 @@
from django.forms.fields import ChoiceField
from django.forms.widgets import TextInput, Select
from django.forms.widgets import Select
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 . import UID
from .constants import SLIDER_DEFAULT_TOOLTIP, SLIDER_DEFAULT_HANDLE
from .forms import SliderInputForm
from .settings import INITIAL, MAX_VALUE, MIN_VALUE, STEP
@ -33,27 +35,83 @@ class SliderInputPlugin(FormFieldPlugin):
max_value = self.data.max_value if self.data.max_value else MAX_VALUE
min_value = self.data.min_value if self.data.min_value else MIN_VALUE
step = self.data.step if self.data.step else STEP
tooltip = self.data.tooltip \
if self.data.tooltip \
else SLIDER_DEFAULT_TOOLTIP
handle = self.data.handle \
if self.data.handle \
else SLIDER_DEFAULT_HANDLE
# custom_ticks = get_select_field_choices(self.data.custom_ticks) \
# if self.data.custom_ticks \
# else []
_choices = range(min_value, max_value, step)
choices = zip(_choices, _choices)
# slider_html_class = "slider-no-background" \
# if self.data.disable_slider_background \
# else "slider"
slider_html_class = "slider"
widget_attrs = {
'class': "{0} {1}".format(
slider_html_class,
theme.form_element_html_class
),
'data-slider-min': min_value,
'data-slider-max': max_value,
'data-slider-step': step,
'data-slider-tooltip': tooltip,
'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
tick_label_end = self.data.tick_label_end \
if self.data.tick_label_end \
else max_value
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
),
})
# I hate to do so, but it seems like a dirty workaround that works.
if 'POST' == request.method:
try:
value = int(request.POST.get(self.data.name, initial))
except (ValueError, TypeError) as err:
value = initial
if value < min_value or value > max_value:
value = initial
widget_attrs.update({
'data-slider-value': value,
})
else:
widget_attrs.update({
'data-slider-value': initial,
})
kwargs = {
'label': self.data.label,
'help_text': self.data.help_text,
'initial': initial,
'required': self.data.required,
'choices': choices,
'widget': Select(
attrs={
'class': "slider {0}".format(
theme.form_element_html_class
),
'data-slider-min': min_value,
'data-slider-max': max_value,
'data-slider-step': step,
'data-slider-value': initial,
}
),
'widget': Select(attrs=widget_attrs),
}
return [(self.data.name, ChoiceField, kwargs)]

View file

@ -3,6 +3,8 @@ from django.utils.translation import ugettext_lazy as _
from fobi.base import BaseFormFieldPluginForm, get_theme
from . import constants
from .settings import INITIAL, MAX_VALUE, MIN_VALUE, STEP
__title__ = 'fobi.contrib.plugins.form_elements.fields.slider.forms'
@ -23,6 +25,13 @@ 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),
# ("disable_slider_background", False),
("enable_ticks", False),
("tick_label_start", ""),
("tick_label_end", ""),
# ("custom_ticks", ""),
("help_text", ""),
("initial", INITIAL),
("required", False)
@ -70,6 +79,72 @@ class SliderInputForm(forms.Form, BaseFormFieldPluginForm):
min_value=MIN_VALUE,
max_value=MAX_VALUE
)
tooltip = forms.ChoiceField(
label=_("Tooltip"),
choices=constants.SLIDER_TOOLTIP_CHOICES,
required=False,
widget=forms.widgets.Select(
attrs={'class': theme.form_element_html_class}
)
)
handle = forms.ChoiceField(
label=_("Handle"),
choices=constants.SLIDER_HANDLE_CHOICES,
required=False,
widget=forms.widgets.Select(
attrs={'class': theme.form_element_html_class}
)
)
# disable_slider_background = forms.BooleanField(
# label=_("Disable slider background"),
# required=False,
# widget=forms.widgets.CheckboxInput(
# attrs={'class': theme.form_element_checkbox_html_class}
# )
# )
enable_ticks = forms.BooleanField(
label=_("Enable ticks"),
help_text=_("Adds ticks (endpoints) at start/end"),
required=False,
widget=forms.widgets.CheckboxInput(
attrs={'class': theme.form_element_checkbox_html_class}
)
)
tick_label_start = forms.CharField(
label=_("Tick start label"),
required=False,
widget=forms.widgets.TextInput(
attrs={'class': theme.form_element_html_class}
)
)
tick_label_end = forms.CharField(
label=_("Tick end label"),
required=False,
widget=forms.widgets.TextInput(
attrs={'class': theme.form_element_html_class}
)
)
# custom_ticks = forms.CharField(
# label=_("Custom ticks"),
# required=False,
# help_text=_("Enter single values/pairs per line. Example:<code><br/>"
# "&nbsp;&nbsp;&nbsp;&nbsp;1<br/>"
# "&nbsp;&nbsp;&nbsp;&nbsp;2<br/>"
# "&nbsp;&nbsp;&nbsp;&nbsp;3, Alpha<br/>"
# "&nbsp;&nbsp;&nbsp;&nbsp;4, Beta<br/>"
# "</code><br/>"
# "It finally transforms into the following HTML "
# "code:<code><br/>"
# '&nbsp;&nbsp;&nbsp;&nbsp;'
# 'data-slider-ticks="[1, 2, 3, 4]"<br/>'
# '&nbsp;&nbsp;&nbsp;&nbsp;'
# "data-slider-ticks-labels='"
# '["1", "2", "Alpha", "Beta"]'
# "'</code>"),
# widget=forms.widgets.Textarea(
# attrs={'class': theme.form_element_html_class}
# )
# )
help_text = forms.CharField(
label=_("Help text"),
required=False,
@ -103,6 +178,8 @@ 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']
handle = self.cleaned_data['handle']
if max_value < min_value:
self.add_error(
@ -127,3 +204,11 @@ class SliderInputForm(forms.Form, BaseFormFieldPluginForm):
'min_value',
_("`initial` should be >= than `min_value`.")
)
if handle in (constants.SLIDER_HANDLE_TRIANGLE,
constants.SLIDER_HANDLE_CUSTOM) and enable_ticks:
self.add_error(
'handle',
_("You are not allowed to use Triangle or Custom handles "
"with ticks enabled.")
)

View file

@ -1,3 +1,11 @@
.slider .slider-selection {
background: #BABABA;
background: none;
background-image: none;
}
.slider .slider-selection.tick-slider-selection {
background: none;
background-image: none;
}
.slider .slider-tick.in-selection {
background-image: linear-gradient(to bottom, #f5f5f5 0, #c5c5c5 100%);
}

View file

@ -9,4 +9,5 @@
$(document).ready(function() {
$('.slider').bootstrapSlider();
$('.slider-no-background').bootstrapSlider();
});