annotate forums/forms.py @ 1090:71685387dd11

Reduce database usage on poll index page.
author Brian Neal <bgneal@gmail.com>
date Mon, 09 May 2016 19:48:51 -0500
parents 21c592cac71c
children
rev   line source
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