annotate forums/forms.py @ 693:ad69236e8501

For issue #52, update many 3rd party Javascript libraries. Updated to jquery 1.10.2, jquery ui 1.10.3. This broke a lot of stuff. - Found a newer version of the jquery cycle all plugin (3.0.3). - Updated JPlayer to 2.4.0. - Updated to MarkItUp 1.1.14. This also required me to add multiline attributes set to true on various buttons in the markdown set. - As per a stackoverflow post, added some code to get multiline titles in a jQuery UI dialog. They removed that functionality but allow you to put it back. Tweaked the MarkItUp preview CSS to show blockquotes in italic. Did not update TinyMCE at this time. I'm not using the JQuery version and this version appears to work ok for now. What I should do is make a repo for MarkItUp and do a vendor branch thing so I don't have to futz around diffing directories to figure out if I'll lose changes when I update.
author Brian Neal <bgneal@gmail.com>
date Wed, 04 Sep 2013 19:55:20 -0500
parents 92101013d5ac
children 71d17d267e27
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@673 71 widget=forms.TextInput(attrs={'style': 'width:70%'}))
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