bgneal@83
|
1 """
|
bgneal@83
|
2 Forms for the forums application.
|
bgneal@469
|
3
|
bgneal@83
|
4 """
|
bgneal@951
|
5 from collections import OrderedDict
|
bgneal@951
|
6
|
bgneal@83
|
7 from django import forms
|
bgneal@95
|
8 from django.conf import settings
|
bgneal@83
|
9
|
bgneal@110
|
10 from forums.models import Forum
|
bgneal@83
|
11 from forums.models import Topic
|
bgneal@83
|
12 from forums.models import Post
|
bgneal@285
|
13 from forums.attachments import AttachmentProcessor
|
bgneal@460
|
14 import forums.permissions as perms
|
bgneal@469
|
15 from forums.signals import notify_new_topic, notify_new_post
|
bgneal@963
|
16 from core.html import ImageCheckError
|
bgneal@963
|
17 from core.html import image_check
|
bgneal@963
|
18 from core.markup import site_markup
|
bgneal@83
|
19
|
bgneal@83
|
20
|
bgneal@722
|
21 FORUMS_FORM_CSS = {
|
bgneal@722
|
22 'all': (settings.GPP_THIRD_PARTY_CSS['markitup'] +
|
bgneal@722
|
23 settings.GPP_THIRD_PARTY_CSS['jquery-ui'])
|
bgneal@722
|
24 }
|
bgneal@722
|
25 FORUMS_FORM_JS = (
|
bgneal@722
|
26 settings.GPP_THIRD_PARTY_JS['markitup'] +
|
bgneal@722
|
27 settings.GPP_THIRD_PARTY_JS['jquery-ui'] +
|
bgneal@722
|
28 ['js/jquery.form.min.js', 'js/forums.js']
|
bgneal@722
|
29 )
|
bgneal@722
|
30
|
bgneal@722
|
31
|
bgneal@106
|
32 class NewPostForm(forms.Form):
|
bgneal@86
|
33 """Form for creating a new post."""
|
bgneal@286
|
34 body = forms.CharField(label='',
|
bgneal@286
|
35 required=False,
|
bgneal@135
|
36 widget=forms.Textarea(attrs={'class': 'markItUp smileyTarget'}))
|
bgneal@89
|
37 topic_id = forms.IntegerField(widget=forms.HiddenInput)
|
bgneal@89
|
38 topic = None
|
bgneal@83
|
39
|
bgneal@89
|
40 class Media:
|
bgneal@722
|
41 css = FORUMS_FORM_CSS
|
bgneal@722
|
42 js = FORUMS_FORM_JS
|
bgneal@89
|
43
|
bgneal@285
|
44 def __init__(self, *args, **kwargs):
|
bgneal@285
|
45 super(NewPostForm, self).__init__(*args, **kwargs)
|
bgneal@285
|
46 attachments = args[0].getlist('attachment') if len(args) else []
|
bgneal@285
|
47 self.attach_proc = AttachmentProcessor(attachments)
|
bgneal@285
|
48
|
bgneal@286
|
49 def clean_body(self):
|
bgneal@963
|
50 body = self.cleaned_data['body']
|
bgneal@963
|
51 self.body_html = None
|
bgneal@963
|
52 if not body and not self.attach_proc.has_attachments():
|
bgneal@963
|
53 raise forms.ValidationError("Please enter some text")
|
bgneal@963
|
54
|
bgneal@963
|
55 if body:
|
bgneal@963
|
56 self.body_html = site_markup(body)
|
bgneal@963
|
57 try:
|
bgneal@963
|
58 image_check(self.body_html)
|
bgneal@963
|
59 except ImageCheckError as ex:
|
bgneal@963
|
60 raise forms.ValidationError(str(ex))
|
bgneal@963
|
61
|
bgneal@963
|
62 return body
|
bgneal@286
|
63
|
bgneal@89
|
64 def clean_topic_id(self):
|
bgneal@89
|
65 id = self.cleaned_data['topic_id']
|
bgneal@89
|
66 try:
|
bgneal@102
|
67 self.topic = Topic.objects.select_related().get(pk=id)
|
bgneal@89
|
68 except Topic.DoesNotExist:
|
bgneal@963
|
69 raise forms.ValidationError('Invalid topic')
|
bgneal@286
|
70 return id
|
bgneal@89
|
71
|
bgneal@89
|
72 def save(self, user, ip=None):
|
bgneal@86
|
73 """
|
bgneal@86
|
74 Creates a new post from the form data and supplied arguments.
|
bgneal@86
|
75 """
|
bgneal@89
|
76 post = Post(topic=self.topic, user=user, body=self.cleaned_data['body'],
|
bgneal@89
|
77 user_ip=ip)
|
bgneal@963
|
78 post.save(html=self.body_html)
|
bgneal@285
|
79 self.attach_proc.save_attachments(post)
|
bgneal@469
|
80 notify_new_post(post)
|
bgneal@89
|
81 return post
|
bgneal@83
|
82
|
bgneal@83
|
83
|
bgneal@83
|
84 class NewTopicForm(forms.Form):
|
bgneal@102
|
85 """
|
bgneal@102
|
86 Form for creating a new topic and 1st post to that topic.
|
bgneal@102
|
87 Superusers and moderators can also create the topic as a sticky or initially
|
bgneal@102
|
88 locked.
|
bgneal@102
|
89 """
|
bgneal@95
|
90 name = forms.CharField(label='Subject', max_length=255,
|
bgneal@673
|
91 widget=forms.TextInput(attrs={'style': 'width:70%'}))
|
bgneal@286
|
92 body = forms.CharField(label='', required=False,
|
bgneal@135
|
93 widget=forms.Textarea(attrs={'class': 'markItUp smileyTarget'}))
|
bgneal@102
|
94 user = None
|
bgneal@102
|
95 forum = None
|
bgneal@102
|
96 has_mod_fields = False
|
bgneal@83
|
97
|
bgneal@95
|
98 class Media:
|
bgneal@722
|
99 css = FORUMS_FORM_CSS
|
bgneal@722
|
100 js = FORUMS_FORM_JS
|
bgneal@95
|
101
|
bgneal@102
|
102 def __init__(self, user, forum, *args, **kwargs):
|
bgneal@102
|
103 super(NewTopicForm, self).__init__(*args, **kwargs)
|
bgneal@102
|
104 self.user = user
|
bgneal@102
|
105 self.forum = forum
|
bgneal@102
|
106
|
bgneal@460
|
107 if perms.can_moderate(forum, user):
|
bgneal@102
|
108 self.fields['sticky'] = forms.BooleanField(required=False)
|
bgneal@102
|
109 self.fields['locked'] = forms.BooleanField(required=False)
|
bgneal@102
|
110 self.has_mod_fields = True
|
bgneal@102
|
111
|
bgneal@285
|
112 attachments = args[0].getlist('attachment') if len(args) else []
|
bgneal@285
|
113 self.attach_proc = AttachmentProcessor(attachments)
|
bgneal@285
|
114
|
bgneal@286
|
115 # If this form is being POSTed, and the user is trying to add
|
bgneal@286
|
116 # attachments, create hidden fields to list the Oembed ids. In
|
bgneal@286
|
117 # case the form isn't valid, the client-side javascript will know
|
bgneal@286
|
118 # which Oembed media to ask for when the form is displayed with
|
bgneal@286
|
119 # errors.
|
bgneal@286
|
120 if self.attach_proc.has_attachments():
|
bgneal@286
|
121 pks = self.attach_proc.get_ids()
|
bgneal@286
|
122 self.fields['attachment'] = forms.MultipleChoiceField(label='',
|
bgneal@286
|
123 widget=forms.MultipleHiddenInput(),
|
bgneal@286
|
124 choices=[(v, v) for v in pks])
|
bgneal@286
|
125
|
bgneal@286
|
126 def clean_body(self):
|
bgneal@963
|
127 body = self.cleaned_data['body']
|
bgneal@963
|
128 self.body_html = None
|
bgneal@963
|
129 if not body and not self.attach_proc.has_attachments():
|
bgneal@286
|
130 raise forms.ValidationError("This field is required.")
|
bgneal@963
|
131
|
bgneal@963
|
132 if body:
|
bgneal@963
|
133 self.body_html = site_markup(body)
|
bgneal@963
|
134 try:
|
bgneal@963
|
135 image_check(self.body_html)
|
bgneal@963
|
136 except ImageCheckError as ex:
|
bgneal@963
|
137 raise forms.ValidationError(str(ex))
|
bgneal@963
|
138
|
bgneal@963
|
139 return body
|
bgneal@286
|
140
|
bgneal@102
|
141 def save(self, ip=None):
|
bgneal@83
|
142 """
|
bgneal@83
|
143 Creates the new Topic and first Post from the form data and supplied
|
bgneal@86
|
144 arguments.
|
bgneal@83
|
145 """
|
bgneal@102
|
146 topic = Topic(forum=self.forum,
|
bgneal@83
|
147 name=self.cleaned_data['name'],
|
bgneal@102
|
148 user=self.user,
|
bgneal@102
|
149 sticky=self.has_mod_fields and self.cleaned_data['sticky'],
|
bgneal@102
|
150 locked=self.has_mod_fields and self.cleaned_data['locked'])
|
bgneal@83
|
151 topic.save()
|
bgneal@83
|
152
|
bgneal@83
|
153 post = Post(topic=topic,
|
bgneal@102
|
154 user=self.user,
|
bgneal@83
|
155 body=self.cleaned_data['body'],
|
bgneal@83
|
156 user_ip=ip)
|
bgneal@963
|
157 post.save(html=self.body_html)
|
bgneal@285
|
158
|
bgneal@285
|
159 self.attach_proc.save_attachments(post)
|
bgneal@285
|
160
|
bgneal@469
|
161 notify_new_topic(topic)
|
bgneal@469
|
162 notify_new_post(post)
|
bgneal@469
|
163
|
bgneal@83
|
164 return topic
|
bgneal@106
|
165
|
bgneal@106
|
166
|
bgneal@106
|
167 class PostForm(forms.ModelForm):
|
bgneal@106
|
168 """
|
bgneal@108
|
169 Form for editing an existing post or a new, non-quick post.
|
bgneal@106
|
170 """
|
bgneal@286
|
171 body = forms.CharField(label='',
|
bgneal@286
|
172 required=False,
|
bgneal@135
|
173 widget=forms.Textarea(attrs={'class': 'markItUp smileyTarget'}))
|
bgneal@106
|
174
|
bgneal@106
|
175 class Meta:
|
bgneal@106
|
176 model = Post
|
bgneal@106
|
177 fields = ('body', )
|
bgneal@106
|
178
|
bgneal@106
|
179 class Media:
|
bgneal@722
|
180 css = FORUMS_FORM_CSS
|
bgneal@722
|
181 js = FORUMS_FORM_JS
|
bgneal@110
|
182
|
bgneal@286
|
183 def __init__(self, *args, **kwargs):
|
bgneal@295
|
184 topic_name = kwargs.pop('topic_name', None)
|
bgneal@286
|
185 super(PostForm, self).__init__(*args, **kwargs)
|
bgneal@286
|
186
|
bgneal@951
|
187 if topic_name is not None:
|
bgneal@951
|
188 # this is a "first post", add a field for a topic name
|
bgneal@951
|
189 new_fields = OrderedDict(name=forms.CharField(
|
bgneal@951
|
190 label='Subject', max_length=255,
|
bgneal@951
|
191 widget=forms.TextInput(attrs={
|
bgneal@951
|
192 'class': 'title',
|
bgneal@951
|
193 'style': 'width: 90%',
|
bgneal@951
|
194 })))
|
bgneal@951
|
195 new_fields.update(self.fields)
|
bgneal@951
|
196 self.fields = new_fields
|
bgneal@295
|
197 self.initial['name'] = topic_name
|
bgneal@295
|
198
|
bgneal@286
|
199 attachments = args[0].getlist('attachment') if len(args) else []
|
bgneal@286
|
200 self.attach_proc = AttachmentProcessor(attachments)
|
bgneal@295
|
201
|
bgneal@286
|
202 # If this form is being used to edit an existing post, and that post
|
bgneal@286
|
203 # has attachments, create a hidden post_id field. The client-side
|
bgneal@286
|
204 # AJAX will use this as a cue to retrieve the HTML for the embedded
|
bgneal@286
|
205 # media.
|
bgneal@286
|
206 if 'instance' in kwargs:
|
bgneal@286
|
207 post = kwargs['instance']
|
bgneal@286
|
208 if post.attachments.count():
|
bgneal@286
|
209 self.fields['post_id'] = forms.CharField(label='',
|
bgneal@286
|
210 widget=forms.HiddenInput(attrs={'value': post.id}))
|
bgneal@286
|
211
|
bgneal@286
|
212 def clean_body(self):
|
bgneal@963
|
213 body = self.cleaned_data['body']
|
bgneal@963
|
214 self.body_html = None
|
bgneal@963
|
215 if not body and not self.attach_proc.has_attachments():
|
bgneal@286
|
216 raise forms.ValidationError('This field is required.')
|
bgneal@963
|
217
|
bgneal@963
|
218 if body:
|
bgneal@963
|
219 self.body_html = site_markup(body)
|
bgneal@963
|
220 try:
|
bgneal@963
|
221 image_check(self.body_html)
|
bgneal@963
|
222 except ImageCheckError as ex:
|
bgneal@963
|
223 raise forms.ValidationError(str(ex))
|
bgneal@963
|
224
|
bgneal@963
|
225 return body
|
bgneal@286
|
226
|
bgneal@295
|
227 def save(self, *args, **kwargs):
|
bgneal@295
|
228 commit = kwargs.get('commit', False)
|
bgneal@295
|
229 post = super(PostForm, self).save(*args, **kwargs)
|
bgneal@295
|
230
|
bgneal@295
|
231 # Are we saving a "first post"?
|
bgneal@295
|
232 if 'name' in self.cleaned_data:
|
bgneal@295
|
233 post.topic.name = self.cleaned_data['name']
|
bgneal@295
|
234 if commit:
|
bgneal@295
|
235 post.topic.save()
|
bgneal@295
|
236 return post
|
bgneal@295
|
237
|
bgneal@110
|
238
|
bgneal@110
|
239 class MoveTopicForm(forms.Form):
|
bgneal@111
|
240 """
|
bgneal@111
|
241 Form for a moderator to move a topic to a forum.
|
bgneal@111
|
242 """
|
bgneal@286
|
243 forums = forms.ModelChoiceField(label='Move to forum',
|
bgneal@111
|
244 queryset=Forum.objects.none())
|
bgneal@110
|
245
|
bgneal@111
|
246 def __init__(self, user, *args, **kwargs):
|
bgneal@286
|
247 hide_label = kwargs.pop('hide_label', False)
|
bgneal@111
|
248 required = kwargs.pop('required', True)
|
bgneal@111
|
249 super(MoveTopicForm, self).__init__(*args, **kwargs)
|
bgneal@111
|
250 self.fields['forums'].queryset = \
|
bgneal@111
|
251 Forum.objects.forums_for_user(user).order_by('name')
|
bgneal@111
|
252 if hide_label:
|
bgneal@111
|
253 self.fields['forums'].label = ''
|
bgneal@111
|
254 self.fields['forums'].required = required
|
bgneal@110
|
255
|
bgneal@115
|
256
|
bgneal@115
|
257 class SplitTopicForm(forms.Form):
|
bgneal@115
|
258 """
|
bgneal@115
|
259 Form for a moderator to split posts from a topic to a new topic.
|
bgneal@115
|
260 """
|
bgneal@115
|
261 name = forms.CharField(label='New topic title', max_length=255,
|
bgneal@115
|
262 widget=forms.TextInput(attrs={'size': 64}))
|
bgneal@286
|
263 forums = forms.ModelChoiceField(label='Forum for new topic',
|
bgneal@115
|
264 queryset=Forum.objects.none())
|
bgneal@115
|
265 post_ids = []
|
bgneal@115
|
266 split_at = False
|
bgneal@115
|
267
|
bgneal@115
|
268 def __init__(self, user, *args, **kwargs):
|
bgneal@115
|
269 super(SplitTopicForm, self).__init__(*args, **kwargs)
|
bgneal@115
|
270 self.fields['forums'].queryset = \
|
bgneal@115
|
271 Forum.objects.forums_for_user(user).order_by('name')
|
bgneal@115
|
272
|
bgneal@115
|
273 def clean(self):
|
bgneal@115
|
274 self.post_ids = self.data.getlist('post_ids')
|
bgneal@115
|
275 if len(self.post_ids) == 0:
|
bgneal@115
|
276 raise forms.ValidationError('Please select some posts')
|
bgneal@115
|
277
|
bgneal@115
|
278 self.split_at = 'split-at' in self.data
|
bgneal@115
|
279 if self.split_at and len(self.post_ids) > 1:
|
bgneal@115
|
280 raise forms.ValidationError('Please select only one post to split the topic at')
|
bgneal@115
|
281
|
bgneal@115
|
282 return self.cleaned_data
|