annotate gpp/forums/forms.py @ 480:1a7ca5fa494f

Fixing #229: Download details view with a bogus download ID produces internal server error isntead of a 404.
author Brian Neal <bgneal@gmail.com>
date Wed, 05 Oct 2011 00:09:30 +0000
parents 3b30286adba5
children bbbc357ac5f3
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@123 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@123 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@123 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