annotate forums/forms.py @ 629:f4c043cf55ac

Wiki integration. Requests don't always have sessions. In particular this occurs when a request is made without a trailing slash. The Common middleware redirects when this happens, and the middleware process_request() processing stops before a session can get added. So just set an attribute on the request object for each operation. This seemed weird to me at first, but there are plenty of examples of this in the Django code base already.
author Brian Neal <bgneal@gmail.com>
date Tue, 13 Nov 2012 13:50:06 -0600
parents ee87ea74d46b
children 92101013d5ac
rev   line source
bgneal@83 1 """
bgneal@83 2 Forms for the forums application.
bgneal@469 3
bgneal@83 4 """
bgneal@83 5 from django import forms
bgneal@95 6 from django.conf import settings
bgneal@83 7
bgneal@110 8 from forums.models import Forum
bgneal@83 9 from forums.models import Topic
bgneal@83 10 from forums.models import Post
bgneal@285 11 from forums.attachments import AttachmentProcessor
bgneal@460 12 import forums.permissions as perms
bgneal@469 13 from forums.signals import notify_new_topic, notify_new_post
bgneal@83 14
bgneal@83 15
bgneal@106 16 class NewPostForm(forms.Form):
bgneal@86 17 """Form for creating a new post."""
bgneal@286 18 body = forms.CharField(label='',
bgneal@286 19 required=False,
bgneal@135 20 widget=forms.Textarea(attrs={'class': 'markItUp smileyTarget'}))
bgneal@89 21 topic_id = forms.IntegerField(widget=forms.HiddenInput)
bgneal@89 22 topic = None
bgneal@83 23
bgneal@89 24 class Media:
bgneal@95 25 css = {
bgneal@123 26 'all': (settings.GPP_THIRD_PARTY_CSS['markitup'] +
bgneal@123 27 settings.GPP_THIRD_PARTY_CSS['jquery-ui']),
bgneal@95 28 }
bgneal@286 29 js = (settings.GPP_THIRD_PARTY_JS['markitup'] +
bgneal@123 30 settings.GPP_THIRD_PARTY_JS['jquery-ui'] +
bgneal@484 31 ['js/forums.js'])
bgneal@89 32
bgneal@285 33 def __init__(self, *args, **kwargs):
bgneal@285 34 super(NewPostForm, self).__init__(*args, **kwargs)
bgneal@285 35 attachments = args[0].getlist('attachment') if len(args) else []
bgneal@285 36 self.attach_proc = AttachmentProcessor(attachments)
bgneal@285 37
bgneal@286 38 def clean_body(self):
bgneal@286 39 data = self.cleaned_data['body']
bgneal@286 40 if not data and not self.attach_proc.has_attachments():
bgneal@286 41 raise forms.ValidationError("This field is required.")
bgneal@286 42 return data
bgneal@286 43
bgneal@89 44 def clean_topic_id(self):
bgneal@89 45 id = self.cleaned_data['topic_id']
bgneal@89 46 try:
bgneal@102 47 self.topic = Topic.objects.select_related().get(pk=id)
bgneal@89 48 except Topic.DoesNotExist:
bgneal@89 49 raise forms.ValidationError('invalid topic')
bgneal@286 50 return id
bgneal@89 51
bgneal@89 52 def save(self, user, ip=None):
bgneal@86 53 """
bgneal@86 54 Creates a new post from the form data and supplied arguments.
bgneal@86 55 """
bgneal@89 56 post = Post(topic=self.topic, user=user, body=self.cleaned_data['body'],
bgneal@89 57 user_ip=ip)
bgneal@86 58 post.save()
bgneal@285 59 self.attach_proc.save_attachments(post)
bgneal@469 60 notify_new_post(post)
bgneal@89 61 return post
bgneal@83 62
bgneal@83 63
bgneal@83 64 class NewTopicForm(forms.Form):
bgneal@102 65 """
bgneal@102 66 Form for creating a new topic and 1st post to that topic.
bgneal@102 67 Superusers and moderators can also create the topic as a sticky or initially
bgneal@102 68 locked.
bgneal@102 69 """
bgneal@95 70 name = forms.CharField(label='Subject', max_length=255,
bgneal@95 71 widget=forms.TextInput(attrs={'size': 64}))
bgneal@286 72 body = forms.CharField(label='', required=False,
bgneal@135 73 widget=forms.Textarea(attrs={'class': 'markItUp smileyTarget'}))
bgneal@102 74 user = None
bgneal@102 75 forum = None
bgneal@102 76 has_mod_fields = False
bgneal@83 77
bgneal@95 78 class Media:
bgneal@95 79 css = {
bgneal@123 80 'all': (settings.GPP_THIRD_PARTY_CSS['markitup'] +
bgneal@123 81 settings.GPP_THIRD_PARTY_CSS['jquery-ui']),
bgneal@95 82 }
bgneal@286 83 js = (settings.GPP_THIRD_PARTY_JS['markitup'] +
bgneal@123 84 settings.GPP_THIRD_PARTY_JS['jquery-ui'] +
bgneal@484 85 ['js/forums.js'])
bgneal@95 86
bgneal@102 87 def __init__(self, user, forum, *args, **kwargs):
bgneal@102 88 super(NewTopicForm, self).__init__(*args, **kwargs)
bgneal@102 89 self.user = user
bgneal@102 90 self.forum = forum
bgneal@102 91
bgneal@460 92 if perms.can_moderate(forum, user):
bgneal@102 93 self.fields['sticky'] = forms.BooleanField(required=False)
bgneal@102 94 self.fields['locked'] = forms.BooleanField(required=False)
bgneal@102 95 self.has_mod_fields = True
bgneal@102 96
bgneal@285 97 attachments = args[0].getlist('attachment') if len(args) else []
bgneal@285 98 self.attach_proc = AttachmentProcessor(attachments)
bgneal@285 99
bgneal@286 100 # If this form is being POSTed, and the user is trying to add
bgneal@286 101 # attachments, create hidden fields to list the Oembed ids. In
bgneal@286 102 # case the form isn't valid, the client-side javascript will know
bgneal@286 103 # which Oembed media to ask for when the form is displayed with
bgneal@286 104 # errors.
bgneal@286 105 if self.attach_proc.has_attachments():
bgneal@286 106 pks = self.attach_proc.get_ids()
bgneal@286 107 self.fields['attachment'] = forms.MultipleChoiceField(label='',
bgneal@286 108 widget=forms.MultipleHiddenInput(),
bgneal@286 109 choices=[(v, v) for v in pks])
bgneal@286 110
bgneal@286 111 def clean_body(self):
bgneal@286 112 data = self.cleaned_data['body']
bgneal@286 113 if not data and not self.attach_proc.has_attachments():
bgneal@286 114 raise forms.ValidationError("This field is required.")
bgneal@286 115 return data
bgneal@286 116
bgneal@102 117 def save(self, ip=None):
bgneal@83 118 """
bgneal@83 119 Creates the new Topic and first Post from the form data and supplied
bgneal@86 120 arguments.
bgneal@83 121 """
bgneal@102 122 topic = Topic(forum=self.forum,
bgneal@83 123 name=self.cleaned_data['name'],
bgneal@102 124 user=self.user,
bgneal@102 125 sticky=self.has_mod_fields and self.cleaned_data['sticky'],
bgneal@102 126 locked=self.has_mod_fields and self.cleaned_data['locked'])
bgneal@83 127 topic.save()
bgneal@83 128
bgneal@83 129 post = Post(topic=topic,
bgneal@102 130 user=self.user,
bgneal@83 131 body=self.cleaned_data['body'],
bgneal@83 132 user_ip=ip)
bgneal@83 133 post.save()
bgneal@285 134
bgneal@285 135 self.attach_proc.save_attachments(post)
bgneal@285 136
bgneal@469 137 notify_new_topic(topic)
bgneal@469 138 notify_new_post(post)
bgneal@469 139
bgneal@83 140 return topic
bgneal@106 141
bgneal@106 142
bgneal@106 143 class PostForm(forms.ModelForm):
bgneal@106 144 """
bgneal@108 145 Form for editing an existing post or a new, non-quick post.
bgneal@106 146 """
bgneal@286 147 body = forms.CharField(label='',
bgneal@286 148 required=False,
bgneal@135 149 widget=forms.Textarea(attrs={'class': 'markItUp smileyTarget'}))
bgneal@106 150
bgneal@106 151 class Meta:
bgneal@106 152 model = Post
bgneal@106 153 fields = ('body', )
bgneal@106 154
bgneal@106 155 class Media:
bgneal@106 156 css = {
bgneal@123 157 'all': (settings.GPP_THIRD_PARTY_CSS['markitup'] +
bgneal@123 158 settings.GPP_THIRD_PARTY_CSS['jquery-ui']),
bgneal@106 159 }
bgneal@286 160 js = (settings.GPP_THIRD_PARTY_JS['markitup'] +
bgneal@123 161 settings.GPP_THIRD_PARTY_JS['jquery-ui'] +
bgneal@484 162 ['js/forums.js'])
bgneal@110 163
bgneal@286 164 def __init__(self, *args, **kwargs):
bgneal@295 165 topic_name = kwargs.pop('topic_name', None)
bgneal@286 166 super(PostForm, self).__init__(*args, **kwargs)
bgneal@286 167
bgneal@295 168 if topic_name is not None: # this is a "first post"
bgneal@295 169 self.fields.insert(0, 'name', forms.CharField(label='Subject',
bgneal@295 170 max_length=255,
bgneal@295 171 widget=forms.TextInput(attrs={'size': 64})))
bgneal@295 172 self.initial['name'] = topic_name
bgneal@295 173
bgneal@286 174 attachments = args[0].getlist('attachment') if len(args) else []
bgneal@286 175 self.attach_proc = AttachmentProcessor(attachments)
bgneal@295 176
bgneal@286 177 # If this form is being used to edit an existing post, and that post
bgneal@286 178 # has attachments, create a hidden post_id field. The client-side
bgneal@286 179 # AJAX will use this as a cue to retrieve the HTML for the embedded
bgneal@286 180 # media.
bgneal@286 181 if 'instance' in kwargs:
bgneal@286 182 post = kwargs['instance']
bgneal@286 183 if post.attachments.count():
bgneal@286 184 self.fields['post_id'] = forms.CharField(label='',
bgneal@286 185 widget=forms.HiddenInput(attrs={'value': post.id}))
bgneal@286 186
bgneal@286 187 def clean_body(self):
bgneal@286 188 data = self.cleaned_data['body']
bgneal@286 189 if not data and not self.attach_proc.has_attachments():
bgneal@286 190 raise forms.ValidationError('This field is required.')
bgneal@286 191 return data
bgneal@286 192
bgneal@295 193 def save(self, *args, **kwargs):
bgneal@295 194 commit = kwargs.get('commit', False)
bgneal@295 195 post = super(PostForm, self).save(*args, **kwargs)
bgneal@295 196
bgneal@295 197 # Are we saving a "first post"?
bgneal@295 198 if 'name' in self.cleaned_data:
bgneal@295 199 post.topic.name = self.cleaned_data['name']
bgneal@295 200 if commit:
bgneal@295 201 post.topic.save()
bgneal@295 202 return post
bgneal@295 203
bgneal@110 204
bgneal@110 205 class MoveTopicForm(forms.Form):
bgneal@111 206 """
bgneal@111 207 Form for a moderator to move a topic to a forum.
bgneal@111 208 """
bgneal@286 209 forums = forms.ModelChoiceField(label='Move to forum',
bgneal@111 210 queryset=Forum.objects.none())
bgneal@110 211
bgneal@111 212 def __init__(self, user, *args, **kwargs):
bgneal@286 213 hide_label = kwargs.pop('hide_label', False)
bgneal@111 214 required = kwargs.pop('required', True)
bgneal@111 215 super(MoveTopicForm, self).__init__(*args, **kwargs)
bgneal@111 216 self.fields['forums'].queryset = \
bgneal@111 217 Forum.objects.forums_for_user(user).order_by('name')
bgneal@111 218 if hide_label:
bgneal@111 219 self.fields['forums'].label = ''
bgneal@111 220 self.fields['forums'].required = required
bgneal@110 221
bgneal@115 222
bgneal@115 223 class SplitTopicForm(forms.Form):
bgneal@115 224 """
bgneal@115 225 Form for a moderator to split posts from a topic to a new topic.
bgneal@115 226 """
bgneal@115 227 name = forms.CharField(label='New topic title', max_length=255,
bgneal@115 228 widget=forms.TextInput(attrs={'size': 64}))
bgneal@286 229 forums = forms.ModelChoiceField(label='Forum for new topic',
bgneal@115 230 queryset=Forum.objects.none())
bgneal@115 231 post_ids = []
bgneal@115 232 split_at = False
bgneal@115 233
bgneal@115 234 def __init__(self, user, *args, **kwargs):
bgneal@115 235 super(SplitTopicForm, self).__init__(*args, **kwargs)
bgneal@115 236 self.fields['forums'].queryset = \
bgneal@115 237 Forum.objects.forums_for_user(user).order_by('name')
bgneal@115 238
bgneal@115 239 def clean(self):
bgneal@115 240 self.post_ids = self.data.getlist('post_ids')
bgneal@115 241 if len(self.post_ids) == 0:
bgneal@115 242 raise forms.ValidationError('Please select some posts')
bgneal@115 243
bgneal@115 244 self.split_at = 'split-at' in self.data
bgneal@115 245 if self.split_at and len(self.post_ids) > 1:
bgneal@115 246 raise forms.ValidationError('Please select only one post to split the topic at')
bgneal@115 247
bgneal@115 248 return self.cleaned_data