Skip to content

Commit

Permalink
Adds request to signature of plugin methods, adds docs, changelog
Browse files Browse the repository at this point in the history
  • Loading branch information
mkoistinen committed Apr 25, 2016
1 parent 6386431 commit 13739ea
Show file tree
Hide file tree
Showing 15 changed files with 936 additions and 121 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

==== 0.1.1 (2016-04-24) ===

* Adds `request` to signature of `get_form_class`, `get_success_url`
and `form_valid`.
* Adds proper documentation
* Adds this `CHANGELOG.txt`
6 changes: 6 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
include LICENSE
include README.rst
include CHANGELOG.txt
recursive-include docs *
recursive-exclude * *.pyc
recursive-exclude * *.scssc
115 changes: 2 additions & 113 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,117 +6,6 @@ This package aims to provide a mechanism for handling form-submissions in
django-CMS plugins.


Background
----------
.. include:: docs/background.rst

Plugins are a key component of `django CMS <https://django-cms.org>`_ for
creating reusable, configurable content fragments in django CMS projects. Due to
their flexibility and utility, project developers would benefit from emitting
forms and handling form submissions using plugins.

Since CMS plugins are fragments of a page, they do not provide a unique, RESTful
URL for receiving and handling form submissions. This presents numerous
challenges when attempting to process form submissions.


Approach
--------

To get around these limitations, the approach taken in this package is to direct
form submissions from plugins which sub-class ``FormPluginBase`` to a URL that
is outside of the django CMS URL-space and handled by a ``ProcessFormView``
provided by this package.

The ``ProcessFormView`` accepts form-submissions, processes them, and if valid,
the request is redirected to a ``success_url`` provided by the plugin. On
validation errors, the view will redirect the request back to the originating
page and provide the form data via a session variable back to the plugin's form.

The user experience is precisely as expected and the handling of the form is
performed without "thrown HTTPRedirectResponses" or special middleware.

This package encapsulates all extra logic so that the plugin developer need
only to subclass ``cmsplugin_form_handler.cms_plugins.FormPluginBase`` rather
than the usual ``cms.plugin_base.CMSPluginBase``.

The ``Form`` or ``ModelForm`` presented in the CMS plugin should also include
the "mixin" ``cmsplugin_form_handler.forms.FormPluginFormMixin``.


------------
Installation
------------

Install package into your project's path::

pip install [TODO]

Add to your project's settings.INSTALLED_APPS::

INSTALLED_APPS = (
...,
'cmsplugin_form_handler',
)

Also, add a line to your project's urls.py file::

urlpatterns = i18n_patterns('',
url(r'^admin/', include(admin.site.urls)), # NOQA
url(r'^sitemap\.xml$', 'django.contrib.sitemaps.views.sitemap',
{'sitemaps': {'cmspages': CMSSitemap}}),
url(r'^select2/', include('django_select2.urls')),

# vvvvv
url(r'^plugin_forms/', include('cmsplugin_form_handler.urls',
namespace='cmsplugin_form_handler')),
# ^^^^^
url(r'^', include('cms.urls')),
)

.. note::

The URL segment ``plugin_forms`` can be anything you like but pick something
that is unlikely to collide with a desired page slug. It should be inserted
before the CMS urls, but after the Admin urls.

-----
Usage
-----

When you create your plugin class, instead of inheriting from CMSPluginBase,
inherit from PluginFormBase as follows::

from django import forms

from cms.models import CMSPlugin

from cmsplugin_form_handler.cms_plugins import PluginFormBase
from cmsplugin_form_handler.forms import FormPluginFormMixin


class CoolForm(FormPluginFormMixin, forms.Form):
# Define your form as per usual...
cool_field = forms.CharField(...)


class CoolFormPlugin(PluginFormBase):
name = 'Cool Form'
model = CMSPlugin
render_template = 'form_plugin.html'
form_class = CoolForm
success_url = 'http://www.google.com/'

plugin_pool.register_plugin(FormPlugin)

As usual, you must define a ``render_template`` for your plugin. Here's one::

{% load cmsplugin_form_tags %}
<h2>Form Plugin ({{ instance.pk }})</h2>
<form action="{% cmsplugin_form_action %}" method="post">
{% csrf_token %}
{{ form }}
<input type="submit">
</form>

Note that the only thing special here is the extra context:
``plugin_form_action`` and ``form``.
.. include:: docs/quickstart.rst
8 changes: 4 additions & 4 deletions cmsplugin_form_handler/cms_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class FormPluginBase(CMSPluginBase):
form_class = None
success_url = None

def get_form_class(self, instance):
def get_form_class(self, request, instance):
"""
Returns the form class to be used by this plugin.
Expand All @@ -27,7 +27,7 @@ def get_form_class(self, instance):
"""
return self.form_class

def get_success_url(self, instance):
def get_success_url(self, request, instance):
"""
Returns the redirect URL for successful form submissions.
Expand All @@ -37,7 +37,7 @@ def get_success_url(self, instance):
"""
return self.success_url

def form_valid(self, instance, form):
def form_valid(self, request, instance, form):
"""
If the form validates, this method will be called before the user is
redirected to the success_url. The default implementation is to just
Expand All @@ -49,7 +49,7 @@ def render(self, context, instance, placeholder):
context = super(FormPluginBase, self).render(context, instance, placeholder) # noqa
request = context.get('request')

form_class = self.get_form_class(instance)
form_class = self.get_form_class(request, instance)
if form_class:
source_url = request.path
data = None
Expand Down
8 changes: 4 additions & 4 deletions cmsplugin_form_handler/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def source_url(self):
def get_form_class(self):
instance, plugin = self.plugin
if hasattr(plugin, 'get_form_class'):
return plugin.get_form_class(instance)
return plugin.get_form_class(self.request, instance)
raise ImproperlyConfigured(
'Source form plugin does not define `get_form_class()`.')

Expand All @@ -61,7 +61,7 @@ def get_form_kwargs(self):
def get_success_url(self):
instance, plugin = self.plugin
try:
url = plugin.get_success_url(instance)
url = plugin.get_success_url(self.request, instance)
return url
except AttributeError:
raise ImproperlyConfigured(
Expand All @@ -74,7 +74,7 @@ def get_form_valid(self):
"""
instance, plugin = self.plugin
try:
callback = plugin.on_valid_form(instance)
callback = plugin.valid_form(self.request, instance)
return callback
except AttributeError:
return None
Expand All @@ -93,7 +93,7 @@ def form_valid(self, form):
# If the source plugin has declared a `form_valid` method, call it with
# the validated form before redirecting to the `success_url`.
instance, plugin = self.plugin
plugin.form_valid(instance, form)
plugin.form_valid(self.request, instance, form)

return super(ProcessFormView, self).form_valid(form)

Expand Down
Loading

0 comments on commit 13739ea

Please sign in to comment.