bgneal@83: """
bgneal@83: Forms for the forums application.
bgneal@83: """
bgneal@83: from django import forms
bgneal@95: from django.conf import settings
bgneal@83: 
bgneal@110: from forums.models import Forum
bgneal@83: from forums.models import Topic
bgneal@83: from forums.models import Post
bgneal@285: from forums.attachments import AttachmentProcessor
bgneal@83: 
bgneal@83: 
bgneal@106: class NewPostForm(forms.Form):
bgneal@86:     """Form for creating a new post."""
bgneal@286:     body = forms.CharField(label='',
bgneal@286:             required=False,
bgneal@135:             widget=forms.Textarea(attrs={'class': 'markItUp smileyTarget'}))
bgneal@89:     topic_id = forms.IntegerField(widget=forms.HiddenInput)
bgneal@89:     topic = None
bgneal@83: 
bgneal@89:     class Media:
bgneal@95:         css = {
bgneal@123:             'all': (settings.GPP_THIRD_PARTY_CSS['markitup'] +
bgneal@123:                 settings.GPP_THIRD_PARTY_CSS['jquery-ui']),
bgneal@95:         }
bgneal@286:         js = (settings.GPP_THIRD_PARTY_JS['markitup'] +
bgneal@123:                 settings.GPP_THIRD_PARTY_JS['jquery-ui'] +
bgneal@123:                 ('js/forums.js', ))
bgneal@89: 
bgneal@285:     def __init__(self, *args, **kwargs):
bgneal@285:         super(NewPostForm, self).__init__(*args, **kwargs)
bgneal@285:         attachments = args[0].getlist('attachment') if len(args) else []
bgneal@285:         self.attach_proc = AttachmentProcessor(attachments)
bgneal@285: 
bgneal@286:     def clean_body(self):
bgneal@286:         data = self.cleaned_data['body']
bgneal@286:         if not data and not self.attach_proc.has_attachments():
bgneal@286:             raise forms.ValidationError("This field is required.")
bgneal@286:         return data
bgneal@286: 
bgneal@89:     def clean_topic_id(self):
bgneal@89:         id = self.cleaned_data['topic_id']
bgneal@89:         try:
bgneal@102:             self.topic = Topic.objects.select_related().get(pk=id)
bgneal@89:         except Topic.DoesNotExist:
bgneal@89:             raise forms.ValidationError('invalid topic')
bgneal@286:         return id
bgneal@89: 
bgneal@89:     def save(self, user, ip=None):
bgneal@86:         """
bgneal@86:         Creates a new post from the form data and supplied arguments.
bgneal@86:         """
bgneal@89:         post = Post(topic=self.topic, user=user, body=self.cleaned_data['body'],
bgneal@89:                 user_ip=ip)
bgneal@86:         post.save()
bgneal@285:         self.attach_proc.save_attachments(post)
bgneal@89:         return post
bgneal@83: 
bgneal@83: 
bgneal@83: class NewTopicForm(forms.Form):
bgneal@102:     """
bgneal@102:     Form for creating a new topic and 1st post to that topic.
bgneal@102:     Superusers and moderators can also create the topic as a sticky or initially
bgneal@102:     locked.
bgneal@102:     """
bgneal@95:     name = forms.CharField(label='Subject', max_length=255,
bgneal@95:             widget=forms.TextInput(attrs={'size': 64}))
bgneal@286:     body = forms.CharField(label='', required=False,
bgneal@135:             widget=forms.Textarea(attrs={'class': 'markItUp smileyTarget'}))
bgneal@102:     user = None
bgneal@102:     forum = None
bgneal@102:     has_mod_fields = False
bgneal@83: 
bgneal@95:     class Media:
bgneal@95:         css = {
bgneal@123:             'all': (settings.GPP_THIRD_PARTY_CSS['markitup'] +
bgneal@123:                 settings.GPP_THIRD_PARTY_CSS['jquery-ui']),
bgneal@95:         }
bgneal@286:         js = (settings.GPP_THIRD_PARTY_JS['markitup'] +
bgneal@123:                 settings.GPP_THIRD_PARTY_JS['jquery-ui'] +
bgneal@123:                 ('js/forums.js', ))
bgneal@95: 
bgneal@102:     def __init__(self, user, forum, *args, **kwargs):
bgneal@102:         super(NewTopicForm, self).__init__(*args, **kwargs)
bgneal@102:         self.user = user
bgneal@102:         self.forum = forum
bgneal@102: 
bgneal@102:         if user.is_superuser or user in forum.moderators.all():
bgneal@102:             self.fields['sticky'] = forms.BooleanField(required=False)
bgneal@102:             self.fields['locked'] = forms.BooleanField(required=False)
bgneal@102:             self.has_mod_fields = True
bgneal@102: 
bgneal@285:         attachments = args[0].getlist('attachment') if len(args) else []
bgneal@285:         self.attach_proc = AttachmentProcessor(attachments)
bgneal@285: 
bgneal@286:         # If this form is being POSTed, and the user is trying to add 
bgneal@286:         # attachments, create hidden fields to list the Oembed ids. In
bgneal@286:         # case the form isn't valid, the client-side javascript will know
bgneal@286:         # which Oembed media to ask for when the form is displayed with
bgneal@286:         # errors.
bgneal@286:         if self.attach_proc.has_attachments():
bgneal@286:             pks = self.attach_proc.get_ids()
bgneal@286:             self.fields['attachment'] = forms.MultipleChoiceField(label='',
bgneal@286:                     widget=forms.MultipleHiddenInput(),
bgneal@286:                     choices=[(v, v) for v in pks])
bgneal@286: 
bgneal@286:     def clean_body(self):
bgneal@286:         data = self.cleaned_data['body']
bgneal@286:         if not data and not self.attach_proc.has_attachments():
bgneal@286:             raise forms.ValidationError("This field is required.")
bgneal@286:         return data
bgneal@286: 
bgneal@102:     def save(self, ip=None):
bgneal@83:         """
bgneal@83:         Creates the new Topic and first Post from the form data and supplied
bgneal@86:         arguments.
bgneal@83:         """
bgneal@102:         topic = Topic(forum=self.forum,
bgneal@83:                 name=self.cleaned_data['name'],
bgneal@102:                 user=self.user,
bgneal@102:                 sticky=self.has_mod_fields and self.cleaned_data['sticky'],
bgneal@102:                 locked=self.has_mod_fields and self.cleaned_data['locked'])
bgneal@83:         topic.save()
bgneal@83: 
bgneal@83:         post = Post(topic=topic,
bgneal@102:                 user=self.user,
bgneal@83:                 body=self.cleaned_data['body'],
bgneal@83:                 user_ip=ip)
bgneal@83:         post.save()
bgneal@285: 
bgneal@285:         self.attach_proc.save_attachments(post)
bgneal@285: 
bgneal@83:         return topic
bgneal@106: 
bgneal@106: 
bgneal@106: class PostForm(forms.ModelForm):
bgneal@106:     """
bgneal@108:     Form for editing an existing post or a new, non-quick post.
bgneal@106:     """
bgneal@286:     body = forms.CharField(label='',
bgneal@286:             required=False,
bgneal@135:             widget=forms.Textarea(attrs={'class': 'markItUp smileyTarget'}))
bgneal@106: 
bgneal@106:     class Meta:
bgneal@106:         model = Post
bgneal@106:         fields = ('body', )
bgneal@106: 
bgneal@106:     class Media:
bgneal@106:         css = {
bgneal@123:             'all': (settings.GPP_THIRD_PARTY_CSS['markitup'] +
bgneal@123:                 settings.GPP_THIRD_PARTY_CSS['jquery-ui']),
bgneal@106:         }
bgneal@286:         js = (settings.GPP_THIRD_PARTY_JS['markitup'] +
bgneal@123:                 settings.GPP_THIRD_PARTY_JS['jquery-ui'] +
bgneal@123:                 ('js/forums.js', ))
bgneal@110: 
bgneal@286:     def __init__(self, *args, **kwargs):
bgneal@295:         topic_name = kwargs.pop('topic_name', None)
bgneal@286:         super(PostForm, self).__init__(*args, **kwargs)
bgneal@286: 
bgneal@295:         if topic_name is not None:  # this is a "first post"
bgneal@295:             self.fields.insert(0, 'name', forms.CharField(label='Subject',
bgneal@295:                     max_length=255,
bgneal@295:                     widget=forms.TextInput(attrs={'size': 64})))
bgneal@295:             self.initial['name'] = topic_name
bgneal@295: 
bgneal@286:         attachments = args[0].getlist('attachment') if len(args) else []
bgneal@286:         self.attach_proc = AttachmentProcessor(attachments)
bgneal@295: 
bgneal@286:         # If this form is being used to edit an existing post, and that post
bgneal@286:         # has attachments, create a hidden post_id field. The client-side
bgneal@286:         # AJAX will use this as a cue to retrieve the HTML for the embedded
bgneal@286:         # media.
bgneal@286:         if 'instance' in kwargs:
bgneal@286:             post = kwargs['instance']
bgneal@286:             if post.attachments.count():
bgneal@286:                 self.fields['post_id'] = forms.CharField(label='',
bgneal@286:                         widget=forms.HiddenInput(attrs={'value': post.id}))
bgneal@286: 
bgneal@286:     def clean_body(self):
bgneal@286:         data = self.cleaned_data['body']
bgneal@286:         if not data and not self.attach_proc.has_attachments():
bgneal@286:             raise forms.ValidationError('This field is required.')
bgneal@286:         return data
bgneal@286: 
bgneal@295:     def save(self, *args, **kwargs):
bgneal@295:         commit = kwargs.get('commit', False)
bgneal@295:         post = super(PostForm, self).save(*args, **kwargs)
bgneal@295: 
bgneal@295:         # Are we saving a "first post"?
bgneal@295:         if 'name' in self.cleaned_data:
bgneal@295:             post.topic.name = self.cleaned_data['name']
bgneal@295:             if commit:
bgneal@295:                 post.topic.save()
bgneal@295:         return post
bgneal@295: 
bgneal@110: 
bgneal@110: class MoveTopicForm(forms.Form):
bgneal@111:     """
bgneal@111:     Form for a moderator to move a topic to a forum.
bgneal@111:     """
bgneal@286:     forums = forms.ModelChoiceField(label='Move to forum',
bgneal@111:           queryset=Forum.objects.none())
bgneal@110: 
bgneal@111:     def __init__(self, user, *args, **kwargs):
bgneal@286:         hide_label = kwargs.pop('hide_label', False)
bgneal@111:         required = kwargs.pop('required', True)
bgneal@111:         super(MoveTopicForm, self).__init__(*args, **kwargs)
bgneal@111:         self.fields['forums'].queryset = \
bgneal@111:             Forum.objects.forums_for_user(user).order_by('name')
bgneal@111:         if hide_label:
bgneal@111:             self.fields['forums'].label = ''
bgneal@111:         self.fields['forums'].required = required
bgneal@110: 
bgneal@115: 
bgneal@115: class SplitTopicForm(forms.Form):
bgneal@115:     """
bgneal@115:     Form for a moderator to split posts from a topic to a new topic.
bgneal@115:     """
bgneal@115:     name = forms.CharField(label='New topic title', max_length=255,
bgneal@115:             widget=forms.TextInput(attrs={'size': 64}))
bgneal@286:     forums = forms.ModelChoiceField(label='Forum for new topic',
bgneal@115:           queryset=Forum.objects.none())
bgneal@115:     post_ids = []
bgneal@115:     split_at = False
bgneal@115: 
bgneal@115:     def __init__(self, user, *args, **kwargs):
bgneal@115:         super(SplitTopicForm, self).__init__(*args, **kwargs)
bgneal@115:         self.fields['forums'].queryset = \
bgneal@115:             Forum.objects.forums_for_user(user).order_by('name')
bgneal@115: 
bgneal@115:     def clean(self):
bgneal@115:         self.post_ids = self.data.getlist('post_ids')
bgneal@115:         if len(self.post_ids) == 0:
bgneal@115:             raise forms.ValidationError('Please select some posts')
bgneal@115: 
bgneal@115:         self.split_at = 'split-at' in self.data
bgneal@115:         if self.split_at and len(self.post_ids) > 1:
bgneal@115:             raise forms.ValidationError('Please select only one post to split the topic at')
bgneal@115: 
bgneal@115:         return self.cleaned_data