diff --git a/.gitignore b/.gitignore index 037bf0da..c2f79f34 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ .zip /.coverage /dev.db +/.cache/ MANIFEST.in~ MIND_BUCKET.rst @@ -31,9 +32,11 @@ README_PARTS.rst /src/django_fobi.egg-info /src/fobi/contrib/plugins/form_elements/fields/hidden_model_object/ /src/fobi/contrib/plugins/form_importers/mailchimp_importer/bucket.py +/src/fobi/contrib/apps/wagtail_integration_/ /scripts/install_django_1_7_wagtail.sh /scripts/reinstall_django_1_7_wagtail.sh +/scripts/copy_lund_files.sh /examples/db/ /examples/tmp/ @@ -41,6 +44,7 @@ README_PARTS.rst /examples/media/fobi_plugins/content_image/ /examples/media/fobi_plugins/file/ /examples/media/cache/ +/examples/simple/lund_urls.py /examples/simple/lund/ /examples/simple/settings/local_settings.py /examples/simple/settings/local_settings_foundation5.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index cee89e08..53fcdd2c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -15,6 +15,123 @@ are used for versioning (schema follows below): 0.3.4 to 0.4). - All backwards incompatible changes are mentioned in this document. +0.10.1 +------ +2016-11-17 + +- Fixed captcha and re-captcha issues in form wizards. + +0.10 +---- +2016-11-16 + +Note, that this release contains minor backwards incompatible changes, that may +break your code. Two additional arguments have been added to the +`submit_plugin_form_data` method of the form element plugins. If you have +written custom form element plugins - update your code. + +Note, that this release contain minor backwards incompatible changes, that +may break your existing code (your data is left intact). If you have written +custom form element plugins you should update your code! + +- Added `form_entry_elements` and `kwargs` to the `submit_plugin_form_data` + method of the form element plugins. Make sure to update your custom + plugins if you have written any. +- Added tests for mailchimp integration plugin. +- Moving all plugins to base submodules of the correspondent sub + packages. +- Add missing whitespace toe the `help_text` of the `title` field of + `FormEntry` and `FormWizardEntry` models. +- Disable GoogleAnalytics while testing (guess what - this change speeds up + selenium tests twice). +- Docs updated. +- Helper scripts updated. +- Multiple pep8 fixes. + +0.9.17 +------ +2016-11-13 + +Announcing dropping support of Python 2.6 and Django 1.7. As of 0.9.17 +everything is still backwards compatible with Django 1.7, but in future +versions it will be wiped out. + +- Value validations for Integer and Text Fields. +- Hide previous button in form wizard template for bootstrap3 on first step. + +0.9.16 +------ +2016-11-10 + +- Introduced form titles (shown in view templates). +- Improved navigation of the form wizards. + +0.9.15 +------ +2016-11-07 + +- Minor fixes. + +0.9.14 +------ +2016-11-07 + +- Minor fixes. + +0.9.13 +------ +2016-11-05 + +Announcing dropping support of Django 1.5 and 1.6. As of 0.9.17 everything is +still backwards compatible with versions 1.5 and 1.6, but in future versions +compatibility with these versions will be wiped out. + +- Fix backwards compatibility of `slider` and `range_select` plugins with + Django versions 1.5 and 1.6. + +0.9.12 +------ +2016-11-02 + +- Better debugging. +- Upgrade example FeinCMS integration to work with 1.12. + +0.9.11 +------ +2016-11-01 + +- Fixes. + +0.9.10 +------ +2016-11-01 + +- Fixed issue with custom labels in the `slider` plugin. +- Made `slider` plugin compatible with Django <= 1.6. +- Fixes `get_absolute_url` methods on `FormEntry` and `FormWizardEntry` + models. #48 + +0.9.9 +----- +2016-10-31 + +- Make it possible to add custom ticks to the `slider` plugin. + +0.9.8 +----- +2016-10-27 + +- Support multiple sliders in one form. + +0.9.7 +----- +2016-10-27 + +- Improvements in the generic integration processor. #47 +- Improved form wizard interface and navigation. +- Fixed a broken test. +- Added import/export functionality for form wizards. + 0.9.6 ----- 2016-10-25 diff --git a/CREDITS.rst b/CREDITS.rst index b74383c1..47d75aea 100644 --- a/CREDITS.rst +++ b/CREDITS.rst @@ -23,3 +23,15 @@ Thanks to the following people for their contributions: - `Mario Taddei `_ for his initiative to make `Select multiple with max` plugin. +- `Andy Babic + `_ + for improvements in the generic integration processor. +- `Heldroe + `_ + for minor fixes. +- `Michal Dabski + `_ + for minor fixes. +- `Marcos Amorim + `_ + for number of validation improvements. diff --git a/README.rst b/README.rst index 04e4aa3e..5355547b 100644 --- a/README.rst +++ b/README.rst @@ -10,8 +10,21 @@ handling the submitted form data). Prerequisites ============= -- Django 1.5, 1.6, 1.7, 1.8, 1.9, 1.10 -- Python >= 2.6.8, >= 2.7, >= 3.3 +Current +------- +- Django 1.8, 1.9, 1.10 +- Python >= 2.7, >= 3.3 + +Past +---- +- Dropping support of Django 1.5, 1.6 has been announced in version + 0.9.13. Dropping support of Django 1.7 has been announced in version 0.9.17. + As of 0.9.17 everything is still backwards compatible with versions 1.5, 1.6 + and 1.7, but in future versions compatibility with these versions will be + wiped out. +- Dropping support of Python 2.6 has been announced in version 0.9.17. + As of 0.9.17 everything is still backwards compatible with Python 2.6, but + in future versions compatibility with it will be wiped out. Key concepts ============ @@ -89,7 +102,7 @@ Roadmap ======= Some of the upcoming/in-development features/improvements are: -- Integration with `django-rest-framework` (in version 0.10). +- Integration with `django-rest-framework` (in version 0.11). See the `TODOS `_ for the full list of planned-, pending- in-development- or to-be-implemented @@ -416,12 +429,16 @@ following arguments: - `request` (django.http.HttpRequest): The Django HTTP request. - `form` (django.forms.Form): Form object (a valid one, which contains the ``cleaned_data`` attribute). +- `form_element_entries` (fobi.models.FormElementEntry): Form element entries + for the `form_entry` given. +- (**)kwargs : Additional arguments. Example (taken from fobi.contrib.plugins.form_elements.fields.file): .. code-block:: python - def submit_plugin_form_data(self, form_entry, request, form): + def submit_plugin_form_data(self, form_entry, request, form, + form_element_entries=None, **kwargs): """Submit plugin form data.""" # Get the file path file_path = form.cleaned_data.get(self.data.name, None) @@ -1486,16 +1503,6 @@ README.rst file in directory of each plugin for details. `__: Send the form data by email. -Limitations ------------ -- At the moment, if you have used `django-simple-captcha` or - `django-recaptcha` plugins in one of the forms of the wizard, the wizard - becomes invalid at the end and sends you back to the form which used - captcha (see the issue `here - `__ and `here - `__). Therefore, - you're not recommended to use captcha solutions in wizard forms (yet). - Permissions =========== Plugin system allows administrators to specify the access rights to every @@ -1783,56 +1790,58 @@ Note, that you should not provide the `fobi_dynamic_values.` as a prefix. Currently, the following variables are available in the `fobi.context_processors.dynamic_values` context processor: -- request: Stripped HttpRequest object. +.. code-block:: text - - request.path: A string representing the full path to the requested page, - not including the scheme or domain. + - request: Stripped HttpRequest object. - - request.get_full_path(): Returns the path, plus an appended query string, - if applicable. + - request.path: A string representing the full path to the requested page, + not including the scheme or domain. - - request.is_secure(): Returns True if the request is secure; that is, if - it was made with HTTPS. + - request.get_full_path(): Returns the path, plus an appended query string, + if applicable. - - request.is_ajax(): Returns True if the request was made via an - XMLHttpRequest, by checking the HTTP_X_REQUESTED_WITH header for the - string 'XMLHttpRequest'. + - request.is_secure(): Returns True if the request is secure; that is, if + it was made with HTTPS. - - request.META: A stripped down standard Python dictionary containing the - available HTTP headers. + - request.is_ajax(): Returns True if the request was made via an + XMLHttpRequest, by checking the HTTP_X_REQUESTED_WITH header for the + string 'XMLHttpRequest'. - - HTTP_ACCEPT_ENCODING: Acceptable encodings for the response. + - request.META: A stripped down standard Python dictionary containing the + available HTTP headers. - - HTTP_ACCEPT_LANGUAGE: Acceptable languages for the response. + - HTTP_ACCEPT_ENCODING: Acceptable encodings for the response. - - HTTP_HOST: The HTTP Host header sent by the client. + - HTTP_ACCEPT_LANGUAGE: Acceptable languages for the response. - - HTTP_REFERER: The referring page, if any. + - HTTP_HOST: The HTTP Host header sent by the client. - - HTTP_USER_AGENT: The client’s user-agent string. + - HTTP_REFERER: The referring page, if any. - - QUERY_STRING: The query string, as a single (unparsed) string. + - HTTP_USER_AGENT: The client’s user-agent string. - - REMOTE_ADDR: The IP address of the client. + - QUERY_STRING: The query string, as a single (unparsed) string. - - request.user: Authenticated user. + - REMOTE_ADDR: The IP address of the client. - - request.user.email: + - request.user: Authenticated user. - - request.user.get_username(): Returns the username for the user. Since - the User model can be swapped out, you should use this method - instead of referencing the username attribute directly. + - request.user.email: - - request.user.get_full_name(): Returns the first_name plus the - last_name, with a space in between. + - request.user.get_username(): Returns the username for the user. Since + the User model can be swapped out, you should use this method + instead of referencing the username attribute directly. - - request.user.get_short_name(): Returns the first_name. + - request.user.get_full_name(): Returns the first_name plus the + last_name, with a space in between. - - request.user.is_anonymous(): + - request.user.get_short_name(): Returns the first_name. -- now: datetime.datetime.now() + - request.user.is_anonymous(): -- today: datetime.date.today() + - now: datetime.datetime.now() + + - today: datetime.date.today() Submitted form element plugins values ===================================== @@ -1985,6 +1994,51 @@ element- and form handler- plugins. shown in case of missing form element handlers, set this to False in your settings module. Default value is True. +Testing +======= +Project is covered by test (functional- and browser-tests). + +To test with all supported Python/Django versions type: + +.. code-block:: sh + + tox + +To test just your working environment type: + +.. code-block:: sh + + ./runtests.py + +It's assumed that you have all the requirements installed. If not, first +install the test requirements: + +.. code-block:: sh + + pip install -r examples/requirements/common_test_requirements.txt + +Selenium +-------- +Latest versions of Firefox are often not supported by Selenium. Current +version of the Selenium for Python (2.53.6) works fine with Firefox 47. +Thus, instead of using system Firefox you could better use a custom one. + +Set up Firefox 47 +~~~~~~~~~~~~~~~~~ +1. Download Firefox 47 from + `this + `__ + location and unzip it into ``/usr/lib/firefox47/`` + +2. Specify the full path to your Firefox in ``FIREFOX_BIN_PATH`` + setting. Example: + + .. code-block:: python + + FIREFOX_BIN_PATH = '/usr/lib/firefox47/firefox' + +After that your Selenium tests would work. + Troubleshooting =============== If you get a ``FormElementPluginDoesNotExist`` or a diff --git a/ROADMAP.rst b/ROADMAP.rst index bc9bf6e7..131679c7 100644 --- a/ROADMAP.rst +++ b/ROADMAP.rst @@ -1,6 +1,6 @@ Roadmap of upcoming releases ============================ -0.10 +0.11 ---- yyyy-mm-ddd (upcoming). diff --git a/TODOS.rst b/TODOS.rst index d978d7c2..6b38a5b6 100644 --- a/TODOS.rst +++ b/TODOS.rst @@ -34,7 +34,7 @@ Regarding the form wizards + 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. ++ Rethink the new navigation of forms and form wizards. - Add support for form wizard conditions. + Fixed broken dependencies for docs. - Add FeinCMS integration app for form wizards. @@ -45,6 +45,8 @@ Regarding the form wizards - Add selenium tests for form wizards. - Make `foundation5` and `django-admin-theme` themes to reflect the latest GUI changes (form wizards). +- Make sure captcha plugins are usable with form wizards (at the moment they + are being invalidated on the last step). Roadmap ------- @@ -54,6 +56,11 @@ Roadmap Uncategorised ------------- +- Implement the clone form functionality. +- Implement the clone form wizard functionality. +- Rethink templating of the integration packages (feincms_integration, + djangocms_integration, mezzanine_integration), as now they are a bit + of a mess. Document integration properly, if not yet done. - Add tests for import/export of forms. - Add tests for export of plugin data (db_store). - In the form element plugins, when handling submit_form_data, somehow @@ -103,9 +110,9 @@ Uncategorised validation method there, which accepts the request, the form and the form_entry object for validation. Also, in the BaseFormFieldPlugin, there should be `name`, `required`, `help_text`, `label` fields to be present ( - scheck other fields of Django formfield). In formfield plugins, subclass + check other fields of Django formfield). In formfield plugins, subclass from BaseFormFieldPlugin, instead of the BaseFormElementPlugin. -+ In the view, validate the form fields (if they are sublcass of ++ In the view, validate the form fields (if they are subclass of BaseFormFieldPlugin). + Actually, if plugin doesn't have a form, save it immediately. Do not wait for POST. @@ -305,7 +312,7 @@ Uncategorised + Check if it's safe to use the initial dynamic values. + In the updated GUI (bootstrap3), if form names are too long, the layout doesn't look nice anymore. -- Somehow, the drag and drop of the form elements got broken. Fix ASAP. ++ Somehow, the drag and drop of the form elements got broken. Fix ASAP. - Since tests have been made quite general, create them for all contrib form elements and handlers (not yet for things like CAPTCHA). - Translate German and Russian URLs. @@ -313,9 +320,7 @@ Uncategorised admin) as much generic so that change between versions doesn't cause styling issues. - Make sure the existing "simple" theme works very well (in looks) in - Django 1.6. -- Make sure the existing "simple" theme works very well (in looks) in - Django 1.7. + Django 1.8, 1.9 and 1.10. - Nicer styling for the radio button (Foundation 5 theme). - Nicer styling for the radio button (Simple theme). - Make it possible to provide an alternative rendering of the form field @@ -327,7 +332,7 @@ Uncategorised part). - Split the ``FOBI_RESTRICT_PLUGIN_ACCESS`` into two: one for form elements and one for form handlers. -- Improve the "simple" theme for Django 1.6 and Django 1.7 (tiny bits of +- Improve the "simple" theme for Django 1.8, 1.9 and 1.10 (tiny bits of styling). - Edit form test. - Edit form element tests. @@ -343,7 +348,7 @@ Uncategorised with the latest versions of the packages. - Add support for `imageurl` and `birthday` fields of MailChimp (they are ignored at the moment). -- Fix layout issue on step 2 of the MailChimp import (step 2 of the wizard). ++ Fix layout issue on step 2 of the MailChimp import (step 2 of the wizard). - Properly document the form importers API. - django-rest-framework integration. @@ -397,7 +402,7 @@ Should haves re-created form from saved JSON sa well. - Add `django-treebeard` field as an alternative (vs MPTT fields). - Make sure that all views are 100% AJAX ready. -- Wagtail integration. +- Wagtail integration (in progress since October 2016). - Document the changes. - Find out why subclassing the ``select_model_object`` plugin didn't work. - Rename the ``simple`` theme into ``django_admin_style_theme``. @@ -451,7 +456,7 @@ Could haves least the FeinCMS). - Make sure that the form view return can be overridden? - Add datetime range and date range fields. -- Configure defaults values of each plugin in projects' settings module. ++ Configure defaults values of each plugin in projects' settings module. - TinyMCE form element cosmetic plugin. - In the cosmetic image plugin, render the sized image. - Add Armenian translation. @@ -467,7 +472,7 @@ Could haves Would haves =========== - Conditional inputs. -- Form wizards (combine forms with each other, having one at a step, finally - ++ Form wizards (combine forms with each other, having one at a step, finally - send it all as one). - Perhaps, completely re-write the base template for the foundation 5 theme? - Make it possible to design a form based on existing models. diff --git a/docs/fobi.contrib.apps.rst b/docs/fobi.contrib.apps.rst index e2492825..ec91225f 100644 --- a/docs/fobi.contrib.apps.rst +++ b/docs/fobi.contrib.apps.rst @@ -9,7 +9,7 @@ Subpackages fobi.contrib.apps.djangocms_integration fobi.contrib.apps.feincms_integration fobi.contrib.apps.mezzanine_integration - fobi.contrib.apps.wagtail_integration + fobi.contrib.apps.wagtail_integration_ Module contents --------------- diff --git a/docs/fobi.contrib.plugins.form_elements.content.content_text.rst b/docs/fobi.contrib.plugins.form_elements.content.content_text.rst index 255b485f..75599544 100644 --- a/docs/fobi.contrib.plugins.form_elements.content.content_text.rst +++ b/docs/fobi.contrib.plugins.form_elements.content.content_text.rst @@ -12,6 +12,22 @@ fobi.contrib.plugins.form_elements.content.content_text.apps module :undoc-members: :show-inheritance: +fobi.contrib.plugins.form_elements.content.content_text.conf module +------------------------------------------------------------------- + +.. automodule:: fobi.contrib.plugins.form_elements.content.content_text.conf + :members: + :undoc-members: + :show-inheritance: + +fobi.contrib.plugins.form_elements.content.content_text.defaults module +----------------------------------------------------------------------- + +.. automodule:: fobi.contrib.plugins.form_elements.content.content_text.defaults + :members: + :undoc-members: + :show-inheritance: + fobi.contrib.plugins.form_elements.content.content_text.fobi_form_elements module --------------------------------------------------------------------------------- @@ -28,6 +44,14 @@ fobi.contrib.plugins.form_elements.content.content_text.forms module :undoc-members: :show-inheritance: +fobi.contrib.plugins.form_elements.content.content_text.settings module +----------------------------------------------------------------------- + +.. automodule:: fobi.contrib.plugins.form_elements.content.content_text.settings + :members: + :undoc-members: + :show-inheritance: + Module contents --------------- diff --git a/docs/fobi.contrib.plugins.form_elements.fields.slider.rst b/docs/fobi.contrib.plugins.form_elements.fields.slider.rst index f413d96e..702229a6 100644 --- a/docs/fobi.contrib.plugins.form_elements.fields.slider.rst +++ b/docs/fobi.contrib.plugins.form_elements.fields.slider.rst @@ -52,6 +52,14 @@ fobi.contrib.plugins.form_elements.fields.slider.forms module :undoc-members: :show-inheritance: +fobi.contrib.plugins.form_elements.fields.slider.helpers module +--------------------------------------------------------------- + +.. automodule:: fobi.contrib.plugins.form_elements.fields.slider.helpers + :members: + :undoc-members: + :show-inheritance: + fobi.contrib.plugins.form_elements.fields.slider.settings module ---------------------------------------------------------------- diff --git a/docs/fobi.migrations.rst b/docs/fobi.migrations.rst index 67a38f37..8580f06a 100644 --- a/docs/fobi.migrations.rst +++ b/docs/fobi.migrations.rst @@ -84,6 +84,30 @@ fobi.migrations.0010_formwizardhandler module :undoc-members: :show-inheritance: +fobi.migrations.0011_formentry_title module +------------------------------------------- + +.. automodule:: fobi.migrations.0011_formentry_title + :members: + :undoc-members: + :show-inheritance: + +fobi.migrations.0012_auto_20161109_1550 module +---------------------------------------------- + +.. automodule:: fobi.migrations.0012_auto_20161109_1550 + :members: + :undoc-members: + :show-inheritance: + +fobi.migrations.0013_formwizardentry_show_all_navigation_buttons module +----------------------------------------------------------------------- + +.. automodule:: fobi.migrations.0013_formwizardentry_show_all_navigation_buttons + :members: + :undoc-members: + :show-inheritance: + Module contents --------------- diff --git a/docs/index.rst b/docs/index.rst index 20c7cfc6..724a1dbb 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,8 +10,21 @@ handling the submitted form data). Prerequisites ============= -- Django 1.5, 1.6, 1.7, 1.8, 1.9, 1.10 -- Python >= 2.6.8, >= 2.7, >= 3.3 +Current +------- +- Django 1.8, 1.9, 1.10 +- Python >= 2.7, >= 3.3 + +Past +---- +- Dropping support of Django 1.5, 1.6 has been announced in version + 0.9.13. Dropping support of Django 1.7 has been announced in version 0.9.17. + As of 0.9.17 everything is still backwards compatible with versions 1.5, 1.6 + and 1.7, but in future versions compatibility with these versions will be + wiped out. +- Dropping support of Python 2.6 has been announced in version 0.9.17. + As of 0.9.17 everything is still backwards compatible with Python 2.6, but + in future versions compatibility with it will be wiped out. Key concepts ============ @@ -89,7 +102,7 @@ Roadmap ======= Some of the upcoming/in-development features/improvements are: -- Integration with `django-rest-framework` (in version 0.10). +- Integration with `django-rest-framework` (in version 0.11). See the `TODOS `_ for the full list of planned-, pending- in-development- or to-be-implemented @@ -416,12 +429,16 @@ following arguments: - `request` (django.http.HttpRequest): The Django HTTP request. - `form` (django.forms.Form): Form object (a valid one, which contains the ``cleaned_data`` attribute). +- `form_element_entries` (fobi.models.FormElementEntry): Form element entries + for the `form_entry` given. +- (**)kwargs : Additional arguments. Example (taken from fobi.contrib.plugins.form_elements.fields.file): .. code-block:: python - def submit_plugin_form_data(self, form_entry, request, form): + def submit_plugin_form_data(self, form_entry, request, form, + form_element_entries=None, **kwargs): """Submit plugin form data.""" # Get the file path file_path = form.cleaned_data.get(self.data.name, None) @@ -1486,16 +1503,6 @@ README.rst file in directory of each plugin for details. `__: Send the form data by email. -Limitations ------------ -- At the moment, if you have used `django-simple-captcha` or - `django-recaptcha` plugins in one of the forms of the wizard, the wizard - becomes invalid at the end and sends you back to the form which used - captcha (see the issue `here - `__ and `here - `__). Therefore, - you're not recommended to use captcha solutions in wizard forms (yet). - Permissions =========== Plugin system allows administrators to specify the access rights to every @@ -1783,56 +1790,58 @@ Note, that you should not provide the `fobi_dynamic_values.` as a prefix. Currently, the following variables are available in the `fobi.context_processors.dynamic_values` context processor: -- request: Stripped HttpRequest object. +.. code-block:: text - - request.path: A string representing the full path to the requested page, - not including the scheme or domain. + - request: Stripped HttpRequest object. - - request.get_full_path(): Returns the path, plus an appended query string, - if applicable. + - request.path: A string representing the full path to the requested page, + not including the scheme or domain. - - request.is_secure(): Returns True if the request is secure; that is, if - it was made with HTTPS. + - request.get_full_path(): Returns the path, plus an appended query string, + if applicable. - - request.is_ajax(): Returns True if the request was made via an - XMLHttpRequest, by checking the HTTP_X_REQUESTED_WITH header for the - string 'XMLHttpRequest'. + - request.is_secure(): Returns True if the request is secure; that is, if + it was made with HTTPS. - - request.META: A stripped down standard Python dictionary containing the - available HTTP headers. + - request.is_ajax(): Returns True if the request was made via an + XMLHttpRequest, by checking the HTTP_X_REQUESTED_WITH header for the + string 'XMLHttpRequest'. - - HTTP_ACCEPT_ENCODING: Acceptable encodings for the response. + - request.META: A stripped down standard Python dictionary containing the + available HTTP headers. - - HTTP_ACCEPT_LANGUAGE: Acceptable languages for the response. + - HTTP_ACCEPT_ENCODING: Acceptable encodings for the response. - - HTTP_HOST: The HTTP Host header sent by the client. + - HTTP_ACCEPT_LANGUAGE: Acceptable languages for the response. - - HTTP_REFERER: The referring page, if any. + - HTTP_HOST: The HTTP Host header sent by the client. - - HTTP_USER_AGENT: The client’s user-agent string. + - HTTP_REFERER: The referring page, if any. - - QUERY_STRING: The query string, as a single (unparsed) string. + - HTTP_USER_AGENT: The client’s user-agent string. - - REMOTE_ADDR: The IP address of the client. + - QUERY_STRING: The query string, as a single (unparsed) string. - - request.user: Authenticated user. + - REMOTE_ADDR: The IP address of the client. - - request.user.email: + - request.user: Authenticated user. - - request.user.get_username(): Returns the username for the user. Since - the User model can be swapped out, you should use this method - instead of referencing the username attribute directly. + - request.user.email: - - request.user.get_full_name(): Returns the first_name plus the - last_name, with a space in between. + - request.user.get_username(): Returns the username for the user. Since + the User model can be swapped out, you should use this method + instead of referencing the username attribute directly. - - request.user.get_short_name(): Returns the first_name. + - request.user.get_full_name(): Returns the first_name plus the + last_name, with a space in between. - - request.user.is_anonymous(): + - request.user.get_short_name(): Returns the first_name. -- now: datetime.datetime.now() + - request.user.is_anonymous(): -- today: datetime.date.today() + - now: datetime.datetime.now() + + - today: datetime.date.today() Submitted form element plugins values ===================================== @@ -1985,6 +1994,51 @@ element- and form handler- plugins. shown in case of missing form element handlers, set this to False in your settings module. Default value is True. +Testing +======= +Project is covered by test (functional- and browser-tests). + +To test with all supported Python/Django versions type: + +.. code-block:: sh + + tox + +To test just your working environment type: + +.. code-block:: sh + + ./runtests.py + +It's assumed that you have all the requirements installed. If not, first +install the test requirements: + +.. code-block:: sh + + pip install -r examples/requirements/common_test_requirements.txt + +Selenium +-------- +Latest versions of Firefox are often not supported by Selenium. Current +version of the Selenium for Python (2.53.6) works fine with Firefox 47. +Thus, instead of using system Firefox you could better use a custom one. + +Set up Firefox 47 +~~~~~~~~~~~~~~~~~ +1. Download Firefox 47 from + `this + `__ + location and unzip it into ``/usr/lib/firefox47/`` + +2. Specify the full path to your Firefox in ``FIREFOX_BIN_PATH`` + setting. Example: + + .. code-block:: python + + FIREFOX_BIN_PATH = '/usr/lib/firefox47/firefox' + +After that your Selenium tests would work. + Troubleshooting =============== If you get a ``FormElementPluginDoesNotExist`` or a diff --git a/examples/requirements/common_python2.txt b/examples/requirements/common_python2.txt index 173a05bd..8a64c212 100644 --- a/examples/requirements/common_python2.txt +++ b/examples/requirements/common_python2.txt @@ -31,7 +31,6 @@ six==1.10.0 snowballstemmer==1.2.0 Sphinx==1.3.1 sphinx-rtd-theme==0.1.9 -sqlparse==0.1.17 traitlets==4.0.0 #Unidecode==0.4.18 virtualenv==13.1.2 diff --git a/examples/requirements/django_1_10.txt b/examples/requirements/django_1_10.txt index fbdbfad2..cb2d975c 100644 --- a/examples/requirements/django_1_10.txt +++ b/examples/requirements/django_1_10.txt @@ -10,3 +10,4 @@ django-debug-toolbar==1.5 django-registration-redux>=1.4 #easy-thumbnails==2.3 #vishap>=0.1.5 +sqlparse==0.2.2 diff --git a/examples/requirements/django_1_5.txt b/examples/requirements/django_1_5.txt index 3f60da62..d671e119 100644 --- a/examples/requirements/django_1_5.txt +++ b/examples/requirements/django_1_5.txt @@ -11,3 +11,4 @@ django-localeurl>=2.0.2 django-registration-redux==1.2 #easy-thumbnails==2.3 #vishap>=0.1.5 +sqlparse==0.1.9 diff --git a/examples/requirements/django_1_6.txt b/examples/requirements/django_1_6.txt index 71effcf5..9213495c 100644 --- a/examples/requirements/django_1_6.txt +++ b/examples/requirements/django_1_6.txt @@ -11,3 +11,4 @@ django-localeurl>=2.0.2 django-registration-redux==1.2 #easy-thumbnails==2.3 #vishap>=0.1.5 +sqlparse==0.1.9 diff --git a/examples/requirements/django_1_7.txt b/examples/requirements/django_1_7.txt index 4b42f139..1d7dc808 100644 --- a/examples/requirements/django_1_7.txt +++ b/examples/requirements/django_1_7.txt @@ -7,6 +7,7 @@ django-debug-toolbar==0.11.0 django-localeurl>=2.0.2 #django-nine>=0.1.10 #django-nonefield>=0.1 -django-registration-redux>=1.3 +django-registration-redux==1.3 #easy-thumbnails==2.3 #vishap>=0.1.5 +sqlparse==0.1.9 \ No newline at end of file diff --git a/examples/requirements/django_1_8.txt b/examples/requirements/django_1_8.txt index d273f90c..47223f35 100644 --- a/examples/requirements/django_1_8.txt +++ b/examples/requirements/django_1_8.txt @@ -11,3 +11,4 @@ django-formtools django-registration-redux>=1.4 #easy-thumbnails==2.3 #vishap>=0.1.5 +sqlparse==0.1.9 \ No newline at end of file diff --git a/examples/requirements/django_1_9.txt b/examples/requirements/django_1_9.txt index 197fa953..0f7a1757 100644 --- a/examples/requirements/django_1_9.txt +++ b/examples/requirements/django_1_9.txt @@ -3,7 +3,7 @@ Django>=1.9,<1.10 django-admin-tools>=0.7.1 django-autoslug==1.9.3 -django-debug-toolbar==0.11.0 +django-debug-toolbar==1.5 django-formtools==1.0 django-localeurl==2.0.2 django-nine>=0.1.10 @@ -11,3 +11,4 @@ django-nonefield>=0.1 django-registration-redux>=1.4 easy-thumbnails==2.3 vishap>=0.1.5 +sqlparse==0.2.2 \ No newline at end of file diff --git a/examples/requirements/feincms.txt b/examples/requirements/feincms_1_10.txt similarity index 100% rename from examples/requirements/feincms.txt rename to examples/requirements/feincms_1_10.txt diff --git a/examples/requirements/feincms_1_12.txt b/examples/requirements/feincms_1_12.txt new file mode 100644 index 00000000..dc5b3403 --- /dev/null +++ b/examples/requirements/feincms_1_12.txt @@ -0,0 +1,5 @@ +-r django_1_9.txt + +FeinCMS==1.12.1 +django-mptt==0.8.6 +django-tinymce==2.4.0 diff --git a/examples/simple/admin_tools_dashboard/__init__.py b/examples/simple/admin_tools_dashboard/__init__.py index d34f6eb5..2acccc1e 100644 --- a/examples/simple/admin_tools_dashboard/__init__.py +++ b/examples/simple/admin_tools_dashboard/__init__.py @@ -1,5 +1,5 @@ """ -This file was generated with the customdashboard management command, it +This file was generated with the custom dashboard management command, it contains the two classes for the main dashboard and app index dashboard. You can customize these classes as you want. @@ -7,83 +7,110 @@ To activate your index dashboard add the following to your settings.py:: ADMIN_TOOLS_INDEX_DASHBOARD = 'admin_tools_dashboard.CustomIndexDashboard' And to activate the app index dashboard:: - ADMIN_TOOLS_APP_INDEX_DASHBOARD = 'admin_tools_dashboard.CustomAppIndexDashboard' + ADMIN_TOOLS_APP_INDEX_DASHBOARD = \ + 'admin_tools_dashboard.CustomAppIndexDashboard' """ - from django.conf import settings -from django.utils.translation import ugettext, ugettext_lazy as _ +from django.utils.translation import ugettext_lazy as _ from admin_tools.dashboard import modules, Dashboard, AppIndexDashboard -from admin_tools.utils import get_admin_site_name +# from admin_tools.utils import get_admin_site_name from . import conf + class CustomIndexDashboard(Dashboard): - """ - Custom index dashboard. - """ + """Custom index dashboard.""" + columns = 3 def init_with_context(self, context): - ## Foo - #self.children.append(modules.ModelList(_('Foo'), - # models = conf.foo_apps, - # collapsible = False, - # deletable = False - #)) + # Foo + # self.children.append( + # modules.ModelList( + # _('Foo'), + # models=conf.foo_apps, + # collapsible=False, + # deletable=False + # ) + # ) # Fobi - self.children.append(modules.Group( - title = _('Fobi'), - display = 'stacked', - children = [ - modules.ModelList(_('Plugins'), models=conf.fobi_plugins, collapsible=False, deletable=False), - modules.ModelList(_('Forms'), models=conf.fobi_forms, collapsible=False, deletable=False), - modules.ModelList(_('Data'), models=conf.fobi_data, collapsible=False, deletable=False), - ] - )) + self.children.append( + modules.Group( + title=_('Fobi'), + display='stacked', + children=[ + modules.ModelList( + _('Plugins'), + models=conf.fobi_plugins, + collapsible=False, + deletable=False + ), + modules.ModelList( + _('Forms'), + models=conf.fobi_forms, + collapsible=False, + deletable=False + ), + modules.ModelList( + _('Data'), + models=conf.fobi_data, + collapsible=False, + deletable=False + ), + ] + ) + ) if 'feincms' in settings.INSTALLED_APPS: # FeinCMS pages - self.children.append(modules.AppList( - _('FeinCMS Pages'), - models = conf.feincms_pages, - collapsible = False, - deletable = False - )) + self.children.append( + modules.AppList( + _('FeinCMS Pages'), + models=conf.feincms_pages, + collapsible=False, + deletable=False + ) + ) if 'cms' in settings.INSTALLED_APPS: # DjangoCMS pages - self.children.append(modules.AppList( - _('DjangoCMS Pages'), - models = conf.djangocms_pages, - collapsible = False, - deletable = False - )) + self.children.append( + modules.AppList( + _('DjangoCMS Pages'), + models=conf.djangocms_pages, + collapsible=False, + deletable=False + ) + ) # Append an app list module for "Administration" - self.children.append(modules.AppList( - _('Administration'), - models = conf.django_contrib_apps, - collapsible = False, - deletable = False - )) + self.children.append( + modules.AppList( + _('Administration'), + models=conf.django_contrib_apps, + collapsible=False, + deletable=False + ) + ) # Append an app list module for "Administration" - self.children.append(modules.AppList( - _('Other apps'), - models = conf.other_apps, - collapsible = False, - deletable = False - )) + self.children.append( + modules.AppList( + _('Other apps'), + models=conf.other_apps, + collapsible=False, + deletable=False + ) + ) # Append a recent actions module self.children.append(modules.RecentActions(_('Recent Actions'), 10)) + class CustomAppIndexDashboard(AppIndexDashboard): - """ - Custom app index dashboard for netcommunities. - """ + """Custom app index dashboard.""" # We disable title because its redundant with the model list module title = '' @@ -91,14 +118,14 @@ class CustomAppIndexDashboard(AppIndexDashboard): def __init__(self, *args, **kwargs): AppIndexDashboard.__init__(self, *args, **kwargs) - self.children.append(modules.RecentActions( + self.children.append( + modules.RecentActions( _('Recent Actions'), include_list=self.get_app_content_types(), limit=10 - )) + ) + ) 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(CustomAppIndexDashboard, self).init_with_context(context) diff --git a/examples/simple/admin_tools_dashboard/conf.py b/examples/simple/admin_tools_dashboard/conf.py index 780111a7..193e9d83 100644 --- a/examples/simple/admin_tools_dashboard/conf.py +++ b/examples/simple/admin_tools_dashboard/conf.py @@ -2,19 +2,23 @@ # ************ Foo ************** # ******************************* foo_apps = [ - 'foo.models.*', 'bar.models.*', + 'foo.models.*', + 'bar.models.*', ] # ******************************* # ************ Fobi ************* # ******************************* fobi_plugins = [ - 'fobi.models.FormElement', 'fobi.models.FormHandler' + 'fobi.models.FormElement', + 'fobi.models.FormHandler' ] fobi_forms = [ - 'fobi.models.FormWizardEntry', 'fobi.models.FormEntry', - 'fobi.models.FormElementEntry', 'fobi.models.FormFieldsetEntry', + 'fobi.models.FormWizardEntry', + 'fobi.models.FormEntry', + 'fobi.models.FormElementEntry', + 'fobi.models.FormFieldsetEntry', 'fobi.models.FormHandlerEntry', ] @@ -33,5 +37,7 @@ djangocms_pages = [ # ******************************* # ************ Django *********** # ******************************* -django_contrib_apps = ['django.contrib.*',] +django_contrib_apps = [ + 'django.contrib.*', +] other_apps = foo_apps diff --git a/examples/simple/admin_tools_dashboard/menu.py b/examples/simple/admin_tools_dashboard/menu.py index 3a7e268c..157e8153 100644 --- a/examples/simple/admin_tools_dashboard/menu.py +++ b/examples/simple/admin_tools_dashboard/menu.py @@ -27,39 +27,50 @@ class CustomMenu(Menu): ] # Foo - self.children.append(items.ModelList(_('Foo'), - models=conf.foo_apps - )) + self.children.append( + items.ModelList( + _('Foo'), + models=conf.foo_apps + ) + ) # Fobi - self.children.append(items.MenuItem( - _('Fobi'), - children=[ - items.ModelList(_('Plugins'), models=conf.fobi_plugins), - items.ModelList(_('Forms'), models=conf.fobi_forms), - items.ModelList(_('Data'), models=conf.fobi_data), - ] - )) + self.children.append( + items.MenuItem( + _('Fobi'), + children=[ + items.ModelList(_('Plugins'), models=conf.fobi_plugins), + items.ModelList(_('Forms'), models=conf.fobi_forms), + items.ModelList(_('Data'), models=conf.fobi_data), + ] + ) + ) if 'feincms' in settings.INSTALLED_APPS: # FeinCMS pages integration - self.children.append(items.AppList( - _('FeinCMS Pages'), - models=conf.feincms_pages - )) + self.children.append( + items.AppList( + _('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 - )) + self.children.append( + items.AppList( + _('DjangoCMS Pages'), + models=conf.djangocms_pages + ) + ) # append an app list module for "Administration" - self.children.append(items.AppList( - _('Administration'), - models=['django.contrib.*',] - )) + self.children.append( + items.AppList( + _('Administration'), + models=['django.contrib.*'] + ) + ) def init_with_context(self, context): """Use this method if you need to access the request context.""" diff --git a/examples/simple/bar/models.py b/examples/simple/bar/models.py index c509b8db..a96a4317 100644 --- a/examples/simple/bar/models.py +++ b/examples/simple/bar/models.py @@ -19,7 +19,7 @@ class Genre(MPTTModel): """MPTT meta.""" # level_attr = 'mptt_level' - order_insertion_by=['name'] + order_insertion_by = ['name'] def __str__(self): return self.name diff --git a/examples/simple/context_processors.py b/examples/simple/context_processors.py index 0e0620ba..25aad7c5 100644 --- a/examples/simple/context_processors.py +++ b/examples/simple/context_processors.py @@ -1,6 +1,13 @@ -__all__ = ('disable_admin_tools',) +from django.conf import settings + +__all__ = ('disable_admin_tools', 'testing',) def disable_admin_tools(request): """Disable admin tools.""" return {'ADMIN_TOOLS_DISABLED': True} + + +def testing(request): + """Put `testing` into context.""" + return {'testing': settings.TESTING} diff --git a/examples/simple/customauth/__init__.py b/examples/simple/customauth/__init__.py index 512b7328..1800d4bf 100644 --- a/examples/simple/customauth/__init__.py +++ b/examples/simple/customauth/__init__.py @@ -1 +1 @@ -from customauth.models import MyUser \ No newline at end of file +from .models import MyUser diff --git a/examples/simple/customauth/admin.py b/examples/simple/customauth/admin.py index 7f7f74e3..56aea2a9 100644 --- a/examples/simple/customauth/admin.py +++ b/examples/simple/customauth/admin.py @@ -93,11 +93,9 @@ class MyUserAdmin(UserAdmin): # add_fieldsets is not a standard ModelAdmin attribute. UserAdmin # overrides get_fieldsets to use this attribute when creating a user. add_fieldsets = ( - (None, { - 'classes': ('wide',), - 'fields': ('username', 'email', 'first_name', 'last_name', - 'date_of_birth', 'password1', 'password2')} - ) + (None, {'classes': ('wide',), + 'fields': ('username', 'email', 'first_name', 'last_name', + 'date_of_birth', 'password1', 'password2')}), ) search_fields = ('email',) ordering = ('email',) diff --git a/examples/simple/foo/fobi_form_callbacks.py b/examples/simple/foo/fobi_form_callbacks.py index 1e09b81e..3f3eb10a 100644 --- a/examples/simple/foo/fobi_form_callbacks.py +++ b/examples/simple/foo/fobi_form_callbacks.py @@ -13,7 +13,7 @@ from fobi.constants import ( logger = logging.getLogger('fobi') -__all__= ( +__all__ = ( 'SaveAsFooItem', 'DummyInvalidCallback', ) diff --git a/examples/simple/override_radio_plugin/fobi_form_elements.py b/examples/simple/override_radio_plugin/fobi_form_elements.py index 347221fb..9d21a1e1 100644 --- a/examples/simple/override_radio_plugin/fobi_form_elements.py +++ b/examples/simple/override_radio_plugin/fobi_form_elements.py @@ -38,7 +38,8 @@ class RadioInputPlugin(FormFieldPlugin): return [(self.data.name, ChoiceField, field_kwargs)] - def submit_plugin_form_data(self, form_entry, request, form): + def submit_plugin_form_data(self, form_entry, request, form, + form_element_entries=None, **kwargs): """Submit plugin form data/process. :param fobi.models.FormEntry form_entry: Instance of 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 2353f2d3..0123ec17 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 @@ -40,7 +40,8 @@ class SelectModelObjectInputPlugin(FormFieldPlugin): return [(self.data.name, ModelChoiceField, field_kwargs)] - def submit_plugin_form_data(self, form_entry, request, form): + def submit_plugin_form_data(self, form_entry, request, form, + form_element_entries=None, **kwargs): """Submit plugin form data/process. :param fobi.models.FormEntry form_entry: Instance of diff --git a/examples/simple/override_simple_theme/fobi_themes.py b/examples/simple/override_simple_theme/fobi_themes.py index 752f425a..67c9b143 100644 --- a/examples/simple/override_simple_theme/fobi_themes.py +++ b/examples/simple/override_simple_theme/fobi_themes.py @@ -8,7 +8,7 @@ __all__ = ('MySimpleTheme',) class MySimpleTheme(SimpleTheme): """Overriding the "simple" theme.""" - html_classes = ['my-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 = \ diff --git a/examples/simple/page/migrations/0001_initial.py b/examples/simple/page/migrations/0001_initial.py index 7638ed71..04a0ac8d 100644 --- a/examples/simple/page/migrations/0001_initial.py +++ b/examples/simple/page/migrations/0001_initial.py @@ -1,133 +1,116 @@ # -*- coding: utf-8 -*- -import datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models +# Generated by Django 1.9.10 on 2016-10-17 20:45 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import feincms.contrib.richtext +import feincms.extensions.base +import feincms.module.mixins +import fobi.integration.processors -class Migration(SchemaMigration): +class Migration(migrations.Migration): - def forwards(self, orm): - # Adding model 'Page' - db.create_table(u'page_page', ( - (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - (u'lft', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)), - (u'rght', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)), - (u'tree_id', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)), - (u'level', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)), - ('active', self.gf('django.db.models.fields.BooleanField')(default=True)), - ('title', self.gf('django.db.models.fields.CharField')(max_length=200)), - ('slug', self.gf('django.db.models.fields.SlugField')(max_length=150)), - ('parent', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name=u'children', null=True, to=orm['page.Page'])), - ('in_navigation', self.gf('django.db.models.fields.BooleanField')(default=False)), - ('override_url', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)), - ('redirect_to', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)), - ('_cached_url', self.gf('django.db.models.fields.CharField')(default=u'', max_length=255, db_index=True, blank=True)), - (u'template_key', self.gf('django.db.models.fields.CharField')(default='page_base', max_length=255)), - )) - db.send_create_signal(u'page', ['Page']) + initial = True - # Adding model 'FobiFormWidget' - db.create_table(u'page_page_fobiformwidget', ( - (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('form_entry', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['fobi.FormEntry'])), - (u'parent', self.gf('django.db.models.fields.related.ForeignKey')(related_name=u'fobiformwidget_set', to=orm['page.Page'])), - (u'region', self.gf('django.db.models.fields.CharField')(max_length=255)), - (u'ordering', self.gf('django.db.models.fields.IntegerField')(default=0)), - )) - db.send_create_signal(u'page', ['FobiFormWidget']) + dependencies = [ + ('fobi', '0010_formwizardhandler'), + ] - - def backwards(self, orm): - # Deleting model 'Page' - db.delete_table(u'page_page') - - # Deleting model 'FobiFormWidget' - db.delete_table(u'page_page_fobiformwidget') - - - models = { - u'auth.group': { - 'Meta': {'object_name': 'Group'}, - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) - }, - u'auth.permission': { - 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, - 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - u'auth.user': { - 'Meta': {'object_name': 'User'}, - 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), - 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), - 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) - }, - u'contenttypes.contenttype': { - 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - u'fobi.formentry': { - 'Meta': {'unique_together': "(('user', 'slug'), ('user', 'name'))", 'object_name': 'FormEntry'}, - 'form_wizard_entry': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['fobi.FormWizardEntry']", 'null': 'True', 'blank': 'True'}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_cloneable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'position': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), - 'slug': ('autoslug.fields.AutoSlugField', [], {'unique': 'True', 'max_length': '50', 'populate_from': "'name'", 'unique_with': '()'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) - }, - u'fobi.formwizardentry': { - 'Meta': {'unique_together': "(('user', 'slug'), ('user', 'name'))", 'object_name': 'FormWizardEntry'}, - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_cloneable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'slug': ('autoslug.fields.AutoSlugField', [], {'unique': 'True', 'max_length': '50', 'populate_from': "'name'", 'unique_with': '()'}), - 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) - }, - u'page.fobiformwidget': { - 'Meta': {'ordering': "[u'ordering']", 'object_name': 'FobiFormWidget', 'db_table': "u'page_page_fobiformwidget'"}, - 'form_entry': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['fobi.FormEntry']"}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - u'ordering': ('django.db.models.fields.IntegerField', [], {'default': '0'}), - u'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'fobiformwidget_set'", 'to': u"orm['page.Page']"}), - u'region': ('django.db.models.fields.CharField', [], {'max_length': '255'}) - }, - u'page.page': { - 'Meta': {'ordering': "[u'tree_id', u'lft']", 'object_name': 'Page'}, - '_cached_url': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'db_index': 'True', 'blank': 'True'}), - 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'in_navigation': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - u'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), - u'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), - 'override_url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), - 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'children'", 'null': 'True', 'to': u"orm['page.Page']"}), - 'redirect_to': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), - u'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), - 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '150'}), - u'template_key': ('django.db.models.fields.CharField', [], {'default': "'page_base'", 'max_length': '255'}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}), - u'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}) - } - } - - complete_apps = ['page'] \ No newline at end of file + operations = [ + migrations.CreateModel( + name='FobiFormWidget', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('form_template_name', models.CharField(blank=True, choices=[(b'fobi/bootstrap3_extras/view_embed_form_entry_ajax.html', b'Custom bootstrap3 embed form view template')], help_text='Template to render the form with.', max_length=255, null=True, verbose_name='Form template name')), + ('hide_form_title', models.BooleanField(default=False, help_text='If checked, no form title is shown.', verbose_name='Hide form title')), + ('form_title', models.CharField(blank=True, help_text='Overrides the default form title.', max_length=255, null=True, verbose_name='Form title')), + ('form_submit_button_text', models.CharField(blank=True, help_text='Overrides the default form submit button text.', max_length=255, null=True, verbose_name='Submit button text')), + ('success_page_template_name', models.CharField(blank=True, choices=[(b'fobi/bootstrap3_extras/embed_form_entry_submitted_ajax.html', b'Custom bootstrap3 embed form entry submitted template')], help_text='Template to render the success page with.', max_length=255, null=True, verbose_name='Success page template name')), + ('hide_success_page_title', models.BooleanField(default=False, help_text='If checked, no success page title is shown.', verbose_name='Hide success page title')), + ('success_page_title', models.CharField(blank=True, help_text='Overrides the default success page title.', max_length=255, null=True, verbose_name='Succes page title')), + ('success_page_text', models.TextField(blank=True, help_text='Overrides the default success page text.', null=True, verbose_name='Succes page text')), + ('region', models.CharField(max_length=255)), + ('ordering', models.IntegerField(default=0, verbose_name='ordering')), + ('form_entry', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fobi.FormEntry', verbose_name='Form')), + ], + options={ + 'ordering': ['ordering'], + 'abstract': False, + 'verbose_name_plural': 'fobi form widgets', + 'db_table': 'page_page_fobiformwidget', + 'verbose_name': 'fobi form widget', + 'permissions': [], + }, + bases=(models.Model, fobi.integration.processors.IntegrationProcessor), + ), + migrations.CreateModel( + name='Page', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('active', models.BooleanField(default=True, verbose_name='active')), + ('title', models.CharField(help_text='This title is also used for navigation menu items.', max_length=200, verbose_name='title')), + ('slug', models.SlugField(help_text='This is used to build the URL for this page', max_length=150, verbose_name='slug')), + ('in_navigation', models.BooleanField(default=False, verbose_name='in navigation')), + ('override_url', models.CharField(blank=True, help_text="Override the target URL. Be sure to include slashes at the beginning and at the end if it is a local URL. This affects both the navigation and subpages' URLs.", max_length=255, verbose_name='override URL')), + ('redirect_to', models.CharField(blank=True, help_text='Target URL for automatic redirects or the primary key of a page.', max_length=255, verbose_name='redirect to')), + ('_cached_url', models.CharField(blank=True, db_index=True, default='', editable=False, max_length=255, verbose_name='Cached URL')), + ('lft', models.PositiveIntegerField(db_index=True, editable=False)), + ('rght', models.PositiveIntegerField(db_index=True, editable=False)), + ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)), + ('level', models.PositiveIntegerField(db_index=True, editable=False)), + ('language', models.CharField(choices=[(b'en', b'English'), (b'hy', b'Armenian'), (b'nl', b'Dutch'), (b'ru', b'Russian'), (b'de', b'German')], default=b'en', max_length=10, verbose_name='language')), + ('template_key', models.CharField(choices=[(b'page_base', 'Base template')], default=b'page_base', max_length=255, verbose_name='template')), + ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='page.Page', verbose_name='Parent')), + ('translation_of', models.ForeignKey(blank=True, help_text='Leave this empty for entries in the primary language.', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='translations', to='page.Page', verbose_name='translation of')), + ], + options={ + 'ordering': ['tree_id', 'lft'], + 'verbose_name': 'page', + 'verbose_name_plural': 'pages', + }, + bases=(models.Model, feincms.extensions.base.ExtensionsMixin, feincms.module.mixins.ContentModelMixin), + ), + migrations.CreateModel( + name='RawContent', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('text', models.TextField(blank=True, verbose_name='content')), + ('region', models.CharField(max_length=255)), + ('ordering', models.IntegerField(default=0, verbose_name='ordering')), + ('parent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rawcontent_set', to='page.Page')), + ], + options={ + 'ordering': ['ordering'], + 'abstract': False, + 'verbose_name_plural': 'raw contents', + 'db_table': 'page_page_rawcontent', + 'verbose_name': 'raw content', + 'permissions': [], + }, + ), + migrations.CreateModel( + name='RichTextContent', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('region', models.CharField(max_length=255)), + ('ordering', models.IntegerField(default=0, verbose_name='ordering')), + ('text', feincms.contrib.richtext.RichTextField(blank=True, verbose_name='text')), + ('parent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='richtextcontent_set', to='page.Page')), + ], + options={ + 'ordering': ['ordering'], + 'abstract': False, + 'verbose_name_plural': 'rich texts', + 'db_table': 'page_page_richtextcontent', + 'verbose_name': 'rich text', + 'permissions': [], + }, + ), + migrations.AddField( + model_name='fobiformwidget', + name='parent', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='fobiformwidget_set', to='page.Page'), + ), + ] diff --git a/examples/simple/page/south_migrations/0001_initial.py b/examples/simple/page/south_migrations/0001_initial.py new file mode 100644 index 00000000..7638ed71 --- /dev/null +++ b/examples/simple/page/south_migrations/0001_initial.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'Page' + db.create_table(u'page_page', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + (u'lft', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)), + (u'rght', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)), + (u'tree_id', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)), + (u'level', self.gf('django.db.models.fields.PositiveIntegerField')(db_index=True)), + ('active', self.gf('django.db.models.fields.BooleanField')(default=True)), + ('title', self.gf('django.db.models.fields.CharField')(max_length=200)), + ('slug', self.gf('django.db.models.fields.SlugField')(max_length=150)), + ('parent', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name=u'children', null=True, to=orm['page.Page'])), + ('in_navigation', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('override_url', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)), + ('redirect_to', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)), + ('_cached_url', self.gf('django.db.models.fields.CharField')(default=u'', max_length=255, db_index=True, blank=True)), + (u'template_key', self.gf('django.db.models.fields.CharField')(default='page_base', max_length=255)), + )) + db.send_create_signal(u'page', ['Page']) + + # Adding model 'FobiFormWidget' + db.create_table(u'page_page_fobiformwidget', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('form_entry', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['fobi.FormEntry'])), + (u'parent', self.gf('django.db.models.fields.related.ForeignKey')(related_name=u'fobiformwidget_set', to=orm['page.Page'])), + (u'region', self.gf('django.db.models.fields.CharField')(max_length=255)), + (u'ordering', self.gf('django.db.models.fields.IntegerField')(default=0)), + )) + db.send_create_signal(u'page', ['FobiFormWidget']) + + + def backwards(self, orm): + # Deleting model 'Page' + db.delete_table(u'page_page') + + # Deleting model 'FobiFormWidget' + db.delete_table(u'page_page_fobiformwidget') + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'fobi.formentry': { + 'Meta': {'unique_together': "(('user', 'slug'), ('user', 'name'))", 'object_name': 'FormEntry'}, + 'form_wizard_entry': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['fobi.FormWizardEntry']", 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_cloneable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'position': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'slug': ('autoslug.fields.AutoSlugField', [], {'unique': 'True', 'max_length': '50', 'populate_from': "'name'", 'unique_with': '()'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) + }, + u'fobi.formwizardentry': { + 'Meta': {'unique_together': "(('user', 'slug'), ('user', 'name'))", 'object_name': 'FormWizardEntry'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_cloneable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('autoslug.fields.AutoSlugField', [], {'unique': 'True', 'max_length': '50', 'populate_from': "'name'", 'unique_with': '()'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) + }, + u'page.fobiformwidget': { + 'Meta': {'ordering': "[u'ordering']", 'object_name': 'FobiFormWidget', 'db_table': "u'page_page_fobiformwidget'"}, + 'form_entry': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['fobi.FormEntry']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + u'ordering': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + u'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'fobiformwidget_set'", 'to': u"orm['page.Page']"}), + u'region': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + u'page.page': { + 'Meta': {'ordering': "[u'tree_id', u'lft']", 'object_name': 'Page'}, + '_cached_url': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'db_index': 'True', 'blank': 'True'}), + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'in_navigation': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + u'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + u'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'override_url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'children'", 'null': 'True', 'to': u"orm['page.Page']"}), + 'redirect_to': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + u'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '150'}), + u'template_key': ('django.db.models.fields.CharField', [], {'default': "'page_base'", 'max_length': '255'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}), + u'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}) + } + } + + complete_apps = ['page'] \ No newline at end of file diff --git a/examples/simple/page/migrations/0002_auto__add_field_page_language__add_field_page_translation_of.py b/examples/simple/page/south_migrations/0002_auto__add_field_page_language__add_field_page_translation_of.py similarity index 100% rename from examples/simple/page/migrations/0002_auto__add_field_page_language__add_field_page_translation_of.py rename to examples/simple/page/south_migrations/0002_auto__add_field_page_language__add_field_page_translation_of.py diff --git a/examples/simple/page/migrations/0003_auto__add_richtextcontent__add_rawcontent.py b/examples/simple/page/south_migrations/0003_auto__add_richtextcontent__add_rawcontent.py similarity index 100% rename from examples/simple/page/migrations/0003_auto__add_richtextcontent__add_rawcontent.py rename to examples/simple/page/south_migrations/0003_auto__add_richtextcontent__add_rawcontent.py diff --git a/examples/simple/page/migrations/0004_auto__add_field_fobiformwidget_form_template_name__add_field_fobiformw.py b/examples/simple/page/south_migrations/0004_auto__add_field_fobiformwidget_form_template_name__add_field_fobiformw.py similarity index 100% rename from examples/simple/page/migrations/0004_auto__add_field_fobiformwidget_form_template_name__add_field_fobiformw.py rename to examples/simple/page/south_migrations/0004_auto__add_field_fobiformwidget_form_template_name__add_field_fobiformw.py diff --git a/examples/simple/page/south_migrations/__init__.py b/examples/simple/page/south_migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/simple/runserver/bootstrap3-theme-django-1-9-captcha.sh b/examples/simple/runserver/bootstrap3-theme-django-1-9-captcha.sh new file mode 100755 index 00000000..83f80bcb --- /dev/null +++ b/examples/simple/runserver/bootstrap3-theme-django-1-9-captcha.sh @@ -0,0 +1 @@ +./manage.py runserver 0.0.0.0:8000 --traceback -v 3 --settings=settings.bootstrap3_theme_django_1_9_captcha --traceback -v 3 diff --git a/examples/simple/runserver/bootstrap3-theme-django-1-9-feincms_integration.sh b/examples/simple/runserver/bootstrap3-theme-django-1-9-feincms_integration.sh new file mode 100755 index 00000000..da4363f1 --- /dev/null +++ b/examples/simple/runserver/bootstrap3-theme-django-1-9-feincms_integration.sh @@ -0,0 +1 @@ +./manage.py runserver 0.0.0.0:8000 --traceback -v 3 --settings=settings.bootstrap3_theme_django_1_7_feincms --traceback -v 3 diff --git a/examples/simple/runserver/foundation5-theme-django-1-10.sh b/examples/simple/runserver/foundation5-theme-django-1-10.sh new file mode 100755 index 00000000..b59057b3 --- /dev/null +++ b/examples/simple/runserver/foundation5-theme-django-1-10.sh @@ -0,0 +1 @@ +./manage.py runserver 0.0.0.0:8001 --traceback -v 3 --settings=settings.foundation5_theme_django_1_10 --traceback -v 3 diff --git a/examples/simple/runserver/foundation5-theme-django-1-8.sh b/examples/simple/runserver/foundation5-theme-django-1-8.sh new file mode 100755 index 00000000..5f1c2fb5 --- /dev/null +++ b/examples/simple/runserver/foundation5-theme-django-1-8.sh @@ -0,0 +1 @@ +./manage.py runserver 0.0.0.0:8001 --traceback -v 3 --settings=settings.foundation5_theme_django_1_8 --traceback -v 3 diff --git a/examples/simple/runserver/foundation5-theme-django-1-9.sh b/examples/simple/runserver/foundation5-theme-django-1-9.sh new file mode 100755 index 00000000..e51eb1d4 --- /dev/null +++ b/examples/simple/runserver/foundation5-theme-django-1-9.sh @@ -0,0 +1 @@ +./manage.py runserver 0.0.0.0:8001 --traceback -v 3 --settings=settings.foundation5_theme_django_1_9 --traceback -v 3 diff --git a/examples/simple/settings/base.py b/examples/simple/settings/base.py index 96aadfdc..5b46b8a0 100644 --- a/examples/simple/settings/base.py +++ b/examples/simple/settings/base.py @@ -4,8 +4,18 @@ from nine.versions import ( DJANGO_GTE_1_7, DJANGO_GTE_1_8, DJANGO_LTE_1_7, DJANGO_GTE_1_9, DJANGO_GTE_1_10 ) -PROJECT_DIR = lambda base : os.path.abspath(os.path.join(os.path.dirname(__file__), base).replace('\\','/')) -gettext = lambda s: s + + +def project_dir(base): + return os.path.abspath( + os.path.join(os.path.dirname(__file__), base).replace('\\', '/') + ) + +PROJECT_DIR = project_dir + + +def gettext(s): + return s # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(__file__)) @@ -22,13 +32,18 @@ MANAGERS = ADMINS DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. - 'NAME': PROJECT_DIR('../../db/example.db'), # Or path to database file if using sqlite3. + # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. + 'ENGINE': 'django.db.backends.sqlite3', + # Or path to database file if using sqlite3. + 'NAME': PROJECT_DIR('../../db/example.db'), # The following settings are not used with sqlite3: 'USER': '', 'PASSWORD': '', - 'HOST': '', # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP. - 'PORT': '', # Set to empty string for default. + # Empty for localhost through domain sockets or '127.0.0.1' for + # localhost through TCP. + 'HOST': '', + # Set to empty string for default. + 'PORT': '', } } @@ -47,7 +62,7 @@ TIME_ZONE = 'America/Chicago' LANGUAGE_CODE = 'en' LANGUAGES = ( - ('en', gettext("English")), # Main language! + ('en', gettext("English")), # Main language! ('hy', gettext("Armenian")), ('nl', gettext("Dutch")), ('ru', gettext("Russian")), @@ -67,7 +82,8 @@ USE_L10N = True # If you set this to False, Django will not use timezone-aware datetimes. USE_TZ = True -# Absolute filesystem path to the directory that will hold user-uploaded files. +# Absolute filesystem path to the directory that will hold user-uploaded +# files. # Example: "/var/www/example.com/media/" MEDIA_ROOT = PROJECT_DIR(os.path.join('..', '..', 'media')) @@ -105,12 +121,17 @@ STATICFILES_FINDERS = ( # Make this unique, and don't share it with anybody. SECRET_KEY = '97818c*w97Zi8a-m^1coRRrmurMI6+q5_kyn*)s@(*_Pk6q423' +try: + from .local_settings import DEBUG_TEMPLATE +except Exception as err: + DEBUG_TEMPLATE = False + if DJANGO_GTE_1_10: TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', # 'APP_DIRS': True, - 'DIRS': [PROJECT_DIR(os.path.join('..', 'templates')),], + 'DIRS': [PROJECT_DIR(os.path.join('..', 'templates'))], 'OPTIONS': { 'context_processors': [ "django.template.context_processors.debug", @@ -123,6 +144,7 @@ if DJANGO_GTE_1_10: "django.contrib.messages.context_processors.messages", "fobi.context_processors.theme", # Important! "fobi.context_processors.dynamic_values", # Optional + "context_processors.testing", # Testing ], 'loaders': [ 'django.template.loaders.filesystem.Loader', @@ -130,7 +152,7 @@ if DJANGO_GTE_1_10: 'django.template.loaders.eggs.Loader', 'admin_tools.template_loaders.Loader', ], - 'debug': DEBUG, + 'debug': DEBUG_TEMPLATE, } }, ] @@ -139,7 +161,7 @@ elif DJANGO_GTE_1_8: { 'BACKEND': 'django.template.backends.django.DjangoTemplates', # 'APP_DIRS': True, - 'DIRS': [PROJECT_DIR(os.path.join('..', 'templates')),], + 'DIRS': [PROJECT_DIR(os.path.join('..', 'templates'))], 'OPTIONS': { 'context_processors': [ "django.contrib.auth.context_processors.auth", @@ -150,8 +172,9 @@ elif DJANGO_GTE_1_8: "django.template.context_processors.tz", "django.contrib.messages.context_processors.messages", "django.template.context_processors.request", - "fobi.context_processors.theme", # Important! - "fobi.context_processors.dynamic_values", # Optional + "fobi.context_processors.theme", # Important! + "fobi.context_processors.dynamic_values", # Optional + "context_processors.testing", # Testing ], 'loaders': [ 'django.template.loaders.filesystem.Loader', @@ -159,14 +182,15 @@ elif DJANGO_GTE_1_8: 'django.template.loaders.eggs.Loader', 'admin_tools.template_loaders.Loader', ], - 'debug': DEBUG, + 'debug': DEBUG_TEMPLATE, } }, ] else: - TEMPLATE_DEBUG = DEBUG + TEMPLATE_DEBUG = DEBUG_TEMPLATE - # List of callables that know how to import templates from various sources. + # 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', @@ -185,12 +209,14 @@ else: "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 + "fobi.context_processors.theme", # Important! + "fobi.context_processors.dynamic_values", # Optional + "context_processors.testing", # Testing ) TEMPLATE_DIRS = ( - # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". + # 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(os.path.join('..', 'templates')), @@ -198,7 +224,6 @@ else: MIDDLEWARE_CLASSES = [ 'django.contrib.sessions.middleware.SessionMiddleware', - # 'localeurl.middleware.LocaleURLMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', @@ -207,9 +232,6 @@ MIDDLEWARE_CLASSES = [ # 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] -# if DJANGO_GTE_1_8: -# MIDDLEWARE_CLASSES.remove('localeurl.middleware.LocaleURLMiddleware') - ROOT_URLCONF = 'urls' # Python dotted path to the WSGI application used by Django's runserver. @@ -240,7 +262,6 @@ INSTALLED_APPS = [ # 'tinymce', # TinyMCE 'easy_thumbnails', # Thumbnailer 'registration', # Auth views and registration app - # 'localeurl', # Locale URL # *********************************************************************** # *********************************************************************** @@ -334,17 +355,23 @@ INSTALLED_APPS = [ # *********************************************************************** 'fobi.contrib.themes.bootstrap3', # Bootstrap 3 theme # DateTime widget - 'fobi.contrib.themes.bootstrap3.widgets.form_elements.datetime_bootstrap3_widget', - 'fobi.contrib.themes.bootstrap3.widgets.form_elements.date_bootstrap3_widget', + 'fobi.contrib.themes.bootstrap3.widgets.form_elements.' + 'datetime_bootstrap3_widget', + + 'fobi.contrib.themes.bootstrap3.widgets.form_elements.' + 'date_bootstrap3_widget', # SliderPercentage widget - 'fobi.contrib.themes.bootstrap3.widgets.form_elements.slider_bootstrap3_widget', + 'fobi.contrib.themes.bootstrap3.widgets.form_elements.' + 'slider_bootstrap3_widget', # *********************************************************************** # ************************ Foundation 5 theme *************************** # *********************************************************************** 'fobi.contrib.themes.foundation5', # Foundation 5 theme - 'fobi.contrib.themes.foundation5.widgets.form_handlers.db_store_foundation5_widget', + + 'fobi.contrib.themes.foundation5.widgets.form_handlers.' + 'db_store_foundation5_widget', # *********************************************************************** # **************************** Simple theme ***************************** @@ -369,9 +396,6 @@ INSTALLED_APPS = [ if DJANGO_LTE_1_7: INSTALLED_APPS.append('south') -# if DJANGO_GTE_1_8: -# INSTALLED_APPS.remove('localeurl') - # LOGIN_URL = '/accounts/login/' # LOGIN_REDIRECT_URL = '/fobi/' # Important for passing the selenium tests @@ -379,23 +403,12 @@ if DJANGO_LTE_1_7: LOGIN_URL = '/en/accounts/login/' LOGIN_REDIRECT_URL = '/en/fobi/' # Important for passing the selenium tests -#LOGIN_URL = '/accounts/login/' -#LOGIN_ERROR_URL = '/accounts/login/' -#LOGOUT_URL = '/accounts/logout/' +# LOGIN_URL = '/accounts/login/' +# LOGIN_ERROR_URL = '/accounts/login/' +# LOGOUT_URL = '/accounts/logout/' -# if not DJANGO_GTE_1_8: -# # Tell localeurl to use sessions for language store. -# LOCALEURL_USE_SESSION = True -# -# # localeurl locale independent paths (language code won't be appended) -# LOCALE_INDEPENDENT_PATHS = ( -# r'^/sitemap.*\.xml$', # Global regex for all XML sitemaps -# r'^/admin/', -# #r'^/dashboard/', -# ) - -PACKAGE_NAME_FILEBROWSER = "filebrowser_safe" # Just for tests -PACKAGE_NAME_GRAPPELLI = "grappelli_safe" # Just for tests +PACKAGE_NAME_FILEBROWSER = "filebrowser_safe" # Just for tests +PACKAGE_NAME_GRAPPELLI = "grappelli_safe" # Just for tests MIGRATION_MODULES = { 'fobi': 'migrations', @@ -426,8 +439,10 @@ FOBI_CUSTOM_THEME_DATA = { ], 'success_page_template_choices': [ ( - 'fobi/bootstrap3_extras/embed_form_entry_submitted_ajax.html', - gettext("Custom bootstrap3 embed form entry submitted template") + 'fobi/bootstrap3_extras/embed_form_entry_' + 'submitted_ajax.html', + gettext("Custom bootstrap3 embed form entry submitted " + "template") ), ], }, @@ -440,8 +455,10 @@ FOBI_CUSTOM_THEME_DATA = { ], 'success_page_template_choices': [ ( - 'fobi/bootstrap3_extras/embed_form_entry_submitted_ajax.html', - gettext("Custom bootstrap3 embed form entry submitted template") + 'fobi/bootstrap3_extras/embed_form_entry_submitted_' + 'ajax.html', + gettext("Custom bootstrap3 embed form entry submitted " + "template") ), ], }, @@ -462,8 +479,10 @@ FOBI_CUSTOM_THEME_DATA = { ], 'success_page_template_choices': [ ( - 'fobi/foundation5_extras/embed_form_entry_submitted_ajax.html', - gettext("Custom foundation5 embed form entry submitted template") + 'fobi/foundation5_extras/embed_form_entry_submitted_' + 'ajax.html', + gettext("Custom foundation5 embed form entry submitted " + "template") ), ], }, @@ -476,8 +495,10 @@ FOBI_CUSTOM_THEME_DATA = { ], 'success_page_template_choices': [ ( - 'fobi/foundation5_extras/embed_form_entry_submitted_ajax.html', - gettext("Custom foundation5 embed form entry submitted template") + 'fobi/foundation5_extras/embed_form_entry_submitted_' + 'ajax.html', + gettext("Custom foundation5 embed form entry submitted " + "template") ), ], }, @@ -500,7 +521,8 @@ FOBI_THEME_FOOTER_TEXT = gettext('© django-fobi example site 2014-2015') # django-admin-tools custom dashboard ADMIN_TOOLS_INDEX_DASHBOARD = 'admin_tools_dashboard.CustomIndexDashboard' -ADMIN_TOOLS_APP_INDEX_DASHBOARD = 'admin_tools_dashboard.CustomAppIndexDashboard' +ADMIN_TOOLS_APP_INDEX_DASHBOARD = \ + 'admin_tools_dashboard.CustomAppIndexDashboard' ADMIN_TOOLS_MENU = 'admin_tools_dashboard.menu.CustomMenu' SOUTH_MIGRATION_MODULES = { @@ -532,7 +554,8 @@ LOGGING = { }, 'formatters': { 'verbose': { - 'format': '\n%(levelname)s %(asctime)s [%(pathname)s:%(lineno)s] %(message)s' + 'format': '\n%(levelname)s %(asctime)s [%(pathname)s:%(lineno)s] ' + '%(message)s' }, 'simple': { 'format': '\n%(levelname)s %(message)s' @@ -550,32 +573,32 @@ LOGGING = { 'formatter': 'verbose' }, 'all_log': { - 'level':'DEBUG', - 'class':'logging.handlers.RotatingFileHandler', + 'level': 'DEBUG', + 'class': 'logging.handlers.RotatingFileHandler', 'filename': PROJECT_DIR("../../logs/all.log"), 'maxBytes': 1048576, 'backupCount': 99, 'formatter': 'verbose', }, 'django_log': { - 'level':'DEBUG', - 'class':'logging.handlers.RotatingFileHandler', + 'level': 'DEBUG', + 'class': 'logging.handlers.RotatingFileHandler', 'filename': PROJECT_DIR("../../logs/django.log"), 'maxBytes': 1048576, 'backupCount': 99, 'formatter': 'verbose', }, 'django_request_log': { - 'level':'DEBUG', - 'class':'logging.handlers.RotatingFileHandler', + 'level': 'DEBUG', + 'class': 'logging.handlers.RotatingFileHandler', 'filename': PROJECT_DIR("../../logs/django_request.log"), 'maxBytes': 1048576, 'backupCount': 99, 'formatter': 'verbose', }, 'fobi_log': { - 'level':'DEBUG', - 'class':'logging.handlers.RotatingFileHandler', + 'level': 'DEBUG', + 'class': 'logging.handlers.RotatingFileHandler', 'filename': PROJECT_DIR("../../logs/fobi.log"), 'maxBytes': 1048576, 'backupCount': 99, @@ -630,6 +653,9 @@ if DJANGO_GTE_1_7 or DJANGO_GTE_1_8: # For Selenium tests FIREFOX_BIN_PATH = '' +# Testing mode +TESTING = False + # Do not put any settings below this line try: from .local_settings import * @@ -660,7 +686,7 @@ if DEBUG: try: # Make sure the django-template-debug is installed. You can then # in templates use it as follows: - # + # # {% load debug_tags %} # {% set_trace %} import template_debug diff --git a/examples/simple/settings/bootstrap3_theme_captcha.py b/examples/simple/settings/bootstrap3_theme_captcha.py index 2794b9c4..2d8ed0dd 100644 --- a/examples/simple/settings/bootstrap3_theme_captcha.py +++ b/examples/simple/settings/bootstrap3_theme_captcha.py @@ -4,8 +4,10 @@ INSTALLED_APPS = list(INSTALLED_APPS) try: INSTALLED_APPS.append('captcha') - INSTALLED_APPS.append('fobi.contrib.plugins.form_elements.security.captcha') + INSTALLED_APPS.append( + 'fobi.contrib.plugins.form_elements.security.captcha' + ) except Exception as e: pass -#FOBI_DEFAULT_THEME = 'simple' +# FOBI_DEFAULT_THEME = 'simple' diff --git a/examples/simple/settings/bootstrap3_theme_django_1_10.py b/examples/simple/settings/bootstrap3_theme_django_1_10.py index e1b4f27c..4a5d0b53 100644 --- a/examples/simple/settings/bootstrap3_theme_django_1_10.py +++ b/examples/simple/settings/bootstrap3_theme_django_1_10.py @@ -5,8 +5,7 @@ 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 - INSTALLED_APPS.remove('localeurl') if 'localeurl' in INSTALLED_APPS else None -except Exception as e: +except Exception as err: pass try: @@ -16,5 +15,5 @@ try: 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: +except Exception as err: pass diff --git a/examples/simple/settings/bootstrap3_theme_django_1_7_captcha.py b/examples/simple/settings/bootstrap3_theme_django_1_7_captcha.py index 73b1f4c3..0a479970 100755 --- a/examples/simple/settings/bootstrap3_theme_django_1_7_captcha.py +++ b/examples/simple/settings/bootstrap3_theme_django_1_7_captcha.py @@ -6,6 +6,8 @@ try: INSTALLED_APPS.remove('south') if 'south' in INSTALLED_APPS else None INSTALLED_APPS.remove('tinymce') if 'tinymce' in INSTALLED_APPS else None INSTALLED_APPS.append('captcha') - INSTALLED_APPS.append('fobi.contrib.plugins.form_elements.security.captcha') + INSTALLED_APPS.append( + 'fobi.contrib.plugins.form_elements.security.captcha' + ) except Exception as e: pass diff --git a/examples/simple/settings/bootstrap3_theme_django_1_7_djangocms.py b/examples/simple/settings/bootstrap3_theme_django_1_7_djangocms.py index 458af220..b955c070 100644 --- a/examples/simple/settings/bootstrap3_theme_django_1_7_djangocms.py +++ b/examples/simple/settings/bootstrap3_theme_django_1_7_djangocms.py @@ -2,40 +2,40 @@ from .base import * INSTALLED_APPS = list(INSTALLED_APPS) INSTALLED_APPS += [ - 'cms', # DjangoCMS + 'cms', # DjangoCMS 'mptt', 'menus', 'sekizai', - #'djangocms_admin_style', + # 'djangocms_admin_style', # Some plugins 'djangocms_picture', 'djangocms_snippet', - 'fobi.contrib.apps.djangocms_integration', # Fobi DjangoCMS app + 'fobi.contrib.apps.djangocms_integration', # Fobi DjangoCMS app - #'djangocms_page', # Example + # 'djangocms_page', # Example ] try: INSTALLED_APPS.remove('south') if 'south' in INSTALLED_APPS else None - #INSTALLED_APPS.remove('admin_tools') \ + # INSTALLED_APPS.remove('admin_tools') \ # if 'admin_tools' in INSTALLED_APPS else None - #INSTALLED_APPS.remove('admin_tools.menu') \ + # 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: +except Exception as err: pass MIDDLEWARE_CLASSES = list(MIDDLEWARE_CLASSES) MIDDLEWARE_CLASSES += [ - #'django.middleware.cache.UpdateCacheMiddleware', + # 'django.middleware.cache.UpdateCacheMiddleware', 'cms.middleware.page.CurrentPageMiddleware', 'cms.middleware.user.CurrentUserMiddleware', 'cms.middleware.toolbar.ToolbarMiddleware', 'cms.middleware.language.LanguageCookieMiddleware', - #'django.middleware.cache.FetchFromCacheMiddleware', + # 'django.middleware.cache.FetchFromCacheMiddleware', ] TEMPLATE_CONTEXT_PROCESSORS = list(TEMPLATE_CONTEXT_PROCESSORS) @@ -46,8 +46,8 @@ TEMPLATE_CONTEXT_PROCESSORS += [ ] FOBI_DEFAULT_THEME = 'bootstrap3' -#FOBI_DEFAULT_THEME = 'foundation5' -#FOBI_DEFAULT_THEME = 'simple' +# FOBI_DEFAULT_THEME = 'foundation5' +# FOBI_DEFAULT_THEME = 'simple' CMS_TEMPLATES = ( ('cms_page/{0}/page_with_sidebar.html'.format(FOBI_DEFAULT_THEME), @@ -63,6 +63,6 @@ MIGRATION_MODULES = { LANGUAGE_CODE = 'en' -#FEINCMS_RICHTEXT_INIT_CONTEXT = { +# FEINCMS_RICHTEXT_INIT_CONTEXT = { # 'TINYMCE_JS_URL': STATIC_URL + 'tiny_mce/tiny_mce.js', -#} +# } diff --git a/examples/simple/settings/bootstrap3_theme_django_1_7_feincms.py b/examples/simple/settings/bootstrap3_theme_django_1_7_feincms.py index 212b37d2..13371298 100644 --- a/examples/simple/settings/bootstrap3_theme_django_1_7_feincms.py +++ b/examples/simple/settings/bootstrap3_theme_django_1_7_feincms.py @@ -4,17 +4,19 @@ 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 - INSTALLED_APPS.remove('admin_tools.dashboard') if 'admin_tools.dashboard' in INSTALLED_APPS else None + # INSTALLED_APPS.remove('tinymce') if 'tinymce' in INSTALLED_APPS else None + INSTALLED_APPS.remove('admin_tools.dashboard') \ + if 'admin_tools.dashboard' in INSTALLED_APPS \ + else None INSTALLED_APPS += [ - 'feincms', # FeinCMS + 'feincms', # FeinCMS - 'fobi.contrib.apps.feincms_integration', # Fobi FeinCMS app + 'fobi.contrib.apps.feincms_integration', # Fobi FeinCMS app - 'page', # Example + 'page', # Example ] -except Exception as e: +except Exception as err: pass diff --git a/examples/simple/settings/bootstrap3_theme_django_1_8.py b/examples/simple/settings/bootstrap3_theme_django_1_8.py index 780c3631..4a5d0b53 100644 --- a/examples/simple/settings/bootstrap3_theme_django_1_8.py +++ b/examples/simple/settings/bootstrap3_theme_django_1_8.py @@ -5,7 +5,7 @@ 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: +except Exception as err: pass try: @@ -15,5 +15,5 @@ try: 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: +except Exception as err: pass diff --git a/examples/simple/settings/bootstrap3_theme_django_1_9_captcha.py b/examples/simple/settings/bootstrap3_theme_django_1_9_captcha.py new file mode 100755 index 00000000..27bb5ed2 --- /dev/null +++ b/examples/simple/settings/bootstrap3_theme_django_1_9_captcha.py @@ -0,0 +1,13 @@ +from .base 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 + INSTALLED_APPS.append('captcha') + INSTALLED_APPS.append( + 'fobi.contrib.plugins.form_elements.security.captcha' + ) +except Exception as err: + pass diff --git a/examples/simple/settings/bootstrap3_theme_django_1_9_feincms.py b/examples/simple/settings/bootstrap3_theme_django_1_9_feincms.py new file mode 100644 index 00000000..032a0ba0 --- /dev/null +++ b/examples/simple/settings/bootstrap3_theme_django_1_9_feincms.py @@ -0,0 +1,30 @@ +from .base 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 + INSTALLED_APPS.remove('admin_tools.dashboard') \ + if 'admin_tools.dashboard' in INSTALLED_APPS \ + else None + + INSTALLED_APPS += [ + 'feincms', # FeinCMS + + 'fobi.contrib.apps.feincms_integration', # Fobi FeinCMS app + + 'page', # Example + ] +except Exception as e: + pass + + +FEINCMS_RICHTEXT_INIT_CONTEXT = { + 'TINYMCE_JS_URL': STATIC_URL + 'tiny_mce/tiny_mce.js', +} + +MIGRATION_MODULES = { + 'fobi': 'fobi.migrations', + 'db_store': 'fobi.contrib.plugins.form_handlers.db_store.migrations', +} diff --git a/examples/simple/settings/bootstrap3_theme_djangocms.py b/examples/simple/settings/bootstrap3_theme_djangocms.py index 2cb234c8..b2025f47 100644 --- a/examples/simple/settings/bootstrap3_theme_djangocms.py +++ b/examples/simple/settings/bootstrap3_theme_djangocms.py @@ -2,39 +2,39 @@ from .base import * INSTALLED_APPS = list(INSTALLED_APPS) INSTALLED_APPS += [ - 'cms', # DjangoCMS + 'cms', # DjangoCMS 'mptt', 'menus', 'sekizai', - #'djangocms_admin_style', + # 'djangocms_admin_style', # Some plugins 'djangocms_picture', 'djangocms_snippet', - 'fobi.contrib.apps.djangocms_integration', # Fobi DjangoCMS app + 'fobi.contrib.apps.djangocms_integration', # Fobi DjangoCMS app - #'djangocms_page', # Example + # 'djangocms_page', # Example ] try: - #INSTALLED_APPS.remove('admin_tools') \ + # INSTALLED_APPS.remove('admin_tools') \ # if 'admin_tools' in INSTALLED_APPS else None - #INSTALLED_APPS.remove('admin_tools.menu') \ + # 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: +except Exception as err: pass MIDDLEWARE_CLASSES = list(MIDDLEWARE_CLASSES) MIDDLEWARE_CLASSES += [ - #'django.middleware.cache.UpdateCacheMiddleware', + # 'django.middleware.cache.UpdateCacheMiddleware', 'cms.middleware.page.CurrentPageMiddleware', 'cms.middleware.user.CurrentUserMiddleware', 'cms.middleware.toolbar.ToolbarMiddleware', 'cms.middleware.language.LanguageCookieMiddleware', - #'django.middleware.cache.FetchFromCacheMiddleware', + # 'django.middleware.cache.FetchFromCacheMiddleware', ] TEMPLATE_CONTEXT_PROCESSORS = list(TEMPLATE_CONTEXT_PROCESSORS) @@ -45,8 +45,8 @@ TEMPLATE_CONTEXT_PROCESSORS += [ ] FOBI_DEFAULT_THEME = 'bootstrap3' -#FOBI_DEFAULT_THEME = 'foundation5' -#FOBI_DEFAULT_THEME = 'simple' +# FOBI_DEFAULT_THEME = 'foundation5' +# FOBI_DEFAULT_THEME = 'simple' CMS_TEMPLATES = ( ('cms_page/{0}/page_with_sidebar.html'.format(FOBI_DEFAULT_THEME), @@ -62,6 +62,6 @@ MIGRATION_MODULES = { LANGUAGE_CODE = 'en' -#FEINCMS_RICHTEXT_INIT_CONTEXT = { +# FEINCMS_RICHTEXT_INIT_CONTEXT = { # 'TINYMCE_JS_URL': STATIC_URL + 'tiny_mce/tiny_mce.js', -#} +# } diff --git a/examples/simple/settings/bootstrap3_theme_djangocms_2.py b/examples/simple/settings/bootstrap3_theme_djangocms_2.py index 8652f926..bb8a476b 100644 --- a/examples/simple/settings/bootstrap3_theme_djangocms_2.py +++ b/examples/simple/settings/bootstrap3_theme_djangocms_2.py @@ -2,63 +2,68 @@ from .base import * DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. - 'NAME': PROJECT_DIR('../db/example_djangocms_2.db'), # Or path to database file if using sqlite3. + # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. + 'ENGINE': 'django.db.backends.sqlite3', + # Or path to database file if using sqlite3. + 'NAME': PROJECT_DIR('../db/example_djangocms_2.db'), # The following settings are not used with sqlite3: 'USER': '', 'PASSWORD': '', - 'HOST': '', # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP. - 'PORT': '', # Set to empty string for default. + # Empty for localhost through domain sockets or '127.0.0.1' for + # localhost through TCP. + 'HOST': '', + # Set to empty string for default. + 'PORT': '', } } INSTALLED_APPS = list(INSTALLED_APPS) INSTALLED_APPS += [ - 'cms', # DjangoCMS + 'cms', # DjangoCMS 'mptt', 'menus', 'sekizai', - #'djangocms_admin_style', + # 'djangocms_admin_style', # Some plugins 'cms.plugins.picture', 'cms.plugins.snippet', - 'fobi.contrib.apps.djangocms_integration', # Fobi DjangoCMS app + 'fobi.contrib.apps.djangocms_integration', # Fobi DjangoCMS app - #'djangocms_page', # Example + # 'djangocms_page', # Example ] try: - #INSTALLED_APPS.remove('admin_tools') \ + # INSTALLED_APPS.remove('admin_tools') \ # if 'admin_tools' in INSTALLED_APPS else None - #INSTALLED_APPS.remove('admin_tools.menu') \ + # 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: +except Exception as err: pass MIDDLEWARE_CLASSES = list(MIDDLEWARE_CLASSES) MIDDLEWARE_CLASSES += [ - #'django.middleware.cache.UpdateCacheMiddleware', + # 'django.middleware.cache.UpdateCacheMiddleware', 'cms.middleware.page.CurrentPageMiddleware', 'cms.middleware.user.CurrentUserMiddleware', 'cms.middleware.toolbar.ToolbarMiddleware', 'cms.middleware.language.LanguageCookieMiddleware', - #'django.middleware.cache.FetchFromCacheMiddleware', + # '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', + # 'cms.context_processors.cms_settings', ] FOBI_DEFAULT_THEME = 'bootstrap3' -#FOBI_DEFAULT_THEME = 'foundation5' -#FOBI_DEFAULT_THEME = 'simple' +# FOBI_DEFAULT_THEME = 'foundation5' +# FOBI_DEFAULT_THEME = 'simple' CMS_TEMPLATES = ( ('cms_page/{0}/page_with_sidebar.html'.format(FOBI_DEFAULT_THEME), @@ -74,6 +79,6 @@ MIGRATION_MODULES = { LANGUAGE_CODE = 'en' -#FEINCMS_RICHTEXT_INIT_CONTEXT = { +# FEINCMS_RICHTEXT_INIT_CONTEXT = { # 'TINYMCE_JS_URL': STATIC_URL + 'tiny_mce/tiny_mce.js', -#} +# } diff --git a/examples/simple/settings/bootstrap3_theme_feincms.py b/examples/simple/settings/bootstrap3_theme_feincms.py index 254797b2..92d2beae 100644 --- a/examples/simple/settings/bootstrap3_theme_feincms.py +++ b/examples/simple/settings/bootstrap3_theme_feincms.py @@ -2,18 +2,23 @@ from .base import * INSTALLED_APPS = list(INSTALLED_APPS) INSTALLED_APPS += [ - 'feincms', # FeinCMS + 'feincms', # FeinCMS - 'fobi.contrib.apps.feincms_integration', # Fobi FeinCMS app + 'fobi.contrib.apps.feincms_integration', # Fobi FeinCMS app - 'page', # Example + 'page', # Example ] 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: + # 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 err: pass FEINCMS_RICHTEXT_INIT_CONTEXT = { diff --git a/examples/simple/settings/bootstrap3_theme_mptt.py b/examples/simple/settings/bootstrap3_theme_mptt.py index fe705e9d..0d091236 100644 --- a/examples/simple/settings/bootstrap3_theme_mptt.py +++ b/examples/simple/settings/bootstrap3_theme_mptt.py @@ -5,7 +5,13 @@ INSTALLED_APPS = list(INSTALLED_APPS) try: INSTALLED_APPS.append('mptt') INSTALLED_APPS.append('bar') - INSTALLED_APPS.append('fobi.contrib.plugins.form_elements.fields.select_mptt_model_object') - INSTALLED_APPS.append('fobi.contrib.plugins.form_elements.fields.select_multiple_mptt_model_objects') -except Exception as e: + INSTALLED_APPS.append( + 'fobi.contrib.plugins.form_elements.fields.' + 'select_mptt_model_object' + ) + INSTALLED_APPS.append( + 'fobi.contrib.plugins.form_elements.fields.' + 'select_multiple_mptt_model_objects' + ) +except Exception as err: pass diff --git a/examples/simple/settings/bootstrap3_theme_python_3_django_1_8.py b/examples/simple/settings/bootstrap3_theme_python_3_django_1_8.py index ba1a5d2d..f9dd3f48 100644 --- a/examples/simple/settings/bootstrap3_theme_python_3_django_1_8.py +++ b/examples/simple/settings/bootstrap3_theme_python_3_django_1_8.py @@ -5,7 +5,7 @@ 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: +except Exception as err: pass try: @@ -15,11 +15,12 @@ try: 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: +except Exception as err: pass -#INSTALLED_APPS.remove('fobi.contrib.plugins.form_handlers.http_repost') -#INSTALLED_APPS.remove('fobi.contrib.plugins.form_handlers.mail') -#INSTALLED_APPS.remove('fobi.contrib.themes.foundation5') -#INSTALLED_APPS.remove('fobi.contrib.themes.foundation5.widgets.form_handlers.db_store_foundation5_widget') -#INSTALLED_APPS.remove('fobi.contrib.themes.simple') +# INSTALLED_APPS.remove('fobi.contrib.plugins.form_handlers.http_repost') +# INSTALLED_APPS.remove('fobi.contrib.plugins.form_handlers.mail') +# INSTALLED_APPS.remove('fobi.contrib.themes.foundation5') +# INSTALLED_APPS.remove('fobi.contrib.themes.foundation5.widgets.' +# 'form_handlers.db_store_foundation5_widget') +# INSTALLED_APPS.remove('fobi.contrib.themes.simple') diff --git a/examples/simple/settings/bootstrap3_theme_recaptcha.py b/examples/simple/settings/bootstrap3_theme_recaptcha.py index 218f2ec3..57d0de82 100644 --- a/examples/simple/settings/bootstrap3_theme_recaptcha.py +++ b/examples/simple/settings/bootstrap3_theme_recaptcha.py @@ -4,11 +4,13 @@ INSTALLED_APPS = list(INSTALLED_APPS) try: INSTALLED_APPS.append('captcha') - INSTALLED_APPS.append('fobi.contrib.plugins.form_elements.security.recaptcha') -except Exception as e: + INSTALLED_APPS.append( + 'fobi.contrib.plugins.form_elements.security.recaptcha' + ) +except Exception as err: pass -#RECAPTCHA_PUBLIC_KEY = '' -#RECAPTCHA_PRIVATE_KEY = '' +# RECAPTCHA_PUBLIC_KEY = '' +# RECAPTCHA_PRIVATE_KEY = '' RECAPTCHA_USE_SSL = True -#FOBI_DEFAULT_THEME = 'simple' +# FOBI_DEFAULT_THEME = 'simple' diff --git a/examples/simple/settings/djangocms_admin_style_theme_djangocms.py b/examples/simple/settings/djangocms_admin_style_theme_djangocms.py index 7198d115..43a1202e 100644 --- a/examples/simple/settings/djangocms_admin_style_theme_djangocms.py +++ b/examples/simple/settings/djangocms_admin_style_theme_djangocms.py @@ -1,20 +1,22 @@ +from nine.versions import DJANGO_GTE_1_8 + from .base import * INSTALLED_APPS = list(INSTALLED_APPS) INSTALLED_APPS.remove('django.contrib.admin') INSTALLED_APPS += [ - 'cms', # DjangoCMS + 'cms', # DjangoCMS 'mptt', 'menus', 'sekizai', - #'djangocms_admin_style', + # 'djangocms_admin_style', # Some plugins 'djangocms_picture', 'djangocms_snippet', 'treebeard', - 'fobi.contrib.apps.djangocms_integration', # Fobi DjangoCMS app + 'fobi.contrib.apps.djangocms_integration', # Fobi DjangoCMS app # Django-CMS admin style 'djangocms_admin_style', @@ -34,15 +36,14 @@ except Exception as e: MIDDLEWARE_CLASSES = list(MIDDLEWARE_CLASSES) MIDDLEWARE_CLASSES += [ - #'django.middleware.cache.UpdateCacheMiddleware', + # 'django.middleware.cache.UpdateCacheMiddleware', 'cms.middleware.page.CurrentPageMiddleware', 'cms.middleware.user.CurrentUserMiddleware', 'cms.middleware.toolbar.ToolbarMiddleware', 'cms.middleware.language.LanguageCookieMiddleware', - #'django.middleware.cache.FetchFromCacheMiddleware', + # 'django.middleware.cache.FetchFromCacheMiddleware', ] -from nine.versions import DJANGO_GTE_1_8 if DJANGO_GTE_1_8: TEMPLATES[0]['OPTIONS']['context_processors'] += [ 'cms.context_processors.cms_settings', @@ -57,9 +58,9 @@ else: 'cms.context_processors.cms_settings', ] -#FOBI_DEFAULT_THEME = 'bootstrap3' -#FOBI_DEFAULT_THEME = 'foundation5' -#FOBI_DEFAULT_THEME = 'simple' +# FOBI_DEFAULT_THEME = 'bootstrap3' +# FOBI_DEFAULT_THEME = 'foundation5' +# FOBI_DEFAULT_THEME = 'simple' FOBI_DEFAULT_THEME = 'djangocms_admin_style_theme' CMS_TEMPLATES = ( diff --git a/examples/simple/settings/foundation5.py b/examples/simple/settings/foundation5.py index 822e422d..0399c27f 100644 --- a/examples/simple/settings/foundation5.py +++ b/examples/simple/settings/foundation5.py @@ -5,8 +5,10 @@ FOBI_DEFAULT_THEME = 'foundation5' INSTALLED_APPS = list(INSTALLED_APPS) INSTALLED_APPS.append( - 'fobi.contrib.themes.foundation5.widgets.form_elements.date_foundation5_widget' + 'fobi.contrib.themes.foundation5.widgets.form_elements.' + 'date_foundation5_widget' ) INSTALLED_APPS.append( - 'fobi.contrib.themes.foundation5.widgets.form_elements.datetime_foundation5_widget' + 'fobi.contrib.themes.foundation5.widgets.form_elements.' + 'datetime_foundation5_widget' ) diff --git a/examples/simple/settings/foundation5_theme_django_1_10.py b/examples/simple/settings/foundation5_theme_django_1_10.py new file mode 100644 index 00000000..1fd04236 --- /dev/null +++ b/examples/simple/settings/foundation5_theme_django_1_10.py @@ -0,0 +1,32 @@ +from .base 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 err: + 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 err: + pass + +FOBI_DEFAULT_THEME = 'foundation5' + +INSTALLED_APPS = list(INSTALLED_APPS) + +INSTALLED_APPS.append( + 'fobi.contrib.themes.foundation5.widgets.form_elements.' + 'date_foundation5_widget' +) +INSTALLED_APPS.append( + 'fobi.contrib.themes.foundation5.widgets.form_elements.' + 'datetime_foundation5_widget' +) diff --git a/examples/simple/settings/foundation5_theme_django_1_8.py b/examples/simple/settings/foundation5_theme_django_1_8.py new file mode 100644 index 00000000..1fd04236 --- /dev/null +++ b/examples/simple/settings/foundation5_theme_django_1_8.py @@ -0,0 +1,32 @@ +from .base 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 err: + 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 err: + pass + +FOBI_DEFAULT_THEME = 'foundation5' + +INSTALLED_APPS = list(INSTALLED_APPS) + +INSTALLED_APPS.append( + 'fobi.contrib.themes.foundation5.widgets.form_elements.' + 'date_foundation5_widget' +) +INSTALLED_APPS.append( + 'fobi.contrib.themes.foundation5.widgets.form_elements.' + 'datetime_foundation5_widget' +) diff --git a/examples/simple/settings/foundation5_theme_django_1_9.py b/examples/simple/settings/foundation5_theme_django_1_9.py new file mode 100644 index 00000000..1fd04236 --- /dev/null +++ b/examples/simple/settings/foundation5_theme_django_1_9.py @@ -0,0 +1,32 @@ +from .base 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 err: + 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 err: + pass + +FOBI_DEFAULT_THEME = 'foundation5' + +INSTALLED_APPS = list(INSTALLED_APPS) + +INSTALLED_APPS.append( + 'fobi.contrib.themes.foundation5.widgets.form_elements.' + 'date_foundation5_widget' +) +INSTALLED_APPS.append( + 'fobi.contrib.themes.foundation5.widgets.form_elements.' + 'datetime_foundation5_widget' +) diff --git a/examples/simple/settings/foundation5_theme_feincms.py b/examples/simple/settings/foundation5_theme_feincms.py index b6d7ff33..479d2857 100644 --- a/examples/simple/settings/foundation5_theme_feincms.py +++ b/examples/simple/settings/foundation5_theme_feincms.py @@ -2,11 +2,11 @@ from .base import * INSTALLED_APPS = list(INSTALLED_APPS) INSTALLED_APPS += [ - 'feincms', # FeinCMS + 'feincms', # FeinCMS - 'fobi.contrib.apps.feincms_integration', # Fobi FeinCMS app + 'fobi.contrib.apps.feincms_integration', # Fobi FeinCMS app - 'page', # Example + 'page', # Example ] FEINCMS_RICHTEXT_INIT_CONTEXT = { diff --git a/examples/simple/settings/local_settings.example b/examples/simple/settings/local_settings.example index 4c9c8906..6287e98c 100644 --- a/examples/simple/settings/local_settings.example +++ b/examples/simple/settings/local_settings.example @@ -4,6 +4,9 @@ PROJECT_DIR = lambda base : os.path.abspath(os.path.join(os.path.dirname(__file_ DEBUG = True DEBUG_TOOLBAR = not True TEMPLATE_DEBUG = not True + +DEBUG_TEMPLATE = True + DEV = True os.environ.setdefault( 'FOBI_SOURCE_PATH', diff --git a/examples/simple/settings/settings_test.py b/examples/simple/settings/settings_test.py index aeb277b3..1aaa3285 100644 --- a/examples/simple/settings/settings_test.py +++ b/examples/simple/settings/settings_test.py @@ -19,7 +19,8 @@ LOGGING = { }, 'formatters': { 'verbose': { - 'format': '\n%(levelname)s %(asctime)s [%(pathname)s:%(lineno)s] %(message)s' + 'format': '\n%(levelname)s %(asctime)s [%(pathname)s:%(lineno)s] ' + '%(message)s' }, 'simple': { 'format': '\n%(levelname)s %(message)s' @@ -37,32 +38,32 @@ LOGGING = { 'formatter': 'verbose' }, 'all_log': { - 'level':'ERROR', - 'class':'logging.handlers.RotatingFileHandler', + 'level': 'ERROR', + 'class': 'logging.handlers.RotatingFileHandler', 'filename': PROJECT_DIR("../../logs/all.log"), 'maxBytes': 1048576, 'backupCount': 99, 'formatter': 'verbose', }, 'django_log': { - 'level':'ERROR', - 'class':'logging.handlers.RotatingFileHandler', + 'level': 'ERROR', + 'class': 'logging.handlers.RotatingFileHandler', 'filename': PROJECT_DIR("../../logs/django.log"), 'maxBytes': 1048576, 'backupCount': 99, 'formatter': 'verbose', }, 'django_request_log': { - 'level':'ERROR', - 'class':'logging.handlers.RotatingFileHandler', + 'level': 'ERROR', + 'class': 'logging.handlers.RotatingFileHandler', 'filename': PROJECT_DIR("../../logs/django_request.log"), 'maxBytes': 1048576, 'backupCount': 99, 'formatter': 'verbose', }, 'fobi_log': { - 'level':'ERROR', - 'class':'logging.handlers.RotatingFileHandler', + 'level': 'ERROR', + 'class': 'logging.handlers.RotatingFileHandler', 'filename': PROJECT_DIR("../../logs/fobi.log"), 'maxBytes': 1048576, 'backupCount': 99, diff --git a/examples/simple/settings/simple.py b/examples/simple/settings/simple.py index 5c26bab1..e9e0828b 100644 --- a/examples/simple/settings/simple.py +++ b/examples/simple/settings/simple.py @@ -3,6 +3,6 @@ from .base import * FOBI_DEFAULT_THEME = 'simple' INSTALLED_APPS = list(INSTALLED_APPS) -#INSTALLED_APPS.remove('admin_tools') -#INSTALLED_APPS.remove('admin_tools.menu') -#INSTALLED_APPS.remove('admin_tools.dashboard') +# INSTALLED_APPS.remove('admin_tools') +# INSTALLED_APPS.remove('admin_tools.menu') +# INSTALLED_APPS.remove('admin_tools.dashboard') diff --git a/examples/simple/settings/test.py b/examples/simple/settings/test.py index 9b5ed21c..c03406ce 100644 --- a/examples/simple/settings/test.py +++ b/examples/simple/settings/test.py @@ -1 +1,95 @@ +# Use in `tox`. +from nine import versions + from .base import * + +TESTING = True + +INSTALLED_APPS = list(INSTALLED_APPS) + +if versions.DJANGO_1_5: + + try: + INSTALLED_APPS.append( + 'south') if 'south' not in INSTALLED_APPS else None + except Exception as err: + pass + +elif versions.DJANGO_1_6: + + try: + INSTALLED_APPS.append( + 'south') if 'south' not in INSTALLED_APPS else None + except Exception as err: + pass + + +elif versions.DJANGO_1_7: + + 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 err: + pass + +elif versions.DJANGO_1_8: + + 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 err: + 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 err: + pass + +elif versions.DJANGO_1_9: + + 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 err: + 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 err: + pass + +elif versions.DJANGO_1_10: + + 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 err: + 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 err: + pass + +LOGGING = {} + +DEBUG_TOOLBAR = False diff --git a/examples/simple/templates/bootstrap3/base.html b/examples/simple/templates/bootstrap3/base.html index 513ea1aa..2776fd3d 100644 --- a/examples/simple/templates/bootstrap3/base.html +++ b/examples/simple/templates/bootstrap3/base.html @@ -2,5 +2,5 @@ {% block theme-javascripts %} {{ block.super }} - {% include "google_analytics.html" %} -{% endblock theme-javascripts %} \ No newline at end of file + {% if not testing %}{% include "google_analytics.html" %}{% endif %} +{% endblock theme-javascripts %} diff --git a/examples/simple/urls.py b/examples/simple/urls.py index 43eaf7ac..94165112 100644 --- a/examples/simple/urls.py +++ b/examples/simple/urls.py @@ -8,6 +8,8 @@ from django.views.generic import TemplateView from fobi.settings import DEFAULT_THEME +from nine import versions + admin.autodiscover() # Mapping. @@ -29,7 +31,7 @@ if DEFAULT_THEME in ('simple', 'djangocms_admin_style_theme'): urlpatterns = [] -urlpatterns += i18n_patterns( +url_patterns_args = [ # DB Store plugin URLs # namespace='fobi' url(r'^fobi/plugins/form-handlers/db-store/', @@ -59,7 +61,12 @@ urlpatterns += i18n_patterns( # django-fobi public forms contrib app: # url(r'^', include('fobi.contrib.apps.public_forms.urls')), -) +] + +if versions.DJANGO_LTE_1_7: + urlpatterns += i18n_patterns('', *url_patterns_args) +else: + urlpatterns += i18n_patterns(*url_patterns_args) # Serving media and static in debug/developer mode. if settings.DEBUG: @@ -72,17 +79,24 @@ if settings.DEBUG: if 'feincms' in settings.INSTALLED_APPS: from page.models import Page Page - urlpatterns += i18n_patterns( + url_patterns_args = [ url(r'^pages/', include('feincms.urls')), - - ) + ] + if versions.DJANGO_LTE_1_7: + urlpatterns += i18n_patterns('', *url_patterns_args) + else: + urlpatterns += i18n_patterns(*url_patterns_args) # Conditionally including DjangoCMS URls in case if # DjangoCMS in installed apps. if 'cms' in settings.INSTALLED_APPS: - urlpatterns += i18n_patterns( + url_patterns_args = [ url(r'^cms-pages/', include('cms.urls')), - ) + ] + if versions.DJANGO_LTE_1_7: + urlpatterns += i18n_patterns('', *url_patterns_args) + else: + urlpatterns += i18n_patterns(*url_patterns_args) # Conditionally including Captcha URls in case if # Captcha in installed apps. diff --git a/pytest.ini b/pytest.ini index 80381d50..8d19bc98 100644 --- a/pytest.ini +++ b/pytest.ini @@ -7,11 +7,13 @@ norecursedirs= _sass build dist + migrations python_files = test_*.py -export = - DJANGO_SETTINGS_MODULE=./examples/simple/settings.testing + tests.py +DJANGO_SETTINGS_MODULE=settings.test addopts= - --cov=src/fobi + --cov=fobi --ignore=.tox --ignore=requirements + --ignore=release diff --git a/runtests.py b/runtests.py new file mode 100755 index 00000000..536a5ded --- /dev/null +++ b/runtests.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +import os +import sys +import pytest + + +def main(): + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings.test") + sys.path.insert(0, "examples/simple") + return pytest.main() + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/scripts/build_docs.sh b/scripts/build_docs.sh index 6ffead85..c4a09fcb 100755 --- a/scripts/build_docs.sh +++ b/scripts/build_docs.sh @@ -1,8 +1,7 @@ ./scripts/uninstall.sh ./scripts/install.sh -#cd .. cat README.rst SCREENSHOTS.rst docs/documentation.rst.distrib > docs/index.rst cat QUICK_START.rst > docs/quickstart.rst sphinx-build -n -a -b html docs builddocs cd builddocs && zip -r ../builddocs.zip . -x ".*" && cd .. -#cd scripts + diff --git a/scripts/compile_messages.sh b/scripts/compile_messages.sh index 064f3662..eddf8aff 100755 --- a/scripts/compile_messages.sh +++ b/scripts/compile_messages.sh @@ -1,4 +1,3 @@ -#cd .. echo 'Compiling messages for django-fobi...' cd src/fobi/ #django-admin.py compilemessages -l hy @@ -12,5 +11,3 @@ cd ../../examples/simple/ django-admin.py compilemessages -l de django-admin.py compilemessages -l nl django-admin.py compilemessages -l ru - -#cd ../../scripts \ No newline at end of file diff --git a/scripts/install_django_1_7_feincms.sh b/scripts/install_django_1_7_feincms.sh index 758bba07..8dac1868 100755 --- a/scripts/install_django_1_7_feincms.sh +++ b/scripts/install_django_1_7_feincms.sh @@ -1,5 +1,4 @@ -#pip install -r examples/requirements/django_1_7.txt -pip install -r examples/requirements/feincms.txt +pip install -r examples/requirements/feincms_1_10.txt python setup.py install mkdir -p examples/logs examples/db examples/media examples/media/static examples/media/fobi_plugins/content_image mkdir -p examples/media/fobi_plugins/file diff --git a/scripts/install_django_1_9_feincms.sh b/scripts/install_django_1_9_feincms.sh new file mode 100755 index 00000000..c7322d41 --- /dev/null +++ b/scripts/install_django_1_9_feincms.sh @@ -0,0 +1,8 @@ +pip install -r examples/requirements/feincms_1_12.txt +python setup.py install +mkdir -p examples/logs examples/db examples/media examples/media/static examples/media/fobi_plugins/content_image +mkdir -p examples/media/fobi_plugins/file +python examples/simple/manage.py collectstatic --noinput --settings=settings.bootstrap3_theme_django_1_9_feincms --traceback -v 3 +python examples/simple/manage.py syncdb --noinput --settings=settings.bootstrap3_theme_django_1_9_feincms --traceback -v 3 +python examples/simple/manage.py migrate --noinput --settings=settings.bootstrap3_theme_django_1_9_feincms --traceback -v 3 +python examples/simple/manage.py fobi_create_test_data --settings=settings.bootstrap3_theme_django_1_9_feincms --traceback -v 3 diff --git a/scripts/make_migrations.sh b/scripts/make_migrations.sh new file mode 100755 index 00000000..09cabfb6 --- /dev/null +++ b/scripts/make_migrations.sh @@ -0,0 +1,9 @@ +echo 'Making messages for django-fobi...' +cd examples/simple/ +./manage.py makemigrations fobi + +echo 'Making messages for example projects...' +./manage.py makemigrations + +echo 'Applying migrations...' +./manage.py migrate diff --git a/scripts/prepare_docs.sh b/scripts/prepare_docs.sh new file mode 100755 index 00000000..b5491e4d --- /dev/null +++ b/scripts/prepare_docs.sh @@ -0,0 +1,2 @@ +cat README.rst SCREENSHOTS.rst docs/documentation.rst.distrib > docs/index.rst +cat QUICK_START.rst > docs/quickstart.rst diff --git a/scripts/pycodestyle.sh b/scripts/pycodestyle.sh new file mode 100755 index 00000000..1290c9a6 --- /dev/null +++ b/scripts/pycodestyle.sh @@ -0,0 +1,2 @@ +reset +pycodestyle src/fobi/ --exclude src/fobi/migrations/,src/fobi/south_migrations/,src/fobi/contrib/plugins/form_handlers/db_store/migrations/ diff --git a/scripts/pycodestyle_example.sh b/scripts/pycodestyle_example.sh new file mode 100755 index 00000000..e0094c32 --- /dev/null +++ b/scripts/pycodestyle_example.sh @@ -0,0 +1,2 @@ +reset +pycodestyle examples/simple/ --exclude examples/simple/page/migrations/,examples/simple/page/south_migrations/,examples/simple/lund/fobi_addons/migrations/,examples/simple/wsgi.py diff --git a/scripts/reinstall_django_1_9_feincms.sh b/scripts/reinstall_django_1_9_feincms.sh new file mode 100755 index 00000000..e1a1e115 --- /dev/null +++ b/scripts/reinstall_django_1_9_feincms.sh @@ -0,0 +1,3 @@ +reset +./scripts/uninstall.sh +./scripts/install_django_1_9_feincms.sh \ No newline at end of file diff --git a/scripts/touch_docs.sh b/scripts/touch_docs.sh new file mode 100755 index 00000000..92975c0c --- /dev/null +++ b/scripts/touch_docs.sh @@ -0,0 +1 @@ +cat README.rst SCREENSHOTS.rst docs/documentation.rst.distrib > docs/index.rst diff --git a/setup.py b/setup.py index b5bfaaec..4130a5e0 100644 --- a/setup.py +++ b/setup.py @@ -4,6 +4,8 @@ import sys from distutils.version import LooseVersion from setuptools import setup, find_packages +version = '0.10.1' + # *************************************************************************** # ************************** Django version ********************************* # *************************************************************************** @@ -204,8 +206,6 @@ for locale_dir in locale_dirs: for f in os.listdir(locale_dir)] -version = '0.9.6' - install_requires = [] # If certain version of Django is already installed, choose version agnostic # dependencies. diff --git a/src/fobi/__init__.py b/src/fobi/__init__.py index 2e4b8e80..0097cc8f 100644 --- a/src/fobi/__init__.py +++ b/src/fobi/__init__.py @@ -1,6 +1,6 @@ __title__ = 'django-fobi' -__version__ = '0.9.6' -__build__ = 0x000068 +__version__ = '0.10.1' +__build__ = 0x000074 __author__ = 'Artur Barseghyan ' __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' diff --git a/src/fobi/base.py b/src/fobi/base.py index 9481bf63..5c53501d 100644 --- a/src/fobi/base.py +++ b/src/fobi/base.py @@ -787,6 +787,11 @@ class BaseFormFieldPluginForm(BasePluginForm): return True + if not DJANGO_GTE_1_7: + def add_error(self, field, error): + """Backwards compatibility hack.""" + raise forms.ValidationError(error, 'invalid') + # ***************************************************************************** # ***************************************************************************** # ******************************** Plugins ************************************ @@ -1055,15 +1060,13 @@ class BasePlugin(object): plugin_form = self.get_form() if plugin_form: try: - plugin_form = self.get_form() - if plugin_form: - kwargs = { - 'data': data, - 'files': files, - } - if initial_data: - kwargs.update({'initial': initial_data}) - return plugin_form(**kwargs) + kwargs = { + 'data': data, + 'files': files, + } + if initial_data: + kwargs.update({'initial': initial_data}) + return plugin_form(**kwargs) except Exception as e: if DEBUG: logger.debug(e) @@ -1532,7 +1535,8 @@ class FormElementPlugin(BasePlugin): logger.debug(str(err)) return {} - def _submit_plugin_form_data(self, form_entry, request, form): + def _submit_plugin_form_data(self, form_entry, request, form, + form_element_entries=None, **kwargs): """Submit plugin form data (internal method). Do not override this method. Use ``submit_plugin_form_data``, @@ -1545,20 +1549,30 @@ class FormElementPlugin(BasePlugin): ``fobi.models.FormEntry``. :param django.http.HttpRequest request: :param django.forms.Form form: + :param iterable form_element_entries: """ if DEBUG: return self.submit_plugin_form_data( - form_entry=form_entry, request=request, form=form + form_entry=form_entry, + request=request, + form=form, + form_element_entries=form_element_entries, + **kwargs ) else: try: return self.submit_plugin_form_data( - form_entry=form_entry, request=request, form=form + form_entry=form_entry, + request=request, + form=form, + form_element_entries=form_element_entries, + **kwargs ) except Exception as e: logger.debug(str(e)) - def submit_plugin_form_data(self, form_entry, request, form): + def submit_plugin_form_data(self, form_entry, request, form, + form_element_entries=None, **kwargs): """Submit plugin form data. Called on form submission (when user actually @@ -1568,6 +1582,7 @@ class FormElementPlugin(BasePlugin): ``fobi.models.FormEntry``. :param django.http.HttpRequest request: :param django.forms.Form form: + :param iterable form_element_entries: """ @@ -1610,24 +1625,30 @@ class FormHandlerPlugin(BasePlugin): if not form_element_entries: form_element_entries = form_entry.formelemententry_set.all()[:] - try: + if FAIL_ON_ERRORS_IN_FORM_HANDLER_PLUGINS: response = self.run(form_entry, request, form, form_element_entries) if response: return response else: return (True, None) - except Exception as err: - if FAIL_ON_ERRORS_IN_FORM_HANDLER_PLUGINS: - raise err.__class__("Exception: {0}. {1}" - "".format(str(err), - traceback.format_exc())) - logger.error( - "Error in class {0}. Details: " - "{1}. Full trace: {2}".format(self.__class__.__name__, - str(err), traceback.format_exc()) - ) - return (False, err) + else: + try: + response = self.run(form_entry, request, form, + form_element_entries) + if response: + return response + else: + return (True, None) + except Exception as err: + logger.error( + "Error in class {0}. Details: " + "{1}. Full trace: {2}".format( + self.__class__.__name__, + str(err), traceback.format_exc() + ) + ) + return (False, err) def run(self, form_entry, request, form, form_element_entries=None): """Run. @@ -2399,19 +2420,27 @@ def validate_form_element_plugin_uid(plugin_uid): return validate_plugin_uid(form_element_plugin_registry, plugin_uid) -def submit_plugin_form_data(form_entry, request, form): +def submit_plugin_form_data(form_entry, request, form, + form_element_entries=None, **kwargs): """Submit plugin form data for all plugins. :param fobi.models.FormEntry form_entry: Instance of ``fobi.models.FormEntry``. :param django.http.HttpRequest request: :param django.forms.Form form: + :param iterable form_element_entries: """ - for form_element_entry in form_entry.formelemententry_set.all(): + if not form_element_entries: + form_element_entries = form_entry.formelemententry_set.all() + for form_element_entry in form_element_entries: # Get the plugin. form_element_plugin = form_element_entry.get_plugin(request=request) updated_form = form_element_plugin._submit_plugin_form_data( - form_entry=form_entry, request=request, form=form + form_entry=form_entry, + request=request, + form=form, + form_element_entries=form_element_entries, + **kwargs ) if updated_form: form = updated_form diff --git a/src/fobi/contrib/plugins/form_elements/content/content_image/README.rst b/src/fobi/contrib/plugins/form_elements/content/content_image/README.rst index 10517a6a..b85bd301 100644 --- a/src/fobi/contrib/plugins/form_elements/content/content_image/README.rst +++ b/src/fobi/contrib/plugins/form_elements/content/content_image/README.rst @@ -19,9 +19,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/plugins/form_elements/content/content_image/base.py b/src/fobi/contrib/plugins/form_elements/content/content_image/base.py new file mode 100644 index 00000000..73413aee --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/content/content_image/base.py @@ -0,0 +1,86 @@ +from __future__ import absolute_import + +from uuid import uuid4 + +from django.conf import settings +from django.template.loader import render_to_string +from django.utils.translation import ugettext_lazy as _ + +from nonefield.fields import NoneField + +from fobi.base import FormElementPlugin +from fobi.helpers import delete_file, clone_file + +from . import UID +from .forms import ContentImageForm +from .helpers import get_crop_filter +from .settings import ( + FIT_METHOD_FIT_WIDTH, FIT_METHOD_FIT_HEIGHT, IMAGES_UPLOAD_DIR +) + +__title__ = 'fobi.contrib.plugins.form_elements.content.content_image.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('ContentImagePlugin',) + + +class ContentImagePlugin(FormElementPlugin): + """Content image plugin.""" + + uid = UID + name = _("Content image") + group = _("Content") + form = ContentImageForm + + def post_processor(self): + """Post process data. + + Always the same. + """ + self.data.name = "{0}_{1}".format(self.uid, uuid4()) + + def delete_plugin_data(self): + """Delete uploaded file.""" + delete_file(self.data.file) + + def clone_plugin_data(self, entry): + """Clone plugin data. + + Clone plugin data, which means we make a copy of the original image. + + TODO: Perhaps rely more on data of ``form_element_entry``? + """ + cloned_image = clone_file( + IMAGES_UPLOAD_DIR, self.data.file, relative_path=True + ) + return self.get_cloned_plugin_data(update={'file': cloned_image}) + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + width, height = self.data.size.split('x') + crop = get_crop_filter(self.data.fit_method) + + if FIT_METHOD_FIT_WIDTH == self.data.fit_method: + thumb_size = (width, 0) + elif FIT_METHOD_FIT_HEIGHT == self.data.fit_method: + thumb_size = (0, height) + else: + thumb_size = (width, height) + + context = { + 'plugin': self, + 'MEDIA_URL': settings.MEDIA_URL, + 'crop': crop, + 'thumb_size': thumb_size + } + rendered_image = render_to_string('content_image/render.html', context) + + field_kwargs = { + 'initial': rendered_image, + 'required': False, + 'label': '', + } + + return [(self.data.name, NoneField, field_kwargs)] 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 e23c9377..2f5f7358 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 @@ -1,20 +1,8 @@ -from uuid import uuid4 +from __future__ import absolute_import -from django.conf import settings -from django.template.loader import render_to_string -from django.utils.translation import ugettext_lazy as _ +from fobi.base import form_element_plugin_registry -from nonefield.fields import NoneField - -from fobi.base import FormElementPlugin, form_element_plugin_registry -from fobi.helpers import delete_file, clone_file - -from . import UID -from .forms import ContentImageForm -from .helpers import get_crop_filter -from .settings import ( - FIT_METHOD_FIT_WIDTH, FIT_METHOD_FIT_HEIGHT, IMAGES_UPLOAD_DIR -) +from .base import ContentImagePlugin __title__ = 'fobi.contrib.plugins.form_elements.content.content_image.' \ 'fobi_form_elements' @@ -24,65 +12,4 @@ __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('ContentImagePlugin',) -class ContentImagePlugin(FormElementPlugin): - """Content image plugin.""" - - uid = UID - name = _("Content image") - group = _("Content") - form = ContentImageForm - - def post_processor(self): - """Post process data. - - Always the same. - """ - self.data.name = "{0}_{1}".format(self.uid, uuid4()) - - def delete_plugin_data(self): - """Delete uploaded file.""" - delete_file(self.data.file) - - def clone_plugin_data(self, entry): - """Clone plugin data. - - Clone plugin data, which means we make a copy of the original image. - - TODO: Perhaps rely more on data of ``form_element_entry``? - """ - cloned_image = clone_file( - IMAGES_UPLOAD_DIR, self.data.file, relative_path=True - ) - return self.get_cloned_plugin_data(update={'file': cloned_image}) - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - width, height = self.data.size.split('x') - crop = get_crop_filter(self.data.fit_method) - - if FIT_METHOD_FIT_WIDTH == self.data.fit_method: - thumb_size = (width, 0) - elif FIT_METHOD_FIT_HEIGHT == self.data.fit_method: - thumb_size = (0, height) - else: - thumb_size = (width, height) - - context = { - 'plugin': self, - 'MEDIA_URL': settings.MEDIA_URL, - 'crop': crop, - 'thumb_size': thumb_size - } - rendered_image = render_to_string('content_image/render.html', context) - - field_kwargs = { - 'initial': rendered_image, - 'required': False, - 'label': '', - } - - return [(self.data.name, NoneField, field_kwargs)] - - form_element_plugin_registry.register(ContentImagePlugin) diff --git a/src/fobi/contrib/plugins/form_elements/content/content_text/README.rst b/src/fobi/contrib/plugins/form_elements/content/content_text/README.rst index 70bbb754..5f7354a6 100644 --- a/src/fobi/contrib/plugins/form_elements/content/content_text/README.rst +++ b/src/fobi/contrib/plugins/form_elements/content/content_text/README.rst @@ -18,9 +18,50 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. + +4. Additionally, for the fine tuning, see the + ``fobi.contrib.plugins.form_elements.content.content_text.defaults`` + module. If necessary, override the settings by prepending + ``FOBI_PLUGIN_CONTENT_TEXT_`` to the desired variable name from the + above mentioned ``defaults`` module. + + By default the content of the text field is stripped using either the + awesome `bleach `_ library or if bleach + is not installed just Django's own `strip_tags` function. To configure + the strip (bleach only) behaviour, two settings are introduced: + +.. code-block:: text + + - ALLOWED_TAGS: + - ALLOWED_ATTRIBUTES: + +The default values are: + +.. code-block:: python + + ALLOWED_TAGS = [ + 'a', + 'abbr', + 'acronym', + 'b', + 'blockquote', + 'code', + 'em', + 'i', + 'li', + 'ol', + 'strong', + 'ul', + ] + + ALLOWED_ATTRIBUTES = { + 'a': ['href', 'title'], + 'abbr': ['title'], + 'acronym': ['title'], + } diff --git a/src/fobi/contrib/plugins/form_elements/content/content_text/base.py b/src/fobi/contrib/plugins/form_elements/content/content_text/base.py new file mode 100644 index 00000000..062b2d18 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/content/content_text/base.py @@ -0,0 +1,46 @@ +from __future__ import absolute_import + +from uuid import uuid4 + +from django.utils.encoding import smart_str +from django.utils.translation import ugettext_lazy as _ + +from nonefield.fields import NoneField + +from fobi.base import FormElementPlugin + +from . import UID +from .forms import ContentTextForm + +__title__ = 'fobi.contrib.plugins.form_elements.content.content_text.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('ContentTextPlugin',) + + +class ContentTextPlugin(FormElementPlugin): + """Content text plugin.""" + + uid = UID + name = _("Content text") + group = _("Content") + form = ContentTextForm + + def post_processor(self): + """Post process data. + + Always the same. + """ + self.data.name = "{0}_{1}".format(self.uid, uuid4()) + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + field_kwargs = { + 'initial': "

{0}

".format(smart_str(self.data.text)), + 'required': False, + 'label': '', + } + + return [(self.data.name, NoneField, field_kwargs)] diff --git a/src/fobi/contrib/plugins/form_elements/content/content_text/conf.py b/src/fobi/contrib/plugins/form_elements/content/content_text/conf.py new file mode 100644 index 00000000..e0ddb414 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/content/content_text/conf.py @@ -0,0 +1,34 @@ +from django.conf import settings + +from . import defaults + +__title__ = 'fobi.contrib.plugins.form_elements.content.content_text.conf' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('get_setting',) + + +def get_setting(setting, override=None): + """Get setting. + + Get a setting from + ``fobi.contrib.plugins.form_elements.content.content_text`` conf module, + falling back to the default. + + If override is not None, it will be used instead of the setting. + + :param setting: String with setting name + :param override: Value to use when no setting is available. Defaults to + None. + :return: Setting value. + """ + if override is not None: + return override + if hasattr(settings, 'FOBI_PLUGIN_CONTENT_TEXT_{0}'.format(setting)): + return getattr( + settings, + 'FOBI_PLUGIN_CONTENT_TEXT_{0}'.format(setting) + ) + else: + return getattr(defaults, setting) diff --git a/src/fobi/contrib/plugins/form_elements/content/content_text/defaults.py b/src/fobi/contrib/plugins/form_elements/content/content_text/defaults.py new file mode 100644 index 00000000..233e87f4 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/content/content_text/defaults.py @@ -0,0 +1,29 @@ +__title__ = 'fobi.contrib.plugins.form_elements.content.content_text.defaults' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ( + 'ALLOWED_TAGS', + 'ALLOWED_TAGS', +) + +ALLOWED_TAGS = [ + 'a', + 'abbr', + 'acronym', + 'b', + 'blockquote', + 'code', + 'em', + 'i', + 'li', + 'ol', + 'strong', + 'ul', +] + +ALLOWED_ATTRIBUTES = { + 'a': ['href', 'title'], + 'abbr': ['title'], + 'acronym': ['title'], +} 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 ca854754..268bd5b2 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 @@ -1,14 +1,8 @@ -from uuid import uuid4 +from __future__ import absolute_import -from django.utils.encoding import smart_str -from django.utils.translation import ugettext_lazy as _ +from fobi.base import form_element_plugin_registry -from nonefield.fields import NoneField - -from fobi.base import FormElementPlugin, form_element_plugin_registry - -from . import UID -from .forms import ContentTextForm +from .base import ContentTextPlugin __title__ = 'fobi.contrib.plugins.form_elements.content.content_text.' \ 'fobi_form_elements' @@ -18,31 +12,4 @@ __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('ContentTextPlugin',) -class ContentTextPlugin(FormElementPlugin): - """Content text plugin.""" - - uid = UID - name = _("Content text") - group = _("Content") - form = ContentTextForm - - def post_processor(self): - """Post process data. - - Always the same. - """ - self.data.name = "{0}_{1}".format(self.uid, uuid4()) - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - field_kwargs = { - 'initial': "

{0}

".format(smart_str(self.data.text)), - 'required': False, - 'label': '', - } - - return [(self.data.name, NoneField, field_kwargs)] - - form_element_plugin_registry.register(ContentTextPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/content/content_text/forms.py b/src/fobi/contrib/plugins/form_elements/content/content_text/forms.py index b23de7fc..71559d4f 100644 --- a/src/fobi/contrib/plugins/form_elements/content/content_text/forms.py +++ b/src/fobi/contrib/plugins/form_elements/content/content_text/forms.py @@ -1,9 +1,18 @@ from django import forms from django.forms.widgets import Textarea +from django.utils.html import strip_tags from django.utils.translation import ugettext_lazy as _ from fobi.base import BasePluginForm, get_theme +from .settings import ALLOWED_TAGS, ALLOWED_ATTRIBUTES + +try: + import bleach + BLEACH_INSTALLED = True +except ImportError as err: + BLEACH_INSTALLED = False + __title__ = 'fobi.contrib.plugins.form_elements.content.content_text.forms' __author__ = 'Artur Barseghyan ' __copyright__ = '2014-2016 Artur Barseghyan' @@ -26,3 +35,16 @@ class ContentTextForm(forms.Form, BasePluginForm): required=True, widget=Textarea(attrs={'class': theme.form_element_html_class}) ) + + def clean_text(self): + """Clean text value.""" + if BLEACH_INSTALLED: + return bleach.clean( + text=self.cleaned_data['text'], + tags=ALLOWED_TAGS, + attributes=ALLOWED_ATTRIBUTES, + strip=True, + strip_comments=True + ) + else: + return strip_tags(self.cleaned_data['text']) diff --git a/src/fobi/contrib/plugins/form_elements/content/content_text/settings.py b/src/fobi/contrib/plugins/form_elements/content/content_text/settings.py new file mode 100644 index 00000000..4d3ff635 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/content/content_text/settings.py @@ -0,0 +1,13 @@ +from .conf import get_setting + +__title__ = 'fobi.contrib.plugins.form_elements.content.content_text.settings' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ( + 'ALLOWED_TAGS', + 'ALLOWED_ATTRIBUTES', +) + +ALLOWED_TAGS = get_setting('ALLOWED_TAGS') +ALLOWED_ATTRIBUTES = get_setting('ALLOWED_ATTRIBUTES') diff --git a/src/fobi/contrib/plugins/form_elements/content/content_video/README.rst b/src/fobi/contrib/plugins/form_elements/content/content_video/README.rst index 9645b95f..79b1c699 100644 --- a/src/fobi/contrib/plugins/form_elements/content/content_video/README.rst +++ b/src/fobi/contrib/plugins/form_elements/content/content_video/README.rst @@ -18,9 +18,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/plugins/form_elements/content/content_video/base.py b/src/fobi/contrib/plugins/form_elements/content/content_video/base.py new file mode 100644 index 00000000..2032e057 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/content/content_video/base.py @@ -0,0 +1,51 @@ +from __future__ import absolute_import + +from uuid import uuid4 + +from django.utils.translation import ugettext_lazy as _ + +from vishap import render_video + +from nonefield.fields import NoneField + +from fobi.base import FormElementPlugin + +from . import UID +from .forms import ContentVideoForm + +__title__ = 'fobi.contrib.plugins.form_elements.content.content_video.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('ContentVideoPlugin',) + + +class ContentVideoPlugin(FormElementPlugin): + """Content video plugin.""" + + uid = UID + name = _("Content video") + group = _("Content") + form = ContentVideoForm + + def post_processor(self): + """Process plugin data. + + Always the same. + """ + self.data.name = "{0}_{1}".format(self.uid, uuid4()) + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + width, height = self.data.size.split('x') + + field_kwargs = { + 'initial': '
{0}
'.format( + render_video(self.data.url, width, height) + ), + 'required': False, + 'label': '', + } + + return [(self.data.name, NoneField, field_kwargs)] 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 c9465ebc..91da92b8 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 @@ -1,15 +1,8 @@ -from uuid import uuid4 +from __future__ import absolute_import -from django.utils.translation import ugettext_lazy as _ +from fobi.base import form_element_plugin_registry -from vishap import render_video - -from nonefield.fields import NoneField - -from fobi.base import FormElementPlugin, form_element_plugin_registry - -from . import UID -from .forms import ContentVideoForm +from .base import ContentVideoPlugin __title__ = 'fobi.contrib.plugins.form_elements.content.content_video.' \ 'fobi_form_elements' @@ -19,35 +12,4 @@ __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('ContentVideoPlugin',) -class ContentVideoPlugin(FormElementPlugin): - """Content video plugin.""" - - uid = UID - name = _("Content video") - group = _("Content") - form = ContentVideoForm - - def post_processor(self): - """Process plugin data. - - Always the same. - """ - self.data.name = "{0}_{1}".format(self.uid, uuid4()) - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - width, height = self.data.size.split('x') - - field_kwargs = { - 'initial': '
{0}
'.format( - render_video(self.data.url, width, height) - ), - 'required': False, - 'label': '', - } - - return [(self.data.name, NoneField, field_kwargs)] - - form_element_plugin_registry.register(ContentVideoPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/boolean/README.rst b/src/fobi/contrib/plugins/form_elements/fields/boolean/README.rst index cb8b8092..caed1cf9 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/boolean/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/boolean/README.rst @@ -19,9 +19,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/plugins/form_elements/fields/boolean/base.py b/src/fobi/contrib/plugins/form_elements/fields/boolean/base.py new file mode 100644 index 00000000..1a5ce71f --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/boolean/base.py @@ -0,0 +1,34 @@ +from django.forms.fields import BooleanField +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin + +from . import UID +from .forms import BooleanSelectForm + +__title__ = 'fobi.contrib.plugins.form_elements.fields.boolean.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('BooleanSelectPlugin',) + + +class BooleanSelectPlugin(FormFieldPlugin): + """Boolean select plugin.""" + + uid = UID + name = _("Boolean") + group = _("Fields") + form = BooleanSelectForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + 'required': self.data.required, + } + + return [(self.data.name, BooleanField, field_kwargs)] 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 af42d223..a36623f2 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 @@ -1,10 +1,8 @@ -from django.forms.fields import BooleanField -from django.utils.translation import ugettext_lazy as _ +from __future__ import absolute_import -from fobi.base import FormFieldPlugin, form_element_plugin_registry +from fobi.base import form_element_plugin_registry -from . import UID -from .forms import BooleanSelectForm +from .base import BooleanSelectPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.boolean.' \ 'fobi_form_elements' @@ -14,25 +12,4 @@ __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('BooleanSelectPlugin',) -class BooleanSelectPlugin(FormFieldPlugin): - """Boolean select plugin.""" - - uid = UID - name = _("Boolean") - group = _("Fields") - form = BooleanSelectForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - 'required': self.data.required, - } - - return [(self.data.name, BooleanField, field_kwargs)] - - form_element_plugin_registry.register(BooleanSelectPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/checkbox_select_multiple/README.rst b/src/fobi/contrib/plugins/form_elements/fields/checkbox_select_multiple/README.rst index aeb85413..7295bb29 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/checkbox_select_multiple/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/checkbox_select_multiple/README.rst @@ -20,16 +20,16 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. 4. By default, the submitted form value of `select_multiple` elements is label (human readable representation of the value chosen). - However, that part of the bahaviour has been made configurable. You can + However, that part of the behaviour has been made configurable. You can choose between the following options: Consider the following list of (value, label) choices (the first element in @@ -45,7 +45,7 @@ Installation - "val": `value` (example: "alpha"). - "repr" (default): `label` (example: "Alpha"). - - "mix": `value (label)` (examle: "Alpha (alpha)"). + - "mix": `value (label)` (example: "Alpha (alpha)"). Simply set the ``FOBI_FORM_ELEMENT_CHECKBOX_SELECT_MULTIPLE_SUBMIT_VALUE_AS`` assign one of @@ -58,7 +58,7 @@ consist of just a single value or value/label pair. For example: -.. code-block:: none +.. code-block:: text 1 2 diff --git a/src/fobi/contrib/plugins/form_elements/fields/checkbox_select_multiple/base.py b/src/fobi/contrib/plugins/form_elements/fields/checkbox_select_multiple/base.py new file mode 100644 index 00000000..a13cac3a --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/checkbox_select_multiple/base.py @@ -0,0 +1,91 @@ +from django.forms.fields import MultipleChoiceField +from django.forms.widgets import CheckboxSelectMultiple +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin, get_theme +from fobi.constants import SUBMIT_VALUE_AS_VAL, SUBMIT_VALUE_AS_REPR +from fobi.helpers import get_select_field_choices, safe_text + +from . import UID +from .forms import CheckboxSelectMultipleInputForm +from .settings import SUBMIT_VALUE_AS + +theme = get_theme(request=None, as_instance=True) + +__title__ = 'fobi.contrib.plugins.form_elements.fields.' \ + 'checkbox_select_multiple.fobi_form_elements' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('CheckboxSelectMultipleInputPlugin',) + + +class CheckboxSelectMultipleInputPlugin(FormFieldPlugin): + """Checkbox select multiple field plugin.""" + + uid = UID + name = _("Checkbox select multiple") + group = _("Fields") + form = CheckboxSelectMultipleInputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + choices = get_select_field_choices(self.data.choices) + + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + 'required': self.data.required, + 'choices': choices, + 'widget': CheckboxSelectMultiple( + attrs={'class': theme.form_element_html_class} + ), + } + + return [(self.data.name, MultipleChoiceField, field_kwargs)] + + def submit_plugin_form_data(self, form_entry, request, form, + form_element_entries=None, **kwargs): + """Submit plugin form data/process. + + :param fobi.models.FormEntry form_entry: Instance of + ``fobi.models.FormEntry``. + :param django.http.HttpRequest request: + :param django.forms.Form form: + """ + # In case if we should submit value as is, we don't return anything. + # In other cases, we proceed further. + if SUBMIT_VALUE_AS != SUBMIT_VALUE_AS_VAL: + # Get the object + values = form.cleaned_data.get(self.data.name, None) + + # Get choices + choices = dict(get_select_field_choices(self.data.choices)) + + # Returned value + ret_values = [] + + for value in values: + # Handle the submitted form value + + if value in choices: + label = safe_text(choices.get(value)) + + # Should be returned as repr + if SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_REPR: + value = label + # Should be returned as mix + else: + value = "{0} ({1})".format(label, value) + + ret_values.append(value) + + # Overwrite ``cleaned_data`` of the ``form`` with object + # qualifier. + form.cleaned_data[self.data.name] = ret_values + + # It's critically important to return the ``form`` with updated + # ``cleaned_data`` + return form 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 2834aa69..12ae2c15 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 @@ -1,18 +1,8 @@ -from django.forms.fields import MultipleChoiceField -from django.forms.widgets import CheckboxSelectMultiple -from django.utils.translation import ugettext_lazy as _ +from __future__ import absolute_import -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme -from fobi.constants import ( - SUBMIT_VALUE_AS_VAL, SUBMIT_VALUE_AS_REPR -) -from fobi.helpers import get_select_field_choices, safe_text +from fobi.base import form_element_plugin_registry -from . import UID -from .forms import CheckboxSelectMultipleInputForm -from .settings import SUBMIT_VALUE_AS - -theme = get_theme(request=None, as_instance=True) +from .base import CheckboxSelectMultipleInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.' \ 'checkbox_select_multiple.fobi_form_elements' @@ -22,74 +12,4 @@ __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('CheckboxSelectMultipleInputPlugin',) -class CheckboxSelectMultipleInputPlugin(FormFieldPlugin): - """Checkbox select multiple field plugin.""" - - uid = UID - name = _("Checkbox select multiple") - group = _("Fields") - form = CheckboxSelectMultipleInputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - choices = get_select_field_choices(self.data.choices) - - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - 'required': self.data.required, - 'choices': choices, - 'widget': CheckboxSelectMultiple( - attrs={'class': theme.form_element_html_class} - ), - } - - return [(self.data.name, MultipleChoiceField, field_kwargs)] - - def submit_plugin_form_data(self, form_entry, request, form): - """Submit plugin form data/process. - - :param fobi.models.FormEntry form_entry: Instance of - ``fobi.models.FormEntry``. - :param django.http.HttpRequest request: - :param django.forms.Form form: - """ - # In case if we should submit value as is, we don't return anything. - # In other cases, we proceed further. - if SUBMIT_VALUE_AS != SUBMIT_VALUE_AS_VAL: - # Get the object - values = form.cleaned_data.get(self.data.name, None) - - # Get choices - choices = dict(get_select_field_choices(self.data.choices)) - - # Returned value - ret_values = [] - - for value in values: - # Handle the submitted form value - - if value in choices: - label = safe_text(choices.get(value)) - - # Should be returned as repr - if SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_REPR: - value = label - # Should be returned as mix - else: - value = "{0} ({1})".format(label, value) - - ret_values.append(value) - - # Overwrite ``cleaned_data`` of the ``form`` with object - # qualifier. - form.cleaned_data[self.data.name] = ret_values - - # It's critically important to return the ``form`` with updated - # ``cleaned_data`` - return form - - form_element_plugin_registry.register(CheckboxSelectMultipleInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/date/README.rst b/src/fobi/contrib/plugins/form_elements/fields/date/README.rst index 92ac50f5..3874b285 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/date/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/date/README.rst @@ -19,9 +19,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/plugins/form_elements/fields/date/base.py b/src/fobi/contrib/plugins/form_elements/fields/date/base.py new file mode 100644 index 00000000..4027644d --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/date/base.py @@ -0,0 +1,72 @@ +from __future__ import absolute_import + +from django.forms.fields import DateField +from django.forms.widgets import DateInput +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin, get_theme + +from . import UID +from .forms import DateInputForm + +__title__ = 'fobi.contrib.plugins.form_elements.fields.date.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('DateInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class DateInputPlugin(FormFieldPlugin): + """Date field plugin.""" + + uid = UID + name = _("Date") + group = _("Fields") + form = DateInputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + widget_attrs = { + 'class': theme.form_element_html_class, + 'type': 'date', + } + + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + # 'input_formats': self.data.input_formats, + 'required': self.data.required, + 'widget': DateInput(attrs=widget_attrs), + } + # if self.data.input_formats: + # kwargs['input_formats'] = self.data.input_formats + + return [(self.data.name, DateField, field_kwargs)] + + def submit_plugin_form_data(self, form_entry, request, form, + form_element_entries=None, **kwargs): + """Submit plugin form data/process. + + :param fobi.models.FormEntry form_entry: Instance of + ``fobi.models.FormEntry``. + :param django.http.HttpRequest request: + :param django.forms.Form form: + """ + # In case if we should submit value as is, we don't return anything. + # In other cases, we proceed further. + + # Get the object + value = form.cleaned_data.get(self.data.name, None) + try: + value = value.strftime("%Y-%m-%d") + except Exception as err: + pass + + # Overwrite ``cleaned_data`` of the ``form`` with object qualifier. + form.cleaned_data[self.data.name] = value + + return form 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 9fe33b04..95e96704 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 @@ -1,13 +1,8 @@ from __future__ import absolute_import -from django.forms.fields import DateField -from django.forms.widgets import DateInput -from django.utils.translation import ugettext_lazy as _ +from fobi.base import form_element_plugin_registry -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme - -from . import UID -from .forms import DateInputForm +from .base import DateInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.date.fobi_form_elements' __author__ = 'Artur Barseghyan ' @@ -15,60 +10,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('DateInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class DateInputPlugin(FormFieldPlugin): - """Date field plugin.""" - - uid = UID - name = _("Date") - group = _("Fields") - form = DateInputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - widget_attrs = { - 'class': theme.form_element_html_class, - 'type': 'date', - } - - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - # 'input_formats': self.data.input_formats, - 'required': self.data.required, - 'widget': DateInput(attrs=widget_attrs), - } - # if self.data.input_formats: - # kwargs['input_formats'] = self.data.input_formats - - return [(self.data.name, DateField, field_kwargs)] - - def submit_plugin_form_data(self, form_entry, request, form): - """Submit plugin form data/process. - - :param fobi.models.FormEntry form_entry: Instance of - ``fobi.models.FormEntry``. - :param django.http.HttpRequest request: - :param django.forms.Form form: - """ - # In case if we should submit value as is, we don't return anything. - # In other cases, we proceed further. - - # Get the object - value = form.cleaned_data.get(self.data.name, None) - try: - value = value.strftime("%Y-%m-%d") - except Exception as err: - pass - - # Overwrite ``cleaned_data`` of the ``form`` with object qualifier. - form.cleaned_data[self.data.name] = value - - return form - form_element_plugin_registry.register(DateInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/date_drop_down/README.rst b/src/fobi/contrib/plugins/form_elements/fields/date_drop_down/README.rst index 03be871d..2a35d9b1 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/date_drop_down/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/date_drop_down/README.rst @@ -20,9 +20,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/plugins/form_elements/fields/date_drop_down/base.py b/src/fobi/contrib/plugins/form_elements/fields/date_drop_down/base.py new file mode 100644 index 00000000..383c2ebc --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/date_drop_down/base.py @@ -0,0 +1,54 @@ +from __future__ import absolute_import + +from django.forms.extras.widgets import SelectDateWidget +from django.forms.fields import DateField +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin, get_theme + +from . import UID +from .forms import DateDropDownInputForm + +__title__ = 'fobi.contrib.plugins.form_elements.fields.' \ + 'date_drop_down.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('DateDropDownInputPlugin',) + + +theme = get_theme(request=None, as_instance=True) + + +class DateDropDownInputPlugin(FormFieldPlugin): + """Date drop down field plugin.""" + + uid = UID + name = _("Date drop down") + group = _("Fields") + form = DateDropDownInputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + widget_attrs = { + 'class': theme.form_element_html_class, + 'type': 'date', + } + + years = None + if self.data.year_min and self.data.year_max: + years = range(self.data.year_min, self.data.year_max) + + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + # 'input_formats': self.data.input_formats, + 'required': self.data.required, + 'widget': SelectDateWidget(attrs=widget_attrs, years=years), + } + # if self.data.input_formats: + # kwargs['input_formats'] = self.data.input_formats + + return [(self.data.name, DateField, field_kwargs)] 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 a007664c..573d0c48 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 @@ -1,11 +1,8 @@ -from django.forms.extras.widgets import SelectDateWidget -from django.forms.fields import DateField -from django.utils.translation import ugettext_lazy as _ +from __future__ import absolute_import -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme +from fobi.base import form_element_plugin_registry -from . import UID -from .forms import DateDropDownInputForm +from .base import DateDropDownInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.' \ 'date_drop_down.fobi_form_elements' @@ -15,41 +12,4 @@ __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('DateDropDownInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class DateDropDownInputPlugin(FormFieldPlugin): - """Date drop down field plugin.""" - - uid = UID - name = _("Date drop down") - group = _("Fields") - form = DateDropDownInputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - widget_attrs = { - 'class': theme.form_element_html_class, - 'type': 'date', - } - - years = None - if self.data.year_min and self.data.year_max: - years = range(self.data.year_min, self.data.year_max) - - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - # 'input_formats': self.data.input_formats, - 'required': self.data.required, - 'widget': SelectDateWidget(attrs=widget_attrs, years=years), - } - # if self.data.input_formats: - # kwargs['input_formats'] = self.data.input_formats - - return [(self.data.name, DateField, field_kwargs)] - - form_element_plugin_registry.register(DateDropDownInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/datetime/README.rst b/src/fobi/contrib/plugins/form_elements/fields/datetime/README.rst index 5340263c..ed7ce351 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/datetime/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/datetime/README.rst @@ -20,9 +20,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/plugins/form_elements/fields/datetime/base.py b/src/fobi/contrib/plugins/form_elements/fields/datetime/base.py new file mode 100644 index 00000000..00c4502b --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/datetime/base.py @@ -0,0 +1,73 @@ +from __future__ import absolute_import + +from django.forms.fields import DateTimeField +from django.forms.widgets import DateTimeInput +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin, get_theme + +from . import UID +from .forms import DateTimeInputForm + +__title__ = 'fobi.contrib.plugins.form_elements.fields.' \ + 'datetime.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('DateTimeInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class DateTimeInputPlugin(FormFieldPlugin): + """DateTime field plugin.""" + + uid = UID + name = _("DateTime") + group = _("Fields") + form = DateTimeInputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + widget_attrs = { + 'class': theme.form_element_html_class, + 'type': 'datetime', + } + + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + # 'input_formats': self.data.input_formats, + 'required': self.data.required, + 'widget': DateTimeInput(attrs=widget_attrs), + } + # if self.data.input_formats: + # kwargs['input_formats'] = self.data.input_formats + + return [(self.data.name, DateTimeField, field_kwargs)] + + def submit_plugin_form_data(self, form_entry, request, form, + form_element_entries=None, **kwargs): + """Submit plugin form data/process. + + :param fobi.models.FormEntry form_entry: Instance of + ``fobi.models.FormEntry``. + :param django.http.HttpRequest request: + :param django.forms.Form form: + """ + # In case if we should submit value as is, we don't return anything. + # In other cases, we proceed further. + + # Get the object + value = form.cleaned_data.get(self.data.name, None) + try: + value = value.strftime("%Y-%m-%d %H:%M:%S") + except Exception as err: + pass + + # Overwrite ``cleaned_data`` of the ``form`` with object qualifier. + form.cleaned_data[self.data.name] = value + + return form 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 44df3f82..55726982 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 @@ -1,13 +1,8 @@ from __future__ import absolute_import -from django.forms.fields import DateTimeField -from django.forms.widgets import DateTimeInput -from django.utils.translation import ugettext_lazy as _ +from fobi.base import form_element_plugin_registry -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme - -from . import UID -from .forms import DateTimeInputForm +from .base import DateTimeInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.' \ 'datetime.fobi_form_elements' @@ -16,60 +11,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('DateTimeInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class DateTimeInputPlugin(FormFieldPlugin): - """DateTime field plugin.""" - - uid = UID - name = _("DateTime") - group = _("Fields") - form = DateTimeInputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - widget_attrs = { - 'class': theme.form_element_html_class, - 'type': 'datetime', - } - - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - # 'input_formats': self.data.input_formats, - 'required': self.data.required, - 'widget': DateTimeInput(attrs=widget_attrs), - } - # if self.data.input_formats: - # kwargs['input_formats'] = self.data.input_formats - - return [(self.data.name, DateTimeField, field_kwargs)] - - def submit_plugin_form_data(self, form_entry, request, form): - """Submit plugin form data/process. - - :param fobi.models.FormEntry form_entry: Instance of - ``fobi.models.FormEntry``. - :param django.http.HttpRequest request: - :param django.forms.Form form: - """ - # In case if we should submit value as is, we don't return anything. - # In other cases, we proceed further. - - # Get the object - value = form.cleaned_data.get(self.data.name, None) - try: - value = value.strftime("%Y-%m-%d %H:%M:%S") - except Exception as err: - pass - - # Overwrite ``cleaned_data`` of the ``form`` with object qualifier. - form.cleaned_data[self.data.name] = value - - return form - form_element_plugin_registry.register(DateTimeInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/decimal/README.rst b/src/fobi/contrib/plugins/form_elements/fields/decimal/README.rst index 6fe1f295..d3fbcfc5 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/decimal/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/decimal/README.rst @@ -21,9 +21,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/plugins/form_elements/fields/decimal/base.py b/src/fobi/contrib/plugins/form_elements/fields/decimal/base.py new file mode 100644 index 00000000..be12489b --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/decimal/base.py @@ -0,0 +1,60 @@ +from __future__ import absolute_import + +from django.forms.fields import DecimalField +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin, get_theme +from fobi.widgets import NumberInput + +from . import UID +from .forms import DecimalInputForm + +__title__ = 'fobi.contrib.plugins.form_elements.fields.' \ + 'decimal.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('DecimalInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class DecimalInputPlugin(FormFieldPlugin): + """Decimal input plugin.""" + + uid = UID + name = _("Decimal") + group = _("Fields") + form = DecimalInputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + widget_attrs = { + 'class': theme.form_element_html_class, + 'type': 'number', + 'placeholder': self.data.placeholder, + } + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + 'required': self.data.required, + } + if self.data.max_value: + field_kwargs['max_value'] = self.data.max_value + widget_attrs['max'] = self.data.max_value + if self.data.min_value: + field_kwargs['min_value'] = self.data.min_value + widget_attrs['min'] = self.data.min_value + + if self.data.max_digits: + field_kwargs['max_digits'] = self.data.max_digits + widget_attrs['max'] = self.data.max_value + if self.data.decimal_places: + field_kwargs['decimal_places'] = self.data.decimal_places + widget_attrs['min'] = self.data.min_value + + field_kwargs['widget'] = NumberInput(attrs=widget_attrs) + + return [(self.data.name, DecimalField, field_kwargs)] 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 21b45b08..6fe2c7e4 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 @@ -1,13 +1,8 @@ from __future__ import absolute_import -from django.forms.fields import DecimalField -from django.utils.translation import ugettext_lazy as _ +from fobi.base import form_element_plugin_registry -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme -from fobi.widgets import NumberInput - -from . import UID -from .forms import DecimalInputForm +from .base import DecimalInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.' \ 'decimal.fobi_form_elements' @@ -16,48 +11,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('DecimalInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class DecimalInputPlugin(FormFieldPlugin): - """Decimal input plugin.""" - - uid = UID - name = _("Decimal") - group = _("Fields") - form = DecimalInputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - widget_attrs = { - 'class': theme.form_element_html_class, - 'type': 'number', - 'placeholder': self.data.placeholder, - } - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - 'required': self.data.required, - } - if self.data.max_value: - field_kwargs['max_value'] = self.data.max_value - widget_attrs['max'] = self.data.max_value - if self.data.min_value: - field_kwargs['min_value'] = self.data.min_value - widget_attrs['min'] = self.data.min_value - - if self.data.max_digits: - field_kwargs['max_digits'] = self.data.max_digits - widget_attrs['max'] = self.data.max_value - if self.data.decimal_places: - field_kwargs['decimal_places'] = self.data.decimal_places - widget_attrs['min'] = self.data.min_value - - field_kwargs['widget'] = NumberInput(attrs=widget_attrs) - - return [(self.data.name, DecimalField, field_kwargs)] - form_element_plugin_registry.register(DecimalInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/decimal/forms.py b/src/fobi/contrib/plugins/form_elements/fields/decimal/forms.py index 9f955199..468bc902 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/decimal/forms.py +++ b/src/fobi/contrib/plugins/form_elements/fields/decimal/forms.py @@ -91,3 +91,32 @@ class DecimalInputForm(forms.Form, BaseFormFieldPluginForm): attrs={'class': theme.form_element_html_class} ) ) + + def clean(self): + """Validating the values.""" + super(DecimalInputForm, self).clean() + + max_value = self.cleaned_data['max_value'] + min_value = self.cleaned_data['min_value'] + initial = self.cleaned_data['initial'] + + if ( + max_value is not None and min_value is not None and + max_value < min_value + ): + self.add_error( + 'max_value', + _("`max_value` should be > than `min_value`.") + ) + + if max_value is not None and initial and max_value < initial: + self.add_error( + 'initial', + _("`max_value` should be >= than `initial`.") + ) + + if min_value is not None and initial and min_value > initial: + self.add_error( + 'min_value', + _("`initial` should be >= than `min_value`.") + ) diff --git a/src/fobi/contrib/plugins/form_elements/fields/email/README.rst b/src/fobi/contrib/plugins/form_elements/fields/email/README.rst index 7d5e5503..509020f9 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/email/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/email/README.rst @@ -19,9 +19,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/plugins/form_elements/fields/email/base.py b/src/fobi/contrib/plugins/form_elements/fields/email/base.py new file mode 100644 index 00000000..f8980558 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/email/base.py @@ -0,0 +1,53 @@ +from __future__ import absolute_import + +from django.forms.fields import EmailField +from django.forms.widgets import TextInput +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin, get_theme + +from . import UID +from .forms import EmailInputForm + +__title__ = 'fobi.contrib.plugins.form_elements.fields.' \ + 'email.fobi_form_elements' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('EmailInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class EmailInputPlugin(FormFieldPlugin): + """Email input plugin.""" + + uid = UID + name = _("Email") + group = _("Fields") + form = EmailInputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + widget_attrs = { + 'class': theme.form_element_html_class, + 'type': 'email', + 'placeholder': self.data.placeholder, + } + + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + 'required': self.data.required, + 'widget': TextInput(attrs=widget_attrs), + } + if self.data.max_length: + field_kwargs['max_length'] = self.data.max_length + + return [(self.data.name, EmailField, field_kwargs)] + + +# For backwards compatibility +EmailPlugin = EmailInputPlugin 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 aadeb46c..3da5bdd1 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 @@ -1,13 +1,8 @@ from __future__ import absolute_import -from django.forms.fields import EmailField -from django.forms.widgets import TextInput -from django.utils.translation import ugettext_lazy as _ +from fobi.base import form_element_plugin_registry -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme - -from . import UID -from .forms import EmailInputForm +from .base import EmailInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.' \ 'email.fobi_form_elements' @@ -16,40 +11,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('EmailInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class EmailInputPlugin(FormFieldPlugin): - """Email input plugin.""" - - uid = UID - name = _("Email") - group = _("Fields") - form = EmailInputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - widget_attrs = { - 'class': theme.form_element_html_class, - 'type': 'email', - 'placeholder': self.data.placeholder, - } - - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - 'required': self.data.required, - 'widget': TextInput(attrs=widget_attrs), - } - if self.data.max_length: - field_kwargs['max_length'] = self.data.max_length - - return [(self.data.name, EmailField, field_kwargs)] - - -# For backwards compatibility -EmailPlugin = EmailInputPlugin form_element_plugin_registry.register(EmailInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/email/forms.py b/src/fobi/contrib/plugins/form_elements/fields/email/forms.py index 31dc4fe4..c5e249e9 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/email/forms.py +++ b/src/fobi/contrib/plugins/form_elements/fields/email/forms.py @@ -2,9 +2,10 @@ from __future__ import absolute_import from django import forms from django.utils.translation import ugettext_lazy as _ +from django.core.validators import MinValueValidator from fobi.base import BaseFormFieldPluginForm, get_theme -from fobi.settings import DEFAULT_MAX_LENGTH +from fobi.settings import DEFAULT_MAX_LENGTH, DEFAULT_MIN_LENGTH from fobi.widgets import NumberInput __title__ = 'fobi.contrib.plugins.form_elements.email.forms' @@ -24,7 +25,7 @@ class EmailInputForm(forms.Form, BaseFormFieldPluginForm): ("name", ""), ("help_text", ""), ("initial", ""), - ("max_length", "255"), + ("max_length", str(DEFAULT_MAX_LENGTH)), ("required", False), ("placeholder", ""), ] @@ -60,8 +61,10 @@ class EmailInputForm(forms.Form, BaseFormFieldPluginForm): max_length = forms.IntegerField( label=_("Max length"), required=True, - widget=NumberInput(attrs={'class': theme.form_element_html_class}), - initial=DEFAULT_MAX_LENGTH + widget=NumberInput(attrs={'class': theme.form_element_html_class, + 'min': str(DEFAULT_MIN_LENGTH)}), + initial=DEFAULT_MAX_LENGTH, + validators=[MinValueValidator(DEFAULT_MIN_LENGTH)] ) required = forms.BooleanField( label=_("Required"), @@ -78,6 +81,20 @@ class EmailInputForm(forms.Form, BaseFormFieldPluginForm): ) ) + def clean(self): + super(EmailInputForm, self).clean() + + max_length = self.cleaned_data.get('max_length', DEFAULT_MAX_LENGTH) + + if self.cleaned_data['initial']: + len_initial = len(self.cleaned_data['initial']) + if len_initial > max_length: + self.add_error( + 'initial', + _("Ensure this value has at most {0} characters " + "(it has {1}).".format(max_length, len_initial)) + ) + # For backwards compatibility EmailForm = EmailInputForm diff --git a/src/fobi/contrib/plugins/form_elements/fields/file/README.rst b/src/fobi/contrib/plugins/form_elements/fields/file/README.rst index d652344a..04821ed3 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/file/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/file/README.rst @@ -20,9 +20,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/plugins/form_elements/fields/file/base.py b/src/fobi/contrib/plugins/form_elements/fields/file/base.py new file mode 100644 index 00000000..172dc3f2 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/file/base.py @@ -0,0 +1,74 @@ +from __future__ import absolute_import + +import os + +from django.forms.fields import FileField +from django.forms.widgets import ClearableFileInput +from django.utils.translation import ugettext_lazy as _ +from django.conf import settings + +from fobi.base import FormFieldPlugin +from fobi.helpers import handle_uploaded_file + +from . import UID +from .forms import FileInputForm +from .settings import FILES_UPLOAD_DIR + +__title__ = 'fobi.contrib.plugins.form_elements.fields.file.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('FileInputPlugin',) + + +class FileInputPlugin(FormFieldPlugin): + """File field plugin.""" + + uid = UID + name = _("File") + group = _("Fields") + form = FileInputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + 'required': self.data.required, + 'widget': ClearableFileInput(attrs={}), + } + if self.data.max_length: + field_kwargs['max_length'] = self.data.max_length + + return [(self.data.name, FileField, field_kwargs)] + + def submit_plugin_form_data(self, form_entry, request, form, + form_element_entries=None, **kwargs): + """Submit plugin form data/process file upload. + + Handling the posted data for file plugin when form is submitted. + This method affects the original form and that's why it returns it. + + :param fobi.models.FormEntry form_entry: Instance + of ``fobi.models.FormEntry``. + :param django.http.HttpRequest request: + :param django.forms.Form form: + """ + # Get the file path + file_path = form.cleaned_data.get(self.data.name, None) + if file_path: + # Handle the upload + saved_file = handle_uploaded_file(FILES_UPLOAD_DIR, file_path) + # Overwrite ``cleaned_data`` of the ``form`` with path to moved + # file. + file_relative_url = saved_file.replace(os.path.sep, '/') + form.cleaned_data[self.data.name] = "{0}{1}".format( + settings.MEDIA_URL, + file_relative_url + ) + + # It's critically important to return the ``form`` with updated + # ``cleaned_data`` + return form 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 354e9786..c442bbf2 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 @@ -1,16 +1,8 @@ -import os +from __future__ import absolute_import -from django.forms.fields import FileField -from django.forms.widgets import ClearableFileInput -from django.utils.translation import ugettext_lazy as _ -from django.conf import settings +from fobi.base import form_element_plugin_registry -from fobi.base import FormFieldPlugin, form_element_plugin_registry -from fobi.helpers import handle_uploaded_file - -from . import UID -from .forms import FileInputForm -from .settings import FILES_UPLOAD_DIR +from .base import FileInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.file.fobi_form_elements' __author__ = 'Artur Barseghyan ' @@ -19,56 +11,4 @@ __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('FileInputPlugin',) -class FileInputPlugin(FormFieldPlugin): - """File field plugin.""" - - uid = UID - name = _("File") - group = _("Fields") - form = FileInputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - 'required': self.data.required, - 'widget': ClearableFileInput(attrs={}), - } - if self.data.max_length: - field_kwargs['max_length'] = self.data.max_length - - return [(self.data.name, FileField, field_kwargs)] - - def submit_plugin_form_data(self, form_entry, request, form): - """Submit plugin form data/process file upload. - - Handling the posted data for file plugin when form is submitted. - This method affects the original form and that's why it returns it. - - :param fobi.models.FormEntry form_entry: Instance - of ``fobi.models.FormEntry``. - :param django.http.HttpRequest request: - :param django.forms.Form form: - """ - # Get the file path - file_path = form.cleaned_data.get(self.data.name, None) - if file_path: - # Handle the upload - saved_file = handle_uploaded_file(FILES_UPLOAD_DIR, file_path) - # Overwrite ``cleaned_data`` of the ``form`` with path to moved - # file. - file_relative_url = saved_file.replace(os.path.sep, '/') - form.cleaned_data[self.data.name] = "{0}{1}".format( - settings.MEDIA_URL, - file_relative_url - ) - - # It's critically important to return the ``form`` with updated - # ``cleaned_data`` - return form - - form_element_plugin_registry.register(FileInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/file/forms.py b/src/fobi/contrib/plugins/form_elements/fields/file/forms.py index 597b885d..d3e08f09 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/file/forms.py +++ b/src/fobi/contrib/plugins/form_elements/fields/file/forms.py @@ -1,8 +1,9 @@ from django import forms from django.utils.translation import ugettext_lazy as _ +from django.core.validators import MinValueValidator from fobi.base import BaseFormFieldPluginForm, get_theme -from fobi.settings import DEFAULT_MAX_LENGTH +from fobi.settings import DEFAULT_MAX_LENGTH, DEFAULT_MIN_LENGTH from fobi.widgets import NumberInput __title__ = 'fobi.contrib.plugins.form_elements.fields.file.forms' @@ -22,7 +23,7 @@ class FileInputForm(forms.Form, BaseFormFieldPluginForm): ("name", ""), ("help_text", ""), ("initial", ""), - ("max_length", "255"), + ("max_length", str(DEFAULT_MAX_LENGTH)), ("required", False) ] @@ -57,8 +58,10 @@ class FileInputForm(forms.Form, BaseFormFieldPluginForm): max_length = forms.IntegerField( label=_("Max length"), required=True, - widget=NumberInput(attrs={'class': theme.form_element_html_class}), - initial=DEFAULT_MAX_LENGTH + widget=NumberInput(attrs={'class': theme.form_element_html_class, + 'min': str(DEFAULT_MIN_LENGTH)}), + initial=DEFAULT_MAX_LENGTH, + validators=[MinValueValidator(DEFAULT_MIN_LENGTH)] ) required = forms.BooleanField( label=_("Required"), @@ -67,3 +70,17 @@ class FileInputForm(forms.Form, BaseFormFieldPluginForm): attrs={'class': theme.form_element_checkbox_html_class} ) ) + + def clean(self): + super(FileInputForm, self).clean() + + max_length = self.cleaned_data.get('max_length', DEFAULT_MAX_LENGTH) + + if self.cleaned_data['initial']: + len_initial = len(self.cleaned_data['initial']) + if len_initial > max_length: + self.add_error( + 'initial', + _("Ensure this value has at most {0} characters " + "(it has {1}).".format(max_length, len_initial)) + ) diff --git a/src/fobi/contrib/plugins/form_elements/fields/float/README.rst b/src/fobi/contrib/plugins/form_elements/fields/float/README.rst index a54e8109..67458df2 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/float/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/float/README.rst @@ -21,9 +21,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/plugins/form_elements/fields/float/base.py b/src/fobi/contrib/plugins/form_elements/fields/float/base.py new file mode 100644 index 00000000..96b6d71b --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/float/base.py @@ -0,0 +1,52 @@ +from __future__ import absolute_import + +from django.forms.fields import FloatField +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin, get_theme +from fobi.widgets import NumberInput + +from . import UID +from .forms import FloatInputForm + +__title__ = 'fobi.contrib.plugins.form_elements.fields.float.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('FloatInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class FloatInputPlugin(FormFieldPlugin): + """Float input plugin.""" + + uid = UID + name = _("Float") + group = _("Fields") + form = FloatInputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + widget_attrs = { + 'class': theme.form_element_html_class, + 'type': 'number', + 'placeholder': self.data.placeholder, + } + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + 'required': self.data.required, + } + if self.data.max_value: + field_kwargs['max_value'] = self.data.max_value + widget_attrs['max'] = self.data.max_value + if self.data.min_value: + field_kwargs['min_value'] = self.data.min_value + widget_attrs['min'] = self.data.min_value + + field_kwargs['widget'] = NumberInput(attrs=widget_attrs) + + return [(self.data.name, FloatField, field_kwargs)] 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 096c1ad6..ed62aba5 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 @@ -1,11 +1,8 @@ -from django.forms.fields import FloatField -from django.utils.translation import ugettext_lazy as _ +from __future__ import absolute_import -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme -from fobi.widgets import NumberInput +from fobi.base import form_element_plugin_registry -from . import UID -from .forms import FloatInputForm +from .base import FloatInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.' \ 'float.fobi_form_elements' @@ -14,41 +11,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('FloatInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class FloatInputPlugin(FormFieldPlugin): - """Float input plugin.""" - - uid = UID - name = _("Float") - group = _("Fields") - form = FloatInputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - widget_attrs = { - 'class': theme.form_element_html_class, - 'type': 'number', - 'placeholder': self.data.placeholder, - } - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - 'required': self.data.required, - } - if self.data.max_value: - field_kwargs['max_value'] = self.data.max_value - widget_attrs['max'] = self.data.max_value - if self.data.min_value: - field_kwargs['min_value'] = self.data.min_value - widget_attrs['min'] = self.data.min_value - - field_kwargs['widget'] = NumberInput(attrs=widget_attrs) - - return [(self.data.name, FloatField, field_kwargs)] - form_element_plugin_registry.register(FloatInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/float/forms.py b/src/fobi/contrib/plugins/form_elements/fields/float/forms.py index ec469d87..a52c3eb7 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/float/forms.py +++ b/src/fobi/contrib/plugins/form_elements/fields/float/forms.py @@ -77,3 +77,32 @@ class FloatInputForm(forms.Form, BaseFormFieldPluginForm): attrs={'class': theme.form_element_html_class} ) ) + + def clean(self): + """Validating the values.""" + super(FloatInputForm, self).clean() + + max_value = self.cleaned_data['max_value'] + min_value = self.cleaned_data['min_value'] + initial = self.cleaned_data['initial'] + + if ( + max_value is not None and min_value is not None and + max_value < min_value + ): + self.add_error( + 'max_value', + _("`max_value` should be > than `min_value`.") + ) + + if max_value is not None and initial and max_value < initial: + self.add_error( + 'initial', + _("`max_value` should be >= than `initial`.") + ) + + if min_value is not None and initial and min_value > initial: + self.add_error( + 'min_value', + _("`initial` should be >= than `min_value`.") + ) diff --git a/src/fobi/contrib/plugins/form_elements/fields/hidden/README.rst b/src/fobi/contrib/plugins/form_elements/fields/hidden/README.rst index 24660550..35d90259 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/hidden/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/hidden/README.rst @@ -19,9 +19,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/plugins/form_elements/fields/hidden/base.py b/src/fobi/contrib/plugins/form_elements/fields/hidden/base.py new file mode 100644 index 00000000..c79837c9 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/hidden/base.py @@ -0,0 +1,45 @@ +from __future__ import absolute_import + +from django.forms.fields import CharField +from django.forms.widgets import HiddenInput +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin, get_theme + +from . import UID +from .forms import HiddenInputForm + +__title__ = 'fobi.contrib.plugins.form_elements.fields.hidden.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('HiddenInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class HiddenInputPlugin(FormFieldPlugin): + """Hidden field plugin.""" + + uid = UID + name = _("Hidden") + group = _("Fields") + form = HiddenInputForm + is_hidden = True + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + field_kwargs = { + 'label': self.data.label, + 'initial': self.data.initial, + 'required': self.data.required, + 'widget': HiddenInput( + attrs={'class': theme.form_element_html_class} + ), + } + if self.data.max_length: + field_kwargs['max_length'] = self.data.max_length + + return [(self.data.name, CharField, field_kwargs)] + # return [(self.data.name, (CharField, TextInput), kwargs)] 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 98f5b33e..23cf99e1 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 @@ -1,11 +1,8 @@ -from django.forms.fields import CharField -from django.forms.widgets import HiddenInput # , TextInput -from django.utils.translation import ugettext_lazy as _ +from __future__ import absolute_import -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme +from fobi.base import form_element_plugin_registry -from . import UID -from .forms import HiddenInputForm +from .base import HiddenInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.' \ 'hidden.fobi_form_elements' @@ -14,34 +11,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('HiddenInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class HiddenInputPlugin(FormFieldPlugin): - """Hidden field plugin.""" - - uid = UID - name = _("Hidden") - group = _("Fields") - form = HiddenInputForm - is_hidden = True - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - field_kwargs = { - 'label': self.data.label, - 'initial': self.data.initial, - 'required': self.data.required, - 'widget': HiddenInput( - attrs={'class': theme.form_element_html_class} - ), - } - if self.data.max_length: - field_kwargs['max_length'] = self.data.max_length - - return [(self.data.name, CharField, field_kwargs)] - # return [(self.data.name, (CharField, TextInput), kwargs)] - form_element_plugin_registry.register(HiddenInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/hidden/forms.py b/src/fobi/contrib/plugins/form_elements/fields/hidden/forms.py index 713fa410..c574d425 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/hidden/forms.py +++ b/src/fobi/contrib/plugins/form_elements/fields/hidden/forms.py @@ -1,8 +1,9 @@ from django import forms from django.utils.translation import ugettext_lazy as _ +from django.core.validators import MinValueValidator from fobi.base import BaseFormFieldPluginForm, get_theme -from fobi.settings import DEFAULT_MAX_LENGTH +from fobi.settings import DEFAULT_MAX_LENGTH, DEFAULT_MIN_LENGTH from fobi.widgets import NumberInput __title__ = 'fobi.contrib.plugins.form_elements.fields.hidden.forms' @@ -21,7 +22,7 @@ class HiddenInputForm(forms.Form, BaseFormFieldPluginForm): ("label", ""), ("name", ""), ("initial", ""), - ("max_length", "255"), + ("max_length", str(DEFAULT_MAX_LENGTH)), ("required", False) ] @@ -49,8 +50,10 @@ class HiddenInputForm(forms.Form, BaseFormFieldPluginForm): max_length = forms.IntegerField( label=_("Max length"), required=True, - widget=NumberInput(attrs={'class': theme.form_element_html_class}), - initial=DEFAULT_MAX_LENGTH + widget=NumberInput(attrs={'class': theme.form_element_html_class, + 'min': str(DEFAULT_MIN_LENGTH)}), + initial=DEFAULT_MAX_LENGTH, + validators=[MinValueValidator(DEFAULT_MIN_LENGTH)] ) required = forms.BooleanField( label=_("Required"), @@ -59,3 +62,17 @@ class HiddenInputForm(forms.Form, BaseFormFieldPluginForm): attrs={'class': theme.form_element_checkbox_html_class} ) ) + + def clean(self): + super(HiddenInputForm, self).clean() + + max_length = self.cleaned_data.get('max_length', DEFAULT_MAX_LENGTH) + + if self.cleaned_data['initial']: + len_initial = len(self.cleaned_data['initial']) + if len_initial > max_length: + self.add_error( + 'initial', + _("Ensure this value has at most {0} characters " + "(it has {1}).".format(max_length, len_initial)) + ) diff --git a/src/fobi/contrib/plugins/form_elements/fields/input/README.rst b/src/fobi/contrib/plugins/form_elements/fields/input/README.rst index 27396ed5..51d26341 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/input/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/input/README.rst @@ -38,9 +38,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/plugins/form_elements/fields/input/base.py b/src/fobi/contrib/plugins/form_elements/fields/input/base.py new file mode 100644 index 00000000..ff6c2409 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/input/base.py @@ -0,0 +1,88 @@ +from __future__ import absolute_import + +from django.forms.fields import Field +from django.forms.widgets import TextInput +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin, get_theme + +from . import UID +from .forms import InputForm + +__title__ = 'fobi.contrib.plugins.form_elements.fields.input.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('InputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class InputPlugin(FormFieldPlugin): + """Input field plugin.""" + + uid = UID + name = _("Input") + group = _("Fields") + form = InputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + widget_attrs = { + 'class': theme.form_element_html_class, + 'placeholder': self.data.placeholder, + 'type': self.data.type_value, + } + + if self.data.autocomplete_value: + widget_attrs.update({'autocomplete': 'on'}) + + if self.data.autofocus_value: + widget_attrs.update({'autofocus': 'autofocus'}) + + if self.data.disabled_value: + widget_attrs.update({'disabled': 'disabled'}) + + # if self.data.formnovalidate_value: + # widget_attrs.update({'formnovalidate': 'formnovalidate'}) + + if self.data.list_value: + widget_attrs.update({'list': self.data.list_value}) + + if self.data.max_value: + widget_attrs.update({'max': self.data.max_value}) + + if self.data.min_value: + widget_attrs.update({'min': self.data.min_value}) + + if self.data.multiple_value: + widget_attrs.update({'multiple': 'multiple'}) + + if self.data.pattern_value: + widget_attrs.update({'pattern': self.data.pattern_value}) + + if self.data.readonly_value: + widget_attrs.update({'readonly': 'readonly'}) + + if self.data.step_value: + widget_attrs.update({'step': self.data.step_value}) + + if self.data.type_value and self.data.type_value in ('submit', + 'button', + 'reset',): + widget_attrs.update({'value': self.data.label}) + + field_kwargs = { + 'label': self.data.label + if self.data.type_value not in ('submit', 'button', 'reset',) + else '', + 'help_text': self.data.help_text, + 'initial': self.data.initial, + 'required': self.data.required, + 'widget': TextInput(attrs=widget_attrs), + } + # if self.data.max_length: + # kwargs['max_length'] = self.data.max_length + + return [(self.data.name, Field, field_kwargs)] 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 7825b6bb..191aca0e 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 @@ -1,11 +1,8 @@ -from django.forms.fields import Field -from django.forms.widgets import TextInput -from django.utils.translation import ugettext_lazy as _ +from __future__ import absolute_import -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme +from fobi.base import form_element_plugin_registry -from . import UID -from .forms import InputForm +from .base import InputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.' \ 'input.fobi_form_elements' @@ -14,77 +11,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('InputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class InputPlugin(FormFieldPlugin): - """Input field plugin.""" - - uid = UID - name = _("Input") - group = _("Fields") - form = InputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - widget_attrs = { - 'class': theme.form_element_html_class, - 'placeholder': self.data.placeholder, - 'type': self.data.type_value, - } - - if self.data.autocomplete_value: - widget_attrs.update({'autocomplete': 'on'}) - - if self.data.autofocus_value: - widget_attrs.update({'autofocus': 'autofocus'}) - - if self.data.disabled_value: - widget_attrs.update({'disabled': 'disabled'}) - - # if self.data.formnovalidate_value: - # widget_attrs.update({'formnovalidate': 'formnovalidate'}) - - if self.data.list_value: - widget_attrs.update({'list': self.data.list_value}) - - if self.data.max_value: - widget_attrs.update({'max': self.data.max_value}) - - if self.data.min_value: - widget_attrs.update({'min': self.data.min_value}) - - if self.data.multiple_value: - widget_attrs.update({'multiple': 'multiple'}) - - if self.data.pattern_value: - widget_attrs.update({'pattern': self.data.pattern_value}) - - if self.data.readonly_value: - widget_attrs.update({'readonly': 'readonly'}) - - if self.data.step_value: - widget_attrs.update({'step': self.data.step_value}) - - if self.data.type_value and self.data.type_value in ('submit', - 'button', - 'reset',): - widget_attrs.update({'value': self.data.label}) - - field_kwargs = { - 'label': self.data.label - if self.data.type_value not in ('submit', 'button', 'reset',) - else '', - 'help_text': self.data.help_text, - 'initial': self.data.initial, - 'required': self.data.required, - 'widget': TextInput(attrs=widget_attrs), - } - # if self.data.max_length: - # kwargs['max_length'] = self.data.max_length - - return [(self.data.name, Field, field_kwargs)] - form_element_plugin_registry.register(InputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/input/forms.py b/src/fobi/contrib/plugins/form_elements/fields/input/forms.py index 8ba28a85..5134b324 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/input/forms.py +++ b/src/fobi/contrib/plugins/form_elements/fields/input/forms.py @@ -1,8 +1,9 @@ from django import forms from django.utils.translation import ugettext_lazy as _ +from django.core.validators import MinValueValidator from fobi.base import BaseFormFieldPluginForm, get_theme -from fobi.settings import DEFAULT_MAX_LENGTH +from fobi.settings import DEFAULT_MAX_LENGTH, DEFAULT_MIN_LENGTH from fobi.widgets import NumberInput from .constants import FORM_FIELD_TYPE_CHOICES @@ -24,7 +25,7 @@ class InputForm(forms.Form, BaseFormFieldPluginForm): ("name", ""), ("help_text", ""), ("initial", ""), - ("max_length", "255"), + ("max_length", str(DEFAULT_MAX_LENGTH)), ("required", False), ("placeholder", ""), @@ -79,8 +80,10 @@ class InputForm(forms.Form, BaseFormFieldPluginForm): max_length = forms.IntegerField( label=_("Max length"), required=True, - widget=NumberInput(attrs={'class': theme.form_element_html_class}), - initial=DEFAULT_MAX_LENGTH + widget=NumberInput(attrs={'class': theme.form_element_html_class, + 'min': str(DEFAULT_MIN_LENGTH)}), + initial=DEFAULT_MAX_LENGTH, + validators=[MinValueValidator(DEFAULT_MIN_LENGTH)] ) required = forms.BooleanField( label=_("Required"), @@ -181,3 +184,17 @@ class InputForm(forms.Form, BaseFormFieldPluginForm): attrs={'class': theme.form_element_html_class} ) ) + + def clean(self): + super(InputForm, self).clean() + + max_length = self.cleaned_data.get('max_length', DEFAULT_MAX_LENGTH) + + if self.cleaned_data['initial']: + len_initial = len(self.cleaned_data['initial']) + if len_initial > max_length: + self.add_error( + 'initial', + _("Ensure this value has at most {0} characters " + "(it has {1}).".format(max_length, len_initial)) + ) diff --git a/src/fobi/contrib/plugins/form_elements/fields/integer/README.rst b/src/fobi/contrib/plugins/form_elements/fields/integer/README.rst index 268aeac2..834e08a8 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/integer/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/integer/README.rst @@ -21,9 +21,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/plugins/form_elements/fields/integer/base.py b/src/fobi/contrib/plugins/form_elements/fields/integer/base.py new file mode 100644 index 00000000..5f9430d5 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/integer/base.py @@ -0,0 +1,52 @@ +from __future__ import absolute_import + +from django.forms.fields import IntegerField +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin, get_theme +from fobi.widgets import NumberInput + +from . import UID +from .forms import IntegerInputForm + +__title__ = 'fobi.contrib.plugins.form_elements.fields.integer.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('IntegerInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class IntegerInputPlugin(FormFieldPlugin): + """Integer input plugin.""" + + uid = UID + name = _("Integer") + group = _("Fields") + form = IntegerInputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + widget_attrs = { + 'class': theme.form_element_html_class, + 'type': 'number', + 'placeholder': self.data.placeholder, + } + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + 'required': self.data.required, + } + if self.data.max_value: + field_kwargs['max_value'] = self.data.max_value + widget_attrs['max'] = self.data.max_value + if self.data.min_value: + field_kwargs['min_value'] = self.data.min_value + widget_attrs['min'] = self.data.min_value + + field_kwargs['widget'] = NumberInput(attrs=widget_attrs) + + return [(self.data.name, IntegerField, field_kwargs)] 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 97eb49a6..f065bed5 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 @@ -1,11 +1,8 @@ -from django.forms.fields import IntegerField # , DecimalField, FloatField -from django.utils.translation import ugettext_lazy as _ +from __future__ import absolute_import -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme -from fobi.widgets import NumberInput +from fobi.base import form_element_plugin_registry -from . import UID -from .forms import IntegerInputForm +from .base import IntegerInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.' \ 'integer.fobi_form_elements' @@ -14,41 +11,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('IntegerInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class IntegerInputPlugin(FormFieldPlugin): - """Integer input plugin.""" - - uid = UID - name = _("Integer") - group = _("Fields") - form = IntegerInputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - widget_attrs = { - 'class': theme.form_element_html_class, - 'type': 'number', - 'placeholder': self.data.placeholder, - } - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - 'required': self.data.required, - } - if self.data.max_value: - field_kwargs['max_value'] = self.data.max_value - widget_attrs['max'] = self.data.max_value - if self.data.min_value: - field_kwargs['min_value'] = self.data.min_value - widget_attrs['min'] = self.data.min_value - - field_kwargs['widget'] = NumberInput(attrs=widget_attrs) - - return [(self.data.name, IntegerField, field_kwargs)] - form_element_plugin_registry.register(IntegerInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/integer/forms.py b/src/fobi/contrib/plugins/form_elements/fields/integer/forms.py index 9d4ee8ed..47e9bb21 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/integer/forms.py +++ b/src/fobi/contrib/plugins/form_elements/fields/integer/forms.py @@ -77,3 +77,32 @@ class IntegerInputForm(forms.Form, BaseFormFieldPluginForm): attrs={'class': theme.form_element_html_class} ) ) + + def clean(self): + """Validating the values.""" + super(IntegerInputForm, self).clean() + + max_value = self.cleaned_data['max_value'] + min_value = self.cleaned_data['min_value'] + initial = self.cleaned_data['initial'] + + if ( + max_value is not None and min_value is not None and + max_value < min_value + ): + self.add_error( + 'max_value', + _("`max_value` should be > than `min_value`.") + ) + + if max_value is not None and initial and max_value < initial: + self.add_error( + 'initial', + _("`max_value` should be >= than `initial`.") + ) + + if min_value is not None and initial and min_value > initial: + self.add_error( + 'min_value', + _("`initial` should be >= than `min_value`.") + ) diff --git a/src/fobi/contrib/plugins/form_elements/fields/ip_address/README.rst b/src/fobi/contrib/plugins/form_elements/fields/ip_address/README.rst index 1e897058..f4751e6f 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/ip_address/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/ip_address/README.rst @@ -20,9 +20,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/plugins/form_elements/fields/ip_address/base.py b/src/fobi/contrib/plugins/form_elements/fields/ip_address/base.py new file mode 100644 index 00000000..44e612ca --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/ip_address/base.py @@ -0,0 +1,47 @@ +from __future__ import absolute_import + +from django.forms.fields import GenericIPAddressField +from django.forms.widgets import TextInput +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin, get_theme + +from . import UID +from .forms import IPAddressInputForm + +__title__ = 'fobi.contrib.plugins.form_elements.fields.ip_address.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('IPAddressInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class IPAddressInputPlugin(FormFieldPlugin): + """IP address field plugin.""" + + uid = UID + name = _("IP address") + group = _("Fields") + form = IPAddressInputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + widget_attrs = { + 'class': theme.form_element_html_class, + 'placeholder': self.data.placeholder, + } + + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + 'required': self.data.required, + 'widget': TextInput(attrs=widget_attrs), + } + if self.data.max_length: + field_kwargs['max_length'] = self.data.max_length + + return [(self.data.name, GenericIPAddressField, field_kwargs)] 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 45d50c0b..3abc01ce 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 @@ -1,13 +1,8 @@ +from __future__ import absolute_import +from fobi.base import form_element_plugin_registry -from django.forms.fields import GenericIPAddressField -from django.forms.widgets import TextInput -from django.utils.translation import ugettext_lazy as _ - -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme - -from . import UID -from .forms import IPAddressInputForm +from .base import IPAddressInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.' \ 'ip_address.fobi_form_elements' @@ -16,36 +11,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('IPAddressInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class IPAddressInputPlugin(FormFieldPlugin): - """IP address field plugin.""" - - uid = UID - name = _("IP address") - group = _("Fields") - form = IPAddressInputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - widget_attrs = { - 'class': theme.form_element_html_class, - 'placeholder': self.data.placeholder, - } - - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - 'required': self.data.required, - 'widget': TextInput(attrs=widget_attrs), - } - if self.data.max_length: - field_kwargs['max_length'] = self.data.max_length - - return [(self.data.name, GenericIPAddressField, field_kwargs)] - form_element_plugin_registry.register(IPAddressInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/ip_address/forms.py b/src/fobi/contrib/plugins/form_elements/fields/ip_address/forms.py index 27031d22..4d3a3909 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/ip_address/forms.py +++ b/src/fobi/contrib/plugins/form_elements/fields/ip_address/forms.py @@ -1,9 +1,9 @@ from django import forms -from django.core.validators import ip_address_validator_map +from django.core.validators import ip_address_validator_map, MinValueValidator from django.utils.translation import ugettext_lazy as _ from fobi.base import BaseFormFieldPluginForm, get_theme -from fobi.settings import DEFAULT_MAX_LENGTH +from fobi.settings import DEFAULT_MAX_LENGTH, DEFAULT_MIN_LENGTH from fobi.widgets import NumberInput __title__ = 'fobi.contrib.plugins.form_elements.fields.ip_address.forms' @@ -25,7 +25,7 @@ class IPAddressInputForm(forms.Form, BaseFormFieldPluginForm): ("initial", ""), ("protocol", ""), ("unpack_ipv4", False), - ("max_length", "255"), + ("max_length", str(DEFAULT_MAX_LENGTH)), ("required", False), ("placeholder", ""), ] @@ -76,8 +76,10 @@ class IPAddressInputForm(forms.Form, BaseFormFieldPluginForm): max_length = forms.IntegerField( label=_("Max length"), required=True, - widget=NumberInput(attrs={'class': theme.form_element_html_class}), - initial=DEFAULT_MAX_LENGTH + widget=NumberInput(attrs={'class': theme.form_element_html_class, + 'min': str(DEFAULT_MIN_LENGTH)}), + initial=DEFAULT_MAX_LENGTH, + validators=[MinValueValidator(DEFAULT_MIN_LENGTH)] ) required = forms.BooleanField( label=_("Required"), @@ -93,3 +95,17 @@ class IPAddressInputForm(forms.Form, BaseFormFieldPluginForm): attrs={'class': theme.form_element_html_class} ) ) + + def clean(self): + super(IPAddressInputForm, self).clean() + + max_length = self.cleaned_data.get('max_length', DEFAULT_MAX_LENGTH) + + if self.cleaned_data['initial']: + len_initial = len(self.cleaned_data['initial']) + if len_initial > max_length: + self.add_error( + 'initial', + _("Ensure this value has at most {0} characters " + "(it has {1}).".format(max_length, len_initial)) + ) diff --git a/src/fobi/contrib/plugins/form_elements/fields/null_boolean/README.rst b/src/fobi/contrib/plugins/form_elements/fields/null_boolean/README.rst index 2a40af8b..2a6d548b 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/null_boolean/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/null_boolean/README.rst @@ -20,9 +20,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/plugins/form_elements/fields/null_boolean/base.py b/src/fobi/contrib/plugins/form_elements/fields/null_boolean/base.py new file mode 100644 index 00000000..aa74e1a5 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/null_boolean/base.py @@ -0,0 +1,42 @@ +from __future__ import absolute_import + +from django.forms.fields import NullBooleanField +from django.forms.widgets import NullBooleanSelect +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin, get_theme + +from . import UID +from .forms import NullBooleanSelectForm + +__title__ = 'fobi.contrib.plugins.form_elements.fields.null_boolean.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('NullBooleanSelectPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class NullBooleanSelectPlugin(FormFieldPlugin): + """Null boolean select plugin.""" + + uid = UID + name = _("Null boolean") + group = _("Fields") + form = NullBooleanSelectForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + 'required': self.data.required, + 'widget': NullBooleanSelect( + attrs={'class': theme.form_element_html_class} + ), + } + + return [(self.data.name, NullBooleanField, field_kwargs)] 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 fec42fdd..0ffa2428 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 @@ -1,11 +1,8 @@ -from django.forms.fields import NullBooleanField -from django.forms.widgets import NullBooleanSelect -from django.utils.translation import ugettext_lazy as _ +from __future__ import absolute_import -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme +from fobi.base import form_element_plugin_registry -from . import UID -from .forms import NullBooleanSelectForm +from .base import NullBooleanSelectPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.' \ 'null_boolean.fobi_form_elements' @@ -14,31 +11,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('NullBooleanSelectPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class NullBooleanSelectPlugin(FormFieldPlugin): - """Null boolean select plugin.""" - - uid = UID - name = _("Null boolean") - group = _("Fields") - form = NullBooleanSelectForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - 'required': self.data.required, - 'widget': NullBooleanSelect( - attrs={'class': theme.form_element_html_class} - ), - } - - return [(self.data.name, NullBooleanField, field_kwargs)] - form_element_plugin_registry.register(NullBooleanSelectPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/password/README.rst b/src/fobi/contrib/plugins/form_elements/fields/password/README.rst index c59a0bac..bfe8d3ef 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/password/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/password/README.rst @@ -19,9 +19,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/plugins/form_elements/fields/password/base.py b/src/fobi/contrib/plugins/form_elements/fields/password/base.py new file mode 100644 index 00000000..0e9ffdc8 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/password/base.py @@ -0,0 +1,47 @@ +from __future__ import absolute_import + +from django.forms.fields import CharField +from django.forms.widgets import PasswordInput +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin, get_theme + +from . import UID +from .forms import PasswordInputForm + +__title__ = 'fobi.contrib.plugins.form_elements.fields.password.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('PasswordInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class PasswordInputPlugin(FormFieldPlugin): + """Password field plugin.""" + + uid = UID + name = _("Password") + group = _("Fields") + form = PasswordInputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + widget_attrs = { + 'class': theme.form_element_html_class, + 'placeholder': self.data.placeholder, + } + + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + 'required': self.data.required, + 'widget': PasswordInput(attrs=widget_attrs), + } + if self.data.max_length: + field_kwargs['max_length'] = self.data.max_length + + return [(self.data.name, CharField, field_kwargs)] 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 84cebb6a..39727b93 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 @@ -1,11 +1,8 @@ -from django.forms.fields import CharField -from django.forms.widgets import PasswordInput -from django.utils.translation import ugettext_lazy as _ +from __future__ import absolute_import -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme +from fobi.base import form_element_plugin_registry -from . import UID -from .forms import PasswordInputForm +from .base import PasswordInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.' \ 'password.fobi_form_elements' @@ -14,36 +11,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('PasswordInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class PasswordInputPlugin(FormFieldPlugin): - """Password field plugin.""" - - uid = UID - name = _("Password") - group = _("Fields") - form = PasswordInputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - widget_attrs = { - 'class': theme.form_element_html_class, - 'placeholder': self.data.placeholder, - } - - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - 'required': self.data.required, - 'widget': PasswordInput(attrs=widget_attrs), - } - if self.data.max_length: - field_kwargs['max_length'] = self.data.max_length - - return [(self.data.name, CharField, field_kwargs)] - form_element_plugin_registry.register(PasswordInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/password/forms.py b/src/fobi/contrib/plugins/form_elements/fields/password/forms.py index f105be5b..b8272312 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/password/forms.py +++ b/src/fobi/contrib/plugins/form_elements/fields/password/forms.py @@ -1,8 +1,9 @@ from django import forms from django.utils.translation import ugettext_lazy as _ +from django.core.validators import MinValueValidator from fobi.base import BaseFormFieldPluginForm, get_theme -from fobi.settings import DEFAULT_MAX_LENGTH +from fobi.settings import DEFAULT_MAX_LENGTH, DEFAULT_MIN_LENGTH from fobi.widgets import NumberInput __title__ = 'fobi.contrib.plugins.form_elements.fields.password.forms' @@ -22,7 +23,7 @@ class PasswordInputForm(forms.Form, BaseFormFieldPluginForm): ("name", ""), ("help_text", ""), ("initial", ""), - ("max_length", "255"), + ("max_length", str(DEFAULT_MAX_LENGTH)), ("required", False), ("placeholder", ""), ] @@ -58,8 +59,10 @@ class PasswordInputForm(forms.Form, BaseFormFieldPluginForm): max_length = forms.IntegerField( label=_("Max length"), required=True, - widget=NumberInput(attrs={'class': theme.form_element_html_class}), - initial=DEFAULT_MAX_LENGTH + widget=NumberInput(attrs={'class': theme.form_element_html_class, + 'min': str(DEFAULT_MIN_LENGTH)}), + initial=DEFAULT_MAX_LENGTH, + validators=[MinValueValidator(DEFAULT_MIN_LENGTH)] ) required = forms.BooleanField( label=_("Required"), @@ -75,3 +78,17 @@ class PasswordInputForm(forms.Form, BaseFormFieldPluginForm): attrs={'class': theme.form_element_html_class} ) ) + + def clean(self): + super(PasswordInputForm, self).clean() + + max_length = self.cleaned_data.get('max_length', DEFAULT_MAX_LENGTH) + + if self.cleaned_data['initial']: + len_initial = len(self.cleaned_data['initial']) + if len_initial > max_length: + self.add_error( + 'initial', + _("Ensure this value has at most {0} characters " + "(it has {1}).".format(max_length, len_initial)) + ) diff --git a/src/fobi/contrib/plugins/form_elements/fields/radio/README.rst b/src/fobi/contrib/plugins/form_elements/fields/radio/README.rst index f5730ac0..38326b8c 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/radio/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/radio/README.rst @@ -19,9 +19,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. @@ -42,9 +42,11 @@ Installation ('gamma', 'Gamma'), ] - - "val": `value` (example: "alpha"). - - "repr" (default): `label` (example: "Alpha"). - - "mix": `value (label)` (example: "Alpha (alpha)"). + .. code-block:: text + + - "val": `value` (example: "alpha"). + - "repr" (default): `label` (example: "Alpha"). + - "mix": `value (label)` (example: "Alpha (alpha)"). Simply set the ``FOBI_FORM_ELEMENT_RADIO_SUBMIT_VALUE_AS`` assign one of the following @@ -57,7 +59,7 @@ consist of just a single value or value/label pair. For example: -.. code-block:: none +.. code-block:: text 1 2 diff --git a/src/fobi/contrib/plugins/form_elements/fields/radio/base.py b/src/fobi/contrib/plugins/form_elements/fields/radio/base.py new file mode 100644 index 00000000..4579fce2 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/radio/base.py @@ -0,0 +1,88 @@ +from __future__ import absolute_import + +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, get_theme +from fobi.constants import ( + SUBMIT_VALUE_AS_VAL, + SUBMIT_VALUE_AS_REPR +) +from fobi.helpers import get_select_field_choices, safe_text + +from . import UID +from .forms import RadioInputForm +from .settings import SUBMIT_VALUE_AS + +__title__ = 'fobi.contrib.plugins.form_elements.fields.radio.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('RadioInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class RadioInputPlugin(FormFieldPlugin): + """Radio field plugin.""" + + uid = UID + name = _("Radio") + group = _("Fields") + form = RadioInputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + choices = get_select_field_choices(self.data.choices) + + widget_attrs = {'class': theme.form_radio_element_html_class} + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + 'required': self.data.required, + 'choices': choices, + 'widget': RadioSelect(attrs=widget_attrs), + } + + return [(self.data.name, ChoiceField, field_kwargs)] + + def submit_plugin_form_data(self, form_entry, request, form, + form_element_entries=None, **kwargs): + """Submit plugin form data/process. + + :param fobi.models.FormEntry form_entry: Instance of + ``fobi.models.FormEntry``. + :param django.http.HttpRequest request: + :param django.forms.Form form: + """ + # In case if we should submit value as is, we don't return anything. + # In other cases, we proceed further. + if SUBMIT_VALUE_AS != SUBMIT_VALUE_AS_VAL: + # Get the object + value = form.cleaned_data.get(self.data.name, None) + + # Get choices + choices = dict(get_select_field_choices(self.data.choices)) + + if value in choices: + # Handle the submitted form value + + label = safe_text(choices.get(value)) + + # Should be returned as repr + if SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_REPR: + value = label + # Should be returned as mix + else: + value = "{0} ({1})".format(label, value) + + # Overwrite ``cleaned_data`` of the ``form`` with object + # qualifier. + form.cleaned_data[self.data.name] = value + + # It's critically important to return the ``form`` with updated + # ``cleaned_data`` + return form 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 96d57032..854dfd2e 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 @@ -1,16 +1,8 @@ -from django.forms.fields import ChoiceField -from django.forms.widgets import RadioSelect -from django.utils.translation import ugettext_lazy as _ +from __future__ import absolute_import -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme -from fobi.constants import ( - SUBMIT_VALUE_AS_VAL, SUBMIT_VALUE_AS_REPR -) -from fobi.helpers import get_select_field_choices, safe_text +from fobi.base import form_element_plugin_registry -from . import UID -from .forms import RadioInputForm -from .settings import SUBMIT_VALUE_AS +from .base import RadioInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.' \ 'radio.fobi_form_elements' @@ -19,70 +11,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('RadioInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class RadioInputPlugin(FormFieldPlugin): - """Radio field plugin.""" - - uid = UID - name = _("Radio") - group = _("Fields") - form = RadioInputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - choices = get_select_field_choices(self.data.choices) - - widget_attrs = {'class': theme.form_radio_element_html_class} - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - 'required': self.data.required, - 'choices': choices, - 'widget': RadioSelect(attrs=widget_attrs), - } - - return [(self.data.name, ChoiceField, field_kwargs)] - - def submit_plugin_form_data(self, form_entry, request, form): - """Submit plugin form data/process. - - :param fobi.models.FormEntry form_entry: Instance of - ``fobi.models.FormEntry``. - :param django.http.HttpRequest request: - :param django.forms.Form form: - """ - # In case if we should submit value as is, we don't return anything. - # In other cases, we proceed further. - if SUBMIT_VALUE_AS != SUBMIT_VALUE_AS_VAL: - # Get the object - value = form.cleaned_data.get(self.data.name, None) - - # Get choices - choices = dict(get_select_field_choices(self.data.choices)) - - if value in choices: - # Handle the submitted form value - - label = safe_text(choices.get(value)) - - # Should be returned as repr - if SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_REPR: - value = label - # Should be returned as mix - else: - value = "{0} ({1})".format(label, value) - - # Overwrite ``cleaned_data`` of the ``form`` with object - # qualifier. - form.cleaned_data[self.data.name] = value - - # It's critically important to return the ``form`` with updated - # ``cleaned_data`` - return form - form_element_plugin_registry.register(RadioInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/range_select/README.rst b/src/fobi/contrib/plugins/form_elements/fields/range_select/README.rst index a56290e7..43ed9ce6 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/range_select/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/range_select/README.rst @@ -19,9 +19,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. @@ -29,7 +29,11 @@ Installation 4. Ranges are specified within the given min/max values. The default values are: +.. code-block:: text + - INITIAL: 50 + - INITIAL_MAX_VALUE: 100 + - INITIAL_MIN_VALUE: 0 - MIN_VALUE: 0 - MAX_VALUE: 100 - STEP: 1 @@ -37,7 +41,11 @@ Installation However, you can override each of them in the settings of your project by prefixing correspondent names with `FOBI_FORM_ELEMENT_RANGE_SELECT_`: +.. code-block:: text + - FOBI_FORM_ELEMENT_RANGE_SELECT_INITIAL + - FOBI_FORM_ELEMENT_RANGE_SELECT_INITIAL_MAX_VALUE + - FOBI_FORM_ELEMENT_RANGE_SELECT_INITIAL_MIN_VALUE - FOBI_FORM_ELEMENT_RANGE_SELECT_MIN_VALUE - FOBI_FORM_ELEMENT_RANGE_SELECT_MAX_VALUE - FOBI_FORM_ELEMENT_RANGE_SELECT_STEP diff --git a/src/fobi/contrib/plugins/form_elements/fields/range_select/base.py b/src/fobi/contrib/plugins/form_elements/fields/range_select/base.py new file mode 100644 index 00000000..9114ccce --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/range_select/base.py @@ -0,0 +1,61 @@ +from __future__ import absolute_import + +from django.forms.fields import ChoiceField +from django.forms.widgets import Select +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin, get_theme + +from . import UID +from .forms import RangeSelectInputForm +from .settings import ( + INITIAL, + INITIAL_MAX_VALUE, + INITIAL_MIN_VALUE, + # MAX_VALUE, + # MIN_VALUE, + STEP +) + +__title__ = 'fobi.contrib.plugins.form_elements.fields.range_select.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('RangeSelectInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class RangeSelectInputPlugin(FormFieldPlugin): + """Range select input plugin.""" + + uid = UID + name = _("Range select") + group = _("Fields") + form = RangeSelectInputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + initial = self.data.initial if self.data.initial else INITIAL + max_value = self.data.max_value \ + if self.data.max_value \ + else INITIAL_MAX_VALUE + min_value = self.data.min_value \ + if self.data.min_value \ + else INITIAL_MIN_VALUE + step = self.data.step if self.data.step else STEP + + _choices = range(min_value, max_value+1, step) + choices = zip(_choices, _choices) + + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': initial, + 'required': self.data.required, + 'choices': choices, + 'widget': Select(attrs={'class': theme.form_element_html_class}), + } + + return [(self.data.name, ChoiceField, field_kwargs)] diff --git a/src/fobi/contrib/plugins/form_elements/fields/range_select/defaults.py b/src/fobi/contrib/plugins/form_elements/fields/range_select/defaults.py index 2a34668e..1f61d5f9 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/range_select/defaults.py +++ b/src/fobi/contrib/plugins/form_elements/fields/range_select/defaults.py @@ -4,6 +4,8 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ( 'INITIAL', + 'INITIAL_MAX_VALUE', + 'INITIAL_MIN_VALUE', 'MAX_VALUE', 'MIN_VALUE', 'STEP', @@ -11,5 +13,7 @@ __all__ = ( INITIAL = 50 MIN_VALUE = 0 +INITIAL_MIN_VALUE = 0 MAX_VALUE = 5000 +INITIAL_MAX_VALUE = 100 STEP = 1 diff --git a/src/fobi/contrib/plugins/form_elements/fields/range_select/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/range_select/fobi_form_elements.py index 112086c7..36718e20 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/range_select/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/range_select/fobi_form_elements.py @@ -1,12 +1,8 @@ -from django.forms.fields import ChoiceField -from django.forms.widgets import Select -from django.utils.translation import ugettext_lazy as _ +from __future__ import absolute_import -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme +from fobi.base import form_element_plugin_registry -from . import UID -from .forms import RangeSelectInputForm -from .settings import INITIAL, MAX_VALUE, MIN_VALUE, STEP +from .base import RangeSelectInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.range_select.' \ 'fobi_form_elements' @@ -15,38 +11,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('RangeSelectInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class RangeSelectInputPlugin(FormFieldPlugin): - """Range select input plugin.""" - - uid = UID - name = _("Range select") - group = _("Fields") - form = RangeSelectInputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - initial = self.data.initial if self.data.initial else INITIAL - 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 - - _choices = range(min_value, max_value+1, step) - choices = zip(_choices, _choices) - - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': initial, - 'required': self.data.required, - 'choices': choices, - 'widget': Select(attrs={'class': theme.form_element_html_class}), - } - - return [(self.data.name, ChoiceField, field_kwargs)] - form_element_plugin_registry.register(RangeSelectInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/range_select/forms.py b/src/fobi/contrib/plugins/form_elements/fields/range_select/forms.py index 67da57ad..19402655 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/range_select/forms.py +++ b/src/fobi/contrib/plugins/form_elements/fields/range_select/forms.py @@ -4,7 +4,14 @@ from django.utils.translation import ugettext_lazy as _ from fobi.base import BaseFormFieldPluginForm, get_theme from fobi.widgets import NumberInput -from .settings import INITIAL, MAX_VALUE, MIN_VALUE, STEP +from .settings import ( + INITIAL, + INITIAL_MAX_VALUE, + INITIAL_MIN_VALUE, + MAX_VALUE, + MIN_VALUE, + STEP +) __title__ = 'fobi.contrib.plugins.form_elements.fields.range_select.forms' __author__ = 'Artur Barseghyan ' @@ -21,8 +28,8 @@ class RangeSelectInputForm(forms.Form, BaseFormFieldPluginForm): plugin_data_fields = [ ("label", ""), ("name", ""), - ("min_value", MIN_VALUE), - ("max_value", MAX_VALUE), + ("min_value", INITIAL_MIN_VALUE), + ("max_value", INITIAL_MAX_VALUE), ("step", STEP), ("help_text", ""), ("initial", INITIAL), @@ -46,6 +53,7 @@ class RangeSelectInputForm(forms.Form, BaseFormFieldPluginForm): min_value = forms.IntegerField( label=_("Min value"), required=True, + initial=INITIAL_MIN_VALUE, widget=NumberInput(attrs={'class': theme.form_element_html_class}), min_value=MIN_VALUE, max_value=MAX_VALUE @@ -53,6 +61,7 @@ class RangeSelectInputForm(forms.Form, BaseFormFieldPluginForm): max_value = forms.IntegerField( label=_("Max value"), required=True, + initial=INITIAL_MAX_VALUE, widget=NumberInput(attrs={'class': theme.form_element_html_class}), min_value=MIN_VALUE, max_value=MAX_VALUE diff --git a/src/fobi/contrib/plugins/form_elements/fields/range_select/settings.py b/src/fobi/contrib/plugins/form_elements/fields/range_select/settings.py index 0f198e68..5b23ccff 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/range_select/settings.py +++ b/src/fobi/contrib/plugins/form_elements/fields/range_select/settings.py @@ -6,6 +6,8 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ( 'INITIAL', + 'INITIAL_MAX_VALUE', + 'INITIAL_MIN_VALUE', 'MAX_VALUE', 'MIN_VALUE', 'STEP', @@ -13,6 +15,10 @@ __all__ = ( INITIAL = get_setting('INITIAL') +INITIAL_MAX_VALUE = get_setting('INITIAL_MAX_VALUE') + +INITIAL_MIN_VALUE = get_setting('INITIAL_MIN_VALUE') + MAX_VALUE = get_setting('MAX_VALUE') MIN_VALUE = get_setting('MIN_VALUE') diff --git a/src/fobi/contrib/plugins/form_elements/fields/regex/README.rst b/src/fobi/contrib/plugins/form_elements/fields/regex/README.rst index 7a4c4522..8a2e784d 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/regex/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/regex/README.rst @@ -19,9 +19,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/plugins/form_elements/fields/regex/base.py b/src/fobi/contrib/plugins/form_elements/fields/regex/base.py new file mode 100644 index 00000000..03e16db2 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/regex/base.py @@ -0,0 +1,49 @@ +from __future__ import absolute_import + +from django.forms.fields import RegexField +from django.forms.widgets import TextInput +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin, get_theme + +from . import UID +from .forms import RegexInputForm + +__title__ = 'fobi.contrib.plugins.form_elements.fields.regex.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('RegexInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class RegexInputPlugin(FormFieldPlugin): + """Regex field plugin.""" + + uid = UID + name = _("Regex") + group = _("Fields") + form = RegexInputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + widget_attrs = { + 'class': theme.form_element_html_class, + 'placeholder': self.data.placeholder, + } + + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'regex': self.data.regex, + 'initial': self.data.initial, + 'required': self.data.required, + 'widget': TextInput(attrs=widget_attrs), + } + + if self.data.max_length: + field_kwargs['max_length'] = self.data.max_length + + return [(self.data.name, RegexField, field_kwargs)] 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 4f281f4c..69ef11c9 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 @@ -1,13 +1,8 @@ from __future__ import absolute_import -from django.forms.fields import RegexField -from django.forms.widgets import TextInput -from django.utils.translation import ugettext_lazy as _ +from fobi.base import form_element_plugin_registry -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme - -from . import UID -from .forms import RegexInputForm +from .base import RegexInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.' \ 'regex.fobi_form_elements' @@ -16,38 +11,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('RegexInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class RegexInputPlugin(FormFieldPlugin): - """Regex field plugin.""" - - uid = UID - name = _("Regex") - group = _("Fields") - form = RegexInputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - widget_attrs = { - 'class': theme.form_element_html_class, - 'placeholder': self.data.placeholder, - } - - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'regex': self.data.regex, - 'initial': self.data.initial, - 'required': self.data.required, - 'widget': TextInput(attrs=widget_attrs), - } - - if self.data.max_length: - field_kwargs['max_length'] = self.data.max_length - - return [(self.data.name, RegexField, field_kwargs)] - form_element_plugin_registry.register(RegexInputPlugin) 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 91ac5a91..38e3829e 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/regex/forms.py +++ b/src/fobi/contrib/plugins/form_elements/fields/regex/forms.py @@ -2,9 +2,10 @@ from __future__ import absolute_import from django import forms from django.utils.translation import ugettext_lazy as _ +from django.core.validators import MinValueValidator from fobi.base import BaseFormFieldPluginForm, get_theme -from fobi.settings import DEFAULT_MAX_LENGTH +from fobi.settings import DEFAULT_MAX_LENGTH, DEFAULT_MIN_LENGTH from fobi.widgets import NumberInput __title__ = 'fobi.contrib.plugins.form_elements.fields.regex.forms' @@ -25,7 +26,7 @@ class RegexInputForm(forms.Form, BaseFormFieldPluginForm): ("help_text", ""), ("initial", ""), ("regex", ""), - ("max_length", "255"), + ("max_length", str(DEFAULT_MAX_LENGTH)), ("required", False), ("placeholder", ""), ] @@ -78,8 +79,10 @@ class RegexInputForm(forms.Form, BaseFormFieldPluginForm): max_length = forms.IntegerField( label=_("Max length"), required=True, - widget=NumberInput(attrs={'class': theme.form_element_html_class}), - initial=DEFAULT_MAX_LENGTH + widget=NumberInput(attrs={'class': theme.form_element_html_class, + 'min': str(DEFAULT_MIN_LENGTH)}), + initial=DEFAULT_MAX_LENGTH, + validators=[MinValueValidator(DEFAULT_MIN_LENGTH)] ) required = forms.BooleanField( label=_("Required"), @@ -95,3 +98,18 @@ class RegexInputForm(forms.Form, BaseFormFieldPluginForm): attrs={'class': theme.form_element_html_class} ) ) + + def clean(self): + """Validation.""" + super(RegexInputForm, self).clean() + + max_length = self.cleaned_data.get('max_length', DEFAULT_MAX_LENGTH) + + if self.cleaned_data['initial']: + len_initial = len(self.cleaned_data['initial']) + if len_initial > max_length: + self.add_error( + 'initial', + _("Ensure this value has at most {0} characters " + "(it has {1}).".format(max_length, len_initial)) + ) diff --git a/src/fobi/contrib/plugins/form_elements/fields/select/README.rst b/src/fobi/contrib/plugins/form_elements/fields/select/README.rst index d89552be..ad905181 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/select/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/select/README.rst @@ -19,9 +19,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. @@ -42,9 +42,11 @@ Installation ('gamma', 'Gamma'), ] - - "val": `value` (example: "alpha"). - - "repr" (default): `label` (example: "Alpha"). - - "mix": `value (label)` (example: "Alpha (alpha)"). + .. code-block:: text + + - "val": `value` (example: "alpha"). + - "repr" (default): `label` (example: "Alpha"). + - "mix": `value (label)` (example: "Alpha (alpha)"). Simply set the ``FOBI_FORM_ELEMENT_SELECT_SUBMIT_VALUE_AS`` assign one of the following @@ -57,7 +59,7 @@ consist of just a single value or value/label pair. For example: -.. code-block:: none +.. code-block:: text 1 2 diff --git a/src/fobi/contrib/plugins/form_elements/fields/select/base.py b/src/fobi/contrib/plugins/form_elements/fields/select/base.py new file mode 100644 index 00000000..1380a097 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/select/base.py @@ -0,0 +1,87 @@ +from __future__ import absolute_import + +from django.forms.fields import ChoiceField +from django.forms.widgets import Select +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin, get_theme +from fobi.constants import ( + SUBMIT_VALUE_AS_VAL, + SUBMIT_VALUE_AS_REPR +) +from fobi.helpers import get_select_field_choices, safe_text + +from . import UID +from .forms import SelectInputForm +from .settings import SUBMIT_VALUE_AS + +__title__ = 'fobi.contrib.plugins.form_elements.fields.select.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('SelectInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class SelectInputPlugin(FormFieldPlugin): + """Select field plugin.""" + + uid = UID + name = _("Select") + group = _("Fields") + form = SelectInputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + choices = get_select_field_choices(self.data.choices) + + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + 'required': self.data.required, + 'choices': choices, + 'widget': Select(attrs={'class': theme.form_element_html_class}), + } + + return [(self.data.name, ChoiceField, field_kwargs)] + + def submit_plugin_form_data(self, form_entry, request, form, + form_element_entries=None, **kwargs): + """Submit plugin form data/process. + + :param fobi.models.FormEntry form_entry: Instance of + ``fobi.models.FormEntry``. + :param django.http.HttpRequest request: + :param django.forms.Form form: + """ + # In case if we should submit value as is, we don't return anything. + # In other cases, we proceed further. + if SUBMIT_VALUE_AS != SUBMIT_VALUE_AS_VAL: + # Get the object + value = form.cleaned_data.get(self.data.name, None) + + # Get choices + choices = dict(get_select_field_choices(self.data.choices)) + + if value in choices: + # Handle the submitted form value + + label = safe_text(choices.get(value)) + + # Should be returned as repr + if SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_REPR: + value = label + # Should be returned as mix + else: + value = "{0} ({1})".format(label, value) + + # Overwrite ``cleaned_data`` of the ``form`` with object + # qualifier. + form.cleaned_data[self.data.name] = value + + # It's critically important to return the ``form`` with + # updated ``cleaned_data`` + return form 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 c0f77614..0db1d61f 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 @@ -1,16 +1,8 @@ -from django.forms.fields import ChoiceField -from django.forms.widgets import Select -from django.utils.translation import ugettext_lazy as _ +from __future__ import absolute_import -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme -from fobi.constants import ( - SUBMIT_VALUE_AS_VAL, SUBMIT_VALUE_AS_REPR -) -from fobi.helpers import get_select_field_choices, safe_text +from fobi.base import form_element_plugin_registry -from . import UID -from .forms import SelectInputForm -from .settings import SUBMIT_VALUE_AS +from .base import SelectInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.' \ 'select.fobi_form_elements' @@ -19,69 +11,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('SelectInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class SelectInputPlugin(FormFieldPlugin): - """Select field plugin.""" - - uid = UID - name = _("Select") - group = _("Fields") - form = SelectInputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - choices = get_select_field_choices(self.data.choices) - - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - 'required': self.data.required, - 'choices': choices, - 'widget': Select(attrs={'class': theme.form_element_html_class}), - } - - return [(self.data.name, ChoiceField, field_kwargs)] - - def submit_plugin_form_data(self, form_entry, request, form): - """Submit plugin form data/process. - - :param fobi.models.FormEntry form_entry: Instance of - ``fobi.models.FormEntry``. - :param django.http.HttpRequest request: - :param django.forms.Form form: - """ - # In case if we should submit value as is, we don't return anything. - # In other cases, we proceed further. - if SUBMIT_VALUE_AS != SUBMIT_VALUE_AS_VAL: - # Get the object - value = form.cleaned_data.get(self.data.name, None) - - # Get choices - choices = dict(get_select_field_choices(self.data.choices)) - - if value in choices: - # Handle the submitted form value - - label = safe_text(choices.get(value)) - - # Should be returned as repr - if SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_REPR: - value = label - # Should be returned as mix - else: - value = "{0} ({1})".format(label, value) - - # Overwrite ``cleaned_data`` of the ``form`` with object - # qualifier. - form.cleaned_data[self.data.name] = value - - # It's critically important to return the ``form`` with - # updated ``cleaned_data`` - return form - form_element_plugin_registry.register(SelectInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_model_object/README.rst b/src/fobi/contrib/plugins/form_elements/fields/select_model_object/README.rst index 75e37416..83783fa9 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/select_model_object/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/select_model_object/README.rst @@ -19,9 +19,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. @@ -35,16 +35,18 @@ Installation FOBI_FORM_ELEMENT_SELECT_MODEL_OBJECT_IGNORED_MODELS = [ 'auth.User', 'auth.Group', - ] + ] 5. By default, the submitted form value of `select_model_object` elements is `app_label.model_name.object_pk.object_repr`. However, that part of the behaviour has been made configurable. You can choose between the following options: - - "val": `app_label.model_name.object_pk.object_repr`. - - "repr": `object_repr` (uses the ``__unicode__`` method of the model). - - "mix" (default): `app_label.model_name.object_pk.object_repr`. + .. code-block:: text + + - "val": `app_label.model_name.object_pk.object_repr`. + - "repr": `object_repr` (uses the ``__unicode__`` method of the model). + - "mix" (default): `app_label.model_name.object_pk.object_repr`. Simply set the ``FOBI_FORM_ELEMENT_SELECT_MODEL_OBJECT_SUBMIT_VALUE_AS`` assign one of the following values: "val", "repr" or "mix" to get the diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_model_object/base.py b/src/fobi/contrib/plugins/form_elements/fields/select_model_object/base.py new file mode 100644 index 00000000..76230d20 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/select_model_object/base.py @@ -0,0 +1,107 @@ +from __future__ import absolute_import + +from django.forms.models import ModelChoiceField +from django.forms.widgets import Select +from django.utils.translation import ugettext_lazy as _ + +from nine.versions import DJANGO_GTE_1_7 + +from fobi.base import FormFieldPlugin, get_theme +from fobi.constants import ( + SUBMIT_VALUE_AS_VAL, + SUBMIT_VALUE_AS_REPR +) +from fobi.helpers import ( + safe_text, + get_app_label_and_model_name, + get_model_name_for_object +) + +from . import UID +from .forms import SelectModelObjectInputForm +from .settings import SUBMIT_VALUE_AS + +if DJANGO_GTE_1_7: + from django.apps import apps + + get_model = apps.get_model +else: + from django.db.models import get_model + +__title__ = 'fobi.contrib.plugins.form_elements.fields.' \ + 'select_model_object.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('SelectModelObjectInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class SelectModelObjectInputPlugin(FormFieldPlugin): + """Select model object field plugin.""" + + uid = UID + name = _("Select model object") + group = _("Fields") + form = SelectModelObjectInputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + app_label, model_name = get_app_label_and_model_name(self.data.model) + model = get_model(app_label, model_name) + queryset = model._default_manager.all() + + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + 'required': self.data.required, + 'queryset': queryset, + 'widget': Select(attrs={'class': theme.form_element_html_class}), + } + + return [(self.data.name, ModelChoiceField, field_kwargs)] + + def submit_plugin_form_data(self, form_entry, request, form, + form_element_entries=None, **kwargs): + """Submit plugin form data/process. + + :param fobi.models.FormEntry form_entry: Instance of + ``fobi.models.FormEntry``. + :param django.http.HttpRequest request: + :param django.forms.Form form: + """ + # In case if we should submit value as is, we don't return anything. + # In other cases, we proceed further. + + # Get the object + obj = form.cleaned_data.get(self.data.name, None) + if obj: + value = None + # Should be returned as repr + if SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_REPR: + value = safe_text(obj) + elif SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_VAL: + value = '{0}.{1}.{2}'.format( + obj._meta.app_label, + get_model_name_for_object(obj), + obj.pk + ) + else: + # Handle the submitted form value + value = '{0}.{1}.{2}.{3}'.format( + obj._meta.app_label, + get_model_name_for_object(obj), + obj.pk, + safe_text(obj) + ) + + # Overwrite ``cleaned_data`` of the ``form`` with object + # qualifier. + form.cleaned_data[self.data.name] = value + + # It's critically important to return the ``form`` with updated + # ``cleaned_data`` + return form 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 b24bf424..d0ac88e5 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 @@ -1,29 +1,8 @@ -from django.forms.models import ModelChoiceField -from django.forms.widgets import Select -from django.utils.translation import ugettext_lazy as _ +from __future__ import absolute_import -from nine.versions import DJANGO_GTE_1_7 +from fobi.base import form_element_plugin_registry -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme -from fobi.constants import ( - SUBMIT_VALUE_AS_VAL, SUBMIT_VALUE_AS_REPR -) -from fobi.helpers import ( - safe_text, - get_app_label_and_model_name, - get_model_name_for_object -) - -from . import UID -from .forms import SelectModelObjectInputForm -from .settings import SUBMIT_VALUE_AS - -if DJANGO_GTE_1_7: - from django.apps import apps - - get_model = apps.get_model -else: - from django.db.models import get_model +from .base import SelectModelObjectInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.' \ 'select_model_object.fobi_form_elements' @@ -32,75 +11,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('SelectModelObjectInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class SelectModelObjectInputPlugin(FormFieldPlugin): - """Select model object field plugin.""" - - uid = UID - name = _("Select model object") - group = _("Fields") - form = SelectModelObjectInputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - app_label, model_name = get_app_label_and_model_name(self.data.model) - model = get_model(app_label, model_name) - queryset = model._default_manager.all() - - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - 'required': self.data.required, - 'queryset': queryset, - 'widget': Select(attrs={'class': theme.form_element_html_class}), - } - - return [(self.data.name, ModelChoiceField, field_kwargs)] - - def submit_plugin_form_data(self, form_entry, request, form): - """Submit plugin form data/process. - - :param fobi.models.FormEntry form_entry: Instance of - ``fobi.models.FormEntry``. - :param django.http.HttpRequest request: - :param django.forms.Form form: - """ - # In case if we should submit value as is, we don't return anything. - # In other cases, we proceed further. - - # Get the object - obj = form.cleaned_data.get(self.data.name, None) - if obj: - value = None - # Should be returned as repr - if SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_REPR: - value = safe_text(obj) - elif SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_VAL: - value = '{0}.{1}.{2}'.format( - obj._meta.app_label, - get_model_name_for_object(obj), - obj.pk - ) - else: - # Handle the submitted form value - value = '{0}.{1}.{2}.{3}'.format( - obj._meta.app_label, - get_model_name_for_object(obj), - obj.pk, - safe_text(obj) - ) - - # Overwrite ``cleaned_data`` of the ``form`` with object - # qualifier. - form.cleaned_data[self.data.name] = value - - # It's critically important to return the ``form`` with updated - # ``cleaned_data`` - return form - form_element_plugin_registry.register(SelectModelObjectInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_mptt_model_object/README.rst b/src/fobi/contrib/plugins/form_elements/fields/select_mptt_model_object/README.rst index ae33eae4..b6066ee1 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/select_mptt_model_object/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/select_mptt_model_object/README.rst @@ -13,9 +13,9 @@ Taken from django-mptt `Getting started 1. Download ``django-mptt`` using pip by running: -.. code-block:: none +.. code-block:: sh - $ pip install django-mptt + pip install django-mptt 2. Add ``mptt`` to the ``INSTALLED_APPS`` in your ``settings.py``. @@ -36,9 +36,9 @@ Install `select_mptt_model_object` plugin 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. @@ -47,21 +47,23 @@ Install `select_mptt_model_object` plugin ``fobi.contrib.plugins.form_elements.fields.select_mptt_model_object.defaults.IGNORED_MODELS``. If necessary, override it in your `settings` as shown in the example below: -.. code-block:: python + .. code-block:: python - FOBI_FORM_ELEMENT_SELECT_MPTT_MODEL_OBJECT_IGNORED_MODELS = [ - 'auth.User', - 'auth.Group', - ] + FOBI_FORM_ELEMENT_SELECT_MPTT_MODEL_OBJECT_IGNORED_MODELS = [ + 'auth.User', + 'auth.Group', + ] 5. By default, the submitted form value of `select_mptt_model_object` elements is `app_label.model_name.object_pk.object_repr`. However, that part of the behaviour has been made configurable. You can choose between the following options: - - "val": `app_label.model_name.object_pk.object_repr`. - - "repr": `object_repr` (uses the ``__unicode__`` method of the model). - - "mix" (default): `app_label.model_name.object_pk.object_repr`. + .. code-block:: text + + - "val": `app_label.model_name.object_pk.object_repr`. + - "repr": `object_repr` (uses the ``__unicode__`` method of the model). + - "mix" (default): `app_label.model_name.object_pk.object_repr`. Simply set the ``FOBI_FORM_ELEMENT_SELECT_MPTT_MODEL_OBJECT_SUBMIT_VALUE_AS`` assign one of the following values: "val", "repr" or "mix" to get the diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_mptt_model_object/base.py b/src/fobi/contrib/plugins/form_elements/fields/select_mptt_model_object/base.py new file mode 100644 index 00000000..01d77437 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/select_mptt_model_object/base.py @@ -0,0 +1,107 @@ +from __future__ import absolute_import + +from django.forms.widgets import Select +from django.utils.translation import ugettext_lazy as _ + +from mptt.fields import TreeNodeChoiceField + +from fobi.base import FormFieldPlugin, get_theme +from fobi.constants import ( + SUBMIT_VALUE_AS_VAL, + SUBMIT_VALUE_AS_REPR +) +from fobi.helpers import ( + safe_text, + get_app_label_and_model_name, + get_model_name_for_object +) + +from nine.versions import DJANGO_GTE_1_7 + +from . import UID +from .forms import SelectMPTTModelObjectInputForm +from .settings import SUBMIT_VALUE_AS + +if DJANGO_GTE_1_7: + from django.apps import apps + + get_model = apps.get_model +else: + from django.db.models import get_model + +__title__ = 'fobi.contrib.plugins.form_elements.fields.' \ + 'select_mptt_model_object.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('SelectMPTTModelObjectInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class SelectMPTTModelObjectInputPlugin(FormFieldPlugin): + """Select MPTT model object field plugin.""" + + uid = UID + name = _("Select MPTT model object") + group = _("Fields") + form = SelectMPTTModelObjectInputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + app_label, model_name = get_app_label_and_model_name(self.data.model) + model = get_model(app_label, model_name) + queryset = model._default_manager.all() + + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + 'required': self.data.required, + 'queryset': queryset, + 'widget': Select(attrs={'class': theme.form_element_html_class}), + } + + return [(self.data.name, TreeNodeChoiceField, field_kwargs)] + + def submit_plugin_form_data(self, form_entry, request, form, + form_element_entries=None, **kwargs): + """Submit plugin form data/process. + + :param fobi.models.FormEntry form_entry: Instance of + ``fobi.models.FormEntry``. + :param django.http.HttpRequest request: + :param django.forms.Form form: + """ + # In case if we should submit value as is, we don't return anything. + # In other cases, we proceed further. + + # Get the object + obj = form.cleaned_data.get(self.data.name, None) + if obj: + value = None + # Should be returned as repr + if SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_REPR: + value = safe_text(obj) + elif SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_VAL: + value = '{0}.{1}.{2}'.format( + obj._meta.app_label, + get_model_name_for_object(obj), + obj.pk + ) + else: + # Handle the submitted form value + value = '{0}.{1}.{2}.{3}'.format( + obj._meta.app_label, + get_model_name_for_object(obj), + obj.pk, + safe_text(obj) + ) + + # Overwrite ``cleaned_data`` of the ``form`` with object qualifier. + form.cleaned_data[self.data.name] = value + + # It's critically important to return the ``form`` with updated + # ``cleaned_data`` + return form 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 033367df..76d740b7 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 @@ -1,30 +1,8 @@ -from django.forms.widgets import Select -from django.utils.translation import ugettext_lazy as _ +from __future__ import absolute_import -from mptt.fields import TreeNodeChoiceField +from fobi.base import form_element_plugin_registry -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme -from fobi.constants import ( - SUBMIT_VALUE_AS_VAL, SUBMIT_VALUE_AS_REPR -) -from fobi.helpers import ( - safe_text, - get_app_label_and_model_name, - get_model_name_for_object -) - -from nine.versions import DJANGO_GTE_1_7 - -from . import UID -from .forms import SelectMPTTModelObjectInputForm -from .settings import SUBMIT_VALUE_AS - -if DJANGO_GTE_1_7: - from django.apps import apps - - get_model = apps.get_model -else: - from django.db.models import get_model +from .base import SelectMPTTModelObjectInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.' \ 'select_mptt_model_object.fobi_form_elements' @@ -33,74 +11,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('SelectMPTTModelObjectInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class SelectMPTTModelObjectInputPlugin(FormFieldPlugin): - """Select MPTT model object field plugin.""" - - uid = UID - name = _("Select MPTT model object") - group = _("Fields") - form = SelectMPTTModelObjectInputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - app_label, model_name = get_app_label_and_model_name(self.data.model) - model = get_model(app_label, model_name) - queryset = model._default_manager.all() - - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - 'required': self.data.required, - 'queryset': queryset, - 'widget': Select(attrs={'class': theme.form_element_html_class}), - } - - return [(self.data.name, TreeNodeChoiceField, field_kwargs)] - - def submit_plugin_form_data(self, form_entry, request, form): - """Submit plugin form data/process. - - :param fobi.models.FormEntry form_entry: Instance of - ``fobi.models.FormEntry``. - :param django.http.HttpRequest request: - :param django.forms.Form form: - """ - # In case if we should submit value as is, we don't return anything. - # In other cases, we proceed further. - - # Get the object - obj = form.cleaned_data.get(self.data.name, None) - if obj: - value = None - # Should be returned as repr - if SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_REPR: - value = safe_text(obj) - elif SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_VAL: - value = '{0}.{1}.{2}'.format( - obj._meta.app_label, - get_model_name_for_object(obj), - obj.pk - ) - else: - # Handle the submitted form value - value = '{0}.{1}.{2}.{3}'.format( - obj._meta.app_label, - get_model_name_for_object(obj), - obj.pk, - safe_text(obj) - ) - - # Overwrite ``cleaned_data`` of the ``form`` with object qualifier. - form.cleaned_data[self.data.name] = value - - # It's critically important to return the ``form`` with updated - # ``cleaned_data`` - return form - form_element_plugin_registry.register(SelectMPTTModelObjectInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_multiple/README.rst b/src/fobi/contrib/plugins/form_elements/fields/select_multiple/README.rst index dc4584fb..b47ddc39 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/select_multiple/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/select_multiple/README.rst @@ -20,9 +20,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. @@ -43,9 +43,11 @@ Installation ('gamma', 'Gamma'), ] - - "val": `value` (example: "alpha"). - - "repr" (default): `label` (example: "Alpha"). - - "mix": `value (label)` (example: "Alpha (alpha)"). + .. code-block:: text + + - "val": `value` (example: "alpha"). + - "repr" (default): `label` (example: "Alpha"). + - "mix": `value (label)` (example: "Alpha (alpha)"). Simply set the ``FOBI_FORM_ELEMENT_SELECT_MULTIPLE_SUBMIT_VALUE_AS`` assign one of the @@ -58,7 +60,7 @@ consist of just a single value or value/label pair. For example: -.. code-block:: none +.. code-block:: text 1 2 diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_multiple/base.py b/src/fobi/contrib/plugins/form_elements/fields/select_multiple/base.py new file mode 100644 index 00000000..1a977691 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/select_multiple/base.py @@ -0,0 +1,98 @@ +from __future__ import absolute_import + +from django.forms.fields import MultipleChoiceField +from django.forms.widgets import SelectMultiple +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin, get_theme +from fobi.constants import ( + SUBMIT_VALUE_AS_VAL, + SUBMIT_VALUE_AS_REPR +) +from fobi.helpers import get_select_field_choices, safe_text + +from . import UID +from .forms import SelectMultipleInputForm +from .settings import SUBMIT_VALUE_AS + +__title__ = 'fobi.contrib.plugins.form_elements.fields.' \ + 'select_multiple.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('SelectMultipleInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class SelectMultipleInputPlugin(FormFieldPlugin): + """Select multiple field plugin.""" + + uid = UID + name = _("Select multiple") + group = _("Fields") + form = SelectMultipleInputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + choices = get_select_field_choices(self.data.choices) + + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + 'required': self.data.required, + 'choices': choices, + 'widget': SelectMultiple( + attrs={'class': theme.form_element_html_class} + ), + } + + return [(self.data.name, MultipleChoiceField, field_kwargs)] + + def submit_plugin_form_data(self, form_entry, request, form, + form_element_entries=None, **kwargs): + """ + Submit plugin form data/process. + + :param fobi.models.FormEntry form_entry: Instance of + ``fobi.models.FormEntry``. + :param django.http.HttpRequest request: + :param django.forms.Form form: + """ + # In case if we should submit value as is, we don't return anything. + # In other cases, we proceed further. + if SUBMIT_VALUE_AS != SUBMIT_VALUE_AS_VAL: + # Get the object + values = form.cleaned_data.get(self.data.name, None) + + # Get choices + choices = dict(get_select_field_choices(self.data.choices)) + + # Returned value + ret_values = [] + + for value in values: + # Handle the submitted form value + + if value in choices: + label = safe_text(choices.get(value)) + + # Should be returned as repr + if SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_REPR: + value = label + # Should be returned as mix + else: + value = "{0} ({1})".format(label, value) + + ret_values.append(value) + + # Overwrite ``cleaned_data`` of the ``form`` with object + # qualifier. + if ret_values: + form.cleaned_data[self.data.name] = ret_values + + # It's critically important to return the ``form`` with updated + # ``cleaned_data`` + return form 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 b8e1f45d..af262108 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 @@ -1,16 +1,8 @@ -from django.forms.fields import MultipleChoiceField -from django.forms.widgets import SelectMultiple -from django.utils.translation import ugettext_lazy as _ +from __future__ import absolute_import -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme -from fobi.constants import ( - SUBMIT_VALUE_AS_VAL, SUBMIT_VALUE_AS_REPR -) -from fobi.helpers import get_select_field_choices, safe_text +from fobi.base import form_element_plugin_registry -from . import UID -from .forms import SelectMultipleInputForm -from .settings import SUBMIT_VALUE_AS +from .base import SelectMultipleInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.' \ 'select_multiple.fobi_form_elements' @@ -19,79 +11,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('SelectMultipleInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class SelectMultipleInputPlugin(FormFieldPlugin): - """Select multiple field plugin.""" - - uid = UID - name = _("Select multiple") - group = _("Fields") - form = SelectMultipleInputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - choices = get_select_field_choices(self.data.choices) - - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - 'required': self.data.required, - 'choices': choices, - 'widget': SelectMultiple( - attrs={'class': theme.form_element_html_class} - ), - } - - return [(self.data.name, MultipleChoiceField, field_kwargs)] - - def submit_plugin_form_data(self, form_entry, request, form): - """ - Submit plugin form data/process. - - :param fobi.models.FormEntry form_entry: Instance of - ``fobi.models.FormEntry``. - :param django.http.HttpRequest request: - :param django.forms.Form form: - """ - # In case if we should submit value as is, we don't return anything. - # In other cases, we proceed further. - if SUBMIT_VALUE_AS != SUBMIT_VALUE_AS_VAL: - # Get the object - values = form.cleaned_data.get(self.data.name, None) - - # Get choices - choices = dict(get_select_field_choices(self.data.choices)) - - # Returned value - ret_values = [] - - for value in values: - # Handle the submitted form value - - if value in choices: - label = safe_text(choices.get(value)) - - # Should be returned as repr - if SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_REPR: - value = label - # Should be returned as mix - else: - value = "{0} ({1})".format(label, value) - - ret_values.append(value) - - # Overwrite ``cleaned_data`` of the ``form`` with object - # qualifier. - if ret_values: - form.cleaned_data[self.data.name] = ret_values - - # It's critically important to return the ``form`` with updated - # ``cleaned_data`` - return form - form_element_plugin_registry.register(SelectMultipleInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_model_objects/README.rst b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_model_objects/README.rst index ef813bce..7151a58d 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_model_objects/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_model_objects/README.rst @@ -21,9 +21,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. @@ -32,21 +32,23 @@ Installation ``fobi.contrib.plugins.form_elements.fields.select_multiple_model_objects.defaults.IGNORED_MODELS``. If necessary, override it in your `settings` as shown in the example below: -.. code-block:: python + .. code-block:: python - FOBI_FORM_ELEMENT_SELECT_MULTIPLE_MODEL_OBJECTS_IGNORED_MODELS = [ - 'auth.User', - 'auth.Group', - ] + FOBI_FORM_ELEMENT_SELECT_MULTIPLE_MODEL_OBJECTS_IGNORED_MODELS = [ + 'auth.User', + 'auth.Group', + ] 5. By default, the submitted form value of `select_multiple_model_objects` elements is `app_label.model_name.object_pk.object_repr`. However, that part of the behaviour has been made configurable. You can choose between the following options: - - "val": `app_label.model_name.object_pk.object_repr`. - - "repr": `object_repr` (uses the ``__unicode__`` method of the model). - - "mix" (default): `app_label.model_name.object_pk.object_repr`. + .. code-block:: text + + - "val": `app_label.model_name.object_pk.object_repr`. + - "repr": `object_repr` (uses the ``__unicode__`` method of the model). + - "mix" (default): `app_label.model_name.object_pk.object_repr`. Simply set the ``FOBI_FORM_ELEMENT_SELECT_MULTIPLE_MODEL_OBJECTS_SUBMIT_VALUE_AS`` assign diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_model_objects/base.py b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_model_objects/base.py new file mode 100644 index 00000000..24b714d0 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_model_objects/base.py @@ -0,0 +1,118 @@ +from __future__ import absolute_import + +import simplejson as json + +from django.forms.models import ModelMultipleChoiceField +from django.forms.widgets import SelectMultiple +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin, get_theme +from fobi.constants import ( + SUBMIT_VALUE_AS_VAL, + SUBMIT_VALUE_AS_REPR +) +from fobi.helpers import ( + safe_text, + get_app_label_and_model_name, + get_model_name_for_object +) + +from nine.versions import DJANGO_GTE_1_7 + +from . import UID +from .forms import SelectMultipleModelObjectsInputForm +from .settings import SUBMIT_VALUE_AS + +if DJANGO_GTE_1_7: + from django.apps import apps + get_model = apps.get_model +else: + from django.db.models import get_model + +__title__ = 'fobi.contrib.plugins.form_elements.fields.' \ + 'select_multiple_model_objects.fobi_form_elements' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('SelectMultipleModelObjectsInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class SelectMultipleModelObjectsInputPlugin(FormFieldPlugin): + """Select multiple model objects field plugin.""" + + uid = UID + name = _("Select multiple model objects") + group = _("Fields") + form = SelectMultipleModelObjectsInputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + app_label, model_name = get_app_label_and_model_name(self.data.model) + model = get_model(app_label, model_name) + queryset = model._default_manager.all() + + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + 'required': self.data.required, + 'queryset': queryset, + 'widget': SelectMultiple( + attrs={'class': theme.form_element_html_class} + ), + } + + return [(self.data.name, ModelMultipleChoiceField, field_kwargs)] + + def submit_plugin_form_data(self, form_entry, request, form, + form_element_entries=None, **kwargs): + """ + Submit plugin form data/process. + + :param fobi.models.FormEntry form_entry: Instance of + ``fobi.models.FormEntry``. + :param django.http.HttpRequest request: + :param django.forms.Form form: + """ + # In case if we should submit value as is, we don't return anything. + # In other cases, we proceed further. + + # Get the object + objs = form.cleaned_data.get(self.data.name, []) + + values = [] + + for obj in objs: + if obj: + value = None + # Should be returned as repr + if SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_REPR: + value = safe_text(obj) + elif SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_VAL: + value = '{0}.{1}.{2}'.format( + obj._meta.app_label, + get_model_name_for_object(obj), + obj.pk + ) + else: + # Handle the submitted form value + value = '{0}.{1}.{2}.{3}'.format( + obj._meta.app_label, + get_model_name_for_object(obj), + obj.pk, + safe_text(obj) + ) + values.append(value) + + # Overwrite ``cleaned_data`` of the ``form`` with object qualifier. + if values: + form.cleaned_data[self.data.name] = json.dumps(values) + else: + del form.cleaned_data[self.data.name] + + # It's critically important to return the ``form`` with updated + # ``cleaned_data`` + return form 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 0debdf35..da043ad8 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 @@ -1,30 +1,8 @@ -import simplejson as json +from __future__ import absolute_import -from django.forms.models import ModelMultipleChoiceField -from django.forms.widgets import SelectMultiple -from django.utils.translation import ugettext_lazy as _ +from fobi.base import form_element_plugin_registry -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme -from fobi.constants import ( - SUBMIT_VALUE_AS_VAL, SUBMIT_VALUE_AS_REPR -) -from fobi.helpers import ( - safe_text, - get_app_label_and_model_name, - get_model_name_for_object -) - -from nine.versions import DJANGO_GTE_1_7 - -from . import UID -from .forms import SelectMultipleModelObjectsInputForm -from .settings import SUBMIT_VALUE_AS - -if DJANGO_GTE_1_7: - from django.apps import apps - get_model = apps.get_model -else: - from django.db.models import get_model +from .base import SelectMultipleModelObjectsInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.' \ 'select_multiple_model_objects.fobi_form_elements' @@ -33,85 +11,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('SelectMultipleModelObjectsInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class SelectMultipleModelObjectsInputPlugin(FormFieldPlugin): - """Select multiple model objects field plugin.""" - - uid = UID - name = _("Select multiple model objects") - group = _("Fields") - form = SelectMultipleModelObjectsInputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - app_label, model_name = get_app_label_and_model_name(self.data.model) - model = get_model(app_label, model_name) - queryset = model._default_manager.all() - - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - 'required': self.data.required, - 'queryset': queryset, - 'widget': SelectMultiple( - attrs={'class': theme.form_element_html_class} - ), - } - - return [(self.data.name, ModelMultipleChoiceField, field_kwargs)] - - def submit_plugin_form_data(self, form_entry, request, form): - """ - Submit plugin form data/process. - - :param fobi.models.FormEntry form_entry: Instance of - ``fobi.models.FormEntry``. - :param django.http.HttpRequest request: - :param django.forms.Form form: - """ - # In case if we should submit value as is, we don't return anything. - # In other cases, we proceed further. - - # Get the object - objs = form.cleaned_data.get(self.data.name, []) - - values = [] - - for obj in objs: - if obj: - value = None - # Should be returned as repr - if SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_REPR: - value = safe_text(obj) - elif SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_VAL: - value = '{0}.{1}.{2}'.format( - obj._meta.app_label, - get_model_name_for_object(obj), - obj.pk - ) - else: - # Handle the submitted form value - value = '{0}.{1}.{2}.{3}'.format( - obj._meta.app_label, - get_model_name_for_object(obj), - obj.pk, - safe_text(obj) - ) - values.append(value) - - # Overwrite ``cleaned_data`` of the ``form`` with object qualifier. - if values: - form.cleaned_data[self.data.name] = json.dumps(values) - else: - del form.cleaned_data[self.data.name] - - # It's critically important to return the ``form`` with updated - # ``cleaned_data`` - return form - form_element_plugin_registry.register(SelectMultipleModelObjectsInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_mptt_model_objects/README.rst b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_mptt_model_objects/README.rst index ef8d74ae..302b410e 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_mptt_model_objects/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_mptt_model_objects/README.rst @@ -14,9 +14,9 @@ Taken from django-mptt `Getting started 1. Download ``django-mptt`` using pip by running: -.. code-block:: none +.. code-block:: sh - $ pip install django-mptt + pip install django-mptt 2. Add ``mptt`` to the ``INSTALLED_APPS`` in your ``settings.py``. @@ -37,9 +37,9 @@ Install `select_multiple_mptt_model_objects` plugin 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. @@ -48,21 +48,23 @@ Install `select_multiple_mptt_model_objects` plugin ``fobi.contrib.plugins.form_elements.fields.select_multiple_mptt_model_objects.defaults.IGNORED_MODELS``. If necessary, override it in your `settings` as shown in the example below: -.. code-block:: python + .. code-block:: python - FOBI_FORM_ELEMENT_SELECT_MULTIPLE_MPTT_MODEL_OBJECTS_IGNORED_MODELS = [ - 'auth.User', - 'auth.Group', - ] + FOBI_FORM_ELEMENT_SELECT_MULTIPLE_MPTT_MODEL_OBJECTS_IGNORED_MODELS = [ + 'auth.User', + 'auth.Group', + ] 5. By default, the submitted form value of `select_multiple_mptt_model_objects` elements is `app_label.model_name.object_pk.object_repr`. However, that part of the behaviour has been made configurable. You can choose between the following options: - - "val": `app_label.model_name.object_pk.object_repr`. - - "repr": `object_repr` (uses the ``__unicode__`` method of the model). - - "mix" (default): `app_label.model_name.object_pk.object_repr`. + .. code-block:: text + + - "val": `app_label.model_name.object_pk.object_repr`. + - "repr": `object_repr` (uses the ``__unicode__`` method of the model). + - "mix" (default): `app_label.model_name.object_pk.object_repr`. Simply set the ``FOBI_FORM_ELEMENT_SELECT_MULTIPLE_MPTT_MODEL_OBJECTS_SUBMIT_VALUE_AS`` diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_mptt_model_objects/base.py b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_mptt_model_objects/base.py new file mode 100644 index 00000000..72529cde --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_mptt_model_objects/base.py @@ -0,0 +1,118 @@ +from __future__ import absolute_import + +import simplejson as json + +from django.forms.widgets import SelectMultiple +from django.utils.translation import ugettext_lazy as _ + +from mptt.fields import TreeNodeMultipleChoiceField + +from fobi.base import FormFieldPlugin, get_theme +from fobi.constants import ( + SUBMIT_VALUE_AS_VAL, + SUBMIT_VALUE_AS_REPR +) +from fobi.helpers import ( + safe_text, + get_app_label_and_model_name, + get_model_name_for_object +) + +from nine.versions import DJANGO_GTE_1_7 + +from . import UID +from .forms import SelectMultipleMPTTModelObjectsInputForm +from .settings import SUBMIT_VALUE_AS + +if DJANGO_GTE_1_7: + from django.apps import apps + get_model = apps.get_model +else: + from django.db.models import get_model + +__title__ = 'fobi.contrib.plugins.form_elements.fields.' \ + 'select_multiple_mptt_model_objects.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('SelectMultipleMPTTModelObjectsInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class SelectMultipleMPTTModelObjectsInputPlugin(FormFieldPlugin): + """Select multiple MPTT model object field plugin.""" + + uid = UID + name = _("Select multiple MPTT model objects") + group = _("Fields") + form = SelectMultipleMPTTModelObjectsInputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + app_label, model_name = get_app_label_and_model_name(self.data.model) + model = get_model(app_label, model_name) + queryset = model._default_manager.all() + + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + 'required': self.data.required, + 'queryset': queryset, + 'widget': SelectMultiple( + attrs={'class': theme.form_element_html_class} + ), + } + + return [(self.data.name, TreeNodeMultipleChoiceField, field_kwargs)] + + def submit_plugin_form_data(self, form_entry, request, form, + form_element_entries=None, **kwargs): + """Submit plugin form data/process. + + :param fobi.models.FormEntry form_entry: Instance of + ``fobi.models.FormEntry``. + :param django.http.HttpRequest request: + :param django.forms.Form form: + """ + # In case if we should submit value as is, we don't return anything. + # In other cases, we proceed further. + + # Get the object + objs = form.cleaned_data.get(self.data.name, []) + + values = [] + + for obj in objs: + if obj: + value = None + # Should be returned as repr + if SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_REPR: + value = safe_text(obj) + elif SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_VAL: + value = '{0}.{1}.{2}'.format( + obj._meta.app_label, + get_model_name_for_object(obj), + obj.pk + ) + else: + # Handle the submitted form value + value = '{0}.{1}.{2}.{3}'.format( + obj._meta.app_label, + get_model_name_for_object(obj), + obj.pk, + safe_text(obj) + ) + values.append(value) + + # Overwrite ``cleaned_data`` of the ``form`` with object qualifier. + if values: + form.cleaned_data[self.data.name] = json.dumps(values) + else: + del form.cleaned_data[self.data.name] + + # It's critically important to return the ``form`` with updated + # ``cleaned_data`` + return form 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 94d0c2c1..f67cf7f2 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 @@ -1,31 +1,8 @@ -import simplejson as json +from __future__ import absolute_import -from django.forms.widgets import SelectMultiple -from django.utils.translation import ugettext_lazy as _ +from fobi.base import form_element_plugin_registry -from mptt.fields import TreeNodeMultipleChoiceField - -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme -from fobi.constants import ( - SUBMIT_VALUE_AS_VAL, SUBMIT_VALUE_AS_REPR - ) -from fobi.helpers import ( - safe_text, - get_app_label_and_model_name, - get_model_name_for_object -) - -from nine.versions import DJANGO_GTE_1_7 - -from . import UID -from .forms import SelectMultipleMPTTModelObjectsInputForm -from .settings import SUBMIT_VALUE_AS - -if DJANGO_GTE_1_7: - from django.apps import apps - get_model = apps.get_model -else: - from django.db.models import get_model +from .base import SelectMultipleMPTTModelObjectsInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.' \ 'select_multiple_mptt_model_objects.fobi_form_elements' @@ -34,85 +11,6 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('SelectMultipleMPTTModelObjectsInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class SelectMultipleMPTTModelObjectsInputPlugin(FormFieldPlugin): - """Select multiple MPTT model object field plugin.""" - - uid = UID - name = _("Select multiple MPTT model objects") - group = _("Fields") - form = SelectMultipleMPTTModelObjectsInputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - app_label, model_name = get_app_label_and_model_name(self.data.model) - model = get_model(app_label, model_name) - queryset = model._default_manager.all() - - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - 'required': self.data.required, - 'queryset': queryset, - 'widget': SelectMultiple( - attrs={'class': theme.form_element_html_class} - ), - } - - return [(self.data.name, TreeNodeMultipleChoiceField, field_kwargs)] - - def submit_plugin_form_data(self, form_entry, request, form): - """Submit plugin form data/process. - - :param fobi.models.FormEntry form_entry: Instance of - ``fobi.models.FormEntry``. - :param django.http.HttpRequest request: - :param django.forms.Form form: - """ - # In case if we should submit value as is, we don't return anything. - # In other cases, we proceed further. - - # Get the object - objs = form.cleaned_data.get(self.data.name, []) - - values = [] - - for obj in objs: - if obj: - value = None - # Should be returned as repr - if SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_REPR: - value = safe_text(obj) - elif SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_VAL: - value = '{0}.{1}.{2}'.format( - obj._meta.app_label, - get_model_name_for_object(obj), - obj.pk - ) - else: - # Handle the submitted form value - value = '{0}.{1}.{2}.{3}'.format( - obj._meta.app_label, - get_model_name_for_object(obj), - obj.pk, - safe_text(obj) - ) - values.append(value) - - # Overwrite ``cleaned_data`` of the ``form`` with object qualifier. - if values: - form.cleaned_data[self.data.name] = json.dumps(values) - else: - del form.cleaned_data[self.data.name] - - # It's critically important to return the ``form`` with updated - # ``cleaned_data`` - return form - form_element_plugin_registry.register( SelectMultipleMPTTModelObjectsInputPlugin diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/README.rst b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/README.rst index aca9aa91..a864db4f 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/README.rst @@ -19,9 +19,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. @@ -42,9 +42,11 @@ Installation ('gamma', 'Gamma'), ] - - "val": `value` (example: "alpha"). - - "repr" (default): `label` (example: "Alpha"). - - "mix": `value (label)` (example: "Alpha (alpha)"). + .. code-block:: text + + - "val": `value` (example: "alpha"). + - "repr" (default): `label` (example: "Alpha"). + - "mix": `value (label)` (example: "Alpha (alpha)"). Simply set the ``FOBI_FORM_ELEMENT_SELECT_MULTIPLE_WITH_MAX_SUBMIT_VALUE_AS`` assign one of the @@ -58,7 +60,7 @@ the 'max_choices' field, the user can choose only or less choices. For example: -.. code-block:: none +.. code-block:: text 1 2 diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/base.py b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/base.py new file mode 100644 index 00000000..006b78be --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/base.py @@ -0,0 +1,99 @@ +from __future__ import absolute_import + +from django.forms.widgets import SelectMultiple +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin, get_theme +from fobi.constants import ( + SUBMIT_VALUE_AS_VAL, + SUBMIT_VALUE_AS_REPR +) +from fobi.helpers import get_select_field_choices, safe_text + +from . import UID +from .fields import MultipleChoiceWithMaxField +from .forms import SelectMultipleWithMaxInputForm +from .settings import SUBMIT_VALUE_AS + +__title__ = 'fobi.contrib.plugins.form_elements.fields.' \ + 'select_multiple_with_max.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('SelectMultipleWithMaxInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class SelectMultipleWithMaxInputPlugin(FormFieldPlugin): + """Select multiple with max field plugin.""" + + uid = UID + name = _("Select multiple with max") + group = _("Fields") + form = SelectMultipleWithMaxInputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + choices = get_select_field_choices(self.data.choices) + + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + 'required': self.data.required, + 'choices': choices, + 'widget': SelectMultiple( + attrs={'class': theme.form_element_html_class} + ), + } + + if self.data.max_choices: + field_kwargs['max_choices'] = self.data.max_choices + + return [(self.data.name, MultipleChoiceWithMaxField, field_kwargs)] + + def submit_plugin_form_data(self, form_entry, request, form, + form_element_entries=None, **kwargs): + """Submit plugin form data/process. + + :param fobi.models.FormEntry form_entry: Instance of + ``fobi.models.FormEntry``. + :param django.http.HttpRequest request: + :param django.forms.Form form: + """ + # In case if we should submit value as is, we don't return anything. + # In other cases, we proceed further. + if SUBMIT_VALUE_AS != SUBMIT_VALUE_AS_VAL: + # Get the object + values = form.cleaned_data.get(self.data.name, None) + + # Get choices + choices = dict(get_select_field_choices(self.data.choices)) + + # Returned value + ret_values = [] + + for value in values: + # Handle the submitted form value + + if value in choices: + label = safe_text(choices.get(value)) + + # Should be returned as repr + if SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_REPR: + value = label + # Should be returned as mix + else: + value = "{0} ({1})".format(label, value) + + ret_values.append(value) + + # Overwrite ``cleaned_data`` of the ``form`` with object + # qualifier. + form.cleaned_data[self.data.name] = ret_values + + # It's critically important to return the ``form`` with updated + # ``cleaned_data`` + return form diff --git a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/fobi_form_elements.py index 2fbab69a..8f2dd1f3 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/select_multiple_with_max/fobi_form_elements.py @@ -1,16 +1,8 @@ -from django.forms.widgets import SelectMultiple -from django.utils.translation import ugettext_lazy as _ +from __future__ import absolute_import -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme -from fobi.constants import ( - SUBMIT_VALUE_AS_VAL, SUBMIT_VALUE_AS_REPR -) -from fobi.helpers import get_select_field_choices, safe_text +from fobi.base import form_element_plugin_registry -from . import UID -from .fields import MultipleChoiceWithMaxField -from .forms import SelectMultipleWithMaxInputForm -from .settings import SUBMIT_VALUE_AS +from .base import SelectMultipleWithMaxInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.' \ 'select_multiple_with_max.fobi_form_elements' @@ -19,80 +11,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('SelectMultipleWithMaxInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class SelectMultipleWithMaxInputPlugin(FormFieldPlugin): - """Select multiple with max field plugin.""" - - uid = UID - name = _("Select multiple with max") - group = _("Fields") - form = SelectMultipleWithMaxInputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - choices = get_select_field_choices(self.data.choices) - - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - 'required': self.data.required, - 'choices': choices, - 'widget': SelectMultiple( - attrs={'class': theme.form_element_html_class} - ), - } - - if self.data.max_choices: - field_kwargs['max_choices'] = self.data.max_choices - - return [(self.data.name, MultipleChoiceWithMaxField, field_kwargs)] - - def submit_plugin_form_data(self, form_entry, request, form): - """Submit plugin form data/process. - - :param fobi.models.FormEntry form_entry: Instance of - ``fobi.models.FormEntry``. - :param django.http.HttpRequest request: - :param django.forms.Form form: - """ - # In case if we should submit value as is, we don't return anything. - # In other cases, we proceed further. - if SUBMIT_VALUE_AS != SUBMIT_VALUE_AS_VAL: - # Get the object - values = form.cleaned_data.get(self.data.name, None) - - # Get choices - choices = dict(get_select_field_choices(self.data.choices)) - - # Returned value - ret_values = [] - - for value in values: - # Handle the submitted form value - - if value in choices: - label = safe_text(choices.get(value)) - - # Should be returned as repr - if SUBMIT_VALUE_AS == SUBMIT_VALUE_AS_REPR: - value = label - # Should be returned as mix - else: - value = "{0} ({1})".format(label, value) - - ret_values.append(value) - - # Overwrite ``cleaned_data`` of the ``form`` with object - # qualifier. - form.cleaned_data[self.data.name] = ret_values - - # It's critically important to return the ``form`` with updated - # ``cleaned_data`` - return form - form_element_plugin_registry.register(SelectMultipleWithMaxInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/slider/README.rst b/src/fobi/contrib/plugins/form_elements/fields/slider/README.rst index 51ca5adc..95c0397c 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/slider/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/slider/README.rst @@ -19,9 +19,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. @@ -29,15 +29,23 @@ Installation 4. Ranges are specified within the given min/max values. The default values are: - - INITIAL: 50 - - MIN_VALUE: 0 - - MAX_VALUE: 100 - - STEP: 1 +.. code-block:: text + + - INITIAL: 50 + - INITIAL_MAX_VALUE: 100 + - INITIAL_MIN_VALUE: 0 + - MIN_VALUE: 0 + - MAX_VALUE: 100 + - STEP: 1 However, you can override each of them in the settings of your project by prefixing correspondent names with `FOBI_FORM_ELEMENT_SLIDER_`: - - FOBI_FORM_ELEMENT_SLIDER_INITIAL - - FOBI_FORM_ELEMENT_SLIDER_MIN_VALUE - - FOBI_FORM_ELEMENT_SLIDER_MAX_VALUE - - FOBI_FORM_ELEMENT_SLIDER_STEP +.. code-block:: text + + - FOBI_FORM_ELEMENT_SLIDER_INITIAL + - FOBI_FORM_ELEMENT_SLIDER_INITIAL_MAX_VALUE + - FOBI_FORM_ELEMENT_SLIDER_INITIAL_MIN_VALUE + - FOBI_FORM_ELEMENT_SLIDER_MIN_VALUE + - FOBI_FORM_ELEMENT_SLIDER_MAX_VALUE + - FOBI_FORM_ELEMENT_SLIDER_STEP diff --git a/src/fobi/contrib/plugins/form_elements/fields/slider/base.py b/src/fobi/contrib/plugins/form_elements/fields/slider/base.py new file mode 100644 index 00000000..2f2dc1a6 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/slider/base.py @@ -0,0 +1,210 @@ +from __future__ import absolute_import + +from six import text_type + +from django.forms.fields import ChoiceField + +from django.utils.html import format_html +from django.utils.safestring import mark_safe +from django.utils.translation import ugettext_lazy as _ + +from nine import versions + +from fobi.base import ( + FormFieldPlugin, + get_theme +) +from fobi.helpers import get_select_field_choices +from fobi.widgets import RichSelectInverseQuotes + +from . import UID +from .constants import ( + SLIDER_DEFAULT_TOOLTIP, + SLIDER_DEFAULT_HANDLE, + SLIDER_SHOW_ENDPOINTS_AS_LABELED_TICKS, + SLIDER_SHOW_ENDPOINTS_AS_TICKS, + SLIDER_DEFAULT_SHOW_ENDPOINTS_AS +) +from .forms import SliderInputForm +from .helpers import generate_ticks +from .settings import ( + INITIAL, + INITIAL_MAX_VALUE, + INITIAL_MIN_VALUE, + # MAX_VALUE, + # MIN_VALUE, + STEP +) + +if versions.DJANGO_GTE_1_7: + from django.forms.utils import flatatt +else: + from django.forms.util import flatatt + +__title__ = 'fobi.contrib.plugins.form_elements.fields.slider.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('SliderInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class SliderInputPlugin(FormFieldPlugin): + """Slider field plugin.""" + + uid = UID + name = _("Slider") + group = _("Fields") + form = SliderInputForm + html_classes = ['slider'] + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + initial = self.data.initial if self.data.initial else INITIAL + max_value = self.data.max_value \ + if self.data.max_value \ + else INITIAL_MAX_VALUE + min_value = self.data.min_value \ + if self.data.min_value \ + else INITIAL_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, + key_type=int, + value_type=text_type) \ + if self.data.custom_ticks \ + else [] + + _choices = range(min_value, max_value+1, 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-value': initial, + 'data-slider-tooltip': tooltip, + 'data-slider-handle': handle, + } + + show_endpoints_as = self.data.show_endpoints_as \ + if self.data.show_endpoints_as \ + else SLIDER_DEFAULT_SHOW_ENDPOINTS_AS + + prepend_html_list = [] + append_html_list = [] + + # Show endpoints as labeled ticks + if SLIDER_SHOW_ENDPOINTS_AS_LABELED_TICKS == show_endpoints_as: + + if custom_ticks: + ticks_data = generate_ticks(custom_ticks) + else: + ticks_data = generate_ticks([ + (min_value, self.data.label_start), + (max_value, self.data.label_end), + ]) + # label_start = self.data.label_start \ + # if self.data.label_start \ + # else text_type(min_value) + # + # label_end = self.data.label_end \ + # if self.data.label_end \ + # else text_type(max_value) + + # widget_attrs.update({ + # 'data-slider-ticks': "[{0}, {1}]".format( + # min_value, max_value + # ), + # 'data-slider-ticks-labels': '["{0!s}", "{1!s}"]'.format( + # label_start.encode('utf8'), label_end.encode('utf8') + # ), + # }) + + widget_attrs.update(ticks_data) + + # Show endpoints as ticks + elif SLIDER_SHOW_ENDPOINTS_AS_TICKS == show_endpoints_as: + + if custom_ticks: + ticks_data = generate_ticks(custom_ticks, empty_labels=True) + else: + ticks_data = generate_ticks([ + (min_value, ""), + (max_value, ""), + ]) + + # widget_attrs.update({ + # 'data-slider-ticks': "[{0}, {1}]".format( + # min_value, max_value + # ), + # 'data-slider-ticks-labels': '["{0}", "{1}"]'.format( + # "", "" + # ), + # }) + + widget_attrs.update(ticks_data) + + # Show endpoints as labels + else: + + if self.data.label_start: + prepend_html_list.append( + format_html( + " ", + flatatt({'class': "slider-endpoint-label-start"}) + ) + ) + prepend_html_list.append(format_html(self.data.label_start)) + prepend_html_list.append(format_html(" ")) + + if self.data.label_end: + append_html_list.append( + format_html( + " ", + flatatt({'class': "slider-endpoint-label-end"}) + ) + ) + append_html_list.append(format_html(self.data.label_end)) + append_html_list.append(format_html(" ")) + + widget_kwargs = {'attrs': widget_attrs} + + # For showing endpoints as labels + if prepend_html_list: + widget_kwargs.update({ + 'prepend_html': mark_safe(''.join(prepend_html_list)), + }) + + if append_html_list: + widget_kwargs.update({ + 'append_html': mark_safe(''.join(append_html_list)), + }) + + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': initial, + 'required': self.data.required, + 'choices': choices, + 'widget': RichSelectInverseQuotes(**widget_kwargs), + } + + return [(self.data.name, ChoiceField, field_kwargs)] diff --git a/src/fobi/contrib/plugins/form_elements/fields/slider/constants.py b/src/fobi/contrib/plugins/form_elements/fields/slider/constants.py index 8110629f..c453aca5 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/slider/constants.py +++ b/src/fobi/contrib/plugins/form_elements/fields/slider/constants.py @@ -27,7 +27,7 @@ __all__ = ( SLIDER_TOOLTIP_SHOW = 'show' SLIDER_TOOLTIP_HIDE = 'hide' SLIDER_TOOLTIP_ALWAYS = 'always' -SLIDER_DEFAULT_TOOLTIP = SLIDER_TOOLTIP_HIDE +SLIDER_DEFAULT_TOOLTIP = SLIDER_TOOLTIP_SHOW SLIDER_TOOLTIP_CHOICES = ( (SLIDER_TOOLTIP_SHOW, _("Show")), diff --git a/src/fobi/contrib/plugins/form_elements/fields/slider/defaults.py b/src/fobi/contrib/plugins/form_elements/fields/slider/defaults.py index 42436b9b..4e48f2f1 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/slider/defaults.py +++ b/src/fobi/contrib/plugins/form_elements/fields/slider/defaults.py @@ -4,12 +4,16 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ( 'INITIAL', + 'INITIAL_MAX_VALUE', + 'INITIAL_MIN_VALUE', 'MAX_VALUE', 'MIN_VALUE', 'STEP', ) INITIAL = 50 +INITIAL_MIN_VALUE = 0 +INITIAL_MAX_VALUE = 100 MIN_VALUE = 0 MAX_VALUE = 5000 STEP = 1 diff --git a/src/fobi/contrib/plugins/form_elements/fields/slider/fobi_form_elements.py b/src/fobi/contrib/plugins/form_elements/fields/slider/fobi_form_elements.py index d22e8528..b290bbb5 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/slider/fobi_form_elements.py +++ b/src/fobi/contrib/plugins/form_elements/fields/slider/fobi_form_elements.py @@ -1,24 +1,8 @@ -from six import text_type +from __future__ import absolute_import -from django.forms.fields import ChoiceField -from django.forms.utils import flatatt -from django.utils.html import format_html -from django.utils.safestring import mark_safe -from django.utils.translation import ugettext_lazy as _ +from fobi.base import form_element_plugin_registry -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme -from fobi.widgets import RichSelect - -from . import UID -from .constants import ( - SLIDER_DEFAULT_TOOLTIP, - SLIDER_DEFAULT_HANDLE, - SLIDER_SHOW_ENDPOINTS_AS_LABELED_TICKS, - SLIDER_SHOW_ENDPOINTS_AS_TICKS, - SLIDER_DEFAULT_SHOW_ENDPOINTS_AS -) -from .forms import SliderInputForm -from .settings import INITIAL, MAX_VALUE, MIN_VALUE, STEP +from .base import SliderInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.slider.' \ 'fobi_form_elements' @@ -27,142 +11,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('SliderInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class SliderInputPlugin(FormFieldPlugin): - """Slider field plugin.""" - - uid = UID - name = _("Slider") - group = _("Fields") - form = SliderInputForm - html_classes = ['slider'] - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - initial = self.data.initial if self.data.initial else INITIAL - 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+1, 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-value': initial, - 'data-slider-tooltip': tooltip, - 'data-slider-handle': handle, - } - - show_endpoints_as = self.data.show_endpoints_as \ - if self.data.show_endpoints_as \ - else SLIDER_DEFAULT_SHOW_ENDPOINTS_AS - - prepend_html_list = [] - append_html_list = [] - - # Show endpoints as labeled ticks - if SLIDER_SHOW_ENDPOINTS_AS_LABELED_TICKS == show_endpoints_as: - - label_start = self.data.label_start \ - if self.data.label_start \ - else text_type(min_value) - - label_end = self.data.label_end \ - if self.data.label_end \ - else text_type(max_value) - - widget_attrs.update({ - 'data-slider-ticks': "[{0}, {1}]".format( - min_value, max_value - ), - 'data-slider-ticks-labels': '["{0!s}", "{1!s}"]'.format( - label_start.encode('utf8'), label_end.encode('utf8') - ), - }) - - # Show endpoints as ticks - elif SLIDER_SHOW_ENDPOINTS_AS_TICKS == show_endpoints_as: - - widget_attrs.update({ - 'data-slider-ticks': "[{0}, {1}]".format( - min_value, max_value - ), - 'data-slider-ticks-labels': '["{0}", "{1}"]'.format( - "", "" - ), - }) - - # Show endpoints as labels - else: - - if self.data.label_start: - prepend_html_list.append( - format_html( - " ", - flatatt({'class': "slider-endpoint-label-start"}) - ) - ) - prepend_html_list.append(format_html(self.data.label_start)) - prepend_html_list.append(format_html(" ")) - - if self.data.label_end: - append_html_list.append( - format_html( - " ", - flatatt({'class': "slider-endpoint-label-end"}) - ) - ) - append_html_list.append(format_html(self.data.label_end)) - append_html_list.append(format_html(" ")) - - widget_kwargs = {'attrs': widget_attrs} - - # For showing endpoints as labels - if prepend_html_list: - widget_kwargs.update({ - 'prepend_html': mark_safe(''.join(prepend_html_list)), - }) - - if append_html_list: - widget_kwargs.update({ - 'append_html': mark_safe(''.join(append_html_list)), - }) - - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': initial, - 'required': self.data.required, - 'choices': choices, - 'widget': RichSelect(**widget_kwargs), - } - - return [(self.data.name, ChoiceField, field_kwargs)] - form_element_plugin_registry.register(SliderInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/slider/forms.py b/src/fobi/contrib/plugins/form_elements/fields/slider/forms.py index b0df30af..04082491 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/slider/forms.py +++ b/src/fobi/contrib/plugins/form_elements/fields/slider/forms.py @@ -2,6 +2,7 @@ from django import forms from django.utils.translation import ugettext_lazy as _ from fobi.base import BaseFormFieldPluginForm, get_theme +from fobi.helpers import get_select_field_choices from fobi.widgets import NumberInput from .constants import ( @@ -17,7 +18,14 @@ from .constants import ( SLIDER_SHOW_ENDPOINTS_AS_TICKS ) -from .settings import INITIAL, MAX_VALUE, MIN_VALUE, STEP +from .settings import ( + INITIAL, + INITIAL_MAX_VALUE, + INITIAL_MIN_VALUE, + MAX_VALUE, + MIN_VALUE, + STEP +) __title__ = 'fobi.contrib.plugins.form_elements.fields.slider.forms' __author__ = 'Artur Barseghyan ' @@ -35,8 +43,8 @@ class SliderInputForm(forms.Form, BaseFormFieldPluginForm): ("label", ""), ("name", ""), ("initial", INITIAL), - ("min_value", MIN_VALUE), - ("max_value", MAX_VALUE), + ("min_value", INITIAL_MIN_VALUE), + ("max_value", INITIAL_MAX_VALUE), ("step", STEP), ("tooltip", SLIDER_DEFAULT_TOOLTIP), ("handle", SLIDER_DEFAULT_HANDLE), @@ -44,7 +52,7 @@ class SliderInputForm(forms.Form, BaseFormFieldPluginForm): ("show_endpoints_as", SLIDER_DEFAULT_SHOW_ENDPOINTS_AS), ("label_start", ""), ("label_end", ""), - # ("custom_ticks", ""), + ("custom_ticks", ""), ("help_text", ""), ("required", False) ] @@ -74,6 +82,7 @@ class SliderInputForm(forms.Form, BaseFormFieldPluginForm): min_value = forms.IntegerField( label=_("Min value"), required=True, + initial=INITIAL_MIN_VALUE, widget=NumberInput(attrs={'class': theme.form_element_html_class}), min_value=MIN_VALUE, max_value=MAX_VALUE @@ -81,6 +90,7 @@ class SliderInputForm(forms.Form, BaseFormFieldPluginForm): max_value = forms.IntegerField( label=_("Max value"), required=True, + initial=INITIAL_MAX_VALUE, widget=NumberInput(attrs={'class': theme.form_element_html_class}), min_value=MIN_VALUE, max_value=MAX_VALUE @@ -140,27 +150,27 @@ class SliderInputForm(forms.Form, BaseFormFieldPluginForm): 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:
" - # "    1
" - # "    2
" - # "    3, Alpha
" - # "    4, Beta
" - # "

" - # "It finally transforms into the following HTML " - # "code:
" - # '    ' - # 'data-slider-ticks="[1, 2, 3, 4]"
' - # '    ' - # "data-slider-ticks-labels='" - # '["1", "2", "Alpha", "Beta"]' - # "'
"), - # widget=forms.widgets.Textarea( - # 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:
" + "    1
" + "    2
" + "    3, Alpha
" + "    4, Beta
" + "

" + "It finally transforms into the following HTML " + "code:
" + '    ' + 'data-slider-ticks="[1, 2, 3, 4]"
' + '    ' + "data-slider-ticks-labels='" + '["1", "2", "Alpha", "Beta"]' + "'
"), + widget=forms.widgets.Textarea( + attrs={'class': theme.form_element_html_class} + ) + ) help_text = forms.CharField( label=_("Help text"), required=False, @@ -186,6 +196,7 @@ class SliderInputForm(forms.Form, BaseFormFieldPluginForm): step = self.cleaned_data['step'] show_endpoints_as = self.cleaned_data['show_endpoints_as'] handle = self.cleaned_data['handle'] + custom_ticks = self.cleaned_data['custom_ticks'] if max_value < min_value: self.add_error( @@ -222,3 +233,17 @@ class SliderInputForm(forms.Form, BaseFormFieldPluginForm): _("You are not allowed to use Triangle or Custom handles " "with ticks enabled.") ) + + if custom_ticks: + ticks = get_select_field_choices( + custom_ticks, + key_type=int, + value_type=str, + fail_silently=False + ) + if ticks is None: + self.add_error( + 'custom_ticks', + _("Invalid format. First value should be an integer, " + "second value should be a string.") + ) diff --git a/src/fobi/contrib/plugins/form_elements/fields/slider/helpers.py b/src/fobi/contrib/plugins/form_elements/fields/slider/helpers.py new file mode 100644 index 00000000..60b7402d --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/slider/helpers.py @@ -0,0 +1,39 @@ +from six import text_type + +from django.utils.html import format_html + +__title__ = 'fobi.contrib.plugins.form_elements.fields.slider.helpers' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2015 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ( + 'generate_ticks', +) + + +def generate_ticks(choices, empty_labels=False): + """Generate ticks. + + :param iterable choices: Iterable of tuples or lists: + :param bool empty_labels: + :return dict: + """ + keys = [int(k) for (k, v) in choices] + # values = [v for (k, v) in choices if v else text_type(k)] + values = [] + + if empty_labels: + values = ["".encode('utf8') for k in keys] + else: + for k, v in choices: + if v is not None: + values.append(v.encode('utf8')) + else: + values.append(text_type(k).encode('utf8')) + + ticks = { + 'data-slider-ticks': format_html(str(keys)), + 'data-slider-ticks-labels': format_html(str(values).replace("'", '"')), + } + + return ticks diff --git a/src/fobi/contrib/plugins/form_elements/fields/slider/settings.py b/src/fobi/contrib/plugins/form_elements/fields/slider/settings.py index bd3ca811..7efae34e 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/slider/settings.py +++ b/src/fobi/contrib/plugins/form_elements/fields/slider/settings.py @@ -6,6 +6,8 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ( 'INITIAL', + 'INITIAL_MAX_VALUE', + 'INITIAL_MIN_VALUE', 'MAX_VALUE', 'MIN_VALUE', 'STEP', @@ -13,6 +15,10 @@ __all__ = ( INITIAL = get_setting('INITIAL') +INITIAL_MAX_VALUE = get_setting('INITIAL_MAX_VALUE') + +INITIAL_MIN_VALUE = get_setting('INITIAL_MIN_VALUE') + MAX_VALUE = get_setting('MAX_VALUE') MIN_VALUE = get_setting('MIN_VALUE') diff --git a/src/fobi/contrib/plugins/form_elements/fields/slider/widgets.py b/src/fobi/contrib/plugins/form_elements/fields/slider/widgets.py index 5d29edb2..5eaaef17 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/slider/widgets.py +++ b/src/fobi/contrib/plugins/form_elements/fields/slider/widgets.py @@ -8,7 +8,9 @@ __title__ = 'fobi.contrib.plugins.form_elements.fields.slider.widgets' __author__ = 'Artur Barseghyan ' __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' -__all__ = ('BaseSliderPluginWidget', ) +__all__ = ( + 'BaseSliderPluginWidget', +) class BaseSliderPluginWidget(FormElementPluginWidget): diff --git a/src/fobi/contrib/plugins/form_elements/fields/slug/README.rst b/src/fobi/contrib/plugins/form_elements/fields/slug/README.rst index a842be28..f4963bbd 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/slug/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/slug/README.rst @@ -19,9 +19,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/plugins/form_elements/fields/slug/base.py b/src/fobi/contrib/plugins/form_elements/fields/slug/base.py new file mode 100644 index 00000000..b65bd0fc --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/slug/base.py @@ -0,0 +1,47 @@ +from __future__ import absolute_import + +from django.forms.fields import SlugField +from django.forms.widgets import TextInput +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin, get_theme + +from . import UID +from .forms import SlugInputForm + +__title__ = 'fobi.contrib.plugins.form_elements.fields.slug.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('SlugInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class SlugInputPlugin(FormFieldPlugin): + """Slug field plugin.""" + + uid = UID + name = _("Slug") + group = _("Fields") + form = SlugInputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + widget_attrs = { + 'class': theme.form_element_html_class, + 'placeholder': self.data.placeholder, + } + + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + 'required': self.data.required, + 'widget': TextInput(attrs=widget_attrs), + } + if self.data.max_length: + field_kwargs['max_length'] = self.data.max_length + + return [(self.data.name, SlugField, field_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 26ac9610..f3dcadfc 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 @@ -1,11 +1,8 @@ -from django.forms.fields import SlugField -from django.forms.widgets import TextInput -from django.utils.translation import ugettext_lazy as _ +from __future__ import absolute_import -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme +from fobi.base import form_element_plugin_registry -from . import UID -from .forms import SlugInputForm +from .base import SlugInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.slug.fobi_form_elements' __author__ = 'Artur Barseghyan ' @@ -13,36 +10,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('SlugInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class SlugInputPlugin(FormFieldPlugin): - """Slug field plugin.""" - - uid = UID - name = _("Slug") - group = _("Fields") - form = SlugInputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - widget_attrs = { - 'class': theme.form_element_html_class, - 'placeholder': self.data.placeholder, - } - - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - 'required': self.data.required, - 'widget': TextInput(attrs=widget_attrs), - } - if self.data.max_length: - field_kwargs['max_length'] = self.data.max_length - - return [(self.data.name, SlugField, field_kwargs)] - form_element_plugin_registry.register(SlugInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/slug/forms.py b/src/fobi/contrib/plugins/form_elements/fields/slug/forms.py index b4bf2310..90d13a03 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/slug/forms.py +++ b/src/fobi/contrib/plugins/form_elements/fields/slug/forms.py @@ -1,8 +1,9 @@ from django import forms from django.utils.translation import ugettext_lazy as _ +from django.core.validators import MinValueValidator from fobi.base import BaseFormFieldPluginForm, get_theme -from fobi.settings import DEFAULT_MAX_LENGTH +from fobi.settings import DEFAULT_MAX_LENGTH, DEFAULT_MIN_LENGTH from fobi.widgets import NumberInput __title__ = 'fobi.contrib.plugins.form_elements.fields.slug.forms' @@ -22,7 +23,7 @@ class SlugInputForm(forms.Form, BaseFormFieldPluginForm): ("name", ""), ("help_text", ""), ("initial", ""), - ("max_length", "255"), + ("max_length", str(DEFAULT_MAX_LENGTH)), ("required", False), ("placeholder", ""), ] @@ -58,8 +59,10 @@ class SlugInputForm(forms.Form, BaseFormFieldPluginForm): max_length = forms.IntegerField( label=_("Max length"), required=True, - widget=NumberInput(attrs={'class': theme.form_element_html_class}), - initial=DEFAULT_MAX_LENGTH + widget=NumberInput(attrs={'class': theme.form_element_html_class, + 'min': str(DEFAULT_MIN_LENGTH)}), + initial=DEFAULT_MAX_LENGTH, + validators=[MinValueValidator(DEFAULT_MIN_LENGTH)] ) required = forms.BooleanField( label=_("Required"), @@ -75,3 +78,17 @@ class SlugInputForm(forms.Form, BaseFormFieldPluginForm): attrs={'class': theme.form_element_html_class} ) ) + + def clean(self): + super(SlugInputForm, self).clean() + + max_length = self.cleaned_data.get('max_length', DEFAULT_MAX_LENGTH) + + if self.cleaned_data['initial']: + len_initial = len(self.cleaned_data['initial']) + if len_initial > max_length: + self.add_error( + 'initial', + _("Ensure this value has at most {0} characters " + "(it has {1}).".format(max_length, len_initial)) + ) diff --git a/src/fobi/contrib/plugins/form_elements/fields/text/README.rst b/src/fobi/contrib/plugins/form_elements/fields/text/README.rst index ff50c177..499f236f 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/text/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/text/README.rst @@ -19,9 +19,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/plugins/form_elements/fields/text/base.py b/src/fobi/contrib/plugins/form_elements/fields/text/base.py new file mode 100644 index 00000000..3e89f02b --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/text/base.py @@ -0,0 +1,47 @@ +from __future__ import absolute_import + +from django.forms.fields import CharField +from django.forms.widgets import TextInput +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin, get_theme + +from . import UID +from .forms import TextInputForm + +__title__ = 'fobi.contrib.plugins.form_elements.fields.text.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('TextInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class TextInputPlugin(FormFieldPlugin): + """Text field plugin.""" + + uid = UID + name = _("Text") + group = _("Fields") + form = TextInputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + widget_attrs = { + 'class': theme.form_element_html_class, + 'placeholder': self.data.placeholder, + } + + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + 'required': self.data.required, + 'widget': TextInput(attrs=widget_attrs), + } + if self.data.max_length: + field_kwargs['max_length'] = self.data.max_length + + return [(self.data.name, CharField, field_kwargs)] 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 9ab469f3..4e07e2e5 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 @@ -1,11 +1,8 @@ -from django.forms.fields import CharField -from django.forms.widgets import TextInput -from django.utils.translation import ugettext_lazy as _ +from __future__ import absolute_import -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme +from fobi.base import form_element_plugin_registry -from . import UID -from .forms import TextInputForm +from .base import TextInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.text.fobi_form_elements' __author__ = 'Artur Barseghyan ' @@ -13,36 +10,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('TextInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class TextInputPlugin(FormFieldPlugin): - """Text field plugin.""" - - uid = UID - name = _("Text") - group = _("Fields") - form = TextInputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - widget_attrs = { - 'class': theme.form_element_html_class, - 'placeholder': self.data.placeholder, - } - - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - 'required': self.data.required, - 'widget': TextInput(attrs=widget_attrs), - } - if self.data.max_length: - field_kwargs['max_length'] = self.data.max_length - - return [(self.data.name, CharField, field_kwargs)] - form_element_plugin_registry.register(TextInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/text/forms.py b/src/fobi/contrib/plugins/form_elements/fields/text/forms.py index 6424c247..8d53d0c7 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/text/forms.py +++ b/src/fobi/contrib/plugins/form_elements/fields/text/forms.py @@ -1,8 +1,9 @@ from django import forms from django.utils.translation import ugettext_lazy as _ +from django.core.validators import MinValueValidator from fobi.base import BaseFormFieldPluginForm, get_theme -from fobi.settings import DEFAULT_MAX_LENGTH +from fobi.settings import DEFAULT_MAX_LENGTH, DEFAULT_MIN_LENGTH from fobi.widgets import NumberInput __title__ = 'fobi.contrib.plugins.form_elements.fields.text.forms' @@ -22,7 +23,7 @@ class TextInputForm(forms.Form, BaseFormFieldPluginForm): ("name", ""), ("help_text", ""), ("initial", ""), - ("max_length", "255"), + ("max_length", str(DEFAULT_MAX_LENGTH)), ("required", False), ("placeholder", ""), ] @@ -58,8 +59,10 @@ class TextInputForm(forms.Form, BaseFormFieldPluginForm): max_length = forms.IntegerField( label=_("Max length"), required=True, - widget=NumberInput(attrs={'class': theme.form_element_html_class}), - initial=DEFAULT_MAX_LENGTH + widget=NumberInput(attrs={'class': theme.form_element_html_class, + 'min': str(DEFAULT_MIN_LENGTH)}), + initial=DEFAULT_MAX_LENGTH, + validators=[MinValueValidator(DEFAULT_MIN_LENGTH)] ) required = forms.BooleanField( label=_("Required"), @@ -75,3 +78,18 @@ class TextInputForm(forms.Form, BaseFormFieldPluginForm): attrs={'class': theme.form_element_html_class} ) ) + + def clean(self): + """Validation.""" + super(TextInputForm, self).clean() + + max_length = self.cleaned_data.get('max_length', DEFAULT_MAX_LENGTH) + + if self.cleaned_data['initial']: + len_initial = len(self.cleaned_data['initial']) + if len_initial > max_length: + self.add_error( + 'initial', + _("Ensure this value has at most {0} characters " + "(it has {1}).".format(max_length, len_initial)) + ) diff --git a/src/fobi/contrib/plugins/form_elements/fields/textarea/README.rst b/src/fobi/contrib/plugins/form_elements/fields/textarea/README.rst index ba664f48..a7e6ec26 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/textarea/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/textarea/README.rst @@ -19,9 +19,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/plugins/form_elements/fields/textarea/base.py b/src/fobi/contrib/plugins/form_elements/fields/textarea/base.py new file mode 100644 index 00000000..9f2a1e3a --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/textarea/base.py @@ -0,0 +1,44 @@ +from __future__ import absolute_import + +from django.forms.fields import CharField +from django.forms.widgets import Textarea +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin, get_theme + +from . import UID +from .forms import TextareaForm + +__title__ = 'fobi.contrib.plugins.form_elements.fields.textarea.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('TextInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class TextareaPlugin(FormFieldPlugin): + """Textarea field plugin.""" + + uid = UID + name = _("Textarea") + group = _("Fields") + form = TextareaForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + widget_attrs = { + 'class': theme.form_element_html_class, + 'placeholder': self.data.placeholder, + } + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + 'required': self.data.required, + 'widget': Textarea(attrs=widget_attrs) + } + + return [(self.data.name, CharField, field_kwargs)] 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 3f99ecc5..219159dd 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 @@ -1,11 +1,8 @@ -from django.forms.fields import CharField -from django.forms.widgets import Textarea -from django.utils.translation import ugettext_lazy as _ +from __future__ import absolute_import -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme +from fobi.base import form_element_plugin_registry -from . import UID -from .forms import TextareaForm +from .base import TextareaPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.' \ 'textarea.fobi_form_elements' @@ -14,33 +11,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('TextInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class TextareaPlugin(FormFieldPlugin): - """Textarea field plugin.""" - - uid = UID - name = _("Textarea") - group = _("Fields") - form = TextareaForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - widget_attrs = { - 'class': theme.form_element_html_class, - 'placeholder': self.data.placeholder, - } - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - 'required': self.data.required, - 'widget': Textarea(attrs=widget_attrs) - } - - return [(self.data.name, CharField, field_kwargs)] - form_element_plugin_registry.register(TextareaPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/time/README.rst b/src/fobi/contrib/plugins/form_elements/fields/time/README.rst index cf90fc58..94a39685 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/time/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/time/README.rst @@ -20,9 +20,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/plugins/form_elements/fields/time/base.py b/src/fobi/contrib/plugins/form_elements/fields/time/base.py new file mode 100644 index 00000000..2ef918e3 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/time/base.py @@ -0,0 +1,72 @@ +from __future__ import absolute_import + +from django.forms.fields import TimeField +from django.forms.widgets import TextInput +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin, get_theme + +from . import UID +from .forms import TimeInputForm + +__title__ = 'fobi.contrib.plugins.form_elements.fields.time.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('TimeInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class TimeInputPlugin(FormFieldPlugin): + """Time field plugin.""" + + uid = UID + name = _("Time") + group = _("Fields") + form = TimeInputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + widget_attrs = { + 'class': theme.form_element_html_class, + 'type': 'time', + } + + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + # 'input_formats': self.data.input_formats, + 'required': self.data.required, + 'widget': TextInput(attrs=widget_attrs), + } + # if self.data.input_formats: + # kwargs['input_formats'] = self.data.input_formats + + return [(self.data.name, TimeField, field_kwargs)] + + def submit_plugin_form_data(self, form_entry, request, form, + form_element_entries=None, **kwargs): + """Submit plugin form data/process. + + :param fobi.models.FormEntry form_entry: Instance of + ``fobi.models.FormEntry``. + :param django.http.HttpRequest request: + :param django.forms.Form form: + """ + # In case if we should submit value as is, we don't return anything. + # In other cases, we proceed further. + + # Get the object + value = form.cleaned_data.get(self.data.name, None) + try: + value = value.strftime("%H:%M:%S") + except Exception as err: + pass + + # Overwrite ``cleaned_data`` of the ``form`` with object qualifier. + form.cleaned_data[self.data.name] = value + + return form 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 cfe280ac..b38ca229 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 @@ -1,13 +1,8 @@ from __future__ import absolute_import -from django.forms.fields import TimeField -from django.forms.widgets import TextInput -from django.utils.translation import ugettext_lazy as _ +from fobi.base import form_element_plugin_registry -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme - -from . import UID -from .forms import TimeInputForm +from .base import TimeInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.' \ 'time.fobi_form_elements' @@ -16,60 +11,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('TimeInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class TimeInputPlugin(FormFieldPlugin): - """Time field plugin.""" - - uid = UID - name = _("Time") - group = _("Fields") - form = TimeInputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - widget_attrs = { - 'class': theme.form_element_html_class, - 'type': 'time', - } - - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - # 'input_formats': self.data.input_formats, - 'required': self.data.required, - 'widget': TextInput(attrs=widget_attrs), - } - # if self.data.input_formats: - # kwargs['input_formats'] = self.data.input_formats - - return [(self.data.name, TimeField, field_kwargs)] - - def submit_plugin_form_data(self, form_entry, request, form): - """Submit plugin form data/process. - - :param fobi.models.FormEntry form_entry: Instance of - ``fobi.models.FormEntry``. - :param django.http.HttpRequest request: - :param django.forms.Form form: - """ - # In case if we should submit value as is, we don't return anything. - # In other cases, we proceed further. - - # Get the object - value = form.cleaned_data.get(self.data.name, None) - try: - value = value.strftime("%H:%M:%S") - except Exception as err: - pass - - # Overwrite ``cleaned_data`` of the ``form`` with object qualifier. - form.cleaned_data[self.data.name] = value - - return form - form_element_plugin_registry.register(TimeInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/url/README.rst b/src/fobi/contrib/plugins/form_elements/fields/url/README.rst index e70596d7..595fb703 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/url/README.rst +++ b/src/fobi/contrib/plugins/form_elements/fields/url/README.rst @@ -20,9 +20,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/plugins/form_elements/fields/url/base.py b/src/fobi/contrib/plugins/form_elements/fields/url/base.py new file mode 100644 index 00000000..4494fb06 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/fields/url/base.py @@ -0,0 +1,58 @@ +from __future__ import absolute_import + +from django.forms.fields import URLField + +try: + from django.forms.widgets import URLInput +except ImportError: + from django.forms.widgets import TextInput + + class URLInput(TextInput): + """URL input.""" + + input_type = 'url' + +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormFieldPlugin, get_theme + +from . import UID +from .forms import URLInputForm + +__title__ = 'fobi.contrib.plugins.form_elements.fields.url.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('URLInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class URLInputPlugin(FormFieldPlugin): + """URL input plugin.""" + + uid = UID + name = _("URL") + group = _("Fields") + form = URLInputForm + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + widget_attrs = { + 'class': theme.form_element_html_class, + 'type': 'url', + 'placeholder': self.data.placeholder, + } + + field_kwargs = { + 'label': self.data.label, + 'help_text': self.data.help_text, + 'initial': self.data.initial, + 'required': self.data.required, + 'widget': URLInput(attrs=widget_attrs), + } + if self.data.max_length: + field_kwargs['max_length'] = self.data.max_length + + return [(self.data.name, URLField, field_kwargs)] 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 88b7608e..eee5cd3b 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 @@ -1,21 +1,8 @@ -from django.forms.fields import URLField +from __future__ import absolute_import -try: - from django.forms.widgets import URLInput -except ImportError: - from django.forms.widgets import TextInput +from fobi.base import form_element_plugin_registry - class URLInput(TextInput): - """URL input.""" - - input_type = 'url' - -from django.utils.translation import ugettext_lazy as _ - -from fobi.base import FormFieldPlugin, form_element_plugin_registry, get_theme - -from . import UID -from .forms import URLInputForm +from .base import URLInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.fields.url.fobi_form_elements' __author__ = 'Artur Barseghyan ' @@ -23,37 +10,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('URLInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class URLInputPlugin(FormFieldPlugin): - """URL input plugin.""" - - uid = UID - name = _("URL") - group = _("Fields") - form = URLInputForm - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - widget_attrs = { - 'class': theme.form_element_html_class, - 'type': 'url', - 'placeholder': self.data.placeholder, - } - - field_kwargs = { - 'label': self.data.label, - 'help_text': self.data.help_text, - 'initial': self.data.initial, - 'required': self.data.required, - 'widget': URLInput(attrs=widget_attrs), - } - if self.data.max_length: - field_kwargs['max_length'] = self.data.max_length - - return [(self.data.name, URLField, field_kwargs)] - form_element_plugin_registry.register(URLInputPlugin) diff --git a/src/fobi/contrib/plugins/form_elements/fields/url/forms.py b/src/fobi/contrib/plugins/form_elements/fields/url/forms.py index d70ec735..c820c5cf 100644 --- a/src/fobi/contrib/plugins/form_elements/fields/url/forms.py +++ b/src/fobi/contrib/plugins/form_elements/fields/url/forms.py @@ -1,8 +1,9 @@ from django import forms from django.utils.translation import ugettext_lazy as _ +from django.core.validators import MinValueValidator from fobi.base import BaseFormFieldPluginForm, get_theme -from fobi.settings import DEFAULT_MAX_LENGTH +from fobi.settings import DEFAULT_MAX_LENGTH, DEFAULT_MIN_LENGTH from fobi.widgets import NumberInput __title__ = 'fobi.contrib.plugins.form_elements.fields.url.forms' @@ -22,7 +23,7 @@ class URLInputForm(forms.Form, BaseFormFieldPluginForm): ("name", ""), ("help_text", ""), ("initial", ""), - ("max_length", "255"), + ("max_length", str(DEFAULT_MAX_LENGTH)), ("required", False), ("placeholder", ""), ] @@ -58,8 +59,10 @@ class URLInputForm(forms.Form, BaseFormFieldPluginForm): max_length = forms.IntegerField( label=_("Max length"), required=True, - widget=NumberInput(attrs={'class': theme.form_element_html_class}), - initial=DEFAULT_MAX_LENGTH + widget=NumberInput(attrs={'class': theme.form_element_html_class, + 'min': str(DEFAULT_MIN_LENGTH)}), + initial=DEFAULT_MAX_LENGTH, + validators=[MinValueValidator(DEFAULT_MIN_LENGTH)] ) required = forms.BooleanField( label=_("Required"), @@ -75,3 +78,17 @@ class URLInputForm(forms.Form, BaseFormFieldPluginForm): attrs={'class': theme.form_element_html_class} ) ) + + def clean(self): + super(URLInputForm, self).clean() + + max_length = self.cleaned_data.get('max_length', DEFAULT_MAX_LENGTH) + + if self.cleaned_data['initial']: + len_initial = len(self.cleaned_data['initial']) + if len_initial > max_length: + self.add_error( + 'initial', + _("Ensure this value has at most {0} characters " + "(it has {1}).".format(max_length, len_initial)) + ) 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 b8d24ff9..03bf3fd4 100644 --- a/src/fobi/contrib/plugins/form_elements/security/captcha/README.rst +++ b/src/fobi/contrib/plugins/form_elements/security/captcha/README.rst @@ -5,6 +5,14 @@ A `CAPTCHA `_ form field plugin. Makes use of the `django-simple-captcha `_. +Prerequisites +============= +You will need `libfreetype6`, otherwise `django-captcha` won't work. + +.. code-block:: sh + + sudo apt-get install libfreetype6-dev + Installation ============ Install `django-simple-captcha` @@ -14,9 +22,9 @@ Taken from django-simple-captcha `installation instructions 1. Download ``django-simple-captcha`` using pip by running: -.. code-block:: none +.. code-block:: sh - $ pip install django-simple-captcha + pip install django-simple-captcha 2. Add ``captcha`` to the ``INSTALLED_APPS`` in your ``settings.py``. @@ -47,9 +55,9 @@ Install `fobi` Captcha plugin 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. @@ -66,12 +74,6 @@ packages. That limitation is likely to be solved in future in the ``django-recaptcha`` package. Until then, you should choose either one or another, but not both on the same time. -In form wizards ---------------- -At the moment, captcha fields do not work in form wizards, as they are -invalidated on the last step, which breaks the cycle. Therefore, it's not -recommended to use captcha plugins in form wizards. - Usage ===== Note, that unlike most of the other form element plugins, default 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 29c71c57..b995851c 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 @@ -1,3 +1,5 @@ +from __future__ import absolute_import + import logging from django.utils.translation import ugettext_lazy as _ 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 0bda21df..894dcdb7 100644 --- a/src/fobi/contrib/plugins/form_elements/security/honeypot/README.rst +++ b/src/fobi/contrib/plugins/form_elements/security/honeypot/README.rst @@ -19,9 +19,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/plugins/form_elements/security/honeypot/base.py b/src/fobi/contrib/plugins/form_elements/security/honeypot/base.py new file mode 100644 index 00000000..ea42dcf2 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/security/honeypot/base.py @@ -0,0 +1,48 @@ +from __future__ import absolute_import + +from django.forms.widgets import HiddenInput +from django.utils.translation import ugettext_lazy as _ + +from fobi.base import FormElementPlugin, get_theme + +from . import UID +from .fields import HoneypotField +from .forms import HoneypotInputForm + +__title__ = 'fobi.contrib.plugins.form_elements.security.honeypot.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('HoneypotInputPlugin',) + +theme = get_theme(request=None, as_instance=True) + + +class HoneypotInputPlugin(FormElementPlugin): + """Honeypot field plugin.""" + + uid = UID + name = _("Honeypot") + group = _("Security") + form = HoneypotInputForm + is_hidden = True + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + + field_kwargs = { + 'label': self.data.label, + 'initial': self.data.initial, + # 'help_text': self.data.help_text, + 'required': self.data.required, + 'widget': HiddenInput( + attrs={'class': theme.form_element_html_class} + ), + } + + if self.data.max_length: + field_kwargs['max_length'] = self.data.max_length + + # return [(self.data.name, (HoneypotField, TextInput), kwargs)] + return [(self.data.name, HoneypotField, field_kwargs)] 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 b1e04de8..9f2a6458 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 @@ -1,13 +1,8 @@ -from django.forms.widgets import HiddenInput -from django.utils.translation import ugettext_lazy as _ +from __future__ import absolute_import -from fobi.base import ( - FormElementPlugin, form_element_plugin_registry, get_theme -) +from fobi.base import form_element_plugin_registry -from . import UID -from .fields import HoneypotField -from .forms import HoneypotInputForm +from .base import HoneypotInputPlugin __title__ = 'fobi.contrib.plugins.form_elements.security.' \ 'honeypot.fobi_form_elements' @@ -16,37 +11,5 @@ __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('HoneypotInputPlugin',) -theme = get_theme(request=None, as_instance=True) - - -class HoneypotInputPlugin(FormElementPlugin): - """Honeypot field plugin.""" - - uid = UID - name = _("Honeypot") - group = _("Security") - form = HoneypotInputForm - is_hidden = True - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - - field_kwargs = { - 'label': self.data.label, - 'initial': self.data.initial, - # 'help_text': self.data.help_text, - 'required': self.data.required, - 'widget': HiddenInput( - attrs={'class': theme.form_element_html_class} - ), - } - - if self.data.max_length: - field_kwargs['max_length'] = self.data.max_length - - # return [(self.data.name, (HoneypotField, TextInput), kwargs)] - return [(self.data.name, HoneypotField, field_kwargs)] - form_element_plugin_registry.register(HoneypotInputPlugin) 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 179c362b..810dfa91 100644 --- a/src/fobi/contrib/plugins/form_elements/security/recaptcha/README.rst +++ b/src/fobi/contrib/plugins/form_elements/security/recaptcha/README.rst @@ -11,9 +11,9 @@ Install `django-recaptcha` -------------------------- 1. Download ``django-recaptcha`` using pip by running: -.. code-block:: none +.. code-block:: sh - $ pip install django-recaptcha + pip install django-recaptcha 2. Add ``captcha`` to the ``INSTALLED_APPS`` in your ``settings.py``. @@ -36,15 +36,17 @@ Install `fobi` ReCAPTCHA plugin 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. 4. Specify the following ReCAPTCHA credentials in your settings. +.. code-block:: text + - ``RECAPTCHA_PUBLIC_KEY`` - ``RECAPTCHA_PRIVATE_KEY`` @@ -66,12 +68,6 @@ invalid", make sure to have defined (and filled in properly) the See the `following `_ thread for more information. -In form wizards ---------------- -At the moment, captcha fields do not work in form wizards, as they are -invalidated on the last step, which breaks the cycle. Therefore, it's not -recommended to use captcha plugins in form wizards. - Usage ===== Note, that unlike most of the other form element plugins, default 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 4674f119..01964285 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 @@ -1,3 +1,5 @@ +from __future__ import absolute_import + import logging from django.utils.translation import ugettext_lazy as _ diff --git a/src/fobi/contrib/plugins/form_elements/test/dummy/README.rst b/src/fobi/contrib/plugins/form_elements/test/dummy/README.rst index 12552731..fc8bcf22 100644 --- a/src/fobi/contrib/plugins/form_elements/test/dummy/README.rst +++ b/src/fobi/contrib/plugins/form_elements/test/dummy/README.rst @@ -18,9 +18,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/plugins/form_elements/test/dummy/base.py b/src/fobi/contrib/plugins/form_elements/test/dummy/base.py new file mode 100644 index 00000000..be4db2f4 --- /dev/null +++ b/src/fobi/contrib/plugins/form_elements/test/dummy/base.py @@ -0,0 +1,46 @@ +from __future__ import absolute_import + +from uuid import uuid4 + +from django.utils.translation import ugettext, ugettext_lazy as _ + +from nonefield.fields import NoneField + +from fobi.base import FormElementPlugin +from fobi.helpers import safe_text + +from . import UID + +__title__ = 'fobi.contrib.plugins.form_elements.test.dummy.base' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('DummyPlugin',) + + +class DummyPlugin(FormElementPlugin): + """Dummy plugin.""" + + uid = UID + name = _("Dummy") + group = _("Testing") + + def post_processor(self): + """Post process data. + + Always the same. + """ + self.data.name = "{0}_{1}".format(self.uid, uuid4()) + + def get_form_field_instances(self, request=None, form_entry=None, + form_element_entries=None, **kwargs): + """Get form field instances.""" + field_kwargs = { + 'initial': "

{0}

".format( + safe_text(ugettext("Dummy content")) + ), + 'required': False, + 'label': '', + } + + return[(self.data.name, NoneField, field_kwargs)] 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 e33a3832..7e71a199 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 @@ -1,13 +1,8 @@ -from uuid import uuid4 +from __future__ import absolute_import -from django.utils.translation import ugettext, ugettext_lazy as _ +from fobi.base import form_element_plugin_registry -from nonefield.fields import NoneField - -from fobi.base import FormElementPlugin, form_element_plugin_registry -from fobi.helpers import safe_text - -from . import UID +from .base import DummyPlugin __title__ = 'fobi.contrib.plugins.form_elements.test.dummy.fobi_form_elements' __author__ = 'Artur Barseghyan ' @@ -16,32 +11,4 @@ __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ('DummyPlugin',) -class DummyPlugin(FormElementPlugin): - """Dummy plugin.""" - - uid = UID - name = _("Dummy") - group = _("Testing") - - def post_processor(self): - """Post process data. - - Always the same. - """ - self.data.name = "{0}_{1}".format(self.uid, uuid4()) - - def get_form_field_instances(self, request=None, form_entry=None, - form_element_entries=None, **kwargs): - """Get form field instances.""" - field_kwargs = { - 'initial': "

{0}

".format( - safe_text(ugettext("Dummy content")) - ), - 'required': False, - 'label': '', - } - - return[(self.data.name, NoneField, field_kwargs)] - - form_element_plugin_registry.register(DummyPlugin) 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 ec46ea88..281817bb 100644 --- a/src/fobi/contrib/plugins/form_handlers/db_store/README.rst +++ b/src/fobi/contrib/plugins/form_handlers/db_store/README.rst @@ -25,11 +25,11 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py syncdb + ./manage.py migrate - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. @@ -49,8 +49,8 @@ For form wizards do: .. code-block:: python urlpatterns = [ - # DB Store plugin URLs - url(r'^fobi/plugins/form-wizard-handlers/db-store/', - include('fobi.contrib.plugins.form_handlers.db_store.urls.' - 'form_wizard_handlers')), - ] + # DB Store plugin URLs + url(r'^fobi/plugins/form-wizard-handlers/db-store/', + include('fobi.contrib.plugins.form_handlers.db_store.urls.' + 'form_wizard_handlers')), + ] 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 737c57f8..1296c478 100644 --- a/src/fobi/contrib/plugins/form_handlers/http_repost/README.rst +++ b/src/fobi/contrib/plugins/form_handlers/http_repost/README.rst @@ -19,9 +19,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/plugins/form_handlers/mail/README.rst b/src/fobi/contrib/plugins/form_handlers/mail/README.rst index 5810ec0a..e189b224 100644 --- a/src/fobi/contrib/plugins/form_handlers/mail/README.rst +++ b/src/fobi/contrib/plugins/form_handlers/mail/README.rst @@ -19,9 +19,9 @@ Installation 2. In the terminal type: -.. code-block:: none +.. code-block:: sh - $ ./manage.py fobi_sync_plugins + ./manage.py fobi_sync_plugins 3. Assign appropriate permissions to the target users/groups to be using the plugin if ``FOBI_RESTRICT_PLUGIN_ACCESS`` is set to True. diff --git a/src/fobi/contrib/themes/bootstrap3/templates/bootstrap3/_base.html b/src/fobi/contrib/themes/bootstrap3/templates/bootstrap3/_base.html index fe62c740..76d78cd1 100644 --- a/src/fobi/contrib/themes/bootstrap3/templates/bootstrap3/_base.html +++ b/src/fobi/contrib/themes/bootstrap3/templates/bootstrap3/_base.html @@ -16,7 +16,9 @@ + {% block dashboard-menu-item %} {% url 'fobi.dashboard' as fobi_dashboard_url %} {{ fobi_theme.project_name }} + {% endblock dashboard-menu-item %} {% endblock navbar-header %} diff --git a/src/fobi/contrib/themes/bootstrap3/templates/bootstrap3/snippets/form_wizard_ajax.html b/src/fobi/contrib/themes/bootstrap3/templates/bootstrap3/snippets/form_wizard_ajax.html index cb0e55ae..d4950264 100644 --- a/src/fobi/contrib/themes/bootstrap3/templates/bootstrap3/snippets/form_wizard_ajax.html +++ b/src/fobi/contrib/themes/bootstrap3/templates/bootstrap3/snippets/form_wizard_ajax.html @@ -1,5 +1,5 @@ {% extends "fobi/generic/snippets/form_wizard_ajax.html" %} - +{% load i18n %} {% block form_page_header_html_class %}page-header{% endblock %} {% block form_html_class %}form-horizontal{% endblock %} @@ -9,3 +9,43 @@ {% block form_button_wrapper_html_class %}controls{% endblock %} {% block form_primary_button_html_class %}btn btn-primary{% endblock %} + +{% block form_wizard_previous_button_html_class %}btn btn-primary{% endblock %} + +{% block form_wizard_first_button_html_class %}btn btn-primary{% endblock %} + +{% block form_wizard_first_button_text %}{% trans "First" %}{% endblock %} + +{% block form_wizard_primary_button_text %}{% if wizard.steps.is_last_step %}{% trans "Submit" %}{% else %}{% trans "Next" %}{% endif %}{% endblock %} + +{% block form_wizard_previous_button_text %}{% trans "Previous" %}{% endblock %} + +{% block form_page_sub_title_wrapper %} + +{% endblock form_page_sub_title_wrapper %} \ No newline at end of file diff --git a/src/fobi/contrib/themes/bootstrap3/widgets/form_elements/date_bootstrap3_widget/README.rst b/src/fobi/contrib/themes/bootstrap3/widgets/form_elements/date_bootstrap3_widget/README.rst index bcbc6379..69cd45be 100644 --- a/src/fobi/contrib/themes/bootstrap3/widgets/form_elements/date_bootstrap3_widget/README.rst +++ b/src/fobi/contrib/themes/bootstrap3/widgets/form_elements/date_bootstrap3_widget/README.rst @@ -6,7 +6,7 @@ theme). Installation ============ -1. Add ``fobi.contrib.themes.bootstrap3.widgets.form_elements.date_bootstrap3_widget`` +1. Add ``fobi.contrib.themes.bootstrap3.widgets.form_elements.date_bootstrap3_widget`` to the ``INSTALLED_APPS`` in your ``settings.py``. .. code-block:: python diff --git a/src/fobi/contrib/themes/bootstrap3/widgets/form_elements/slider_bootstrap3_widget/static/bootstrap3/js/fobi.plugin.slider-bootstrap3-widget.js b/src/fobi/contrib/themes/bootstrap3/widgets/form_elements/slider_bootstrap3_widget/static/bootstrap3/js/fobi.plugin.slider-bootstrap3-widget.js index b8cae56c..00a84a2d 100644 --- a/src/fobi/contrib/themes/bootstrap3/widgets/form_elements/slider_bootstrap3_widget/static/bootstrap3/js/fobi.plugin.slider-bootstrap3-widget.js +++ b/src/fobi/contrib/themes/bootstrap3/widgets/form_elements/slider_bootstrap3_widget/static/bootstrap3/js/fobi.plugin.slider-bootstrap3-widget.js @@ -8,13 +8,17 @@ ; $(document).ready(function() { - var selectElement = $('select.slider'); - var selectedValue = null; - try { - selectedValue = parseInt(selectElement.val()); - } catch(err) { - selectedValue = parseInt(selectElement.data('data-slider-value')); - } - var sliderElement = $('.slider').bootstrapSlider(); - sliderElement.bootstrapSlider('setValue', selectedValue); + // We consider multiple sliders + $('select.slider').each(function() { + var selectElement = $(this); + var selectedValue = null; + try { + selectedValue = parseInt(selectElement.val()); + } catch(err) { + selectedValue = parseInt(selectElement.data('data-slider-value')); + } + var sliderElement = $(this).bootstrapSlider(); + sliderElement.bootstrapSlider('setValue', selectedValue); + }); + }); diff --git a/src/fobi/contrib/themes/djangocms_admin_style_theme/README.rst b/src/fobi/contrib/themes/djangocms_admin_style_theme/README.rst index 165c79da..3fbab0a8 100644 --- a/src/fobi/contrib/themes/djangocms_admin_style_theme/README.rst +++ b/src/fobi/contrib/themes/djangocms_admin_style_theme/README.rst @@ -15,9 +15,9 @@ See the original `installation instructions 1. Install the ``djangocms-admin-style`` package. -.. code-block:: none +.. code-block:: sh - $ pip install djangocms-admin-style + pip install djangocms-admin-style 2. Add 'djangocms_admin_style' to your INSTALLED_APPS just before 'django.contrib.admin'. diff --git a/src/fobi/defaults.py b/src/fobi/defaults.py index 44e346ab..204d5b4d 100644 --- a/src/fobi/defaults.py +++ b/src/fobi/defaults.py @@ -86,6 +86,7 @@ THEME_FOOTER_TEXT = '© django-fobi example site 2014' # ************************************************************** DEFAULT_MAX_LENGTH = 255 +DEFAULT_MIN_LENGTH = 1 FORM_HANDLER_PLUGINS_EXECUTION_ORDER = ( 'http_repost', diff --git a/src/fobi/forms.py b/src/fobi/forms.py index b6723e2c..6eb14b86 100644 --- a/src/fobi/forms.py +++ b/src/fobi/forms.py @@ -53,6 +53,7 @@ __all__ = ( 'FormWizardFormEntryFormSet', 'FormWizardHandlerEntryForm', 'ImportFormEntryForm', + 'ImportFormWizardEntryForm', ) # ***************************************************************************** @@ -69,7 +70,7 @@ class FormEntryForm(forms.ModelForm): """Meta class.""" model = FormEntry - fields = ('name', 'is_public', 'success_page_title', + fields = ('name', 'title', 'is_public', 'success_page_title', 'success_page_message', 'action',) # 'is_cloneable', def __init__(self, *args, **kwargs): @@ -88,6 +89,10 @@ class FormEntryForm(forms.ModelForm): attrs={'class': theme.form_element_html_class} ) + self.fields['title'].widget = forms.widgets.TextInput( + attrs={'class': theme.form_element_html_class} + ) + self.fields['success_page_title'].widget = forms.widgets.TextInput( attrs={'class': theme.form_element_html_class} ) @@ -314,8 +319,8 @@ class FormWizardEntryForm(forms.ModelForm): """Meta class.""" model = FormWizardEntry - fields = ('name', 'is_public', 'success_page_title', - 'success_page_message',) + fields = ('name', 'title', 'is_public', 'success_page_title', + 'success_page_message', 'show_all_navigation_buttons',) # 'wizard_type' # 'action', # 'is_cloneable', @@ -336,6 +341,15 @@ class FormWizardEntryForm(forms.ModelForm): attrs={'class': theme.form_element_html_class} ) + self.fields['title'].widget = forms.widgets.TextInput( + attrs={'class': theme.form_element_html_class} + ) + + self.fields['show_all_navigation_buttons'].widget = \ + forms.widgets.CheckboxInput( + attrs={'data-customforms': 'disabled'} + ) + self.fields['success_page_title'].widget = forms.widgets.TextInput( attrs={'class': theme.form_element_html_class} ) @@ -512,3 +526,14 @@ class ImportFormEntryForm(forms.Form): # ignore_broken_form_handler_entries = forms.BooleanField( # required=False, # label=_("Ignore broken form handler entries")) + + +# ***************************************************************************** +# ***************************************************************************** +# ************************** Import form wizard entry ************************* +# ***************************************************************************** +# ***************************************************************************** + + +class ImportFormWizardEntryForm(ImportFormEntryForm): + """Import form entry wizard form.""" diff --git a/src/fobi/helpers.py b/src/fobi/helpers.py index e9db70fd..06c36afd 100644 --- a/src/fobi/helpers.py +++ b/src/fobi/helpers.py @@ -19,6 +19,7 @@ from django.core.files.base import File from django.contrib.contenttypes.models import ContentType from django.db.utils import DatabaseError from django.utils.encoding import force_text +from django.utils.html import format_html_join from django import forms from django.utils.translation import ugettext_lazy as _ from django.contrib.auth.models import AnonymousUser @@ -50,6 +51,7 @@ __all__ = ( 'do_slugify', 'empty_string', 'ensure_unique_filename', + 'flatatt_inverse_quotes', 'get_app_label_and_model_name', 'get_form_element_entries_for_form_wizard_entry', 'get_model_name_for_object', @@ -396,13 +398,19 @@ def update_plugin_data(entry, request=None): return plugin._update_plugin_data(entry) -def get_select_field_choices(raw_choices_data): +def get_select_field_choices(raw_choices_data, + key_type=None, + value_type=None, + fail_silently=True): """Get select field choices. Used in ``radio``, ``select`` and other choice based fields. :param str raw_choices_data: + :param type key_type: + :param type value_type: + :param bool fail_silently: :return list: """ choices = [] # Holds return value @@ -417,8 +425,25 @@ def get_select_field_choices(raw_choices_data): if ',' in choice: key, value = choice.split(',', 1) key = key.strip() + + # If type specified, cast to the type + if key_type and key is not None: + try: + key = key_type(key) + except (ValueError, TypeError): + return [] if fail_silently else None + value = value.strip() - if key and key not in keys and value not in values: + # If type specified, cast to the type + if value_type and value is not None: + try: + value = value_type(value) + except (ValueError, TypeError): + return [] if fail_silently else None + + if key is not None \ + and key not in keys \ + and value not in values: choices.append((key, value)) keys.add(key) values.add(value) @@ -426,7 +451,9 @@ def get_select_field_choices(raw_choices_data): # If key is also the value else: choice = choice.strip() - if choice and choice not in keys and choice not in values: + if choice is not None \ + and choice not in keys \ + and choice not in values: choices.append((choice, choice)) keys.add(choice) values.add(choice) @@ -633,6 +660,12 @@ class StrippedRequest(object): } return _meta +# ***************************************************************************** +# ***************************************************************************** +# ******************************** Export related ***************************** +# ***************************************************************************** +# ***************************************************************************** + class JSONDataExporter(object): """Exporting the data into JSON.""" @@ -852,3 +885,35 @@ def get_wizard_form_field_value_from_request(request, ) return value + +# ***************************************************************************** +# ***************************************************************************** +# ******************************** Export related ***************************** +# ***************************************************************************** +# ***************************************************************************** + + +def flatatt_inverse_quotes(attrs): + """Convert a dictionary of attributes to a single string. + + The returned string will contain a leading space followed by key="value", + XML-style pairs. In the case of a boolean value, the key will appear + without a value. It is assumed that the keys do not need to be + XML-escaped. If the passed dictionary is empty, then return an empty + string. + + The result is passed through 'mark_safe' (by way of 'format_html_join'). + """ + key_value_attrs = [] + boolean_attrs = [] + for attr, value in attrs.items(): + if isinstance(value, bool): + if value: + boolean_attrs.append((attr,)) + else: + key_value_attrs.append((attr, value)) + + return ( + format_html_join("", " {}='{}'", sorted(key_value_attrs)) + + format_html_join("", " {}", sorted(boolean_attrs)) + ) diff --git a/src/fobi/integration/processors.py b/src/fobi/integration/processors.py index 4c76ff2e..1ece3d5c 100644 --- a/src/fobi/integration/processors.py +++ b/src/fobi/integration/processors.py @@ -209,12 +209,16 @@ class IntegrationProcessor(object): theme = get_theme(request=request, as_instance=True) theme.collect_plugin_media(form_element_entries) + form_title = instance.form_title \ + if instance.form_title \ + else instance.form_entry.title + context = self.get_context_data( request=request, instance=instance, form=form, fobi_theme=theme, - fobi_form_title=instance.form_title, + fobi_form_title=form_title, fobi_hide_form_title=instance.hide_form_title, fobi_form_submit_button_text=instance.form_submit_button_text ) diff --git a/src/fobi/migrations/0011_formentry_title.py b/src/fobi/migrations/0011_formentry_title.py new file mode 100644 index 00000000..e69b6b6b --- /dev/null +++ b/src/fobi/migrations/0011_formentry_title.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.11 on 2016-11-08 21:53 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('fobi', '0010_formwizardhandler'), + ] + + operations = [ + migrations.AddField( + model_name='formentry', + name='title', + field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Title'), + ), + ] diff --git a/src/fobi/migrations/0012_auto_20161109_1550.py b/src/fobi/migrations/0012_auto_20161109_1550.py new file mode 100644 index 00000000..ba607d3d --- /dev/null +++ b/src/fobi/migrations/0012_auto_20161109_1550.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.11 on 2016-11-09 21:50 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('fobi', '0011_formentry_title'), + ] + + operations = [ + migrations.AddField( + model_name='formwizardentry', + name='title', + field=models.CharField(blank=True, help_text='Shown in templates ifavailable.', max_length=255, null=True, verbose_name='Title'), + ), + migrations.AlterField( + model_name='formentry', + name='title', + field=models.CharField(blank=True, help_text='Shown in templates ifavailable.', max_length=255, null=True, verbose_name='Title'), + ), + ] diff --git a/src/fobi/migrations/0013_formwizardentry_show_all_navigation_buttons.py b/src/fobi/migrations/0013_formwizardentry_show_all_navigation_buttons.py new file mode 100644 index 00000000..c7eccc3b --- /dev/null +++ b/src/fobi/migrations/0013_formwizardentry_show_all_navigation_buttons.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.3 on 2016-11-09 23:40 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('fobi', '0012_auto_20161109_1550'), + ] + + operations = [ + migrations.AddField( + model_name='formwizardentry', + name='show_all_navigation_buttons', + field=models.BooleanField(default=False, help_text='Show all navigation buttons.', verbose_name='Show all navigation buttons?'), + ), + ] diff --git a/src/fobi/models.py b/src/fobi/models.py index 4055055e..ff393e28 100644 --- a/src/fobi/models.py +++ b/src/fobi/models.py @@ -256,6 +256,9 @@ class FormWizardEntry(models.Model): user = models.ForeignKey(AUTH_USER_MODEL, verbose_name=_("User")) name = models.CharField(_("Name"), max_length=255) + title = models.CharField(_("Title"), max_length=255, null=True, + blank=True, help_text=_("Shown in templates if " + "available.")) slug = AutoSlugField(populate_from='name', verbose_name=_("Slug"), unique=True) is_public = models.BooleanField( @@ -276,6 +279,10 @@ class FormWizardEntry(models.Model): help_text=_("Custom message text to display after valid form is " "submitted") ) + show_all_navigation_buttons = models.BooleanField( + _("Show all navigation buttons?"), default=False, + help_text=_("Show all navigation buttons.") + ) # action = models.CharField( # _("Action"), max_length=255, null=True, blank=True, # help_text=_("Custom form action; don't fill this field, unless " @@ -308,7 +315,8 @@ class FormWizardEntry(models.Model): :return string: """ - return reverse('fobi.form_wizard', kwargs={'slug': self.slug}) + return reverse('fobi.view_form_wizard_entry', + kwargs={'slug': self.slug}) @python_2_unicode_compatible @@ -318,20 +326,20 @@ class FormEntry(models.Model): :Properties: - `user` (django.contrib.auth.models.User: User owning the plugin. - - `wizard` (str): Form wizard to which the form entry belongs to. - `name` (str): Form name. + - `title` (str): Form title - used in templates. - `slug` (str): Form slug. - `description` (str): Form description. - `is_public` (bool): If set to True, is visible to public. - `is_cloneable` (bool): If set to True, is cloneable. - `position` (int): Ordering position in the wizard. """ - # form_wizard_entry = models.ForeignKey( - # FormWizardEntry, verbose_name=_("Form wizard"), null=True, blank=True - # ) user = models.ForeignKey(AUTH_USER_MODEL, verbose_name=_("User")) name = models.CharField(_("Name"), max_length=255) + title = models.CharField(_("Title"), max_length=255, null=True, + blank=True, help_text=_("Shown in templates if " + "available.")) slug = AutoSlugField( populate_from='name', verbose_name=_("Slug"), unique=True ) @@ -383,9 +391,10 @@ class FormEntry(models.Model): :return string: """ - return reverse('fobi.form_entry', kwargs={'slug': self.slug}) + return reverse('fobi.view_form_entry', kwargs={'slug': self.slug}) +@python_2_unicode_compatible class FormWizardFormEntry(models.Model): """Form wizard form entry. @@ -409,6 +418,9 @@ class FormWizardFormEntry(models.Model): ordering = ['position'] unique_together = (('form_wizard_entry', 'form_entry'),) + def __str__(self): + return "{0} - {1}".format(self.form_wizard_entry, self.form_entry) + @python_2_unicode_compatible class FormFieldsetEntry(models.Model): diff --git a/src/fobi/settings.py b/src/fobi/settings.py index 572c9502..6cfbc142 100644 --- a/src/fobi/settings.py +++ b/src/fobi/settings.py @@ -38,6 +38,7 @@ __all__ = ( 'CUSTOM_THEME_DATA', 'THEME_FOOTER_TEXT', + 'DEFAULT_MIN_LENGTH', 'DEFAULT_MAX_LENGTH', 'FORM_HANDLER_PLUGINS_EXECUTION_ORDER', 'FORM_WIZARD_HANDLER_PLUGINS_EXECUTION_ORDER', @@ -101,6 +102,7 @@ THEME_FOOTER_TEXT = get_setting('THEME_FOOTER_TEXT') # ************************************************************** # ************************************************************** +DEFAULT_MIN_LENGTH = get_setting('DEFAULT_MIN_LENGTH') DEFAULT_MAX_LENGTH = get_setting('DEFAULT_MAX_LENGTH') FORM_HANDLER_PLUGINS_EXECUTION_ORDER = \ diff --git a/src/fobi/templates/fobi/generic/_base.html b/src/fobi/templates/fobi/generic/_base.html index 3fa2c9c2..5c2103f7 100644 --- a/src/fobi/templates/fobi/generic/_base.html +++ b/src/fobi/templates/fobi/generic/_base.html @@ -60,8 +60,10 @@ + {% block dashboard-menu-item %} {% url 'fobi.dashboard' as fobi_dashboard_url %} {{ fobi_theme.project_name }} + {% endblock dashboard-menu-item %} {% endblock navbar-header %} @@ -85,6 +87,7 @@ --> {% endblock navbar-menu-content %} {% block navbar-menu-navigation-content %} + {% comment %}
  • {% trans "Form wizards dashboard" %}
  • + {% endcomment %} {% endblock navbar-menu-navigation-content %} {% endblock navbar-menu %} + + {% block navbar-menu-right %} + + {% endblock navbar-menu-right %} + {% endblock navbar-menu-wrapper %} {% endblock navbar-content %} diff --git a/src/fobi/templates/fobi/generic/add_form_element_entry.html b/src/fobi/templates/fobi/generic/add_form_element_entry.html index 168ea189..15c54922 100644 --- a/src/fobi/templates/fobi/generic/add_form_element_entry.html +++ b/src/fobi/templates/fobi/generic/add_form_element_entry.html @@ -7,6 +7,11 @@ {% block navbar-menu-content %} {% endblock navbar-menu-content %} +{% block navbar-menu-right-content %} +
  • {% trans "Forms" %}
  • +
  • {% trans "Wizards" %}
  • +{% endblock navbar-menu-right-content %} + {% block content %} {% include fobi_theme.add_form_element_entry_ajax_template %} {% endblock content %} diff --git a/src/fobi/templates/fobi/generic/add_form_handler_entry.html b/src/fobi/templates/fobi/generic/add_form_handler_entry.html index 8619242c..467f573c 100644 --- a/src/fobi/templates/fobi/generic/add_form_handler_entry.html +++ b/src/fobi/templates/fobi/generic/add_form_handler_entry.html @@ -7,6 +7,11 @@ {% block navbar-menu-content %} {% endblock navbar-menu-content %} +{% block navbar-menu-right-content %} +
  • {% trans "Forms" %}
  • +
  • {% trans "Wizards" %}
  • +{% endblock navbar-menu-right-content %} + {% block content %} {% include fobi_theme.add_form_handler_entry_ajax_template %} {% endblock content %} diff --git a/src/fobi/templates/fobi/generic/add_form_wizard_handler_entry.html b/src/fobi/templates/fobi/generic/add_form_wizard_handler_entry.html index 131e82c4..9551f99d 100644 --- a/src/fobi/templates/fobi/generic/add_form_wizard_handler_entry.html +++ b/src/fobi/templates/fobi/generic/add_form_wizard_handler_entry.html @@ -7,6 +7,11 @@ {% block navbar-menu-content %} {% endblock navbar-menu-content %} +{% block navbar-menu-right-content %} +
  • {% trans "Forms" %}
  • +
  • {% trans "Wizards" %}
  • +{% endblock navbar-menu-right-content %} + {% block content %} {% include fobi_theme.add_form_wizard_handler_entry_ajax_template %} {% endblock content %} diff --git a/src/fobi/templates/fobi/generic/create_form_entry.html b/src/fobi/templates/fobi/generic/create_form_entry.html index 84142d89..bd5a9540 100644 --- a/src/fobi/templates/fobi/generic/create_form_entry.html +++ b/src/fobi/templates/fobi/generic/create_form_entry.html @@ -7,6 +7,11 @@ {% block navbar-menu-content %} {% endblock navbar-menu-content %} +{% block navbar-menu-right-content %} +
  • {% trans "Forms" %}
  • +
  • {% trans "Wizards" %}
  • +{% endblock navbar-menu-right-content %} + {% block content %} {% include fobi_theme.create_form_entry_ajax_template %} {% endblock content %} diff --git a/src/fobi/templates/fobi/generic/create_form_wizard_entry.html b/src/fobi/templates/fobi/generic/create_form_wizard_entry.html index cdd028e5..8061b726 100644 --- a/src/fobi/templates/fobi/generic/create_form_wizard_entry.html +++ b/src/fobi/templates/fobi/generic/create_form_wizard_entry.html @@ -7,6 +7,15 @@ {% block navbar-menu-content %} {% endblock navbar-menu-content %} +{% block navbar-menu-right-content %} +
  • {% trans "Forms" %}
  • +
  • {% trans "Wizards" %}
  • +{% endblock navbar-menu-right-content %} + +{% block dashboard-menu-item %} +{{ fobi_theme.project_name }} +{% endblock dashboard-menu-item %} + {% block content %} {% include fobi_theme.create_form_wizard_entry_ajax_template %} {% endblock content %} diff --git a/src/fobi/templates/fobi/generic/dashboard.html b/src/fobi/templates/fobi/generic/dashboard.html index cea16fa7..2a92cfef 100644 --- a/src/fobi/templates/fobi/generic/dashboard.html +++ b/src/fobi/templates/fobi/generic/dashboard.html @@ -7,6 +7,11 @@ {% block navbar-menu-content %} {% endblock navbar-menu-content %} +{% block navbar-menu-right-content %} +
  • {% trans "Forms" %}
  • +
  • {% trans "Wizards" %}
  • +{% endblock navbar-menu-right-content %} + {% block main-content-inner-attrs %}{% endblock main-content-inner-attrs %} {% block content-wrapper %} diff --git a/src/fobi/templates/fobi/generic/edit_form_element_entry.html b/src/fobi/templates/fobi/generic/edit_form_element_entry.html index 1cbe1963..df348d08 100644 --- a/src/fobi/templates/fobi/generic/edit_form_element_entry.html +++ b/src/fobi/templates/fobi/generic/edit_form_element_entry.html @@ -4,9 +4,21 @@ {% block page-title %}{% blocktrans with form_element_plugin.name as plugin_name %}Edit "{{ plugin_name }}" element of the form{% endblocktrans %}{% endblock page-title %} +{% block extrahead %} + {% if form %} + {{ form.media }} + {% endif %} + {{ block.super }} +{% endblock extrahead %} + {% block navbar-menu-content %} {% endblock navbar-menu-content %} +{% block navbar-menu-right-content %} +
  • {% trans "Forms" %}
  • +
  • {% trans "Wizards" %}
  • +{% endblock navbar-menu-right-content %} + {% block content %} {% include fobi_theme.edit_form_element_entry_ajax_template %} {% endblock content %} diff --git a/src/fobi/templates/fobi/generic/edit_form_entry.html b/src/fobi/templates/fobi/generic/edit_form_entry.html index 09e5f98e..9afeca58 100644 --- a/src/fobi/templates/fobi/generic/edit_form_entry.html +++ b/src/fobi/templates/fobi/generic/edit_form_entry.html @@ -4,6 +4,11 @@ {% block page-title %}{% trans "Edit form entry" %}{% endblock page-title %} +{% block navbar-menu-right-content %} +
  • {% trans "Forms" %}
  • +
  • {% trans "Wizards" %}
  • +{% endblock navbar-menu-right-content %} + {% block navbar-menu-content %}
  • {% trans "Edit" %}
  • {% trans "View" %}
  • diff --git a/src/fobi/templates/fobi/generic/edit_form_handler_entry.html b/src/fobi/templates/fobi/generic/edit_form_handler_entry.html index f4e5f310..e4ca4b5e 100644 --- a/src/fobi/templates/fobi/generic/edit_form_handler_entry.html +++ b/src/fobi/templates/fobi/generic/edit_form_handler_entry.html @@ -7,6 +7,11 @@ {% block navbar-menu-content %} {% endblock navbar-menu-content %} +{% block navbar-menu-right-content %} +
  • {% trans "Forms" %}
  • +
  • {% trans "Wizards" %}
  • +{% endblock navbar-menu-right-content %} + {% block content %} {% include fobi_theme.edit_form_handler_entry_ajax_template %} {% endblock content %} diff --git a/src/fobi/templates/fobi/generic/edit_form_wizard_entry.html b/src/fobi/templates/fobi/generic/edit_form_wizard_entry.html index 9c8bc939..35cd2f78 100644 --- a/src/fobi/templates/fobi/generic/edit_form_wizard_entry.html +++ b/src/fobi/templates/fobi/generic/edit_form_wizard_entry.html @@ -4,6 +4,15 @@ {% block page-title %}{% trans "Edit form wizard entry" %}{% endblock page-title %} +{% block dashboard-menu-item %} +{{ fobi_theme.project_name }} +{% endblock dashboard-menu-item %} + +{% block navbar-menu-right-content %} +
  • {% trans "Forms" %}
  • +
  • {% trans "Wizards" %}
  • +{% endblock navbar-menu-right-content %} + {% block navbar-menu-content %}
  • {% trans "Edit" %}
  • {% if form_wizard_entry_forms %}
  • {% trans "View" %}
  • {% endif %} diff --git a/src/fobi/templates/fobi/generic/edit_form_wizard_entry_ajax.html b/src/fobi/templates/fobi/generic/edit_form_wizard_entry_ajax.html index a79316b6..58d976ed 100644 --- a/src/fobi/templates/fobi/generic/edit_form_wizard_entry_ajax.html +++ b/src/fobi/templates/fobi/generic/edit_form_wizard_entry_ajax.html @@ -192,17 +192,15 @@
    - {% comment %}
    -

    {% trans "Export your form as JSON" %}

    +

    {% trans "Export your form wizard as JSON" %}

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

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

    - {% endcomment %}

    {% trans "Delete your form wizard" %}

    {% trans "Once deleted, can't be undone!" %}

    diff --git a/src/fobi/templates/fobi/generic/form_entry_submitted.html b/src/fobi/templates/fobi/generic/form_entry_submitted.html index bc42d75a..f22d5ab4 100644 --- a/src/fobi/templates/fobi/generic/form_entry_submitted.html +++ b/src/fobi/templates/fobi/generic/form_entry_submitted.html @@ -7,6 +7,11 @@ {% block navbar-menu-content %} {% endblock navbar-menu-content %} +{% block navbar-menu-right-content %} +
  • {% trans "Forms" %}
  • +
  • {% trans "Wizards" %}
  • +{% endblock navbar-menu-right-content %} + {% block content %} {% include fobi_theme.form_entry_submitted_ajax_template %} {% endblock content %} diff --git a/src/fobi/templates/fobi/generic/form_importer.html b/src/fobi/templates/fobi/generic/form_importer.html index 756c7717..f4836aa7 100644 --- a/src/fobi/templates/fobi/generic/form_importer.html +++ b/src/fobi/templates/fobi/generic/form_importer.html @@ -7,6 +7,11 @@ {% block navbar-menu-content %} {% endblock navbar-menu-content %} +{% block navbar-menu-right-content %} +
  • {% trans "Forms" %}
  • +
  • {% trans "Wizards" %}
  • +{% endblock navbar-menu-right-content %} + {% block content %} {% include fobi_theme.form_importer_ajax_template %} {% endblock content %} diff --git a/src/fobi/templates/fobi/generic/form_wizard_entry_submitted.html b/src/fobi/templates/fobi/generic/form_wizard_entry_submitted.html index 21130bfe..563b7d2c 100644 --- a/src/fobi/templates/fobi/generic/form_wizard_entry_submitted.html +++ b/src/fobi/templates/fobi/generic/form_wizard_entry_submitted.html @@ -7,6 +7,11 @@ {% block navbar-menu-content %} {% endblock navbar-menu-content %} +{% block navbar-menu-right-content %} +
  • {% trans "Forms" %}
  • +
  • {% trans "Wizards" %}
  • +{% endblock navbar-menu-right-content %} + {% block content %} {% include fobi_theme.form_wizard_entry_submitted_ajax_template %} {% endblock content %} diff --git a/src/fobi/templates/fobi/generic/form_wizards_dashboard.html b/src/fobi/templates/fobi/generic/form_wizards_dashboard.html index c5834c30..34c9b8a2 100644 --- a/src/fobi/templates/fobi/generic/form_wizards_dashboard.html +++ b/src/fobi/templates/fobi/generic/form_wizards_dashboard.html @@ -9,9 +9,18 @@ {% block main-content-inner-attrs %}{% endblock main-content-inner-attrs %} +{% block dashboard-menu-item %} +{{ fobi_theme.project_name }} +{% endblock dashboard-menu-item %} + +{% block navbar-menu-right-content %} +
  • {% trans "Forms" %}
  • +
  • {% trans "Wizards" %}
  • +{% endblock navbar-menu-right-content %} + {% block content-wrapper %}
    @@ -51,13 +60,11 @@ {% trans "Delete" %} - {% comment %}
  • - + {% trans "Export" %}
  • - {% endcomment %} @@ -78,10 +85,10 @@ {% trans "Create form wizard" %} - {% comment %} - - {% trans "Import form" %} + + {% trans "Import form wizard" %} + {% comment %} {% for form_importer_uid,form_importer_name,form_importer_url in form_importers %} {{ form_importer_name }} diff --git a/src/fobi/templates/fobi/generic/import_form_entry.html b/src/fobi/templates/fobi/generic/import_form_entry.html index 66c48292..a24ac902 100644 --- a/src/fobi/templates/fobi/generic/import_form_entry.html +++ b/src/fobi/templates/fobi/generic/import_form_entry.html @@ -7,6 +7,11 @@ {% block navbar-menu-content %} {% endblock navbar-menu-content %} +{% block navbar-menu-right-content %} +
  • {% trans "Forms" %}
  • +
  • {% trans "Wizards" %}
  • +{% endblock navbar-menu-right-content %} + {% block content %} {% include fobi_theme.import_form_entry_ajax_template %} {% endblock content %} diff --git a/src/fobi/templates/fobi/generic/snippets/form_wizard_ajax.html b/src/fobi/templates/fobi/generic/snippets/form_wizard_ajax.html index 6e47c6bc..101a6790 100644 --- a/src/fobi/templates/fobi/generic/snippets/form_wizard_ajax.html +++ b/src/fobi/templates/fobi/generic/snippets/form_wizard_ajax.html @@ -1,6 +1,7 @@ {% block form_page_header_wrapper %}
    {% block form_page_title_wrapper %}

    {% block form_page_title %}{% endblock %}

    {% endblock form_page_title_wrapper %} + {% block form_page_sub_title_wrapper %}{% endblock form_page_sub_title_wrapper %}
    {% endblock form_page_header_wrapper %} @@ -14,7 +15,11 @@
    {% block form_buttons %} - + {% if form_wizard_entry.show_all_navigation_buttons and wizard.steps.prev %} + {% block form_wizard_first_button_wrapper %}{% endblock form_wizard_first_button_wrapper %} + {% block form_wizard_previous_button_wrapper %}{% endblock form_wizard_previous_button_wrapper %} + {% endif %} + {% block form_wizard_next_button_wrapper %}{% endblock form_wizard_next_button_wrapper %} {% endblock form_buttons %}
    diff --git a/src/fobi/templates/fobi/generic/snippets/form_wizard_view_ajax.html b/src/fobi/templates/fobi/generic/snippets/form_wizard_view_ajax.html index cae6facb..853d23b0 100644 --- a/src/fobi/templates/fobi/generic/snippets/form_wizard_view_ajax.html +++ b/src/fobi/templates/fobi/generic/snippets/form_wizard_view_ajax.html @@ -1,6 +1,7 @@ {% block form_page_header_wrapper %}
    {% block form_page_title_wrapper %}

    {% block form_page_title %}{% endblock form_page_title %}

    {% endblock form_page_title_wrapper %} + {% block form_page_sub_title_wrapper %}{% block form_page_sub_title %}{% endblock form_page_sub_title %}{% endblock form_page_sub_title_wrapper %}
    {% endblock form_page_header_wrapper %} diff --git a/src/fobi/templates/fobi/generic/view_form_entry.html b/src/fobi/templates/fobi/generic/view_form_entry.html index 864b675c..b57580fe 100644 --- a/src/fobi/templates/fobi/generic/view_form_entry.html +++ b/src/fobi/templates/fobi/generic/view_form_entry.html @@ -2,13 +2,18 @@ {% load i18n %} -{% block page-title %}{% trans "View form" %}{% endblock page-title %} +{% block page-title %}{% if form_entry.title %}{{ form_entry.title }}{% else %}{% trans "View form" %}{% endif %}{% endblock page-title %} {% block navbar-menu-content %}
  • {% trans "Edit" %}
  • {% trans "View" %}
  • {% endblock navbar-menu-content %} +{% block navbar-menu-right-content %} +
  • {% trans "Forms" %}
  • +
  • {% trans "Wizards" %}
  • +{% endblock navbar-menu-right-content %} + {% block content %} {% include fobi_theme.view_form_entry_ajax_template %} {% endblock content %} diff --git a/src/fobi/templates/fobi/generic/view_form_wizard_entry.html b/src/fobi/templates/fobi/generic/view_form_wizard_entry.html index b3a53c4b..1b6052dd 100644 --- a/src/fobi/templates/fobi/generic/view_form_wizard_entry.html +++ b/src/fobi/templates/fobi/generic/view_form_wizard_entry.html @@ -2,13 +2,22 @@ {% load i18n %} -{% block page-title %}{% trans "View form wizard" %}{% endblock page-title %} +{% block page-title %}{% if fobi_form_wizard_title %}{{ fobi_form_wizard_title }}{% else %}{% trans "View form wizard" %}{% endif %}{% endblock page-title %} + +{% block dashboard-menu-item %} +{{ fobi_theme.project_name }} +{% endblock dashboard-menu-item %} {% block navbar-menu-content %}
  • {% trans "Edit" %}
  • {% trans "View" %}
  • {% endblock navbar-menu-content %} +{% block navbar-menu-right-content %} +
  • {% trans "Forms" %}
  • +
  • {% trans "Wizards" %}
  • +{% endblock navbar-menu-right-content %} + {% block content %} {% include fobi_theme.view_form_wizard_entry_ajax_template %} {% endblock content %} diff --git a/src/fobi/templates/fobi/generic/view_form_wizard_entry_ajax.html b/src/fobi/templates/fobi/generic/view_form_wizard_entry_ajax.html index 0f3a9202..012042d6 100644 --- a/src/fobi/templates/fobi/generic/view_form_wizard_entry_ajax.html +++ b/src/fobi/templates/fobi/generic/view_form_wizard_entry_ajax.html @@ -6,7 +6,7 @@ {% if fobi_form_title %}{{ fobi_form_title }}{% else %}{% trans "View form" %}{% endif %} {% endblock form_page_title %} -{% block form_primary_button_text %}{% if fobi_form_submit_button_text %}{{ fobi_form_submit_button_text }}{% else %}{% trans "Submit" %}{% endif %}{% endblock %} +{% block form_primary_button_text %}{% if fobi_form_submit_button_text %}{{ fobi_form_submit_button_text }}{% else %}{% trans "Next" %}{% endif %}{% endblock %} {% block form_buttons %} {{ block.super }} {% endblock form_buttons %} diff --git a/src/fobi/tests/data.py b/src/fobi/tests/data.py index 029bc025..d026deea 100644 --- a/src/fobi/tests/data.py +++ b/src/fobi/tests/data.py @@ -3,70 +3,72 @@ import datetime from django.utils.text import force_text -# from fobi.contrib.plugins.form_elements.content.content_image.fobi_form_elements \ -# import ContentImagePlugin -# from fobi.contrib.plugins.form_elements.content.content_text.fobi_form_elements \ -# import ContentTextPlugin -# from fobi.contrib.plugins.form_elements.content.content_video.fobi_form_elements \ -# import ContentVideoPlugin +# from fobi.contrib.plugins.form_elements.content \ +# .content_image.fobi_form_elements import ContentImagePlugin +# from fobi.contrib.plugins.form_elements.content \ +# .content_text.fobi_form_elements import ContentTextPlugin +# from fobi.contrib.plugins.form_elements.content \ +# .content_video.fobi_form_elements import ContentVideoPlugin -from fobi.contrib.plugins.form_elements.fields.boolean.fobi_form_elements \ - import BooleanSelectPlugin -# from fobi.contrib.plugins.form_elements.fields.checkbox_select_multiple.fobi_form_elements \ -# import CheckboxSelectMultipleInputPlugin -from fobi.contrib.plugins.form_elements.fields.date.fobi_form_elements \ - import DateInputPlugin -# from fobi.contrib.plugins.form_elements.fields.date_drop_down.fobi_form_elements \ -# import DateDropDownInputPlugin -from fobi.contrib.plugins.form_elements.fields.datetime.fobi_form_elements \ - import DateTimeInputPlugin -from fobi.contrib.plugins.form_elements.fields.decimal.fobi_form_elements \ - import DecimalInputPlugin -from fobi.contrib.plugins.form_elements.fields.email.fobi_form_elements \ - import EmailInputPlugin -# from fobi.contrib.plugins.form_elements.fields.file.fobi_form_elements \ -# import FileInputPlugin -from fobi.contrib.plugins.form_elements.fields.float.fobi_form_elements \ - import FloatInputPlugin -# from fobi.contrib.plugins.form_elements.fields.hidden.fobi_form_elements \ -# import HiddenInputPlugin -# from fobi.contrib.plugins.form_elements.fields.hidden_model_object.fobi_form_elements \ -# import HiddenModelObjectInputPlugin -from fobi.contrib.plugins.form_elements.fields.integer.fobi_form_elements \ - import IntegerInputPlugin -from fobi.contrib.plugins.form_elements.fields.ip_address.fobi_form_elements \ - import IPAddressInputPlugin -from fobi.contrib.plugins.form_elements.fields.null_boolean.fobi_form_elements \ - import NullBooleanSelectPlugin -from fobi.contrib.plugins.form_elements.fields.select.fobi_form_elements \ - import SelectInputPlugin -from fobi.contrib.plugins.form_elements.fields.select_model_object.fobi_form_elements \ - import SelectModelObjectInputPlugin -from fobi.contrib.plugins.form_elements.fields.select_multiple.fobi_form_elements \ - import SelectMultipleInputPlugin -from fobi.contrib.plugins.form_elements.fields.slug.fobi_form_elements \ - import SlugInputPlugin -from fobi.contrib.plugins.form_elements.fields.text.fobi_form_elements \ - import TextInputPlugin -from fobi.contrib.plugins.form_elements.fields.textarea.fobi_form_elements \ - import TextareaPlugin -from fobi.contrib.plugins.form_elements.fields.url.fobi_form_elements \ - import URLInputPlugin +from fobi.contrib.plugins.form_elements.fields \ + .boolean.fobi_form_elements import BooleanSelectPlugin +# from fobi.contrib.plugins.form_elements.fields.checkbox_select_multiple \ +# .fobi_form_elements import CheckboxSelectMultipleInputPlugin +from fobi.contrib.plugins.form_elements.fields \ + .date.fobi_form_elements import DateInputPlugin +# from fobi.contrib.plugins.form_elements.fields \ +# .date_drop_down.fobi_form_elements import DateDropDownInputPlugin +from fobi.contrib.plugins.form_elements.fields \ + .datetime.fobi_form_elements import DateTimeInputPlugin +from fobi.contrib.plugins.form_elements.fields \ + .decimal.fobi_form_elements import DecimalInputPlugin +from fobi.contrib.plugins.form_elements.fields \ + .email.fobi_form_elements import EmailInputPlugin +# from fobi.contrib.plugins.form_elements.fields \ +# .file.fobi_form_elements import FileInputPlugin +from fobi.contrib.plugins.form_elements.fields \ + .float.fobi_form_elements import FloatInputPlugin +# from fobi.contrib.plugins.form_elements.fields \ +# .hidden.fobi_form_elements import HiddenInputPlugin +# from fobi.contrib.plugins.form_elements.fields.hidden_model_object \ +# .fobi_form_elements import HiddenModelObjectInputPlugin +from fobi.contrib.plugins.form_elements.fields \ + .integer.fobi_form_elements import IntegerInputPlugin +from fobi.contrib.plugins.form_elements.fields \ + .ip_address.fobi_form_elements import IPAddressInputPlugin +from fobi.contrib.plugins.form_elements.fields \ + .null_boolean.fobi_form_elements import NullBooleanSelectPlugin +from fobi.contrib.plugins.form_elements.fields \ + .select.fobi_form_elements import SelectInputPlugin +from fobi.contrib.plugins.form_elements.fields.select_model_object \ + .fobi_form_elements import SelectModelObjectInputPlugin +from fobi.contrib.plugins.form_elements.fields \ + .select_multiple.fobi_form_elements import SelectMultipleInputPlugin +from fobi.contrib.plugins.form_elements.fields.slug \ + .fobi_form_elements import SlugInputPlugin +from fobi.contrib.plugins.form_elements.fields \ + .text.fobi_form_elements import TextInputPlugin +from fobi.contrib.plugins.form_elements.fields \ + .textarea.fobi_form_elements import TextareaPlugin +from fobi.contrib.plugins.form_elements.fields \ + .url.fobi_form_elements import URLInputPlugin -from fobi.contrib.plugins.form_handlers.db_store.fobi_form_handlers \ - import DBStoreHandlerPlugin -from fobi.contrib.plugins.form_handlers.mail.fobi_form_handlers \ - import MailHandlerPlugin -from fobi.contrib.plugins.form_handlers.http_repost.fobi_form_handlers \ - import HTTPRepostHandlerPlugin +from fobi.contrib.plugins.form_handlers \ + .db_store.fobi_form_handlers import DBStoreHandlerPlugin +from fobi.contrib.plugins.form_handlers \ + .mail.fobi_form_handlers import MailHandlerPlugin +from fobi.contrib.plugins.form_handlers \ + .http_repost.fobi_form_handlers import HTTPRepostHandlerPlugin __title__ = 'fobi.tests.data' __author__ = 'Artur Barseghyan ' __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ( - 'TEST_FORM_ELEMENT_PLUGIN_DATA', 'TEST_FORM_FIELD_DATA', + 'TEST_FORM_ELEMENT_PLUGIN_DATA', + 'TEST_FORM_FIELD_DATA', 'TEST_FORM_HANDLER_PLUGIN_DATA', + 'TEST_MAILCHIMP_IMPORTER_FORM_DATA' ) TEST_FORM_ELEMENT_PLUGIN_DATA = { @@ -84,14 +86,14 @@ TEST_FORM_ELEMENT_PLUGIN_DATA = { # 'required': False, # }, - # Add a "Date" input form elelement + # Add a "Date" input form element force_text(DateInputPlugin.name): { 'label': "Test date input", 'help_text': "Lorem ipsum select multiple input", 'required': False, }, - # Add a "DateTime" input form elelement + # Add a "DateTime" input form element force_text(DateTimeInputPlugin.name): { 'label': "Test datetime input", 'help_text': "Lorem ipsum select multiple input", @@ -113,7 +115,7 @@ TEST_FORM_ELEMENT_PLUGIN_DATA = { }, # TODO: Add file test. - # Add a "File" (file) form elelement + # Add a "File" (file) form element # force_text(FileInputPlugin.name): { # 'label': "Test file input", # #'name': "test_file_input", @@ -129,7 +131,7 @@ TEST_FORM_ELEMENT_PLUGIN_DATA = { }, # TODO: Find out why selenium fails here! - # Add a "Hidden" (boolean) form elelement + # Add a "Hidden" (boolean) form element # force_text(HiddenInputPlugin.name): { # 'label': "Test hidden input", # #'name': "test_hidden_input", @@ -137,70 +139,70 @@ TEST_FORM_ELEMENT_PLUGIN_DATA = { # 'required': True, # }, - # Add a "Integer" (text input) form elelement + # Add a "Integer" (text input) form element force_text(IntegerInputPlugin.name): { 'label': "Test integer", 'help_text': "Lorem ipsum text input", 'required': True, }, - # Add a "IP address" (text input) form elelement + # Add a "IP address" (text input) form element force_text(IPAddressInputPlugin.name): { 'label': "Test IP address", 'help_text': "Lorem ipsum text input", 'required': True, }, - # Add a "null boolean" form elelement + # Add a "null boolean" form element force_text(NullBooleanSelectPlugin.name): { 'label': "Test null boolean", 'help_text': "Lorem ipsum text input", 'required': True, }, - # Add a "Select Input" (select input) form elelement + # Add a "Select Input" (select input) form element force_text(SelectInputPlugin.name): { 'label': "Test select", 'help_text': "Lorem ipsum text input", 'required': False, }, - # Add a "Select model object" (select input) form elelement + # Add a "Select model object" (select input) form element force_text(SelectModelObjectInputPlugin.name): { 'label': "Test select model object", 'help_text': "Lorem ipsum select model object input", 'required': False, }, - # Add a "Select multiple" (select multiple input) form elelement + # Add a "Select multiple" (select multiple input) form element force_text(SelectMultipleInputPlugin.name): { 'label': "Test select multiple input", 'help_text': "Lorem ipsum select multiple input", 'required': False, }, - # Add a "Select multiple" (select multiple input) form elelement + # Add a "Select multiple" (select multiple input) form element force_text(SlugInputPlugin.name): { 'label': "Test slug input", 'help_text': "Lorem ipsum select multiple input", 'required': False, }, - # Add a "Text" (text input) form elelement + # Add a "Text" (text input) form element force_text(TextInputPlugin.name): { 'label': "Test text", 'help_text': "Lorem ipsum text input", 'required': True, }, - # Add a "Textarea" (text area) form elelement + # Add a "Textarea" (text area) form element force_text(TextareaPlugin.name): { 'label': "Test text area", 'help_text': "Lorem ipsum text area", 'required': True, }, - # Add a "URL input" form elelement + # Add a "URL input" form element force_text(URLInputPlugin.name): { 'label': "Test URL input", 'help_text': "Lorem ipsum text area", @@ -246,3 +248,210 @@ TEST_FORM_HANDLER_PLUGIN_DATA = { 'endpoint_url': 'http://dev.example.com' } } + + +TEST_MAILCHIMP_IMPORTER_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' + } +] diff --git a/src/fobi/tests/helpers.py b/src/fobi/tests/helpers.py index 63606f31..9dcddb60 100644 --- a/src/fobi/tests/helpers.py +++ b/src/fobi/tests/helpers.py @@ -4,37 +4,37 @@ from django.core.exceptions import ObjectDoesNotExist from django.core.management import call_command from fobi.models import FormEntry, FormElementEntry, FormHandlerEntry -from \ - fobi.contrib.plugins.form_elements.content.content_text.fobi_form_elements \ - import ContentTextPlugin -from \ - fobi.contrib.plugins.form_elements.content.content_image.fobi_form_elements \ - import ContentImagePlugin +from fobi.contrib.plugins.form_elements.content \ + .content_text.fobi_form_elements import ContentTextPlugin +from fobi.contrib.plugins.form_elements.content \ + .content_image.fobi_form_elements import ContentImagePlugin -from fobi.contrib.plugins.form_elements.fields.boolean.fobi_form_elements \ - import BooleanSelectPlugin -from fobi.contrib.plugins.form_elements.fields.email.fobi_form_elements \ - import EmailInputPlugin -from fobi.contrib.plugins.form_elements.fields.hidden.fobi_form_elements \ - import HiddenInputPlugin -from fobi.contrib.plugins.form_elements.fields.integer.fobi_form_elements \ - import IntegerInputPlugin -from fobi.contrib.plugins.form_elements.fields.text.fobi_form_elements \ - import TextInputPlugin -from fobi.contrib.plugins.form_elements.fields.textarea.fobi_form_elements \ - import TextareaPlugin +from fobi.contrib.plugins.form_elements.fields \ + .boolean.fobi_form_elements import BooleanSelectPlugin +from fobi.contrib.plugins.form_elements.fields \ + .email.fobi_form_elements import EmailInputPlugin +from fobi.contrib.plugins.form_elements.fields \ + .hidden.fobi_form_elements import HiddenInputPlugin +from fobi.contrib.plugins.form_elements.fields \ + .integer.fobi_form_elements import IntegerInputPlugin +from fobi.contrib.plugins.form_elements.fields \ + .text.fobi_form_elements import TextInputPlugin +from fobi.contrib.plugins.form_elements.fields \ + .textarea.fobi_form_elements import TextareaPlugin -from fobi.contrib.plugins.form_handlers.db_store.fobi_form_handlers \ - import DBStoreHandlerPlugin -from fobi.contrib.plugins.form_handlers.mail.fobi_form_handlers \ - import MailHandlerPlugin +from fobi.contrib.plugins.form_handlers \ + .db_store.fobi_form_handlers import DBStoreHandlerPlugin +from fobi.contrib.plugins.form_handlers \ + .mail.fobi_form_handlers import MailHandlerPlugin -from fobi.tests.base import ( +from .base import ( is_fobi_setup_completed, mark_fobi_setup_as_completed ) -from fobi.tests.constants import ( - FOBI_TEST_USER_USERNAME, FOBI_TEST_USER_PASSWORD, - TEST_FORM_NAME, TEST_FORM_SLUG +from .constants import ( + FOBI_TEST_USER_USERNAME, + FOBI_TEST_USER_PASSWORD, + TEST_FORM_NAME, + TEST_FORM_SLUG ) __title__ = 'fobi.tests.helpers' @@ -42,8 +42,10 @@ __author__ = 'Artur Barseghyan ' __copyright__ = '2014-2016 Artur Barseghyan' __license__ = 'GPL 2.0/LGPL 2.1' __all__ = ( - 'get_or_create_admin_user', 'get_or_create_admin_user', - 'create_form_with_entries', 'db_clean_up', + 'get_or_create_admin_user', + 'get_or_create_admin_user', + 'create_form_with_entries', + 'db_clean_up', ) # **************************************************************************** @@ -126,9 +128,9 @@ def create_form_with_entries(user=None, create_entries_if_form_exist=True): ) form_entry.save() - # ************************************************************************* - # ******************************** Form elements ************************** - # ************************************************************************* + # ************************************************************************ + # ******************************** Form elements ************************* + # ************************************************************************ position = 1 # Text input form_element_entry = FormElementEntry( @@ -225,8 +227,8 @@ def create_form_with_entries(user=None, create_entries_if_form_exist=True): '{"text": "Suspendisse potenti. Etiam in nunc sodales, ' 'congue lectus ut, suscipit massa. In commodo fringilla ' 'orci, in varius eros gravida a! Aliquam erat volutpat. ' - 'Donec sodales orci nec massa aliquam bibendum. Aenean sed ' - 'condimentum velit. Mauris luctus bibendum nulla vel ' + 'Donec sodales orci nec massa aliquam bibendum. Aenean ' + 'sed condimentum velit. Mauris luctus bibendum nulla vel ' 'tempus. Integer tempor condimentum ligula sed feugiat. ' 'Aenean scelerisque ultricies vulputate. Donec semper ' 'lorem rhoncus sem cras amet."}', @@ -235,9 +237,9 @@ def create_form_with_entries(user=None, create_entries_if_form_exist=True): form_element_entry.save() position += 1 - # ************************************************************************* - # ******************************** Form handlers ************************** - # ************************************************************************* + # ************************************************************************ + # ******************************** Form handlers ************************* + # ************************************************************************ # DB save form_handler_entry = FormHandlerEntry( diff --git a/src/fobi/tests/test_browser_build_dynamic_forms.py b/src/fobi/tests/test_browser_build_dynamic_forms.py index b33e545a..ba85dfa3 100644 --- a/src/fobi/tests/test_browser_build_dynamic_forms.py +++ b/src/fobi/tests/test_browser_build_dynamic_forms.py @@ -17,15 +17,19 @@ from django.conf import settings from fobi.models import FormEntry -from fobi.tests.base import print_info, skip # , BaseBrowserTest -from fobi.tests.helpers import ( - setup_fobi, get_or_create_admin_user, db_clean_up -) -from fobi.tests import constants -from fobi.tests.data import ( - TEST_FORM_ELEMENT_PLUGIN_DATA, TEST_FORM_FIELD_DATA, +from . import constants +from .base import print_info, skip +from .data import ( + TEST_FORM_ELEMENT_PLUGIN_DATA, + TEST_FORM_FIELD_DATA, TEST_FORM_HANDLER_PLUGIN_DATA ) +from .helpers import ( + setup_fobi, + get_or_create_admin_user, + db_clean_up +) + __title__ = 'fobi.tests.test_browser_build_dynamic_forms' __author__ = 'Artur Barseghyan ' @@ -239,7 +243,8 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase): self.selenium.find_element_by_xpath('//button[@type="submit"]').click() logger.debug( - """//div[contains(text(), 'Form {0} was created successfully.') and contains(@class, "alert-info")]""".format( + """//div[contains(text(), 'Form {0} was created """ + """successfully.') and contains(@class, "alert-info")]""".format( constants.TEST_FORM_NAME ) ) @@ -247,7 +252,9 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase): # Wait until the fobi page opens with the form element in WebDriverWait(self.selenium, timeout=TIMEOUT).until( lambda driver: driver.find_element_by_xpath( - """//div[contains(text(), 'Form {0} was created successfully.') and contains(@class, "alert-info")]""".format( + """//div[contains(text(), 'Form {0} was created """ + """successfully.') """ + """and contains(@class, "alert-info")]""".format( constants.TEST_FORM_NAME ) ) @@ -272,7 +279,8 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase): # Click the add form element button to add a new form element to the # form. add_form_element_link = self.selenium.find_element_by_xpath( - """//a[contains(text(), 'Choose form element to add') and contains(@class, "dropdown-toggle")]""" + """//a[contains(text(), 'Choose form element to add') and """ + """contains(@class, "dropdown-toggle")]""" ) self._scroll_to(0, 0) @@ -290,7 +298,8 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase): # Click on the element we want form_element_to_add = \ - add_form_element_available_elements_container.find_element_by_xpath( + add_form_element_available_elements_container \ + .find_element_by_xpath( '//a[text()="{0}"]'.format(form_element_name) ) @@ -303,7 +312,8 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase): # Wait until the add widget view opens WebDriverWait(self.selenium, timeout=TIMEOUT).until( lambda driver: driver.find_element_by_xpath( - """//h1[contains(text(), 'Add "{0}" element to the form')]""".format( + """//h1[contains(text(), 'Add "{0}" element to """ + """the form')]""".format( form_element_name ) ) @@ -323,7 +333,9 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase): # Wait until the fobi page opens with the form element in. WebDriverWait(self.selenium, timeout=TIMEOUT).until( lambda driver: driver.find_element_by_xpath( - """//div[contains(text(), 'The form element plugin "{0}" was added successfully.') and contains(@class, "alert-info")]""".format( + """//div[contains(text(), 'The form element plugin "{0}" """ + """was added successfully.') """ + """and contains(@class, "alert-info")]""".format( form_element_name ) ) @@ -364,7 +376,8 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase): # Get the label of the given form element in order to delete it later # from the form. delete_form_element_label = self.selenium.find_element_by_xpath( - """//label[contains(text(), '({0})') and contains(@class, "control-label")]""".format( + """//label[contains(text(), '({0})') """ + """and contains(@class, "control-label")]""".format( form_element_name ) ) @@ -376,18 +389,21 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase): # Click the add form element button to add a new form element to the # form. delete_form_element_link = \ - delete_form_element_label_parent_container.find_element_by_partial_link_text( + delete_form_element_label_parent_container \ + .find_element_by_partial_link_text( 'Delete' ) - # delete_form_element_link.click() - self._click(delete_form_element_link) + delete_form_element_link.click() + # self._click(delete_form_element_link) logger.debug(form_element_name) # Wait until the fobi page opens with the form element in. WebDriverWait(self.selenium, timeout=LONG_TIMEOUT).until( lambda driver: driver.find_element_by_xpath( - """//div[contains(text(), 'The form element plugin "{0}" was deleted successfully.') and contains(@class, "alert-info")]""".format( + """//div[contains(text(), 'The form element plugin "{0}" """ + """was deleted successfully.') """ + """and contains(@class, "alert-info")]""".format( form_element_name ) ) @@ -424,7 +440,8 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase): # Click the add form element button to add a new form element to the # form. add_form_handler_link = self.selenium.find_element_by_xpath( - """//a[contains(text(), 'Choose form handler to add') and contains(@class, "dropdown-toggle")]""" + """//a[contains(text(), 'Choose form handler to add') """ + """and contains(@class, "dropdown-toggle")]""" ) add_form_handler_link.click() @@ -440,7 +457,8 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase): # Click on the element we want form_handler_to_add = \ - add_form_handler_available_elements_container.find_element_by_xpath( + add_form_handler_available_elements_container \ + .find_element_by_xpath( '//a[text()="{0}"]'.format(form_handler_name) ) form_handler_to_add.click() @@ -450,7 +468,8 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase): # Wait until the add widget view opens WebDriverWait(self.selenium, timeout=TIMEOUT).until( lambda driver: driver.find_element_by_xpath( - """//h1[contains(text(), 'Add "{0}" handler to the form')]""".format( + """//h1[contains(text(), 'Add "{0}" handler to """ + """the form')]""".format( form_handler_name ) ) @@ -469,7 +488,9 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase): # Wait until the fobi page opens with the form element in. WebDriverWait(self.selenium, timeout=TIMEOUT).until( lambda driver: driver.find_element_by_xpath( - """//div[contains(text(), 'The form handler plugin "{0}" was added successfully.') and contains(@class, "alert-info")]""".format( + """//div[contains(text(), 'The form handler plugin "{0}" """ + """was added successfully.') """ + """and contains(@class, "alert-info")]""".format( form_handler_name ) ) @@ -529,7 +550,8 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase): # Click the add form element button to add a new form element to the # form. delete_form_handler_link = \ - delete_form_handler_label_parent_container.find_element_by_partial_link_text( + delete_form_handler_label_parent_container \ + .find_element_by_partial_link_text( 'Delete' ) delete_form_handler_link.click() @@ -539,7 +561,9 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase): # Wait until the fobi page opens with the form element in. WebDriverWait(self.selenium, timeout=TIMEOUT).until( lambda driver: driver.find_element_by_xpath( - """//div[contains(text(), 'The form handler plugin "{0}" was deleted successfully.') and contains(@class, "alert-info")]""".format( + """//div[contains(text(), 'The form handler plugin "{0}" """ + """was deleted successfully.') """ + """and contains(@class, "alert-info")]""".format( form_handler_name ) ) @@ -652,7 +676,9 @@ class BaseFobiBrowserBuldDynamicFormsTest(LiveServerTestCase): # Wait until the submit success page opens a clear success message. WebDriverWait(self.selenium, timeout=TIMEOUT).until( lambda driver: driver.find_element_by_xpath( - """//div[contains(text(), 'Form {0} was submitted successfully.') and contains(@class, "alert-info")]""".format( + """//div[contains(text(), 'Form {0} was submitted """ + """successfully.') """ + """and contains(@class, "alert-info")]""".format( constants.TEST_FORM_NAME ) ) diff --git a/src/fobi/tests/test_core.py b/src/fobi/tests/test_core.py index 416cdd1b..a0d1fd1c 100644 --- a/src/fobi/tests/test_core.py +++ b/src/fobi/tests/test_core.py @@ -3,14 +3,17 @@ import unittest from django.test import TestCase, RequestFactory from fobi.base import ( - get_registered_form_element_plugins, get_registered_form_handler_plugins, - get_registered_themes, get_registered_form_callbacks + get_registered_form_element_plugins, + get_registered_form_handler_plugins, + get_registered_themes, + get_registered_form_callbacks ) from fobi.models import FormEntry from fobi.forms import FormEntryForm -from fobi.tests.constants import TEST_FORM_NAME, TEST_FORM_SLUG -from fobi.tests.base import print_info -from fobi.tests.helpers import setup_fobi, get_or_create_admin_user + +from .base import print_info +from .constants import TEST_FORM_NAME, TEST_FORM_SLUG +from .helpers import setup_fobi, get_or_create_admin_user __title__ = 'fobi.tests.test_core' __author__ = 'Artur Barseghyan ' diff --git a/src/fobi/tests/test_dynamic_forms.py b/src/fobi/tests/test_dynamic_forms.py index aec68b32..1979177d 100644 --- a/src/fobi/tests/test_dynamic_forms.py +++ b/src/fobi/tests/test_dynamic_forms.py @@ -3,9 +3,12 @@ import unittest from django.test import TestCase from fobi.dynamic import assemble_form_class -from fobi.tests.base import print_info -from fobi.tests.helpers import ( - setup_fobi, get_or_create_admin_user, create_form_with_entries + +from .base import print_info +from .helpers import ( + setup_fobi, + get_or_create_admin_user, + create_form_with_entries ) __title__ = 'fobi.tests.test_dynamic_forms' diff --git a/src/fobi/tests/test_form_importers_mailchimp.py b/src/fobi/tests/test_form_importers_mailchimp.py index b160c544..7dc8c1c4 100644 --- a/src/fobi/tests/test_form_importers_mailchimp.py +++ b/src/fobi/tests/test_form_importers_mailchimp.py @@ -1,227 +1,54 @@ +import unittest + from django.contrib.auth import get_user_model +from django.test import TestCase -from \ - fobi.contrib.plugins.form_importers.mailchimp_importer.fobi_form_importers \ - import MailChimpImporter +from fobi.contrib.plugins.form_importers \ + .mailchimp_importer.fobi_form_importers import MailChimpImporter +from fobi.models import FormEntry, FormElementEntry -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' - } -] +from .base import print_info +from .data import TEST_MAILCHIMP_IMPORTER_FORM_DATA +from .helpers import setup_fobi, get_or_create_admin_user + +__title__ = 'fobi.tests.test_form_importers_mailchimp' +__author__ = 'Artur Barseghyan ' +__copyright__ = '2014-2016 Artur Barseghyan' +__license__ = 'GPL 2.0/LGPL 2.1' +__all__ = ('FormImpotersMailchimpTest',) -def do(): - """Do test. +class FormImpotersMailchimpTest(TestCase): + """Tests of form importers mailchimp functionality.""" - TODO: Make a normal test out of this. - """ - User = get_user_model() - kwargs = {User.USERNAME_FIELD: 'test_user'} - user = User.objects.get(**kwargs) + def setUp(self): + """Set up.""" + setup_fobi(fobi_sync_plugins=True) - form_properties = {'name': 'Test mailchimp form', 'user': user} + @print_info + def test_01_test_mailchimp_impoter(self): + """Test mailchimp impoter.""" + user = get_or_create_admin_user() - importer = MailChimpFormImporter() + form_properties = { + 'name': 'Test mailchimp form', + 'user': user + } - importer.import_data(form_properties, test_form_data) + importer = MailChimpImporter( + form_entry_cls=FormEntry, + form_element_entry_cls=FormElementEntry + ) + + importer.import_data( + form_properties=form_properties, + form_data=TEST_MAILCHIMP_IMPORTER_FORM_DATA + ) + + form_entry = FormEntry.objects.get(**form_properties) + + self.assertIsNotNone(form_entry.pk) + + +if __name__ == '__main__': + unittest.main() diff --git a/src/fobi/tests/test_sortable_dict.py b/src/fobi/tests/test_sortable_dict.py index 366b65da..8ed7b014 100644 --- a/src/fobi/tests/test_sortable_dict.py +++ b/src/fobi/tests/test_sortable_dict.py @@ -6,7 +6,7 @@ from django.test import TestCase from fobi.data_structures import SortableDict -from fobi.tests.base import print_info +from .base import print_info __title__ = 'fobi.tests.test_dynamic_forms' __author__ = 'Artur Barseghyan ' diff --git a/src/fobi/urls/edit.py b/src/fobi/urls/edit.py index 90cf690a..9d6dc765 100644 --- a/src/fobi/urls/edit.py +++ b/src/fobi/urls/edit.py @@ -21,9 +21,11 @@ from fobi.views import ( edit_form_wizard_entry, edit_form_wizard_handler_entry, export_form_entry, + export_form_wizard_entry, form_importer, form_wizards_dashboard, import_form_entry, + import_form_wizard_entry ) __title__ = 'fobi.urls.edit' @@ -171,6 +173,20 @@ urlpatterns = [ delete_form_wizard_handler_entry, name='fobi.delete_form_wizard_handler_entry'), + # *********************************************************************** + # *********************** Form wizard entry add-ons ********************* + # *********************************************************************** + + # Export form wizard entry + url(_(r'^wizard/export/(?P\d+)/$'), + export_form_wizard_entry, + name='fobi.export_form_wizard_entry'), + + # Import form wizard entry + url(_(r'^wizard/import/$'), + import_form_wizard_entry, + name='fobi.import_form_wizard_entry'), + # *********************************************************************** # ****************************** Dashboard ****************************** # *********************************************************************** diff --git a/src/fobi/utils.py b/src/fobi/utils.py index c77e73a0..be94c06b 100644 --- a/src/fobi/utils.py +++ b/src/fobi/utils.py @@ -2,16 +2,18 @@ Another helper module. This module can NOT be safely imported from any fobi (sub)module - thus should be imported carefully. """ +import datetime import os import logging from six import PY3 from django.conf import settings +from django.contrib import messages from django.core.urlresolvers import reverse from django.forms.widgets import TextInput from django.utils.encoding import force_text -from django.utils.translation import ugettext +from django.utils.translation import ugettext, ugettext_lazy as _ from .base import ( form_element_plugin_registry, @@ -31,8 +33,11 @@ from .base import ( from .dynamic import assemble_form_class from .helpers import update_plugin_data, safe_text from .models import ( + FormEntry, FormElement, + FormElementEntry, FormHandler, + FormHandlerEntry, FormWizardHandler ) from .settings import RESTRICT_PLUGIN_ACCESS, DEBUG, WIZARD_FILES_UPLOAD_DIR @@ -59,6 +64,8 @@ __all__ = ( 'get_user_form_element_plugins_grouped', 'get_user_form_handler_plugins_grouped', 'get_user_form_wizard_handler_plugins_grouped', + 'prepare_form_entry_export_data', + 'perform_form_entry_import', ) logger = logging.getLogger(__name__) @@ -645,3 +652,140 @@ def get_wizard_files_upload_dir(): return os.path.abspath( os.path.join(settings.BASE_DIR, WIZARD_FILES_UPLOAD_DIR) ) + +# ***************************************************************************** +# ***************************************************************************** +# ******************************** Export related ***************************** +# ***************************************************************************** +# ***************************************************************************** + + +def prepare_form_entry_export_data(form_entry, + form_element_entries=None, + form_handler_entries=None): + """Prepare form entry export data. + + :param fobi.modes.FormEntry form_entry: Instance of. + :param django.db.models.QuerySet form_element_entries: QuerySet of + FormElementEntry instances. + :param django.db.models.QuerySet form_handler_entries: QuerySet of + FormHandlerEntry instances. + :return str: + """ + data = { + 'name': form_entry.name, + 'slug': form_entry.slug, + 'is_public': False, + 'is_cloneable': False, + # 'position': form_entry.position, + 'success_page_title': form_entry.success_page_title, + 'success_page_message': form_entry.success_page_message, + 'action': form_entry.action, + 'form_elements': [], + 'form_handlers': [], + } + + if not form_element_entries: + form_element_entries = form_entry.formelemententry_set.all()[:] + + if not form_handler_entries: + form_handler_entries = form_entry.formhandlerentry_set.all()[:] + + for form_element_entry in form_element_entries: + data['form_elements'].append( + { + 'plugin_uid': form_element_entry.plugin_uid, + 'position': form_element_entry.position, + 'plugin_data': form_element_entry.plugin_data, + } + ) + + for form_handler_entry in form_handler_entries: + data['form_handlers'].append( + { + 'plugin_uid': form_handler_entry.plugin_uid, + 'plugin_data': form_handler_entry.plugin_data, + } + ) + return data + + +def perform_form_entry_import(request, form_data): + """Perform form entry import. + + :param django.http.HttpRequest request: + :param dict form_data: + :return :class:`fobi.modes.FormEntry: Instance of. + """ + form_elements_data = form_data.pop('form_elements', []) + form_handlers_data = form_data.pop('form_handlers', []) + + form_data_keys_whitelist = ( + 'name', + 'slug', + 'is_public', + 'is_cloneable', + # 'position', + 'success_page_title', + 'success_page_message', + 'action', + ) + + # In this way we keep possible trash out. + for key in form_data.keys(): + if key not in form_data_keys_whitelist: + form_data.pop(key) + + # User information we always recreate! + form_data['user'] = request.user + + form_entry = FormEntry(**form_data) + + form_entry.name += ugettext(" (imported on {0})").format( + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + ) + form_entry.save() + + # One by one, importing form element plugins. + for form_element_data in form_elements_data: + if form_element_plugin_registry.registry.get( + form_element_data.get('plugin_uid', None), None): + form_element = FormElementEntry(**form_element_data) + form_element.form_entry = form_entry + form_element.save() + else: + if form_element_data.get('plugin_uid', None): + messages.warning( + request, + _('Plugin {0} is missing in the system.' + '').format(form_element_data.get('plugin_uid')) + ) + else: + messages.warning( + request, + _('Some essential plugin data missing in the JSON ' + 'import.') + ) + + # One by one, importing form handler plugins. + for form_handler_data in form_handlers_data: + if form_handler_plugin_registry.registry.get( + form_handler_data.get('plugin_uid', None), None): + form_handler = FormHandlerEntry(**form_handler_data) + form_handler.form_entry = form_entry + form_handler.save() + else: + if form_handler_data.get('plugin_uid', None): + messages.warning( + request, + _('Plugin {0} is missing in the system.' + '').format(form_handler_data.get('plugin_uid')) + ) + else: + messages.warning( + request, + _('Some essential data missing in the JSON ' + 'import.') + ) + + return form_entry diff --git a/src/fobi/validators.py b/src/fobi/validators.py index e5b1e315..0d5b55dd 100644 --- a/src/fobi/validators.py +++ b/src/fobi/validators.py @@ -1,7 +1,11 @@ import requests from requests.exceptions import ( - ConnectionError, ConnectTimeout, ReadTimeout, SSLError, ProxyError, + ConnectionError, + ConnectTimeout, + ReadTimeout, + SSLError, + ProxyError, RetryError ) diff --git a/src/fobi/views.py b/src/fobi/views.py index f6016501..41a9bfb1 100644 --- a/src/fobi/views.py +++ b/src/fobi/views.py @@ -53,6 +53,7 @@ from .forms import ( FormEntryForm, FormElementEntryFormSet, ImportFormEntryForm, + ImportFormWizardEntryForm, FormWizardEntryForm, FormWizardFormEntry, FormWizardFormEntryFormSet, @@ -79,7 +80,9 @@ from .utils import ( get_user_form_wizard_handler_plugins, get_user_form_handler_plugin_uids, get_user_form_wizard_handler_plugin_uids, - get_wizard_files_upload_dir + get_wizard_files_upload_dir, + perform_form_entry_import, + prepare_form_entry_export_data ) from .wizard import DynamicSessionWizardView, DynamicCookieWizardView @@ -121,6 +124,7 @@ __all__ = ( 'form_wizards_dashboard', 'FormWizardView', 'import_form_entry', + 'import_form_wizard_entry', 'view_form_entry', ) @@ -665,7 +669,7 @@ def add_form_element_entry(request, form = form_element_plugin.get_initialised_create_form_or_404( data=request.POST, files=request.FILES - ) + ) form.validate_plugin_data(form_elements, request=request) if form.is_valid(): # Saving the plugin form data. @@ -1403,15 +1407,23 @@ class FormWizardView(DynamicSessionWizardView): context_data = super(FormWizardView, self).get_context_data( form=form, **kwargs ) - + form_entry = self.get_form_entry_for_step(self.steps.step0) context_data.update({ 'form_wizard_entry': self.form_wizard_entry, 'form_wizard_mode': True, 'fobi_theme': self.fobi_theme, + 'fobi_form_title': form_entry.title, + 'fobi_form_wizard_title': self.form_wizard_entry.title, + 'steps_range': range(1, self.steps.count+1), }) return context_data + def get_form_entry_for_step(self, step): + """Get form entry title for step.""" + form_slug = self.form_list[self.steps.step0][0] + return self.form_entry_mapping[form_slug] + def get_initial_wizard_data(self, request, *args, **kwargs): """Get initial wizard data.""" try: @@ -1433,6 +1445,7 @@ class FormWizardView(DynamicSessionWizardView): ] form_list = [] form_entry_mapping = {} + form_element_entry_mapping = {} wizard_form_element_entries = [] for creation_counter, form_entry in enumerate(form_entries): # Using frozen queryset to minimize query usage @@ -1451,6 +1464,7 @@ class FormWizardView(DynamicSessionWizardView): (form_entry.slug, form_cls) ) form_entry_mapping[form_entry.slug] = form_entry + form_element_entry_mapping[form_entry.slug] = form_element_entries if 0 == len(form_list): raise Http404( @@ -1466,6 +1480,7 @@ class FormWizardView(DynamicSessionWizardView): 'form_wizard_entry': form_wizard_entry, 'wizard_form_element_entries': wizard_form_element_entries, 'form_entry_mapping': form_entry_mapping, + 'form_element_entry_mapping': form_element_entry_mapping, 'fobi_theme': theme, } @@ -1506,9 +1521,17 @@ class FormWizardView(DynamicSessionWizardView): if form.is_valid(): # Get current form entry form_entry = self.form_entry_mapping[self.steps.current] + # Get form elements for the current form entry + form_element_entries = \ + self.form_element_entry_mapping[self.steps.current] # Fire plugin processors - form = submit_plugin_form_data(form_entry=form_entry, - request=self.request, form=form) + form = submit_plugin_form_data( + form_entry=form_entry, + request=self.request, + form=form, + form_element_entries=form_element_entries, + **{'form_wizard_entry': self.form_wizard_entry} + ) # Form wizards make use of form.data instead of form.cleaned_data. # Therefore, we update the form.data with values from # form.cleaned_data. @@ -1550,6 +1573,18 @@ class FormWizardView(DynamicSessionWizardView): return self.render_next_step(form) return self.render(form) + def get_ignorable_field_names(self, form_element_entries): + """Get ignorable field names.""" + ignorable_field_names = [] + for form_element_entry in form_element_entries: + plugin = form_element_entry.get_plugin() + # If plugin doesn't have a value, we don't need to have it + # on the last step (otherwise validation issues may arise, as + # it happens with captcha/re-captcha). + if not plugin.has_value: + ignorable_field_names.append(plugin.data.name) + return ignorable_field_names + def render_done(self, form, **kwargs): """Render done. @@ -1568,17 +1603,32 @@ class FormWizardView(DynamicSessionWizardView): files=self.storage.get_step_files(form_key) ) + # Get form elements for the current form entry + form_element_entries = \ + self.form_element_entry_mapping[form_key] + + ignorable_field_names = self.get_ignorable_field_names( + form_element_entries + ) + + for ignorable_field_name in ignorable_field_names: + form_obj.fields.pop(ignorable_field_name) + if not form_obj.is_valid(): return self.render_revalidation_failure(form_key, form_obj, **kwargs) # Fire plugin processors + # Get current form entry form_entry = self.form_entry_mapping[form_key] + form_obj = submit_plugin_form_data( form_entry=form_entry, request=self.request, - form=form_obj + form=form_obj, + form_element_entries=form_element_entries, + **{'form_wizard_entry': self.form_wizard_entry} ) final_forms[form_key] = form_obj @@ -2119,8 +2169,11 @@ def view_form_entry(request, form_entry_slug, theme=None, template_name=None): ) # Fire plugin processors - form = submit_plugin_form_data(form_entry=form_entry, - request=request, form=form) + form = submit_plugin_form_data( + form_entry=form_entry, + request=request, + form=form + ) # Fire form valid callbacks form = fire_form_callbacks(form_entry=form_entry, @@ -2190,6 +2243,7 @@ def view_form_entry(request, form_entry_slug, theme=None, template_name=None): 'form': form, 'form_entry': form_entry, 'fobi_theme': theme, + 'fobi_form_title': form_entry.title, } if not template_name: @@ -2271,38 +2325,40 @@ def export_form_entry(request, form_entry_id, template_name=None): except ObjectDoesNotExist as err: raise Http404(ugettext("Form entry not found.")) - data = { - 'name': form_entry.name, - 'slug': form_entry.slug, - 'is_public': False, - 'is_cloneable': False, - # 'position': form_entry.position, - 'success_page_title': form_entry.success_page_title, - 'success_page_message': form_entry.success_page_message, - 'action': form_entry.action, - 'form_elements': [], - 'form_handlers': [], - } + data = prepare_form_entry_export_data(form_entry) - form_element_entries = form_entry.formelemententry_set.all()[:] - form_handler_entries = form_entry.formhandlerentry_set.all()[:] - - for form_element_entry in form_element_entries: - data['form_elements'].append( - { - 'plugin_uid': form_element_entry.plugin_uid, - 'position': form_element_entry.position, - 'plugin_data': form_element_entry.plugin_data, - } - ) - - for form_handler_entry in form_handler_entries: - data['form_handlers'].append( - { - 'plugin_uid': form_handler_entry.plugin_uid, - 'plugin_data': form_handler_entry.plugin_data, - } - ) + # data = { + # 'name': form_entry.name, + # 'slug': form_entry.slug, + # 'is_public': False, + # 'is_cloneable': False, + # # 'position': form_entry.position, + # 'success_page_title': form_entry.success_page_title, + # 'success_page_message': form_entry.success_page_message, + # 'action': form_entry.action, + # 'form_elements': [], + # 'form_handlers': [], + # } + # + # form_element_entries = form_entry.formelemententry_set.all()[:] + # form_handler_entries = form_entry.formhandlerentry_set.all()[:] + # + # for form_element_entry in form_element_entries: + # data['form_elements'].append( + # { + # 'plugin_uid': form_element_entry.plugin_uid, + # 'position': form_element_entry.position, + # 'plugin_data': form_element_entry.plugin_data, + # } + # ) + # + # for form_handler_entry in form_handler_entries: + # data['form_handlers'].append( + # { + # 'plugin_uid': form_handler_entry.plugin_uid, + # 'plugin_data': form_handler_entry.plugin_data, + # } + # ) data_exporter = JSONDataExporter(json.dumps(data), form_entry.slug) @@ -2340,76 +2396,77 @@ def import_form_entry(request, template_name=None): # we need to make sure it doesn't have strange fields in. # Furthermore, we will use the `form_element_data` and # `form_handler_data` for filling the missing plugin data. - form_elements_data = form_data.pop('form_elements', []) - form_handlers_data = form_data.pop('form_handlers', []) - - form_data_keys_whitelist = ( - 'name', - 'slug', - 'is_public', - 'is_cloneable', - # 'position', - 'success_page_title', - 'success_page_message', - 'action', - ) - - # In this way we keep possible trash out. - for key in form_data.keys(): - if key not in form_data_keys_whitelist: - form_data.pop(key) - - # User information we always recreate! - form_data['user'] = request.user - - form_entry = FormEntry(**form_data) - - form_entry.name += ugettext(" (imported on {0})").format( - datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") - ) - form_entry.save() - - # One by one, importing form element plugins. - for form_element_data in form_elements_data: - if form_element_plugin_registry._registry.get( - form_element_data.get('plugin_uid', None), None): - form_element = FormElementEntry(**form_element_data) - form_element.form_entry = form_entry - form_element.save() - else: - if form_element_data.get('plugin_uid', None): - messages.warning( - request, - _('Plugin {0} is missing in the system.' - '').format(form_element_data.get('plugin_uid')) - ) - else: - messages.warning( - request, - _('Some essential plugin data missing in the JSON ' - 'import.') - ) - - # One by one, importing form handler plugins. - for form_handler_data in form_handlers_data: - if form_handler_plugin_registry._registry.get( - form_handler_data.get('plugin_uid', None), None): - form_handler = FormHandlerEntry(**form_handler_data) - form_handler.form_entry = form_entry - form_handler.save() - else: - if form_handler.get('plugin_uid', None): - messages.warning( - request, - _('Plugin {0} is missing in the system.' - '').format(form_handler.get('plugin_uid')) - ) - else: - messages.warning( - request, - _('Some essential data missing in the JSON ' - 'import.') - ) + form_entry = perform_form_entry_import(request, form_data) + # form_elements_data = form_data.pop('form_elements', []) + # form_handlers_data = form_data.pop('form_handlers', []) + # + # form_data_keys_whitelist = ( + # 'name', + # 'slug', + # 'is_public', + # 'is_cloneable', + # # 'position', + # 'success_page_title', + # 'success_page_message', + # 'action', + # ) + # + # # In this way we keep possible trash out. + # for key in form_data.keys(): + # if key not in form_data_keys_whitelist: + # form_data.pop(key) + # + # # User information we always recreate! + # form_data['user'] = request.user + # + # form_entry = FormEntry(**form_data) + # + # form_entry.name += ugettext(" (imported on {0})").format( + # datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + # ) + # form_entry.save() + # + # # One by one, importing form element plugins. + # for form_element_data in form_elements_data: + # if form_element_plugin_registry._registry.get( + # form_element_data.get('plugin_uid', None), None): + # form_element = FormElementEntry(**form_element_data) + # form_element.form_entry = form_entry + # form_element.save() + # else: + # if form_element_data.get('plugin_uid', None): + # messages.warning( + # request, + # _('Plugin {0} is missing in the system.' + # '').format(form_element_data.get('plugin_uid')) + # ) + # else: + # messages.warning( + # request, + # _('Some essential plugin data missing in the ' + # 'JSON import.') + # ) + # + # # One by one, importing form handler plugins. + # for form_handler_data in form_handlers_data: + # if form_handler_plugin_registry._registry.get( + # form_handler_data.get('plugin_uid', None), None): + # form_handler = FormHandlerEntry(**form_handler_data) + # form_handler.form_entry = form_entry + # form_handler.save() + # else: + # if form_handler.get('plugin_uid', None): + # messages.warning( + # request, + # _('Plugin {0} is missing in the system.' + # '').format(form_handler.get('plugin_uid')) + # ) + # else: + # messages.warning( + # request, + # _('Some essential data missing in the JSON ' + # 'import.') + # ) messages.info( request, @@ -2450,6 +2507,211 @@ def import_form_entry(request, template_name=None): template_name, context, context_instance=RequestContext(request) ) +# ***************************************************************************** +# ***************************************************************************** +# ************************* Export form wizard entry ************************** +# ***************************************************************************** +# ***************************************************************************** + + +@login_required +@permissions_required(satisfy=SATISFY_ALL, + perms=create_form_wizard_entry_permissions) +def export_form_wizard_entry(request, + form_wizard_entry_id, + template_name=None): + """Export form entry to JSON. + + :param django.http.HttpRequest request: + :param int form_wizard_entry_id: + :param string template_name: + :return django.http.HttpResponse: + """ + try: + form_wizard_entry = FormWizardEntry._default_manager \ + .get(pk=form_wizard_entry_id, user__pk=request.user.pk) + + except ObjectDoesNotExist as err: + raise Http404(ugettext("Form wizard entry not found.")) + + data = { + 'name': form_wizard_entry.name, + 'slug': form_wizard_entry.slug, + 'is_public': False, + 'is_cloneable': False, + 'success_page_title': form_wizard_entry.success_page_title, + 'success_page_message': form_wizard_entry.success_page_message, + 'form_wizard_forms': [], + 'form_wizard_handlers': [], + } + + form_wizard_form_entries = \ + form_wizard_entry.formwizardformentry_set.all()[:] + form_wizard_handler_entries = \ + form_wizard_entry.formwizardhandlerentry_set.all()[:] + + for wizard_form_entry in form_wizard_form_entries: + data['form_wizard_forms'].append( + prepare_form_entry_export_data(wizard_form_entry.form_entry) + ) + + for wizard_handler_entry in form_wizard_handler_entries: + data['form_wizard_handlers'].append( + { + 'plugin_uid': wizard_handler_entry.plugin_uid, + 'plugin_data': wizard_handler_entry.plugin_data, + } + ) + + data_exporter = JSONDataExporter(json.dumps(data), form_wizard_entry.slug) + + return data_exporter.export() + + +# ***************************************************************************** +# ***************************************************************************** +# **************************** Import form entry ****************************** +# ***************************************************************************** +# ***************************************************************************** + + +@login_required +@permissions_required(satisfy=SATISFY_ALL, + perms=create_form_wizard_entry_permissions) +def import_form_wizard_entry(request, template_name=None): + """Import form wizard entry. + + :param django.http.HttpRequest request: + :param string template_name: + :return django.http.HttpResponse: + """ + if 'POST' == request.method: + form = ImportFormWizardEntryForm(request.POST, request.FILES) + + if form.is_valid(): + # Reading the contents of the file into JSON + json_file = form.cleaned_data['file'] + file_contents = json_file.read() + + # This is the form data which we are going to use when recreating + # the form. + form_wizard_data = json.loads(file_contents) + + # Since we just feed all the data to the `FormEntry` class, + # we need to make sure it doesn't have strange fields in. + # Furthermore, we will use the `form_element_data` and + # `form_handler_data` for filling the missing plugin data. + form_wizard_forms_data = form_wizard_data.pop( + 'form_wizard_forms', [] + ) + form_wizard_handlers_data = form_wizard_data.pop( + 'form_wizard_handlers', [] + ) + + form_wizard_data_keys_whitelist = ( + 'name', + 'slug', + 'is_public', + 'is_cloneable', + 'success_page_title', + 'success_page_message', + 'action', + ) + + # In this way we keep possible trash out. + for key in form_wizard_data.keys(): + if key not in form_wizard_data_keys_whitelist: + form_wizard_data.pop(key) + + # User information we always recreate! + form_wizard_data['user'] = request.user + + form_wizard_entry = FormWizardEntry(**form_wizard_data) + + form_wizard_entry.name += ugettext(" (imported on {0})").format( + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + ) + form_wizard_entry.save() + + # One by one, importing form element plugins. + for counter, form_entry_data \ + in enumerate(form_wizard_forms_data): + form_entry = perform_form_entry_import( + request, + form_entry_data + ) + FormWizardFormEntry.objects.create( + form_wizard_entry=form_wizard_entry, + form_entry=form_entry, + position=counter + ) + # One by one, importing form handler plugins. + for form_wizard_handler_data in form_wizard_handlers_data: + if form_wizard_handler_plugin_registry.registry.get( + form_wizard_handler_data.get('plugin_uid', None), + None + ): + form_wizard_handler = FormWizardHandlerEntry( + **form_wizard_handler_data + ) + form_wizard_handler.form_wizard_entry = form_wizard_entry + form_wizard_handler.save() + else: + if form_wizard_handler_data.get('plugin_uid', None): + messages.warning( + request, + _('Plugin {0} is missing in the system.').format( + form_wizard_handler_data.get('plugin_uid') + ) + ) + else: + messages.warning( + request, + _('Some essential data missing in the JSON ' + 'import.') + ) + + messages.info( + request, + _('The form wizard was imported successfully.') + ) + return redirect( + 'fobi.edit_form_wizard_entry', + form_wizard_entry_id=form_wizard_entry.pk + ) + else: + form = ImportFormWizardEntryForm() + + # When importing entries from saved JSON we shouldn't just save + # them into database and consider it done, since there might be cases + # if a certain plugin doesn't exist in the system, which will lead + # to broken form entries. Instead, we should check every single + # form-element or form-handler plugin for existence. If not doesn't exist + # in the system, we might: (1) roll entire transaction back or (2) ignore + # broken entries. The `ImportFormEntryForm` form has two fields to + # additional fields which serve the purpose: + # `ignore_broken_form_element_entries` and + # `ignore_broken_form_handler_entries`. When set to True, when a broken + # form element/handler plugin has been discovered, the import would + # continue, having the broken form element/handler entries not imported. + + context = { + 'form': form, + # 'form_entry': form_entry + } + + if not template_name: + theme = get_theme(request=request, as_instance=True) + template_name = theme.import_form_entry_template + + if versions.DJANGO_GTE_1_10: + return render(request, template_name, context) + else: + return render_to_response( + template_name, context, context_instance=RequestContext(request) + ) + + # ***************************************************************************** # ***************************************************************************** # ****************************** Form importers ******************************* diff --git a/src/fobi/widgets.py b/src/fobi/widgets.py index b557a6f6..eb3aaaeb 100644 --- a/src/fobi/widgets.py +++ b/src/fobi/widgets.py @@ -3,6 +3,10 @@ from django.utils.html import format_html from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _ +from nine import versions + +from .helpers import flatatt_inverse_quotes + # Safe import of ``NumberInput`` try: from django.forms.widgets import NumberInput @@ -22,6 +26,7 @@ __all__ = ( 'NumberInput', 'BooleanRadioSelect', 'RichSelect', + 'RichSelectInverseQuotes', ) @@ -96,3 +101,58 @@ class RichSelect(Select): format_html(self.append_html) ]) ) + + +class RichSelectInverseQuotes(RichSelect): + """Almost same as original, but uses alternative flatatt function. + + Uses inverse quotes. + """ + if versions.DJANGO_GTE_1_10: + def render(self, name, value, attrs=None): + if self.override_name is not None: + name = self.override_name + + if value is None: + value = '' + final_attrs = self.build_attrs(attrs, name=name) + output = [ + format_html('', flatatt_inverse_quotes(final_attrs)) + ] + options = self.render_options([value]) + if options: + output.append(options) + output.append('') + rendered_select = mark_safe('\n'.join(output)) + + return mark_safe( + '\n'.join([ + format_html(self.prepend_html), + rendered_select, + format_html(self.append_html) + ]) + ) + else: + def render(self, name, value, attrs=None, choices=()): + if self.override_name is not None: + name = self.override_name + + if value is None: + value = '' + final_attrs = self.build_attrs(attrs, name=name) + output = [ + format_html('', flatatt_inverse_quotes(final_attrs)) + ] + options = self.render_options(choices, [value]) + if options: + output.append(options) + output.append('') + rendered_select = mark_safe('\n'.join(output)) + + return mark_safe( + '\n'.join([ + format_html(self.prepend_html), + rendered_select, + format_html(self.append_html) + ]) + ) diff --git a/src/fobi/wizard/views/dynamic.py b/src/fobi/wizard/views/dynamic.py index 253e546c..e3a58b59 100644 --- a/src/fobi/wizard/views/dynamic.py +++ b/src/fobi/wizard/views/dynamic.py @@ -107,9 +107,14 @@ class StepsHelper(object): @property def index(self): - """Return the index for the current step.""" + """Return the index for the current step (0 based).""" return self._wizard.get_step_index() + @property + def index1(self): + """Return the index for the current step (1 based).""" + return self.index + 1 + @property def step0(self): """Step 0.""" @@ -120,6 +125,11 @@ class StepsHelper(object): """Step 1.""" return int(self.index) + 1 + @property + def is_last_step(self): + """Check if last step.""" + return self.index1 == self.count + # class DynamicTemplateView(TemplateView): # """Dynamic template view.""" diff --git a/tox.ini b/tox.ini index 5e8144ec..09e60123 100644 --- a/tox.ini +++ b/tox.ini @@ -1,35 +1,15 @@ [tox] envlist = - #py{27,33,34}-{django15,django16}, - #py{27,33,34}-{django17,django18} - py{27,34}-{django17,django18} - py{27,34}-{django19} +# py{27,33,34}-{django15,django16}, +# py{27,33,34}-{django17,django18} +# py{27,34}-{django17,django18} + py{27,34,35}-{django18,django19} py{27,35}-{django110} #flake8, #isort -[testenv:django15] -passenv = * -deps = - django15: -r{toxinidir}/examples/requirements/django_1_5.txt -commands = - {envpython} examples/simple/manage.py test {posargs:fobi} --settings=settings.bootstrap3_theme_django_1_5 --traceback -v 3 - -[testenv:django16] -passenv = * -deps = - django16: -r{toxinidir}/examples/requirements/django_1_6.txt -commands = - {envpython} examples/simple/manage.py test {posargs:fobi} --settings=settings.bootstrap3_theme_django_1_6 - -[testenv:django17] -passenv = * -deps = - django17: -r{toxinidir}/examples/requirements/django_1_7.txt -commands = - {envpython} examples/simple/manage.py test {posargs:fobi} --settings=settings.bootstrap3_theme_django_1_7 - [testenv] +envlogdir=examples/logs/ passenv = * deps = # django15: -r{toxinidir}/examples/requirements/django_1_5.txt @@ -39,7 +19,8 @@ deps = django19: -r{toxinidir}/examples/requirements/django_1_9.txt django110: -r{toxinidir}/examples/requirements/django_1_10.txt commands = - {envpython} examples/simple/manage.py test {posargs:fobi} --settings=settings.test --traceback -v 3 +# {envpython} examples/simple/manage.py test {posargs:fobi} --settings=settings.test --traceback -v 3 + {envpython} runtests.py #[testenv:flake8] #basepython = python3.5