|
0
|
1 |
""" |
|
|
2 |
Formtools Preview application. |
|
|
3 |
""" |
|
|
4 |
|
|
|
5 |
import cPickle as pickle |
|
|
6 |
|
|
|
7 |
from django.conf import settings |
|
|
8 |
from django.http import Http404 |
|
|
9 |
from django.shortcuts import render_to_response |
|
|
10 |
from django.template.context import RequestContext |
|
|
11 |
from django.utils.hashcompat import md5_constructor |
|
|
12 |
from django.contrib.formtools.utils import security_hash |
|
|
13 |
|
|
|
14 |
AUTO_ID = 'formtools_%s' # Each form here uses this as its auto_id parameter. |
|
|
15 |
|
|
|
16 |
class FormPreview(object): |
|
|
17 |
preview_template = 'formtools/preview.html' |
|
|
18 |
form_template = 'formtools/form.html' |
|
|
19 |
|
|
|
20 |
# METHODS SUBCLASSES SHOULDN'T OVERRIDE ################################### |
|
|
21 |
|
|
|
22 |
def __init__(self, form): |
|
|
23 |
# form should be a Form class, not an instance. |
|
|
24 |
self.form, self.state = form, {} |
|
|
25 |
|
|
|
26 |
def __call__(self, request, *args, **kwargs): |
|
|
27 |
stage = {'1': 'preview', '2': 'post'}.get(request.POST.get(self.unused_name('stage')), 'preview') |
|
|
28 |
self.parse_params(*args, **kwargs) |
|
|
29 |
try: |
|
|
30 |
method = getattr(self, stage + '_' + request.method.lower()) |
|
|
31 |
except AttributeError: |
|
|
32 |
raise Http404 |
|
|
33 |
return method(request) |
|
|
34 |
|
|
|
35 |
def unused_name(self, name): |
|
|
36 |
""" |
|
|
37 |
Given a first-choice name, adds an underscore to the name until it |
|
|
38 |
reaches a name that isn't claimed by any field in the form. |
|
|
39 |
|
|
|
40 |
This is calculated rather than being hard-coded so that no field names |
|
|
41 |
are off-limits for use in the form. |
|
|
42 |
""" |
|
|
43 |
while 1: |
|
|
44 |
try: |
|
|
45 |
f = self.form.base_fields[name] |
|
|
46 |
except KeyError: |
|
|
47 |
break # This field name isn't being used by the form. |
|
|
48 |
name += '_' |
|
|
49 |
return name |
|
|
50 |
|
|
|
51 |
def preview_get(self, request): |
|
|
52 |
"Displays the form" |
|
|
53 |
f = self.form(auto_id=AUTO_ID) |
|
|
54 |
return render_to_response(self.form_template, |
|
|
55 |
{'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state}, |
|
|
56 |
context_instance=RequestContext(request)) |
|
|
57 |
|
|
|
58 |
def preview_post(self, request): |
|
|
59 |
"Validates the POST data. If valid, displays the preview page. Else, redisplays form." |
|
|
60 |
f = self.form(request.POST, auto_id=AUTO_ID) |
|
|
61 |
context = {'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state} |
|
|
62 |
if f.is_valid(): |
|
29
|
63 |
self.process_preview(request, f, context) |
|
0
|
64 |
context['hash_field'] = self.unused_name('hash') |
|
|
65 |
context['hash_value'] = self.security_hash(request, f) |
|
|
66 |
return render_to_response(self.preview_template, context, context_instance=RequestContext(request)) |
|
|
67 |
else: |
|
|
68 |
return render_to_response(self.form_template, context, context_instance=RequestContext(request)) |
|
|
69 |
|
|
|
70 |
def post_post(self, request): |
|
|
71 |
"Validates the POST data. If valid, calls done(). Else, redisplays form." |
|
|
72 |
f = self.form(request.POST, auto_id=AUTO_ID) |
|
|
73 |
if f.is_valid(): |
|
|
74 |
if self.security_hash(request, f) != request.POST.get(self.unused_name('hash')): |
|
|
75 |
return self.failed_hash(request) # Security hash failed. |
|
|
76 |
return self.done(request, f.cleaned_data) |
|
|
77 |
else: |
|
|
78 |
return render_to_response(self.form_template, |
|
|
79 |
{'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state}, |
|
|
80 |
context_instance=RequestContext(request)) |
|
|
81 |
|
|
|
82 |
# METHODS SUBCLASSES MIGHT OVERRIDE IF APPROPRIATE ######################## |
|
|
83 |
|
|
|
84 |
def parse_params(self, *args, **kwargs): |
|
|
85 |
""" |
|
|
86 |
Given captured args and kwargs from the URLconf, saves something in |
|
|
87 |
self.state and/or raises Http404 if necessary. |
|
|
88 |
|
|
|
89 |
For example, this URLconf captures a user_id variable: |
|
|
90 |
|
|
|
91 |
(r'^contact/(?P<user_id>\d{1,6})/$', MyFormPreview(MyForm)), |
|
|
92 |
|
|
|
93 |
In this case, the kwargs variable in parse_params would be |
|
|
94 |
{'user_id': 32} for a request to '/contact/32/'. You can use that |
|
|
95 |
user_id to make sure it's a valid user and/or save it for later, for |
|
|
96 |
use in done(). |
|
|
97 |
""" |
|
|
98 |
pass |
|
|
99 |
|
|
29
|
100 |
def process_preview(self, request, form, context): |
|
|
101 |
""" |
|
|
102 |
Given a validated form, performs any extra processing before displaying |
|
|
103 |
the preview page, and saves any extra data in context. |
|
|
104 |
""" |
|
|
105 |
pass |
|
|
106 |
|
|
0
|
107 |
def security_hash(self, request, form): |
|
|
108 |
""" |
|
|
109 |
Calculates the security hash for the given HttpRequest and Form instances. |
|
|
110 |
|
|
|
111 |
Subclasses may want to take into account request-specific information, |
|
|
112 |
such as the IP address. |
|
|
113 |
""" |
|
|
114 |
return security_hash(request, form) |
|
|
115 |
|
|
|
116 |
def failed_hash(self, request): |
|
|
117 |
"Returns an HttpResponse in the case of an invalid security hash." |
|
|
118 |
return self.preview_post(request) |
|
|
119 |
|
|
|
120 |
# METHODS SUBCLASSES MUST OVERRIDE ######################################## |
|
|
121 |
|
|
|
122 |
def done(self, request, cleaned_data): |
|
|
123 |
""" |
|
|
124 |
Does something with the cleaned_data and returns an |
|
|
125 |
HttpResponseRedirect. |
|
|
126 |
""" |
|
|
127 |
raise NotImplementedError('You must define a done() method on your %s subclass.' % self.__class__.__name__) |