diff forums/forms.py @ 581:ee87ea74d46b

For Django 1.4, rearranged project structure for new manage.py.
author Brian Neal <bgneal@gmail.com>
date Sat, 05 May 2012 17:10:48 -0500
parents gpp/forums/forms.py@bbbc357ac5f3
children 92101013d5ac
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/forums/forms.py	Sat May 05 17:10:48 2012 -0500
@@ -0,0 +1,248 @@
+"""
+Forms for the forums application.
+
+"""
+from django import forms
+from django.conf import settings
+
+from forums.models import Forum
+from forums.models import Topic
+from forums.models import Post
+from forums.attachments import AttachmentProcessor
+import forums.permissions as perms
+from forums.signals import notify_new_topic, notify_new_post
+
+
+class NewPostForm(forms.Form):
+    """Form for creating a new post."""
+    body = forms.CharField(label='',
+            required=False,
+            widget=forms.Textarea(attrs={'class': 'markItUp smileyTarget'}))
+    topic_id = forms.IntegerField(widget=forms.HiddenInput)
+    topic = None
+
+    class Media:
+        css = {
+            'all': (settings.GPP_THIRD_PARTY_CSS['markitup'] +
+                settings.GPP_THIRD_PARTY_CSS['jquery-ui']),
+        }
+        js = (settings.GPP_THIRD_PARTY_JS['markitup'] +
+                settings.GPP_THIRD_PARTY_JS['jquery-ui'] +
+                ['js/forums.js'])
+
+    def __init__(self, *args, **kwargs):
+        super(NewPostForm, self).__init__(*args, **kwargs)
+        attachments = args[0].getlist('attachment') if len(args) else []
+        self.attach_proc = AttachmentProcessor(attachments)
+
+    def clean_body(self):
+        data = self.cleaned_data['body']
+        if not data and not self.attach_proc.has_attachments():
+            raise forms.ValidationError("This field is required.")
+        return data
+
+    def clean_topic_id(self):
+        id = self.cleaned_data['topic_id']
+        try:
+            self.topic = Topic.objects.select_related().get(pk=id)
+        except Topic.DoesNotExist:
+            raise forms.ValidationError('invalid topic')
+        return id
+
+    def save(self, user, ip=None):
+        """
+        Creates a new post from the form data and supplied arguments.
+        """
+        post = Post(topic=self.topic, user=user, body=self.cleaned_data['body'],
+                user_ip=ip)
+        post.save()
+        self.attach_proc.save_attachments(post)
+        notify_new_post(post)
+        return post
+
+
+class NewTopicForm(forms.Form):
+    """
+    Form for creating a new topic and 1st post to that topic.
+    Superusers and moderators can also create the topic as a sticky or initially
+    locked.
+    """
+    name = forms.CharField(label='Subject', max_length=255,
+            widget=forms.TextInput(attrs={'size': 64}))
+    body = forms.CharField(label='', required=False,
+            widget=forms.Textarea(attrs={'class': 'markItUp smileyTarget'}))
+    user = None
+    forum = None
+    has_mod_fields = False
+
+    class Media:
+        css = {
+            'all': (settings.GPP_THIRD_PARTY_CSS['markitup'] +
+                settings.GPP_THIRD_PARTY_CSS['jquery-ui']),
+        }
+        js = (settings.GPP_THIRD_PARTY_JS['markitup'] +
+                settings.GPP_THIRD_PARTY_JS['jquery-ui'] +
+                ['js/forums.js'])
+
+    def __init__(self, user, forum, *args, **kwargs):
+        super(NewTopicForm, self).__init__(*args, **kwargs)
+        self.user = user
+        self.forum = forum
+
+        if perms.can_moderate(forum, user):
+            self.fields['sticky'] = forms.BooleanField(required=False)
+            self.fields['locked'] = forms.BooleanField(required=False)
+            self.has_mod_fields = True
+
+        attachments = args[0].getlist('attachment') if len(args) else []
+        self.attach_proc = AttachmentProcessor(attachments)
+
+        # If this form is being POSTed, and the user is trying to add 
+        # attachments, create hidden fields to list the Oembed ids. In
+        # case the form isn't valid, the client-side javascript will know
+        # which Oembed media to ask for when the form is displayed with
+        # errors.
+        if self.attach_proc.has_attachments():
+            pks = self.attach_proc.get_ids()
+            self.fields['attachment'] = forms.MultipleChoiceField(label='',
+                    widget=forms.MultipleHiddenInput(),
+                    choices=[(v, v) for v in pks])
+
+    def clean_body(self):
+        data = self.cleaned_data['body']
+        if not data and not self.attach_proc.has_attachments():
+            raise forms.ValidationError("This field is required.")
+        return data
+
+    def save(self, ip=None):
+        """
+        Creates the new Topic and first Post from the form data and supplied
+        arguments.
+        """
+        topic = Topic(forum=self.forum,
+                name=self.cleaned_data['name'],
+                user=self.user,
+                sticky=self.has_mod_fields and self.cleaned_data['sticky'],
+                locked=self.has_mod_fields and self.cleaned_data['locked'])
+        topic.save()
+
+        post = Post(topic=topic,
+                user=self.user,
+                body=self.cleaned_data['body'],
+                user_ip=ip)
+        post.save()
+
+        self.attach_proc.save_attachments(post)
+
+        notify_new_topic(topic)
+        notify_new_post(post)
+
+        return topic
+
+
+class PostForm(forms.ModelForm):
+    """
+    Form for editing an existing post or a new, non-quick post.
+    """
+    body = forms.CharField(label='',
+            required=False,
+            widget=forms.Textarea(attrs={'class': 'markItUp smileyTarget'}))
+
+    class Meta:
+        model = Post
+        fields = ('body', )
+
+    class Media:
+        css = {
+            'all': (settings.GPP_THIRD_PARTY_CSS['markitup'] +
+                settings.GPP_THIRD_PARTY_CSS['jquery-ui']),
+        }
+        js = (settings.GPP_THIRD_PARTY_JS['markitup'] +
+                settings.GPP_THIRD_PARTY_JS['jquery-ui'] +
+                ['js/forums.js'])
+
+    def __init__(self, *args, **kwargs):
+        topic_name = kwargs.pop('topic_name', None)
+        super(PostForm, self).__init__(*args, **kwargs)
+
+        if topic_name is not None:  # this is a "first post"
+            self.fields.insert(0, 'name', forms.CharField(label='Subject',
+                    max_length=255,
+                    widget=forms.TextInput(attrs={'size': 64})))
+            self.initial['name'] = topic_name
+
+        attachments = args[0].getlist('attachment') if len(args) else []
+        self.attach_proc = AttachmentProcessor(attachments)
+
+        # If this form is being used to edit an existing post, and that post
+        # has attachments, create a hidden post_id field. The client-side
+        # AJAX will use this as a cue to retrieve the HTML for the embedded
+        # media.
+        if 'instance' in kwargs:
+            post = kwargs['instance']
+            if post.attachments.count():
+                self.fields['post_id'] = forms.CharField(label='',
+                        widget=forms.HiddenInput(attrs={'value': post.id}))
+
+    def clean_body(self):
+        data = self.cleaned_data['body']
+        if not data and not self.attach_proc.has_attachments():
+            raise forms.ValidationError('This field is required.')
+        return data
+
+    def save(self, *args, **kwargs):
+        commit = kwargs.get('commit', False)
+        post = super(PostForm, self).save(*args, **kwargs)
+
+        # Are we saving a "first post"?
+        if 'name' in self.cleaned_data:
+            post.topic.name = self.cleaned_data['name']
+            if commit:
+                post.topic.save()
+        return post
+
+
+class MoveTopicForm(forms.Form):
+    """
+    Form for a moderator to move a topic to a forum.
+    """
+    forums = forms.ModelChoiceField(label='Move to forum',
+          queryset=Forum.objects.none())
+
+    def __init__(self, user, *args, **kwargs):
+        hide_label = kwargs.pop('hide_label', False)
+        required = kwargs.pop('required', True)
+        super(MoveTopicForm, self).__init__(*args, **kwargs)
+        self.fields['forums'].queryset = \
+            Forum.objects.forums_for_user(user).order_by('name')
+        if hide_label:
+            self.fields['forums'].label = ''
+        self.fields['forums'].required = required
+
+
+class SplitTopicForm(forms.Form):
+    """
+    Form for a moderator to split posts from a topic to a new topic.
+    """
+    name = forms.CharField(label='New topic title', max_length=255,
+            widget=forms.TextInput(attrs={'size': 64}))
+    forums = forms.ModelChoiceField(label='Forum for new topic',
+          queryset=Forum.objects.none())
+    post_ids = []
+    split_at = False
+
+    def __init__(self, user, *args, **kwargs):
+        super(SplitTopicForm, self).__init__(*args, **kwargs)
+        self.fields['forums'].queryset = \
+            Forum.objects.forums_for_user(user).order_by('name')
+
+    def clean(self):
+        self.post_ids = self.data.getlist('post_ids')
+        if len(self.post_ids) == 0:
+            raise forms.ValidationError('Please select some posts')
+
+        self.split_at = 'split-at' in self.data
+        if self.split_at and len(self.post_ids) > 1:
+            raise forms.ValidationError('Please select only one post to split the topic at')
+
+        return self.cleaned_data