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