diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9783ddc1..fc852588 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -15,6 +15,19 @@ are used for versioning (schema follows below): 0.3.4 to 0.4). - All backwards incompatible changes are mentioned in this document. +0.5.19 +------ +2015-12-15 + +- New style urls everywhere. + +0.5.18 +------ +2015-12-08 + +- Minor improvements. Adding request to the `get_form_field_instances` method + of the `FormElementPlugin`. + 0.5.17 ------ 2015-10-22 diff --git a/MANIFEST.in b/MANIFEST.in index ec8f591c..6be9ca70 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -46,3 +46,5 @@ recursive-include src/fobi/contrib/plugins/form_elements/content/content_image/t recursive-include src/fobi/contrib/plugins/form_handlers/db_store/templates * recursive-include src/fobi/contrib/plugins/form_handlers/mail/templates * recursive-include src/fobi/contrib/plugins/form_handlers/http_repost/templates * + +#recursive-include src/fobi/contrib/plugins/form_importers/mailchimp_importer/templates * diff --git a/QUICK_START.rst b/QUICK_START.rst index f957209f..80a9095c 100644 --- a/QUICK_START.rst +++ b/QUICK_START.rst @@ -180,7 +180,7 @@ Add the following line to ``urlpatterns`` of your `urls` module. .. code-block:: python - urlpatterns = patterns('', + urlpatterns = [ # ... # DB Store plugin URLs @@ -195,7 +195,7 @@ Add the following line to ``urlpatterns`` of your `urls` module. # ... - ) + ] Update the database ^^^^^^^^^^^^^^^^^^^ diff --git a/README.rst b/README.rst index d44f34ca..7e4de898 100644 --- a/README.rst +++ b/README.rst @@ -75,19 +75,18 @@ Main features and highlights `_ (in a form of a Mezzanine page). - Reordering of form elements using drag-n-drop. -- Data export (`db_store +- Data export (`DB store `_ form handler plugin) into XLS/CSV format. - `Dynamic initial values`_ for form elements. - Import/export forms to/from JSON format. +- Import forms from MailChimp using `mailchimp importer + `_. Roadmap ======= Some of the upcoming/in-development features/improvements are: -- Form importers (and as a part of it - MailChimp integration, - which would allow to import forms from MailChimp into `django-fobi` using - a user-friendly wizard). - Fieldsets. See the `TODOS `_ @@ -114,10 +113,10 @@ Credentials: Run demo locally ---------------- -In order to be able to quickly evaluate the `django-fobi`, a demo app (with a quick -installer) has been created (works on Ubuntu/Debian, may work on other Linux -systems as well, although not guaranteed). Follow the instructions below for -having the demo running within a minute. +In order to be able to quickly evaluate the `django-fobi`, a demo app (with a +quick installer) has been created (works on Ubuntu/Debian, may work on other +Linux systems as well, although not guaranteed). Follow the instructions below +for having the demo running within a minute. Grab the latest `django_fobi_example_app_installer.sh`: @@ -369,7 +368,7 @@ Defining the Sample textarea plugin. form = SampleTextareaForm group = "Samples" # Group to which the plugin belongs to - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): kwargs = { 'required': self.data.required, 'label': self.data.label, diff --git a/ROADMAP.rst b/ROADMAP.rst index 4c8ddb6e..645a6671 100644 --- a/ROADMAP.rst +++ b/ROADMAP.rst @@ -4,21 +4,16 @@ Changelog for upcoming releases --- yyyy-mm-ddd (upcoming). +This release contains minor backwards incompatible changes, related to the +change of the name of the "simple" theme into "django_admin_style" theme. + - Fieldsets. +- The "simple" theme has been renamed to "django_admin_style". 0.6 --- yyyy-mm-ddd (upcoming). -This release contains minor backwards incompatible changes, related to the -change of the name of the "simple" theme into "django_admin_style" theme. - -- Mailchimp support. -- Kube framework integration (theme). -- PureCSS framework integration (theme). -- Skeleton framework integration (theme). -- Baseline framework integration (theme). -- Amazium framework integration (theme). -- The "simple" theme has been renamed to "django_admin_style". -- Internally, make a date when form has been created. Also keep track of when - the form has been last edited. +- Form importers (and as a part of it - MailChimp integration, + which would allow to import forms from MailChimp into `django-fobi` using + a user-friendly wizard). diff --git a/TODOS.rst b/TODOS.rst index c03ea3eb..2c4bb0de 100644 --- a/TODOS.rst +++ b/TODOS.rst @@ -260,7 +260,7 @@ Must haves - Nicer styling for the radio button (Simple theme). - Make it possible to provide an alternative rendering of the form field in the correspondent form field plugin widget (in such a way, that it - falls back to the defaut rendering when no custom is available and + falls back to the default rendering when no custom is available and uses the custom rendering if available). This should be done on the widget level, so that it's not necessary to update the theme in case of customisations made for one or more form field plugins (the rendering @@ -279,6 +279,8 @@ Must haves `get_form_hidden_fields_errors` template tags into another template tag library or product to reuse it in Django-dash as well. Move the permission code from `decorators` into a separate package. +- Update the `djangocms_admin_style` theme, since it stopped looking nice + with the latest versions of the packages. Should haves ============ @@ -359,6 +361,7 @@ Should haves - Think of making putting several actions (repair) into the management interface (UI). - Make Django's CSRF validation optional. +- Quiz mode (randomize the ordering of the form elements). Could haves =========== @@ -389,6 +392,11 @@ Could haves - Add option to redirect to another page. - Make a Django<->Fobi list of supported fields with proper `referencies `_. +- Kube framework integration (theme). +- PureCSS framework integration (theme). +- Skeleton framework integration (theme). +- Baseline framework integration (theme). +- Amazium framework integration (theme). Would haves =========== diff --git a/docs/index.rst b/docs/index.rst index 40249686..077b4965 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -13,12 +13,6 @@ Prerequisites - Django 1.5, 1.6, 1.7, 1.8 - Python >= 2.6.8, >= 2.7, >= 3.3 -Note, that Django 1.8 is not yet proclaimed to be flawlessly supported. The -core and contrib packages (with no additional dependencies) have been tested -against the latest stable Django 1.8 release. All tests have successfully -passed, although it's yet too early to claim that Django 1.8 is fully -supported. - Key concepts ============ - Each form consists of elements. Form elements are divided diff --git a/examples/mezzanine_example/urls.py b/examples/mezzanine_example/urls.py index 3d84bf69..35f13914 100644 --- a/examples/mezzanine_example/urls.py +++ b/examples/mezzanine_example/urls.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals -from django.conf.urls import patterns, include, url +from django.conf.urls import include, url from django.conf.urls.i18n import i18n_patterns from django.contrib import admin @@ -41,17 +41,17 @@ admin.autodiscover() # You can also change the ``home`` view to add your own functionality # to the project's homepage. -urlpatterns = i18n_patterns("", +urlpatterns = i18n_patterns([ # Change the admin prefix here to use an alternate URL for the # admin interface, which would be marginally more secure. ("^admin/", include(admin.site.urls)), -) +]) # *********** # Fobi patterns # *********** -urlpatterns += patterns('', +urlpatterns += [ # DB Store plugin URLs url(r'^fobi/plugins/form-handlers/db-store/', include('fobi.contrib.plugins.form_handlers.db_store.urls')), @@ -66,13 +66,13 @@ urlpatterns += patterns('', # django-fobi public forms contrib app: #url(r'^', include('fobi.contrib.apps.public_forms.urls')), - ) +] # *********** # End fobi patterns # *********** -urlpatterns += patterns('', +urlpatterns += [ # We don't want to presume how your homepage works, so here are a # few patterns you can use to set it up. @@ -122,7 +122,7 @@ urlpatterns += patterns('', # ``mezzanine.urls``, go right ahead and take the parts you want # from it, and use them directly below instead of using # ``mezzanine.urls``. - ("^", include("mezzanine.urls")), + url("^", include("mezzanine.urls")), # MOUNTING MEZZANINE UNDER A PREFIX # --------------------------------- @@ -140,7 +140,7 @@ urlpatterns += patterns('', # ("^%s/" % settings.SITE_PREFIX, include("mezzanine.urls")) -) +] # Adds ``STATIC_URL`` to the context of error pages, so that error # pages can use JS, CSS and images. diff --git a/examples/quick_start/quick_start/urls.py b/examples/quick_start/quick_start/urls.py index 049ecf95..8e3a230f 100644 --- a/examples/quick_start/quick_start/urls.py +++ b/examples/quick_start/quick_start/urls.py @@ -1,7 +1,7 @@ -from django.conf.urls import patterns, include, url +from django.conf.urls import include, url from django.contrib import admin -urlpatterns = patterns('', +urlpatterns = [ # Examples: # url(r'^$', 'quick_start.views.home', name='home'), # url(r'^blog/', include('blog.urls')), @@ -20,4 +20,4 @@ urlpatterns = patterns('', # Edit URLs url(r'^fobi/', include('fobi.urls.edit')), #, namespace='fobi' -) +] diff --git a/examples/quick_start/requirements.txt b/examples/quick_start/requirements.txt index a8555aac..a07cfb6f 100644 --- a/examples/quick_start/requirements.txt +++ b/examples/quick_start/requirements.txt @@ -1,13 +1,12 @@ -Django==1.7.4 -Pillow==2.7.0 -Unidecode==0.04.17 -argparse==1.2.1 -django-autoslug==1.7.2 -django-fobi==0.4.18 -easy-thumbnails==1.5 -ipython==3.0.0-b1 +Django==1.8.5 +Pillow==3.0.0 +Unidecode==0.04.18 +argparse==1.4.0 +django-autoslug==1.9.3 +django-fobi==0.5.17 +easy-thumbnails==2.2 +ipython==4.0.0 ordereddict==1.1 -requests==2.5.1 -six==1.9.0 -vishap==0.1.4 -wsgiref==0.1.2 +requests==2.8.1 +six==1.10.0 +vishap==0.1.5 diff --git a/examples/requirements.txt b/examples/requirements.txt index cbb35854..cbb444b9 100644 --- a/examples/requirements.txt +++ b/examples/requirements.txt @@ -3,6 +3,7 @@ Django Jinja2 MarkupSafe MySQL-python +mailchimp #South Sphinx django-admin-tools>=0.5.2 diff --git a/examples/requirements_django_1_8.txt b/examples/requirements_django_1_8.txt index d840ac07..426c8c06 100644 --- a/examples/requirements_django_1_8.txt +++ b/examples/requirements_django_1_8.txt @@ -3,11 +3,13 @@ Django>=1.8,<1.9 Jinja2 MarkupSafe +mailchimp #MySQL-python Sphinx django-admin-tools>=0.5.2 django-autoslug==1.7.1 django-debug-toolbar==0.11.0 +django-formtools django-localeurl>=2.0.2 django-registration-redux>=1.1 docutils diff --git a/examples/requirements_django_1_9.txt b/examples/requirements_django_1_9.txt new file mode 100644 index 00000000..e8df3956 --- /dev/null +++ b/examples/requirements_django_1_9.txt @@ -0,0 +1,48 @@ +alabaster==0.7.6 +Babel==2.1.1 +decorator==4.0.4 +Django==1.9 +django-admin-tools==0.7.1 +django-autoslug==1.9.3 +django-debug-toolbar==0.11 +django-fobi==0.5.18 +django-formtools==1.0 +django-localeurl==2.0.2 +django-nine==0.1.6 +django-nonefield==0.1 +django-registration-redux==1.3a0 +docopt==0.4.0 +docutils==0.12 +easy-thumbnails==2.3 +ipdb==0.8.1 +ipython==4.0.0 +ipython-genutils==0.1.0 +Jinja2==2.8 +mailchimp==2.0.9 +MarkupSafe==0.23 +ordereddict==1.1 +path.py==8.1.2 +pexpect==4.0.1 +pickleshare==0.5 +Pillow==3.0.0 +pluggy==0.3.1 +ptyprocess==0.5 +py==1.4.30 +Pygments==2.0.2 +pytz==2015.6 +requests==2.8.1 +selenium==2.48.0 +simple-timer==0.2 +simplegeneric==0.8.1 +simplejson==3.8.0 +six==1.10.0 +snowballstemmer==1.2.0 +Sphinx==1.3.1 +sphinx-rtd-theme==0.1.9 +sqlparse==0.1.17 +tox==2.1.1 +traitlets==4.0.0 +Unidecode==0.4.18 +virtualenv==13.1.2 +vishap==0.1.5 +wheel==0.24.0 diff --git a/examples/simple/foo/urls.py b/examples/simple/foo/urls.py index 14a200dc..366fda06 100644 --- a/examples/simple/foo/urls.py +++ b/examples/simple/foo/urls.py @@ -1,5 +1,7 @@ -from django.conf.urls import patterns, url +from django.conf.urls import url -urlpatterns = patterns('foo.views', - url(r'^endpoint/$', view='endpoint', name='foo.endpoint'), - ) +from foo.views import endpoint as foo_views_endpoint + +urlpatterns = [ + url(r'^endpoint/$', view=foo_views_endpoint, name='foo.endpoint'), +] diff --git a/examples/simple/local_settings.example b/examples/simple/local_settings.example index 6b984b2e..59cde6e6 100644 --- a/examples/simple/local_settings.example +++ b/examples/simple/local_settings.example @@ -4,6 +4,7 @@ PROJECT_DIR = lambda base : os.path.abspath(os.path.join(os.path.dirname(__file_ DEBUG = True DEBUG_TOOLBAR = not True TEMPLATE_DEBUG = not True +DEV = True DATABASES = { 'default': { diff --git a/examples/simple/override_radio_plugin/fobi_form_elements.py b/examples/simple/override_radio_plugin/fobi_form_elements.py index 5f6844d6..901e66ea 100644 --- a/examples/simple/override_radio_plugin/fobi_form_elements.py +++ b/examples/simple/override_radio_plugin/fobi_form_elements.py @@ -21,7 +21,7 @@ class RadioInputPlugin(FormFieldPlugin): group = _("Fields") form = RadioInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/examples/simple/override_select_model_object_plugin/fobi_form_elements.py b/examples/simple/override_select_model_object_plugin/fobi_form_elements.py index b203bd92..dedd9b8e 100644 --- a/examples/simple/override_select_model_object_plugin/fobi_form_elements.py +++ b/examples/simple/override_select_model_object_plugin/fobi_form_elements.py @@ -20,7 +20,7 @@ class SelectModelObjectInputPlugin(FormFieldPlugin): group = _("Fields") form = SelectModelObjectInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/examples/simple/runserver-bootstrap3-theme-django-1-9.sh b/examples/simple/runserver-bootstrap3-theme-django-1-9.sh new file mode 100755 index 00000000..5059f5ee --- /dev/null +++ b/examples/simple/runserver-bootstrap3-theme-django-1-9.sh @@ -0,0 +1,2 @@ +#workon fobi +./manage.py runserver 0.0.0.0:8000 --traceback -v 3 --settings=settings_bootstrap3_theme_django_1_9 --traceback -v 3 diff --git a/examples/simple/settings.py b/examples/simple/settings.py index 2806a040..2e747130 100644 --- a/examples/simple/settings.py +++ b/examples/simple/settings.py @@ -8,7 +8,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(__file__)) DEBUG = False DEBUG_TOOLBAR = False -TEMPLATE_DEBUG = DEBUG +DEV = False ADMINS = ( # ('Your Name', 'your_email@example.com'), @@ -101,12 +101,67 @@ STATICFILES_FINDERS = ( # Make this unique, and don't share it with anybody. SECRET_KEY = '97818c*w97Zi8a-m^1coRRrmurMI6+q5_kyn*)s@(*_Pk6q423' -# List of callables that know how to import templates from various sources. -TEMPLATE_LOADERS = ( - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', - 'django.template.loaders.eggs.Loader', -) +from nine.versions import DJANGO_GTE_1_7, DJANGO_GTE_1_8 + +if DJANGO_GTE_1_8: + TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + #'APP_DIRS': True, + 'DIRS': [PROJECT_DIR('templates'),], + 'OPTIONS': { + 'context_processors': [ + "django.contrib.auth.context_processors.auth", + "django.core.context_processors.debug", + "django.core.context_processors.i18n", + "django.core.context_processors.media", + "django.core.context_processors.static", + "django.core.context_processors.tz", + "django.contrib.messages.context_processors.messages", + "django.core.context_processors.request", + "fobi.context_processors.theme", # Important! + "fobi.context_processors.dynamic_values", # Optional + ], + 'loaders': [ + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', + 'django.template.loaders.eggs.Loader', + 'admin_tools.template_loaders.Loader', + ], + 'debug': DEBUG, + } + }, + ] +else: + TEMPLATE_DEBUG = DEBUG + + # List of callables that know how to import templates from various sources. + TEMPLATE_LOADERS = ( + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', + 'django.template.loaders.eggs.Loader', + 'admin_tools.template_loaders.Loader', + ) + + TEMPLATE_CONTEXT_PROCESSORS = ( + "django.contrib.auth.context_processors.auth", + "django.core.context_processors.debug", + "django.core.context_processors.i18n", + "django.core.context_processors.media", + "django.core.context_processors.static", + "django.core.context_processors.tz", + "django.contrib.messages.context_processors.messages", + "django.core.context_processors.request", + "fobi.context_processors.theme", # Important! + "fobi.context_processors.dynamic_values", # Optional + ) + + TEMPLATE_DIRS = ( + # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. + PROJECT_DIR('templates'), + ) MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', @@ -124,26 +179,6 @@ ROOT_URLCONF = 'urls' # Python dotted path to the WSGI application used by Django's runserver. WSGI_APPLICATION = 'wsgi.application' -TEMPLATE_CONTEXT_PROCESSORS = ( - "django.contrib.auth.context_processors.auth", - "django.core.context_processors.debug", - "django.core.context_processors.i18n", - "django.core.context_processors.media", - "django.core.context_processors.static", - "django.core.context_processors.tz", - "django.contrib.messages.context_processors.messages", - "django.core.context_processors.request", - "fobi.context_processors.theme", # Important! - "fobi.context_processors.dynamic_values", # Optional -) - -TEMPLATE_DIRS = ( - # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". - # Always use forward slashes, even on Windows. - # Don't forget to use absolute paths, not relative paths. - PROJECT_DIR('templates'), -) - #FIXTURE_DIRS = ( # PROJECT_DIR(os.path.join('..', 'fixtures')) #) @@ -243,6 +278,13 @@ INSTALLED_APPS = ( 'fobi.contrib.plugins.form_handlers.http_repost', 'fobi.contrib.plugins.form_handlers.mail', + # *********************************************************************** + # *********************************************************************** + # ************************* Fobi form importers ************************* + # *********************************************************************** + # *********************************************************************** + 'fobi.contrib.plugins.form_importers.mailchimp_importer', + # *********************************************************************** # *********************************************************************** # ************************** Fobi themes ******************************** @@ -505,7 +547,6 @@ LOGGING = { } # Make settings quite compatible among various Django versions used. -from nine.versions import DJANGO_GTE_1_7, DJANGO_GTE_1_8 if DJANGO_GTE_1_7 or DJANGO_GTE_1_8: INSTALLED_APPS = list(INSTALLED_APPS) @@ -557,7 +598,7 @@ if DEBUG and DEBUG_TOOLBAR: except ImportError: pass -if DEBUG and TEMPLATE_DEBUG: +if DEBUG: try: # Make sure the django-template-debug is installed. You can then # in templates use it as follows: @@ -571,3 +612,7 @@ if DEBUG and TEMPLATE_DEBUG: except ImportError: pass +# Make the `django-fobi` package available without installation. +if DEV: + import sys + sys.path.insert(0, os.path.abspath('../../src')) diff --git a/examples/simple/settings_bootstrap3_theme_django_1_9.py b/examples/simple/settings_bootstrap3_theme_django_1_9.py new file mode 100644 index 00000000..8e580cae --- /dev/null +++ b/examples/simple/settings_bootstrap3_theme_django_1_9.py @@ -0,0 +1,19 @@ +from settings import * + +INSTALLED_APPS = list(INSTALLED_APPS) + +try: + INSTALLED_APPS.remove('south') if 'south' in INSTALLED_APPS else None + INSTALLED_APPS.remove('tinymce') if 'tinymce' in INSTALLED_APPS else None +except Exception as e: + pass + +try: + INSTALLED_APPS.remove('admin_tools') \ + if 'admin_tools' in INSTALLED_APPS else None + INSTALLED_APPS.remove('admin_tools.menu') \ + if 'admin_tools.menu' in INSTALLED_APPS else None + INSTALLED_APPS.remove('admin_tools.dashboard') \ + if 'admin_tools.dashboard' in INSTALLED_APPS else None +except Exception as e: + pass diff --git a/examples/simple/settings_djangocms_admin_style_theme_djangocms.py b/examples/simple/settings_djangocms_admin_style_theme_djangocms.py index 7aadd9c5..8832bf3a 100644 --- a/examples/simple/settings_djangocms_admin_style_theme_djangocms.py +++ b/examples/simple/settings_djangocms_admin_style_theme_djangocms.py @@ -12,6 +12,7 @@ INSTALLED_APPS += [ # Some plugins 'djangocms_picture', 'djangocms_snippet', + 'treebeard', 'fobi.contrib.apps.djangocms_integration', # Fobi DjangoCMS app @@ -41,12 +42,20 @@ MIDDLEWARE_CLASSES += [ #'django.middleware.cache.FetchFromCacheMiddleware', ] -TEMPLATE_CONTEXT_PROCESSORS = list(TEMPLATE_CONTEXT_PROCESSORS) -TEMPLATE_CONTEXT_PROCESSORS += [ - 'cms.context_processors.media', - 'sekizai.context_processors.sekizai', - 'cms.context_processors.cms_settings', -] +from nine.versions import DJANGO_GTE_1_8 +if DJANGO_GTE_1_8: + TEMPLATES[0]['OPTIONS']['context_processors'] += [ + 'cms.context_processors.cms_settings', + 'sekizai.context_processors.sekizai', + 'cms.context_processors.cms_settings', + ] +else: + TEMPLATE_CONTEXT_PROCESSORS = list(TEMPLATE_CONTEXT_PROCESSORS) + TEMPLATE_CONTEXT_PROCESSORS += [ + 'cms.context_processors.cms_settings', + 'sekizai.context_processors.sekizai', + 'cms.context_processors.cms_settings', + ] #FOBI_DEFAULT_THEME = 'bootstrap3' #FOBI_DEFAULT_THEME = 'foundation5' diff --git a/examples/simple/urls.py b/examples/simple/urls.py index 8e4cc5bd..b39afff8 100644 --- a/examples/simple/urls.py +++ b/examples/simple/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls import patterns, include, url +from django.conf.urls import include, url from django.conf import settings from django.contrib import admin @@ -27,7 +27,7 @@ FOBI_EDIT_URLS_PREFIX = '' if DEFAULT_THEME in ('simple', 'djangocms_admin_style_theme'): FOBI_EDIT_URLS_PREFIX = 'admin/' -urlpatterns = patterns('', +urlpatterns = [ # DB Store plugin URLs url(r'^fobi/plugins/form-handlers/db-store/', include('fobi.contrib.plugins.form_handlers.db_store.urls')), #,namespace='fobi' @@ -42,7 +42,7 @@ urlpatterns = patterns('', url(r'^admin/', include(admin.site.urls)), # django-registration URLs: - (r'^accounts/', include('registration.backends.default.urls')), + url(r'^accounts/', include('registration.backends.default.urls')), # foo URLs: url(r'^foo/', include('foo.urls')), @@ -51,7 +51,7 @@ urlpatterns = patterns('', # django-fobi public forms contrib app: #url(r'^', include('fobi.contrib.apps.public_forms.urls')), - ) +] # Serving media and static in debug/developer mode. if settings.DEBUG: @@ -63,16 +63,16 @@ if settings.DEBUG: if 'feincms' in settings.INSTALLED_APPS: from page.models import Page Page - urlpatterns += patterns('', + urlpatterns += [ url(r'^pages/', include('feincms.urls')), - ) + ] # Conditionally including DjangoCMS URls in case if # DjangoCMS in installed apps. if 'cms' in settings.INSTALLED_APPS: - urlpatterns += patterns('', + urlpatterns += [ url(r'^cms-pages/', include('cms.urls')), - ) + ] # Conditionally including Captcha URls in case if # Captcha in installed apps. @@ -80,6 +80,6 @@ try: from captcha.fields import ReCaptchaField except ImportError as e: if 'captcha' in settings.INSTALLED_APPS: - urlpatterns += patterns('', + urlpatterns += [ url(r'^captcha/', include('captcha.urls')), - ) + ] diff --git a/examples/tutorial/sample_textarea/fobi_form_elements.py b/examples/tutorial/sample_textarea/fobi_form_elements.py index 87df83ff..c012666a 100644 --- a/examples/tutorial/sample_textarea/fobi_form_elements.py +++ b/examples/tutorial/sample_textarea/fobi_form_elements.py @@ -9,7 +9,7 @@ class SampleTextareaPlugin(FormFieldPlugin): form = SampleTextareaForm group = "Samples" # Group to which the plugin belongs to - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): kwargs = { 'required': self.data.required, 'label': self.data.label, diff --git a/examples/tutorial/urls.py b/examples/tutorial/urls.py index 52218cc8..4c997e13 100644 --- a/examples/tutorial/urls.py +++ b/examples/tutorial/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls import patterns, include, url +from django.conf.urls import include, url from django.conf import settings from django.contrib import admin @@ -22,7 +22,7 @@ fobi_home_template = fobi_theme_home_template_mapping.get( 'home/base.html' ) -urlpatterns = patterns('', +urlpatterns = [ # DB Store plugin URLs url(r'^fobi/plugins/form-handlers/db-store/', include('fobi.contrib.plugins.form_handlers.db_store.urls')), @@ -36,7 +36,7 @@ urlpatterns = patterns('', url(r'^admin/', include(admin.site.urls)), # django-registration URLs: - (r'^accounts/', include('registration.backends.default.urls')), + url(r'^accounts/', include('registration.backends.default.urls')), # foo URLs: url(r'^foo/', include('foo.urls')), @@ -45,7 +45,7 @@ urlpatterns = patterns('', # django-fobi public forms contrib app: #url(r'^', include('fobi.contrib.apps.public_forms.urls')), -) +] # Serving media and static in debug/developer mode. if settings.DEBUG: @@ -57,6 +57,6 @@ if settings.DEBUG: if 'feincms' in settings.INSTALLED_APPS: from page.models import Page Page - urlpatterns += patterns('', + urlpatterns += [ url(r'', include('feincms.urls')), - ) + ] diff --git a/scripts/install.sh b/scripts/install.sh index df1dcced..5256b247 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -1,5 +1,3 @@ -#pip install -r examples/requirements.txt --allow-all-external --allow-unverified django-admin-tools -#cd .. pip install -r examples/requirements.txt python setup.py install mkdir -p examples/logs examples/db examples/media examples/media/static examples/media/fobi_plugins/content_image @@ -8,4 +6,3 @@ python examples/simple/manage.py collectstatic --noinput python examples/simple/manage.py syncdb --noinput python examples/simple/manage.py migrate --noinput python examples/simple/manage.py fobi_create_test_data -#cd scripts \ No newline at end of file diff --git a/setup.py b/setup.py index a8dca4d6..ac6fd4a5 100644 --- a/setup.py +++ b/setup.py @@ -69,7 +69,7 @@ for static_dir in static_dirs: for locale_dir in locale_dirs: locale_files += [os.path.join(locale_dir, f) for f in os.listdir(locale_dir)] -version = '0.5.17' +version = '0.6' install_requires = [ 'Pillow>=2.0.0', @@ -78,10 +78,12 @@ install_requires = [ 'django-nonefield>=0.1', 'ordereddict>=1.1', 'six>=1.4.1', + 'easy-thumbnails>=1.4', 'vishap>=0.1.3,<2.0', 'Unidecode>=0.04.1', 'django-nine>=0.1.6', ] +# There are also conditional PY3/PY2 requirements. Scroll down to see them. tests_require = [ 'selenium', diff --git a/src/fobi/__init__.py b/src/fobi/__init__.py index 7bf5495a..c53ee6e8 100644 --- a/src/fobi/__init__.py +++ b/src/fobi/__init__.py @@ -1,6 +1,6 @@ __title__ = 'django-fobi' -__version__ = '0.5.17' -__build__ = 0x000046 +__version__ = '0.6' +__build__ = 0x000049 __author__ = 'Artur Barseghyan ' __copyright__ = '2014-2015 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' diff --git a/src/fobi/admin.py b/src/fobi/admin.py index 3c78ccf8..3188e5c9 100644 --- a/src/fobi/admin.py +++ b/src/fobi/admin.py @@ -9,7 +9,7 @@ from django.utils.translation import ugettext_lazy as _ from django.utils.html import strip_tags from django.contrib.admin.views.decorators import staff_member_required from django.utils.decorators import method_decorator -from django.conf.urls import patterns, url +from django.conf.urls import url from django.shortcuts import render_to_response, redirect from django.template import RequestContext from django.contrib import messages @@ -389,11 +389,11 @@ class FormElementAdmin(BasePluginModelAdmin): return 'admin:fobi_formelement_changelist' def get_urls(self): - my_urls = patterns('', + my_urls = [ # Bulk change plugins url(r'^bulk-change-form-element-plugins/$', self.bulk_change_plugins, name='bulk_change_form_element_plugins'), - ) + ] return my_urls + super(FormElementAdmin, self).get_urls() @@ -418,11 +418,11 @@ class FormHandlerAdmin(BasePluginModelAdmin): return 'admin:fobi_formhandler_changelist' def get_urls(self): - my_urls = patterns('', + my_urls = [ # Bulk change plugins url(r'^bulk-change-form-handler-plugins/$', self.bulk_change_plugins, name='bulk_change_form_handler_plugins'), - ) + ] return my_urls + super(FormHandlerAdmin, self).get_urls() diff --git a/src/fobi/base.py b/src/fobi/base.py index 76d08cf5..f85b00ec 100644 --- a/src/fobi/base.py +++ b/src/fobi/base.py @@ -36,7 +36,6 @@ import traceback import logging import copy import uuid -#import json import re import simplejson as json @@ -52,13 +51,11 @@ from django import forms from django.forms import ModelForm from django.http import Http404 from django.utils.translation import ugettext_lazy as _ -from django.contrib.auth.models import AnonymousUser -from django.test import RequestFactory -from django.template import RequestContext, Template, Context +from django.template import RequestContext, Template -from nine.versions import DJANGO_GTE_1_8 +from nine.versions import DJANGO_GTE_1_7 -if DJANGO_GTE_1_8: +if DJANGO_GTE_1_7: from django.forms.utils import ErrorList else: from django.forms.util import ErrorList @@ -77,9 +74,8 @@ from fobi.exceptions import ( FormElementPluginDoesNotExist, FormHandlerPluginDoesNotExist ) from fobi.helpers import ( - uniquify_sequence, map_field_name_to_label, clean_dict, - map_field_name_to_label, get_ignorable_form_values, safe_text, - StrippedRequest, + uniquify_sequence, clean_dict, map_field_name_to_label, + get_ignorable_form_values, safe_text, StrippedRequest, ) from fobi.data_structures import SortableDict @@ -151,6 +147,7 @@ class BaseTheme(object): base_view_template = None base_edit_template = None form_snippet_template_name = 'fobi/generic/snippets/form_snippet.html' + form_wizard_template = 'fobi/generic/snippets/form_wizard.html' form_view_snippet_template_name = None form_edit_snippet_template_name = None form_properties_snippet_template_name = \ @@ -192,6 +189,8 @@ class BaseTheme(object): import_form_entry_template = 'fobi/generic/import_form_entry.html' import_form_entry_ajax_template = 'fobi/generic/import_form_entry_ajax.html' + form_importer_template = 'fobi/generic/form_importer.html' + form_importer_ajax_template = 'fobi/generic/form_importer_ajax.html' # ************************************************************************* # ******************** Extras that make things easy *********************** @@ -1151,7 +1150,9 @@ class FormElementPlugin(BasePlugin): # methods in plugins). In DEBUG mode raise an exception if something # goes wrong. Otherwise - skip the element. try: - form_field_instances = self.get_form_field_instances() + form_field_instances = self.get_form_field_instances( + request=request + ) except AttributeError as e: if DEBUG: raise e @@ -1230,7 +1231,7 @@ class FormElementPlugin(BasePlugin): return processed_field_instances - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Gets the instances of form fields, that plugin contains. @@ -1821,7 +1822,9 @@ form_handler_plugin_widget_registry = FormHandlerPluginWidgetRegistry() def ensure_autodiscover(): """ - Ensures that plugins are autodiscovered. + Ensures that plugins are auto-discovered. The form callbacks registry + is intentionally left out, since they will be auto-discovered in + any case if other modules are discovered. """ if not (form_element_plugin_registry._registry and form_handler_plugin_registry._registry diff --git a/src/fobi/context_processors.py b/src/fobi/context_processors.py index 217d181f..84eb7fd5 100644 --- a/src/fobi/context_processors.py +++ b/src/fobi/context_processors.py @@ -2,12 +2,13 @@ __title__ = 'fobi.context_processors' __author__ = 'Artur Barseghyan ' __copyright__ = 'Copyright (c) 2014 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' -__all__ = ('theme',) +__all__ = ('theme', 'dynamic_values', 'form_importers',) import datetime from fobi.base import get_theme from fobi.helpers import StrippedRequest +from fobi.form_importers import get_form_impoter_plugin_urls def theme(request): """ @@ -29,3 +30,11 @@ def dynamic_values(request): 'today': datetime.date.today(), } } + +def form_importers(request): + """ + Form importers. + """ + return { + 'form_importers': get_form_impoter_plugin_urls(), + } diff --git a/src/fobi/contrib/plugins/form_elements/content/content_image/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/content/content_image/fobi_form_elements.py index 9e422e68..8a77b67a 100644 --- a/src/fobi/contrib/plugins/form_elements/content/content_image/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/content/content_image/fobi_form_elements.py @@ -54,7 +54,7 @@ class ContentImagePlugin(FormElementPlugin): ) return self.get_cloned_plugin_data(update={'file': cloned_image}) - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/content/content_text/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/content/content_text/fobi_form_elements.py index 7c67857b..0e506dbb 100644 --- a/src/fobi/contrib/plugins/form_elements/content/content_text/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/content/content_text/fobi_form_elements.py @@ -31,7 +31,7 @@ class ContentTextPlugin(FormElementPlugin): """ self.data.name = "{0}_{1}".format(self.uid, uuid4()) - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/content/content_video/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/content/content_video/fobi_form_elements.py index 4b5ce83e..898f4a1c 100644 --- a/src/fobi/contrib/plugins/form_elements/content/content_video/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/content/content_video/fobi_form_elements.py @@ -32,7 +32,7 @@ class ContentVideoPlugin(FormElementPlugin): """ self.data.name = "{0}_{1}".format(self.uid, uuid4()) - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/fields/boolean/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/boolean/fobi_form_elements.py index d396af1b..620d3f45 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/boolean/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/boolean/fobi_form_elements.py @@ -21,7 +21,7 @@ class BooleanSelectPlugin(FormFieldPlugin): group = _("Fields") form = BooleanSelectForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/fields/checkbox_select_multiple/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/checkbox_select_multiple/fobi_form_elements.py index 1511b207..8f94985f 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/checkbox_select_multiple/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/checkbox_select_multiple/fobi_form_elements.py @@ -29,7 +29,7 @@ class CheckboxSelectMultipleInputPlugin(FormFieldPlugin): group = _("Fields") form = CheckboxSelectMultipleInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/fields/date/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/date/fobi_form_elements.py index 25b2223c..9af1de28 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/date/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/date/fobi_form_elements.py @@ -26,7 +26,7 @@ class DateInputPlugin(FormFieldPlugin): group = _("Fields") form = DateInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/fields/date_drop_down/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/date_drop_down/fobi_form_elements.py index 37ff72c9..e155e6e6 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/date_drop_down/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/date_drop_down/fobi_form_elements.py @@ -24,7 +24,7 @@ class DateDropDownInputPlugin(FormFieldPlugin): group = _("Fields") form = DateDropDownInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/fields/datetime/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/datetime/fobi_form_elements.py index c53a4944..361a8b1d 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/datetime/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/datetime/fobi_form_elements.py @@ -26,7 +26,7 @@ class DateTimeInputPlugin(FormFieldPlugin): group = _("Fields") form = DateTimeInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/fields/decimal/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/decimal/fobi_form_elements.py index 4af7cdba..29cf6dff 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/decimal/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/decimal/fobi_form_elements.py @@ -26,7 +26,7 @@ class DecimalInputPlugin(FormFieldPlugin): group = _("Fields") form = DecimalInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/fields/email/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/email/fobi_form_elements.py index c9d4f83b..04462095 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/email/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/email/fobi_form_elements.py @@ -26,7 +26,7 @@ class EmailInputPlugin(FormFieldPlugin): group = _("Fields") form = EmailInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/fields/file/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/file/fobi_form_elements.py index 4496f860..4bcd4c45 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/file/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/file/fobi_form_elements.py @@ -27,7 +27,7 @@ class FileInputPlugin(FormFieldPlugin): group = _("Fields") form = FileInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/fields/float/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/float/fobi_form_elements.py index 98f441a6..1d411ab9 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/float/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/float/fobi_form_elements.py @@ -24,7 +24,7 @@ class FloatInputPlugin(FormFieldPlugin): group = _("Fields") form = FloatInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/fields/hidden/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/hidden/fobi_form_elements.py index 35dde7f6..0065a448 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/hidden/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/hidden/fobi_form_elements.py @@ -25,7 +25,7 @@ class HiddenInputPlugin(FormFieldPlugin): form = HiddenInputForm is_hidden = True - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/fields/input/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/input/fobi_form_elements.py index a1074e07..447cdf94 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/input/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/input/fobi_form_elements.py @@ -24,7 +24,7 @@ class InputPlugin(FormFieldPlugin): group = _("Fields") form = InputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/fields/integer/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/integer/fobi_form_elements.py index e597fabf..e3591d52 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/integer/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/integer/fobi_form_elements.py @@ -24,7 +24,7 @@ class IntegerInputPlugin(FormFieldPlugin): group = _("Fields") form = IntegerInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/fields/ip_address/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/ip_address/fobi_form_elements.py index 820493c5..ab8d2f00 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/ip_address/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/ip_address/fobi_form_elements.py @@ -24,7 +24,7 @@ class IPAddressInputPlugin(FormFieldPlugin): group = _("Fields") form = IPAddressInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/fields/null_boolean/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/null_boolean/fobi_form_elements.py index dc6905f5..183328f4 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/null_boolean/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/null_boolean/fobi_form_elements.py @@ -24,7 +24,7 @@ class NullBooleanSelectPlugin(FormFieldPlugin): group = _("Fields") form = NullBooleanSelectForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/fields/password/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/password/fobi_form_elements.py index decfbbe8..5b42e0ae 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/password/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/password/fobi_form_elements.py @@ -24,7 +24,7 @@ class PasswordInputPlugin(FormFieldPlugin): group = _("Fields") form = PasswordInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/fields/radio/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/radio/fobi_form_elements.py index e8b4b75a..4675ea3d 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/radio/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/radio/fobi_form_elements.py @@ -29,7 +29,7 @@ class RadioInputPlugin(FormFieldPlugin): group = _("Fields") form = RadioInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/fields/regex/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/regex/fobi_form_elements.py index a7c24776..330eb609 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/regex/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/regex/fobi_form_elements.py @@ -26,7 +26,7 @@ class RegexInputPlugin(FormFieldPlugin): group = _("Fields") form = RegexInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/fields/regex/forms.py b/src/fobi/contrib/plugins/form_elements/fields/regex/forms.py index b5c12530..5e500bc6 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/regex/forms.py +++ b/src/fobi/contrib/plugins/form_elements/fields/regex/forms.py @@ -32,42 +32,67 @@ class RegexInputForm(forms.Form, BaseFormFieldPluginForm): label = forms.CharField( label = _("Label"), required = True, - widget = forms.widgets.TextInput(attrs={'class': theme.form_element_html_class}) + 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}) + widget = forms.widgets.TextInput( + 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}) + widget = forms.widgets.Textarea( + attrs={'class': theme.form_element_html_class} ) + ) regex = forms.RegexField( label = _("Regex"), required = True, - widget = forms.widgets.TextInput(attrs={'class': theme.form_element_html_class}), - regex = "" - ) + widget = forms.widgets.TextInput( + attrs={'class': theme.form_element_html_class} + ), + regex = "", + help_text = _("Enter a valid regular expression. A couple of common " + "examples are listed below.
" + "- Allow a single digit from 1 to 9 (example value 6): " + "^[1-9]$
" + "- Allow any combination of characters from a to z, " + "including capitals (example value abcXYZ):" + "^([a-zA-Z])+$
" + "- Allow a hex value (example value #a5c125:" + "^#?([a-f0-9]{6}|[a-f0-9]{3})$
") + ) initial = forms.CharField( label = _("Initial"), required = False, - widget = forms.widgets.TextInput(attrs={'class': theme.form_element_html_class}) + widget = forms.widgets.TextInput( + attrs={'class': theme.form_element_html_class} ) + ) max_length = forms.IntegerField( label = _("Max length"), required = True, - widget = forms.widgets.TextInput(attrs={'class': theme.form_element_html_class}), + widget = forms.widgets.TextInput( + attrs={'class': theme.form_element_html_class} + ), initial = DEFAULT_MAX_LENGTH - ) + ) required = forms.BooleanField( label = _("Required"), required = False, - widget = forms.widgets.CheckboxInput(attrs={'class': theme.form_element_checkbox_html_class}) + widget = forms.widgets.CheckboxInput( + attrs={'class': theme.form_element_checkbox_html_class} ) + ) placeholder = forms.CharField( label = _("Placeholder"), required = False, - widget = forms.widgets.TextInput(attrs={'class': theme.form_element_html_class}) + widget = forms.widgets.TextInput( + attrs={'class': theme.form_element_html_class} ) + ) diff --git a/src/fobi/contrib/plugins/form_elements/fields/select/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/select/fobi_form_elements.py index 04254939..5845c49a 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/select/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/select/fobi_form_elements.py @@ -29,7 +29,7 @@ class SelectInputPlugin(FormFieldPlugin): group = _("Fields") form = SelectInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_model_object/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/select_model_object/fobi_form_elements.py index e5296fe8..4780452d 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/select_model_object/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/select_model_object/fobi_form_elements.py @@ -15,6 +15,14 @@ from fobi.constants import ( ) from fobi.helpers import safe_text, get_app_label_and_model_name +from nine.versions import DJANGO_GTE_1_7 + +if DJANGO_GTE_1_7: + from django.apps import apps + get_model = apps.get_model +else: + from django.db.models import get_model + from . import UID from .forms import SelectModelObjectInputForm from .settings import SUBMIT_VALUE_AS @@ -30,12 +38,12 @@ class SelectModelObjectInputPlugin(FormFieldPlugin): group = _("Fields") form = SelectModelObjectInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ app_label, model_name = get_app_label_and_model_name(self.data.model) - model = models.get_model(app_label, model_name) + model = get_model(app_label, model_name) queryset = model._default_manager.all() kwargs = { diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_mptt_model_object/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/select_mptt_model_object/fobi_form_elements.py index 56ab86e3..64ce08d9 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/select_mptt_model_object/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/select_mptt_model_object/fobi_form_elements.py @@ -16,6 +16,14 @@ from fobi.constants import ( ) from fobi.helpers import safe_text, get_app_label_and_model_name +from nine.versions import DJANGO_GTE_1_7 + +if DJANGO_GTE_1_7: + from django.apps import apps + get_model = apps.get_model +else: + from django.db.models import get_model + from . import UID from .forms import SelectMPTTModelObjectInputForm from .settings import SUBMIT_VALUE_AS @@ -31,12 +39,12 @@ class SelectMPTTModelObjectInputPlugin(FormFieldPlugin): group = _("Fields") form = SelectMPTTModelObjectInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ app_label, model_name = get_app_label_and_model_name(self.data.model) - model = models.get_model(app_label, model_name) + model = get_model(app_label, model_name) queryset = model._default_manager.all() kwargs = { diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_multiple/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/select_multiple/fobi_form_elements.py index 7f94421e..024c90dd 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/select_multiple/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/select_multiple/fobi_form_elements.py @@ -29,7 +29,7 @@ class SelectMultipleInputPlugin(FormFieldPlugin): group = _("Fields") form = SelectMultipleInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_model_objects/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_model_objects/fobi_form_elements.py index 20fbebed..291cf35d 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_model_objects/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_model_objects/fobi_form_elements.py @@ -17,6 +17,14 @@ from fobi.constants import ( ) from fobi.helpers import safe_text, get_app_label_and_model_name +from nine.versions import DJANGO_GTE_1_7 + +if DJANGO_GTE_1_7: + from django.apps import apps + get_model = apps.get_model +else: + from django.db.models import get_model + from . import UID from .forms import SelectMultipleModelObjectsInputForm from .settings import SUBMIT_VALUE_AS @@ -32,12 +40,12 @@ class SelectMultipleModelObjectsInputPlugin(FormFieldPlugin): group = _("Fields") form = SelectMultipleModelObjectsInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ app_label, model_name = get_app_label_and_model_name(self.data.model) - model = models.get_model(app_label, model_name) + model = get_model(app_label, model_name) queryset = model._default_manager.all() kwargs = { diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_mptt_model_objects/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_mptt_model_objects/fobi_form_elements.py index add19cc6..80b3cfdf 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_mptt_model_objects/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_mptt_model_objects/fobi_form_elements.py @@ -18,6 +18,14 @@ from fobi.constants import ( ) from fobi.helpers import safe_text, get_app_label_and_model_name +from nine.versions import DJANGO_GTE_1_7 + +if DJANGO_GTE_1_7: + from django.apps import apps + get_model = apps.get_model +else: + from django.db.models import get_model + from . import UID from .forms import SelectMultipleMPTTModelObjectsInputForm from .settings import SUBMIT_VALUE_AS @@ -33,12 +41,12 @@ class SelectMultipleMPTTModelObjectsInputPlugin(FormFieldPlugin): group = _("Fields") form = SelectMultipleMPTTModelObjectsInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ app_label, model_name = get_app_label_and_model_name(self.data.model) - model = models.get_model(app_label, model_name) + model = get_model(app_label, model_name) queryset = model._default_manager.all() kwargs = { diff --git a/src/fobi/contrib/plugins/form_elements/fields/slug/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/slug/fobi_form_elements.py index a40bb0d1..495edd39 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/slug/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/slug/fobi_form_elements.py @@ -24,7 +24,7 @@ class SlugInputPlugin(FormFieldPlugin): group = _("Fields") form = SlugInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/fields/text/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/text/fobi_form_elements.py index 170b8ebc..eb361825 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/text/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/text/fobi_form_elements.py @@ -24,7 +24,7 @@ class TextInputPlugin(FormFieldPlugin): group = _("Fields") form = TextInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/fields/textarea/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/textarea/fobi_form_elements.py index 351d1a03..76cc24f9 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/textarea/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/textarea/fobi_form_elements.py @@ -24,7 +24,7 @@ class TextareaPlugin(FormFieldPlugin): group = _("Fields") form = TextareaForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/fields/time/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/time/fobi_form_elements.py index f84d4fd4..5c484a1d 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/time/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/time/fobi_form_elements.py @@ -26,7 +26,7 @@ class TimeInputPlugin(FormFieldPlugin): group = _("Fields") form = TimeInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/fields/url/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/url/fobi_form_elements.py index 0bd7fc42..fcdfa8f4 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/url/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/url/fobi_form_elements.py @@ -31,7 +31,7 @@ class URLInputPlugin(FormFieldPlugin): group = _("Fields") form = URLInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/security/captcha/README.rst b/src/fobi/contrib/plugins/form_elements/security/captcha/README.rst index 4b05541d..0acc0d5c 100644 --- a/src/fobi/contrib/plugins/form_elements/security/captcha/README.rst +++ b/src/fobi/contrib/plugins/form_elements/security/captcha/README.rst @@ -28,9 +28,9 @@ Taken from django-simple-captcha `installation instructions .. code-block:: python - urlpatterns += patterns('', + urlpatterns += [ url(r'^captcha/', include('captcha.urls')), - ) + ] Install `fobi` Captcha plugin ----------------------------- diff --git a/src/fobi/contrib/plugins/form_elements/security/captcha/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/security/captcha/fobi_form_elements.py index 895ec186..996ff264 100644 --- a/src/fobi/contrib/plugins/form_elements/security/captcha/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/security/captcha/fobi_form_elements.py @@ -75,7 +75,7 @@ class CaptchaInputPlugin(FormElementPlugin): group = _("Security") form = CaptchaInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/security/honeypot/README.rst b/src/fobi/contrib/plugins/form_elements/security/honeypot/README.rst index 4940e945..fe58edc7 100644 --- a/src/fobi/contrib/plugins/form_elements/security/honeypot/README.rst +++ b/src/fobi/contrib/plugins/form_elements/security/honeypot/README.rst @@ -5,7 +5,7 @@ A `Honeypot `_ form field plugin. Just another anti-spam technique. Installation -=============================================== +============ 1. Add ``fobi.contrib.plugins.form_elements.security.honeypot`` to the ``INSTALLED_APPS`` in your ``settings.py``. @@ -27,5 +27,4 @@ Installation the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. Usage -=============================================== - +===== diff --git a/src/fobi/contrib/plugins/form_elements/security/honeypot/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/security/honeypot/fobi_form_elements.py index 0f8ec944..1cb77204 100644 --- a/src/fobi/contrib/plugins/form_elements/security/honeypot/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/security/honeypot/fobi_form_elements.py @@ -26,7 +26,7 @@ class HoneypotInputPlugin(FormElementPlugin): form = HoneypotInputForm is_hidden = True - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/security/recaptcha/README.rst b/src/fobi/contrib/plugins/form_elements/security/recaptcha/README.rst index ee141bb1..c827af07 100644 --- a/src/fobi/contrib/plugins/form_elements/security/recaptcha/README.rst +++ b/src/fobi/contrib/plugins/form_elements/security/recaptcha/README.rst @@ -6,9 +6,9 @@ Makes use of the `django-recaptcha `_. Installation -=============================================== +============ Install `django-recaptcha` ------------------------------------------------ +-------------------------- 1. Download ``django-recaptcha`` using pip by running: .. code-block:: none @@ -22,7 +22,7 @@ Install `django-recaptcha` tables. Install `fobi` ReCAPTCHA plugin ------------------------------------------------ +------------------------------- 1. Add ``fobi.contrib.plugins.form_elements.security.recaptcha`` to the ``INSTALLED_APPS`` in your ``settings.py``. @@ -49,7 +49,7 @@ Install `fobi` ReCAPTCHA plugin - ``RECAPTCHA_PRIVATE_KEY`` Troubleshooting and usage limitations -=============================================== +===================================== At the moment, you can't use both ``CAPTCHA`` (fobi.contrib.plugins.form_elements.security.captcha) and ``ReCAPTCHA`` (fobi.contrib.plugins.form_elements.security.recaptcha) plugins alongside due @@ -65,7 +65,7 @@ See the `following `_ thread for more information. Usage -=============================================== +===== Note, that unlike most of the other form element plugins, default value for the ``required`` attribute is True, which makes the ReCaptcha obligatory. Although you could still set it to False, it does not make diff --git a/src/fobi/contrib/plugins/form_elements/security/recaptcha/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/security/recaptcha/fobi_form_elements.py index eee9a67b..6d4f4dc6 100644 --- a/src/fobi/contrib/plugins/form_elements/security/recaptcha/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/security/recaptcha/fobi_form_elements.py @@ -77,7 +77,7 @@ class ReCaptchaInputPlugin(FormElementPlugin): group = _("Security") form = ReCaptchaInputForm - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_elements/test/dummy/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/test/dummy/fobi_form_elements.py index 71653002..1f3ff849 100644 --- a/src/fobi/contrib/plugins/form_elements/test/dummy/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/test/dummy/fobi_form_elements.py @@ -29,7 +29,7 @@ class DummyPlugin(FormElementPlugin): """ self.data.name = "{0}_{1}".format(self.uid, uuid4()) - def get_form_field_instances(self): + def get_form_field_instances(self, request=None): """ Get form field instances. """ diff --git a/src/fobi/contrib/plugins/form_handlers/db_store/README.rst b/src/fobi/contrib/plugins/form_handlers/db_store/README.rst index c69f41ee..a6d6caed 100644 --- a/src/fobi/contrib/plugins/form_handlers/db_store/README.rst +++ b/src/fobi/contrib/plugins/form_handlers/db_store/README.rst @@ -1,17 +1,17 @@ -=============================================== +=========================================== fobi.contrib.plugins.form_handlers.db_store -=============================================== +=========================================== A ``Fobi`` Database Store form handler plugin. Saves submitted form data into the ``SavedFormDataEntry`` model. Dependencies -=============================================== +============ The `xlwt `_ package is required (optional) for XLS export. If not present, export format falls back to CSV. Installation -=============================================== +============ 1. Add ``fobi.contrib.plugins.form_handlers.db_store`` to the ``INSTALLED_APPS`` in your ``settings.py``. @@ -38,8 +38,8 @@ Installation .. code-block:: python - urlpatterns = patterns('', + urlpatterns = [ # DB Store plugin URLs url(r'^fobi/plugins/form-handlers/db-store/', include('fobi.contrib.plugins.form_handlers.db_store.urls')), - ) + ] diff --git a/src/fobi/contrib/plugins/form_handlers/http_repost/README.rst b/src/fobi/contrib/plugins/form_handlers/http_repost/README.rst index 2960b29f..737c57f8 100644 --- a/src/fobi/contrib/plugins/form_handlers/http_repost/README.rst +++ b/src/fobi/contrib/plugins/form_handlers/http_repost/README.rst @@ -1,11 +1,11 @@ -=============================================== +============================================== fobi.contrib.plugins.form_handlers.http_repost -=============================================== +============================================== A ``Fobi`` HTTP Repost form handler plugin. Submits the form data as is to the given endpoint. Installation -=============================================== +============ 1. Add ``fobi.contrib.plugins.form_handlers.http_repost`` to the ``INSTALLED_APPS`` in your ``settings.py``. diff --git a/src/fobi/contrib/plugins/form_handlers/mail/README.rst b/src/fobi/contrib/plugins/form_handlers/mail/README.rst index c2ad2f7c..5810ec0a 100644 --- a/src/fobi/contrib/plugins/form_handlers/mail/README.rst +++ b/src/fobi/contrib/plugins/form_handlers/mail/README.rst @@ -1,11 +1,11 @@ -=============================================== +======================================= fobi.contrib.plugins.form_handlers.mail -=============================================== +======================================= A ``Fobi`` Mail form handler plugin. Submits the form data by email to the specified email address. Installation -=============================================== +============ 1. Add ``fobi.contrib.plugins.form_handlers.mail`` to the ``INSTALLED_APPS`` in your ``settings.py``. diff --git a/src/fobi/contrib/themes/bootstrap3/fobi_themes.py b/src/fobi/contrib/themes/bootstrap3/fobi_themes.py index 7e6f708c..05221156 100644 --- a/src/fobi/contrib/themes/bootstrap3/fobi_themes.py +++ b/src/fobi/contrib/themes/bootstrap3/fobi_themes.py @@ -56,6 +56,7 @@ class Bootstrap3Theme(BaseTheme): base_template = 'bootstrap3/base.html' form_ajax = 'bootstrap3/snippets/form_ajax.html' + form_wizard_template = 'bootstrap3/snippets/form_wizard.html' form_snippet_template_name = 'bootstrap3/snippets/form_snippet.html' form_properties_snippet_template_name = \ 'bootstrap3/snippets/form_properties_snippet.html' @@ -98,5 +99,8 @@ class Bootstrap3Theme(BaseTheme): view_embed_form_entry_ajax_template = \ 'bootstrap3/view_embed_form_entry_ajax.html' + form_importer_template = 'bootstrap3/form_importer.html' + form_importer_ajax_template = 'bootstrap3/form_importer_ajax.html' + theme_registry.register(Bootstrap3Theme) diff --git a/src/fobi/contrib/themes/bootstrap3/templates/bootstrap3/form_importer.html b/src/fobi/contrib/themes/bootstrap3/templates/bootstrap3/form_importer.html new file mode 100644 index 00000000..fbda5ed0 --- /dev/null +++ b/src/fobi/contrib/themes/bootstrap3/templates/bootstrap3/form_importer.html @@ -0,0 +1 @@ +{% extends "fobi/generic/form_importer.html" %} \ No newline at end of file diff --git a/src/fobi/contrib/themes/bootstrap3/templates/bootstrap3/form_importer_ajax.html b/src/fobi/contrib/themes/bootstrap3/templates/bootstrap3/form_importer_ajax.html new file mode 100644 index 00000000..04c19276 --- /dev/null +++ b/src/fobi/contrib/themes/bootstrap3/templates/bootstrap3/form_importer_ajax.html @@ -0,0 +1 @@ +{% extends "fobi/generic/form_importer_ajax.html" %} \ No newline at end of file diff --git a/src/fobi/contrib/themes/bootstrap3/templates/bootstrap3/snippets/form_wizard.html b/src/fobi/contrib/themes/bootstrap3/templates/bootstrap3/snippets/form_wizard.html new file mode 100644 index 00000000..318e74d4 --- /dev/null +++ b/src/fobi/contrib/themes/bootstrap3/templates/bootstrap3/snippets/form_wizard.html @@ -0,0 +1 @@ +{% extends "fobi/generic/snippets/form_wizard.html" %} \ No newline at end of file diff --git a/src/fobi/contrib/themes/djangocms_admin_style_theme/templates/djangocms_admin_style_theme/dashboard.html b/src/fobi/contrib/themes/djangocms_admin_style_theme/templates/djangocms_admin_style_theme/dashboard.html index db700167..e1ed5744 100644 --- a/src/fobi/contrib/themes/djangocms_admin_style_theme/templates/djangocms_admin_style_theme/dashboard.html +++ b/src/fobi/contrib/themes/djangocms_admin_style_theme/templates/djangocms_admin_style_theme/dashboard.html @@ -30,6 +30,11 @@
  • {% trans "Import form"%}
  • + {% for form_importer_uid,form_importer_name,form_importer_url in form_importers %} +
  • + {{ form_importer_name }} +
  • + {% endfor %}
    diff --git a/src/fobi/contrib/themes/djangocms_admin_style_theme/templates/djangocms_admin_style_theme/edit_form_entry_ajax.html b/src/fobi/contrib/themes/djangocms_admin_style_theme/templates/djangocms_admin_style_theme/edit_form_entry_ajax.html index d04d08a7..206f5134 100644 --- a/src/fobi/contrib/themes/djangocms_admin_style_theme/templates/djangocms_admin_style_theme/edit_form_entry_ajax.html +++ b/src/fobi/contrib/themes/djangocms_admin_style_theme/templates/djangocms_admin_style_theme/edit_form_entry_ajax.html @@ -191,7 +191,7 @@

    {% trans "Export your form into JSON format and import it again any time!" %}

    - {% trans "Export form data" %} + {% trans "Export form" %}

    diff --git a/src/fobi/contrib/themes/foundation5/templates/foundation5/dashboard.html b/src/fobi/contrib/themes/foundation5/templates/foundation5/dashboard.html index 90de7e18..c28875d0 100644 --- a/src/fobi/contrib/themes/foundation5/templates/foundation5/dashboard.html +++ b/src/fobi/contrib/themes/foundation5/templates/foundation5/dashboard.html @@ -65,7 +65,11 @@ - + {% for form_importer_uid,form_importer_name,form_importer_url in form_importers %} + + {% endfor %} {% endblock content-wrapper %} diff --git a/src/fobi/contrib/themes/foundation5/templates/foundation5/edit_form_entry_ajax.html b/src/fobi/contrib/themes/foundation5/templates/foundation5/edit_form_entry_ajax.html index cdc6e5cf..403a13d8 100644 --- a/src/fobi/contrib/themes/foundation5/templates/foundation5/edit_form_entry_ajax.html +++ b/src/fobi/contrib/themes/foundation5/templates/foundation5/edit_form_entry_ajax.html @@ -181,7 +181,7 @@

    {% trans "Export your form into JSON format and import it again any time!" %}

    - {% trans "Export form data" %} + {% trans "Export form" %}

    diff --git a/src/fobi/contrib/themes/simple/templates/simple/dashboard.html b/src/fobi/contrib/themes/simple/templates/simple/dashboard.html index f33e3566..0810c9bc 100644 --- a/src/fobi/contrib/themes/simple/templates/simple/dashboard.html +++ b/src/fobi/contrib/themes/simple/templates/simple/dashboard.html @@ -29,6 +29,11 @@
  • {% trans "Import form"%}
  • + {% for form_importer_uid,form_importer_name,form_importer_url in form_importers %} +
  • + {{ form_importer_name }} +
  • + {% endfor %}
    diff --git a/src/fobi/contrib/themes/simple/templates/simple/edit_form_entry_ajax.html b/src/fobi/contrib/themes/simple/templates/simple/edit_form_entry_ajax.html index a27baf97..e5e6a659 100644 --- a/src/fobi/contrib/themes/simple/templates/simple/edit_form_entry_ajax.html +++ b/src/fobi/contrib/themes/simple/templates/simple/edit_form_entry_ajax.html @@ -191,7 +191,7 @@

    {% trans "Export your form into JSON format and import it again any time!" %}

    - {% trans "Export form data" %} + {% trans "Export form" %}

    diff --git a/src/fobi/discover.py b/src/fobi/discover.py index 2f7da14a..7bb17328 100644 --- a/src/fobi/discover.py +++ b/src/fobi/discover.py @@ -68,9 +68,9 @@ def autodiscover(): 'FORM_CALLBACKS_MODULE_NAME' ) - #FORM_IMPORTER_PLUGINS_MODULE_NAME = get_setting( - # 'FORM_IMPORTER_PLUGINS_MODULE_NAME' - # ) + FORM_IMPORTER_PLUGINS_MODULE_NAME = get_setting( + 'FORM_IMPORTER_PLUGINS_MODULE_NAME' + ) # Discover modules autodiscover_modules(FORM_ELEMENT_PLUGINS_MODULE_NAME) @@ -79,7 +79,7 @@ def autodiscover(): autodiscover_modules(FORM_CALLBACKS_MODULE_NAME) # Do not yet discover form importers - #autodiscover_modules(FORM_IMPORTER_PLUGINS_MODULE_NAME) + autodiscover_modules(FORM_IMPORTER_PLUGINS_MODULE_NAME) if six.PY3 and recursion_limit > default_recursion_limit: sys.setrecursionlimit(default_recursion_limit) diff --git a/src/fobi/dynamic.py b/src/fobi/dynamic.py index f673af0c..068cb6f1 100644 --- a/src/fobi/dynamic.py +++ b/src/fobi/dynamic.py @@ -8,10 +8,17 @@ __all__ = ('assemble_form_class',) from six import with_metaclass -from django.utils.datastructures import SortedDict + from django.forms.forms import BaseForm#, get_declared_fields from django.forms.widgets import media_property +from nine.versions import DJANGO_GTE_1_7 + +if DJANGO_GTE_1_7: + from collections import OrderedDict +else: + from django.utils.datastructures import SortedDict as OrderedDict + #logger = logging.getLogger(__file__) # **************************************************************************** @@ -48,25 +55,30 @@ def assemble_form_class(form_entry, base_class=BaseForm, request=None, def __new__(cls, name, bases, attrs): base_fields = [] - for creation_counter, form_element_entry in enumerate(form_element_entries): + for creation_counter, form_element_entry \ + in enumerate(form_element_entries): plugin = form_element_entry.get_plugin(request=request) # We simply make sure the plugin exists. We don't handle # exceptions relate to the non-existent plugins here. They # are instead handled in registry. if plugin: - plugin_form_field_instances = plugin._get_form_field_instances( - form_element_entry = form_element_entry, - origin = origin, - kwargs_update_func = origin_kwargs_update_func, - return_func = origin_return_func, - extra = {'counter': creation_counter}, - request = request + plugin_form_field_instances = \ + plugin._get_form_field_instances( + form_element_entry = form_element_entry, + origin = origin, + kwargs_update_func = origin_kwargs_update_func, + return_func = origin_return_func, + extra = {'counter': creation_counter}, + request = request + ) + for form_field_name, form_field_instance \ + in plugin_form_field_instances: + base_fields.append( + (form_field_name, form_field_instance) ) - for form_field_name, form_field_instance in plugin_form_field_instances: - base_fields.append((form_field_name, form_field_instance)) - attrs['base_fields'] = SortedDict(base_fields) + attrs['base_fields'] = OrderedDict(base_fields) new_class = super(DeclarativeFieldsMetaclass, cls).__new__( cls, name, bases, attrs ) diff --git a/src/fobi/form_importers.py b/src/fobi/form_importers.py index e856de6a..dd64c3c6 100644 --- a/src/fobi/form_importers.py +++ b/src/fobi/form_importers.py @@ -1,8 +1,21 @@ -#import json +__title__ = 'fobi.form_importers' +__author__ = 'Artur Barseghyan ' +__copyright__ = 'Copyright (c) 2014-2015 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ( + 'BaseFormImporter', 'FormImporterPluginRegistry', + 'form_importer_plugin_registry', 'ensure_autodiscover', + 'get_form_importer_plugin_uids', 'get_form_impoter_plugin_urls', +) + +from six import text_type import simplejson as json +from django.core.urlresolvers import reverse + from fobi.base import BaseRegistry +from fobi.discover import autodiscover class BaseFormImporter(object): """ @@ -21,8 +34,11 @@ class BaseFormImporter(object): # Position is stored in a model (field) field_type_prop_name = None position_prop_name = None + wizard = None + templates = None - def __init__(self, form_properties, form_data): + def __init__(self, form_entry_cls, form_element_entry_cls, + form_properties=None, form_data=None): """ :param django.contrib.auth.models.User user: User importing the form. :param dict form_properties: Properties of the form, that @@ -32,11 +48,19 @@ class BaseFormImporter(object): assert self.name assert self.fields_mapping is not None assert self.field_properties_mapping is not None + assert self.wizard is not None + assert self.templates is not None + + assert form_entry_cls is not None + assert form_element_entry_cls is not None + for prop in ('name', 'label', 'help_text', 'initial', 'required'): assert prop in self.field_properties_mapping self.form_data = form_data self.form_properties = form_properties + self.form_entry_cls = form_entry_cls + self.form_element_entry_cls = form_element_entry_cls def get_form_data(self): return self.form_data @@ -48,14 +72,15 @@ class BaseFormImporter(object): field_properties[prop] = field_data[val] return field_properties - def import_data(self): + def import_data(self, form_properties, form_data): """ Imports data. """ - # TODO: Move this to top level and ensure it works! - from fobi.models import FormEntry, FormElementEntry + self.form_properties = form_properties + self.form_data = form_data + assert 'name' in self.form_properties - form_entry = FormEntry() + form_entry = self.form_entry_cls() for prop, val in self.form_properties.items(): setattr(form_entry, prop, val) @@ -67,9 +92,10 @@ class BaseFormImporter(object): if not field_data[self.field_type_prop_name] in self.fields_mapping: continue - form_element_entry = FormElementEntry() + form_element_entry = self.form_element_entry_cls() form_element_entry.form_entry = form_entry - form_element_entry.plugin_uid = self.fields_mapping[field_data[self.field_type_prop_name]] + form_element_entry.plugin_uid = self.fields_mapping[ + field_data[self.field_type_prop_name]] # Assign form data form_element_entry.plugin_data = json.dumps( @@ -78,10 +104,34 @@ class BaseFormImporter(object): # Assign position in form if self.position_prop_name in field_data: - form_element_entry.position = field_data[self.position_prop_name] + form_element_entry.position = field_data[ + self.position_prop_name] form_element_entry.save() + return form_entry + + def get_template_names(self): + """ + """ + return {text_type(idx): tpl for idx, tpl in enumerate(self.templates)} + + def get_wizard(self, request, *args, **kwargs): + """ + """ + template_names = self.get_template_names() + + class FormImporterWizard(self.wizard): + """ + Constructing the importer class dynamically. + """ + _form_importer = self + + def get_template_names(self): + return [template_names[self.steps.current]] + + wizard = FormImporterWizard.as_view() + return wizard(request, *args, **kwargs) class FormImporterPluginRegistry(BaseRegistry): @@ -93,3 +143,29 @@ class FormImporterPluginRegistry(BaseRegistry): # Register form field plugins by calling form_field_plugin_registry.register() form_importer_plugin_registry = FormImporterPluginRegistry() + +def ensure_autodiscover(): + """ + Ensures that form importer plugins are auto-discovered. + """ + if not (form_importer_plugin_registry._registry): + autodiscover() + +def get_form_importer_plugin_uids(): + """ + + """ + ensure_autodiscover() + return list(form_importer_plugin_registry._registry.keys()) + +def get_form_impoter_plugin_urls(): + """ + Gets the form importers as a list of tuples. + """ + urls = [] + ensure_autodiscover() + for uid, plugin in form_importer_plugin_registry._registry.items(): + urls.append( + (uid, plugin.name, reverse('fobi.form_importer', kwargs={'form_importer_plugin_uid': uid})) + ) + return urls diff --git a/src/fobi/templates/fobi/generic/dashboard.html b/src/fobi/templates/fobi/generic/dashboard.html index 64da5ffa..cea16fa7 100644 --- a/src/fobi/templates/fobi/generic/dashboard.html +++ b/src/fobi/templates/fobi/generic/dashboard.html @@ -79,6 +79,11 @@ {% trans "Import form" %} + {% for form_importer_uid,form_importer_name,form_importer_url in form_importers %} + + {{ form_importer_name }} + + {% endfor %} diff --git a/src/fobi/templates/fobi/generic/edit_form_entry_ajax.html b/src/fobi/templates/fobi/generic/edit_form_entry_ajax.html index d3bb581b..aade66b0 100644 --- a/src/fobi/templates/fobi/generic/edit_form_entry_ajax.html +++ b/src/fobi/templates/fobi/generic/edit_form_entry_ajax.html @@ -177,7 +177,7 @@

    {% trans "Export your form into JSON format and import it again any time!" %}

    - {% trans "Export form data" %} + {% trans "Export form" %}

    diff --git a/src/fobi/templates/fobi/generic/form_importer.html b/src/fobi/templates/fobi/generic/form_importer.html new file mode 100644 index 00000000..756c7717 --- /dev/null +++ b/src/fobi/templates/fobi/generic/form_importer.html @@ -0,0 +1,15 @@ +{% extends fobi_theme.base_edit_template %} + +{% load i18n %} + +{% block page-title %}{% trans "Form importer" %}{% endblock page-title %} + +{% block navbar-menu-content %} +{% endblock navbar-menu-content %} + +{% block content %} + {% include fobi_theme.form_importer_ajax_template %} +{% endblock content %} + +{% block sidebar-wrapper %} +{% endblock sidebar-wrapper %} diff --git a/src/fobi/templates/fobi/generic/form_importer_ajax.html b/src/fobi/templates/fobi/generic/form_importer_ajax.html new file mode 100644 index 00000000..19468241 --- /dev/null +++ b/src/fobi/templates/fobi/generic/form_importer_ajax.html @@ -0,0 +1,9 @@ +{% extends fobi_theme.form_wizard_template %} + +{% load i18n %} + +{% block form_page_title %} +{% trans "Form importer" %} +{% endblock form_page_title %} + +{% block form_primary_button_text %}{% trans "Import" %}{% endblock %} diff --git a/src/fobi/templates/fobi/generic/snippets/form_wizard.html b/src/fobi/templates/fobi/generic/snippets/form_wizard.html new file mode 100644 index 00000000..77102895 --- /dev/null +++ b/src/fobi/templates/fobi/generic/snippets/form_wizard.html @@ -0,0 +1,56 @@ +{% load i18n %} + +
    +

    {% block form_page_title %}{% endblock %}

    +
    + +
    + {% csrf_token %} + {{ wizard.form.media }} + {{ wizard.management_form }} + {% if wizard.form.forms %} + {{ wizard.form.management_form }} + {% for wizard_form in wizard.form.forms %} + {{ wizard_form.as_p }} + {#% include fobi_theme.form_snippet_template_name %#} + {% endfor %} + {% else %} + {{ wizard.form.as_p }} + {% endif %} + +
    +
    + {% block form_buttons %} + + {% if wizard.steps.prev %} + + {#% endif %#} + + {#% if wizard.steps.prev %#} + + {% endif %} + + + + {% endblock form_buttons %} +
    +
    + +
    \ No newline at end of file diff --git a/src/fobi/templatetags/fobi_tags.py b/src/fobi/templatetags/fobi_tags.py index 1124984b..54e4742d 100644 --- a/src/fobi/templatetags/fobi_tags.py +++ b/src/fobi/templatetags/fobi_tags.py @@ -12,7 +12,13 @@ from django.template import Library, TemplateSyntaxError, Node from django.conf import settings from django import forms from django.utils.translation import ugettext_lazy as _ -from django.forms.util import ErrorDict + +from nine.versions import DJANGO_GTE_1_7 + +if DJANGO_GTE_1_7: + from django.forms.utils import ErrorDict +else: + from django.forms.util import ErrorDict from fobi.settings import DISPLAY_AUTH_LINK diff --git a/src/fobi/templatetags/future_compat.py b/src/fobi/templatetags/future_compat.py index 9b6d7429..7ad352db 100644 --- a/src/fobi/templatetags/future_compat.py +++ b/src/fobi/templatetags/future_compat.py @@ -13,8 +13,9 @@ except ImportError: import warnings from django.template.base import ( - Node, TemplateSyntaxError, Library + Node, TemplateSyntaxError ) + from django.template import Library from django.utils.timezone import template_localtime from django.utils.formats import localize from django.utils.encoding import force_text diff --git a/src/fobi/tests/test_form_importers_mailchimp.py b/src/fobi/tests/test_form_importers_mailchimp.py new file mode 100644 index 00000000..a8b3df2c --- /dev/null +++ b/src/fobi/tests/test_form_importers_mailchimp.py @@ -0,0 +1,222 @@ +from django.conf import settings +from django.contrib.auth import get_user_model + +from fobi.contrib.plugins.form_importers.mailchimp_importer.fobi_form_importers \ + import MailChimpFormImporter + +test_form_data = [ + { + u'default': u'', + u'field_type': u'email', + u'helptext': u'', + u'id': 0, + u'name': u'Email Address', + u'order': u'1', + u'public': True, + u'req': True, + u'show': True, + u'size': u'25', + u'tag': u'EMAIL' + }, + { + u'default': u'', + u'field_type': u'text', + u'helptext': u'', + u'id': 1, + u'name': u'First Name', + u'order': u'2', + u'public': True, + u'req': False, + u'show': True, + u'size': u'25', + u'tag': u'FNAME' + }, + { + u'default': u'', + u'field_type': u'text', + u'helptext': u'', + u'id': 2, + u'name': u'Last Name', + u'order': u'3', + u'public': True, + u'req': False, + u'show': True, + u'size': u'25', + u'tag': u'LNAME' + }, + { + u'default': u'', + u'field_type': u'text', + u'helptext': u'', + u'id': 3, + u'name': u'Organisation', + u'order': u'4', + u'public': True, + u'req': False, + u'show': True, + u'size': u'25', + u'tag': u'ORG' + }, + { + u'default': u'Type Text Default Value', + u'field_type': u'text', + u'helptext': u'Type Text Help Text', + u'id': 4, + u'name': u'type_text', + u'order': u'5', + u'public': True, + u'req': True, + u'show': True, + u'size': u'25', + u'tag': u'TYPE_TEXT' + }, + { + u'default': u'1', + u'field_type': u'number', + u'helptext': u'Type Number Help Text', + u'id': 5, + u'name': u'type_number', + u'order': u'6', + u'public': True, + u'req': False, + u'show': True, + u'size': u'25', + u'tag': u'TYPE_NUMBE' + }, + { + u'choices': [u'First Choice', u'Second Choice', u'Third Choice'], + u'default': u'Second Choice', + u'field_type': u'radio', + u'helptext': u'Type Radio Buttons Help Text', + u'id': 6, + u'name': u'type_radio_buttons', + u'order': u'7', + u'public': True, + u'req': True, + u'show': True, + u'size': u'25', + u'tag': u'TYPE_RADIO' + }, + { + u'choices': [u'First Choice', u'Second Choice', u'Third Choice'], + u'default': u'Third Choice', + u'field_type': u'dropdown', + u'helptext': u'Drop Down Help Text', + u'id': 7, + u'name': u'type_drop_down', + u'order': u'9', + u'public': True, + u'req': True, + u'show': True, + u'size': u'25', + u'tag': u'TYPE_DROPD' + }, + { + u'dateformat': u'MM/DD/YYYY', + u'default': u'', + u'field_type': u'date', + u'helptext': u'Type Date Help Text', + u'id': 8, + u'name': u'type_date', + u'order': u'10', + u'public': True, + u'req': True, + u'show': True, + u'size': u'25', + u'tag': u'TYPE_DATE' + }, + { + u'dateformat': u'MM/DD', + u'default': u'', + u'field_type': u'birthday', + u'helptext': u'Type Birthday Help Text', + u'id': 9, + u'name': u'type_birthday', + u'order': u'11', + u'public': True, + u'req': True, + u'show': True, + u'size': u'25', + u'tag': u'TYPE_BIRTH' + }, + { + u'default': u'', + u'defaultcountry': u'109', + u'defaultcountry_cc': u'NL', + u'defaultcountry_name': u'Netherlands', + u'field_type': u'address', + u'helptext': u'Type Address Help Text', + u'id': 10, + u'name': u'type_address', + u'order': u'12', + u'public': True, + u'req': False, + u'show': True, + u'size': u'25', + u'tag': u'TYPE_ADDRE' + }, + { + u'default': u'', + u'field_type': u'zip', + u'helptext': u'Type Zip Code Help Text', + u'id': 11, + u'name': u'type_zip_code', + u'order': u'13', + u'public': True, + u'req': False, + u'show': True, + u'size': u'25', + u'tag': u'TYPE_ZIP_C' + }, + { + u'default': u'', + u'field_type': u'phone', + u'helptext': u'Type Phone Help Text', + u'id': 12, + u'name': u'type_phone', + u'order': u'14', + u'phoneformat': u'none', + u'public': True, + u'req': False, + u'show': True, + u'size': u'25', + u'tag': u'TYPE_PHONE' + }, + { + u'default': u'', + u'field_type': u'url', + u'helptext': u'Type Website Help Text', + u'id': 13, + u'name': u'type_website', + u'order': u'15', + u'public': True, + u'req': True, + u'show': True, + u'size': u'25', + u'tag': u'TYPE_WEBSI' + }, + { + u'default': u'', + u'field_type': u'imageurl', + u'helptext': u'Type Image Help Text', + u'id': 14, + u'name': u'type_image', + u'order': u'16', + u'public': True, + u'req': False, + u'show': True, + u'size': u'25', + u'tag': u'TYPE_IMAGE' + } +] + +def do(): + User = get_user_model() + kwargs = {User.USERNAME_FIELD: 'test_user',} + user = User.objects.get(**kwargs) + + form_properties = {'name': 'Test mailchimp form', 'user': user} + + importer = MailChimpFormImporter() + + importer.import_data(form_properties, test_form_data) diff --git a/src/fobi/urls/edit.py b/src/fobi/urls/edit.py index f4452349..2a7f376b 100644 --- a/src/fobi/urls/edit.py +++ b/src/fobi/urls/edit.py @@ -11,7 +11,7 @@ from fobi.views import ( dashboard, create_form_entry, edit_form_entry, delete_form_entry, add_form_element_entry, edit_form_element_entry, delete_form_element_entry, add_form_handler_entry, edit_form_handler_entry, delete_form_handler_entry, - export_form_entry, import_form_entry, + export_form_entry, import_form_entry, form_importer ) #urlpatterns = patterns('fobi.views', @@ -41,6 +41,11 @@ urlpatterns = [ import_form_entry, name='fobi.import_form_entry'), + # Form importers + url(_(r'^forms/importer/(?P[\w_\-]+)/$'), + form_importer, + name='fobi.form_importer'), + # Add form element entry url(_(r'^forms/elements/add/(?P\d+)/(?P[\w_\-]+)/$'), add_form_element_entry, diff --git a/src/fobi/views.py b/src/fobi/views.py index df7781ac..608912f0 100644 --- a/src/fobi/views.py +++ b/src/fobi/views.py @@ -12,7 +12,7 @@ __all__ = ( 'delete_form_element_entry', 'add_form_handler_entry', 'edit_form_handler_entry', 'delete_form_handler_entry', 'dashboard', 'view_form_entry', 'form_entry_submitted', - 'export_form_entry', 'import_form_entry', + 'export_form_entry', 'import_form_entry', 'form_importer', ) import datetime @@ -42,6 +42,10 @@ from fobi.base import ( form_handler_plugin_registry, submit_plugin_form_data, get_theme, #get_registered_form_handler_plugins ) +from fobi.form_importers import ( + form_importer_plugin_registry, get_form_impoter_plugin_urls, + ensure_autodiscover as ensure_importers_autodiscover +) from fobi.constants import ( CALLBACK_BEFORE_FORM_VALIDATION, CALLBACK_FORM_VALID_BEFORE_SUBMIT_PLUGIN_FORM_DATA, @@ -127,7 +131,10 @@ def dashboard(request, theme=None, template_name=None): .filter(user__pk=request.user.pk) \ .select_related('user') - context = {'form_entries': form_entries} + context = { + 'form_entries': form_entries, + 'form_importers': get_form_impoter_plugin_urls(), + } # If given, pass to the template (and override the value set by # the context processor. @@ -1232,3 +1239,25 @@ def import_form_entry(request, template_name=None): return render_to_response(template_name, context, context_instance=RequestContext(request)) + + +# ***************************************************************************** +# ***************************************************************************** +# ****************************** Form importers ******************************* +# ***************************************************************************** +# ***************************************************************************** + +@login_required +@permissions_required(satisfy=SATISFY_ALL, perms=create_form_entry_permissions) +def form_importer(request, form_importer_plugin_uid, template_name=None, + *args, **kwargs): + """ + """ + ensure_importers_autodiscover() + form_importer_cls = form_importer_plugin_registry._registry.get( + form_importer_plugin_uid + ) + form_importer = form_importer_cls(form_entry_cls=FormEntry, + form_element_entry_cls=FormElementEntry) + + return form_importer.get_wizard(request, *args, **kwargs)