# HG changeset patch # User Brian Neal # Date 1256180238 0 # Node ID 0ce0104c7df3156f63229a66187e22bafa456bc2 # Parent 535d02d1c017841c15e6b818e306a155188ccec2 Forums: split topic. diff -r 535d02d1c017 -r 0ce0104c7df3 gpp/forums/forms.py --- a/gpp/forums/forms.py Sun Oct 11 20:27:07 2009 +0000 +++ b/gpp/forums/forms.py Thu Oct 22 02:57:18 2009 +0000 @@ -125,3 +125,30 @@ 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 diff -r 535d02d1c017 -r 0ce0104c7df3 gpp/forums/models.py --- a/gpp/forums/models.py Sun Oct 11 20:27:07 2009 +0000 +++ b/gpp/forums/models.py Thu Oct 22 02:57:18 2009 +0000 @@ -228,7 +228,7 @@ topic = models.ForeignKey(Topic, related_name='posts') user = models.ForeignKey(User, related_name='posts') creation_date = models.DateTimeField(auto_now_add=True) - update_date = models.DateTimeField(auto_now=True) + update_date = models.DateTimeField(null=True) body = models.TextField() html = models.TextField() user_ip = models.IPAddressField(blank=True, default='', null=True) @@ -262,7 +262,11 @@ self.topic.delete() def has_been_edited(self): - return (self.update_date - self.creation_date) > POST_EDIT_DELTA + return self.update_date is not None + + def touch(self): + """Call this function to indicate the post has been edited.""" + self.update_date = datetime.datetime.now() class FlaggedPost(models.Model): @@ -327,7 +331,7 @@ self.last_visit.strftime('%Y-%m-%d %H:%M:%S')) def save(self, *args, **kwargs): - if self.id is None: + if self.id is one: self.touch() super(TopicLastVisit, self).save(*args, **kwargs) diff -r 535d02d1c017 -r 0ce0104c7df3 gpp/forums/urls.py --- a/gpp/forums/urls.py Sun Oct 11 20:27:07 2009 +0000 +++ b/gpp/forums/urls.py Thu Oct 22 02:57:18 2009 +0000 @@ -17,6 +17,7 @@ url(r'^mod/topic/delete/(\d+)/$', 'mod_topic_delete', name='forums-mod_topic_delete'), url(r'^mod/topic/lock/(\d+)/$', 'mod_topic_lock', name='forums-mod_topic_lock'), url(r'^mod/topic/move/(\d+)/$', 'mod_topic_move', name='forums-mod_topic_move'), + url(r'^mod/topic/split/(\d+)/$', 'mod_topic_split', name='forums-mod_topic_split'), url(r'^mod/topic/stick/(\d+)/$', 'mod_topic_stick', name='forums-mod_topic_stick'), url(r'^post/(\d+)/$', 'goto_post', name='forums-goto_post'), url(r'^post/new/(?P\d+)/$', 'new_post', name='forums-new_post'), diff -r 535d02d1c017 -r 0ce0104c7df3 gpp/forums/views.py --- a/gpp/forums/views.py Sun Oct 11 20:27:07 2009 +0000 +++ b/gpp/forums/views.py Thu Oct 22 02:57:18 2009 +0000 @@ -22,7 +22,8 @@ from core.functions import email_admins from forums.models import Forum, Topic, Post, FlaggedPost, TopicLastVisit, \ ForumLastVisit -from forums.forms import NewTopicForm, NewPostForm, PostForm, MoveTopicForm +from forums.forms import NewTopicForm, NewPostForm, PostForm, MoveTopicForm, \ + SplitTopicForm from forums.unread import get_forum_unread_status, get_topic_unread_status, \ get_post_unread_status @@ -271,7 +272,9 @@ if request.method == "POST": form = PostForm(request.POST, instance=post) if form.is_valid(): - form.save() + post = form.save(commit=False) + post.touch() + post.save() return HttpResponseRedirect(post.get_absolute_url()) else: form = PostForm(instance=post) @@ -544,6 +547,42 @@ return HttpResponseRedirect(forum.get_absolute_url()) +@login_required +def mod_topic_split(request, id): + """ + This view function allows moderators to split posts off to a new topic. + """ + topic = get_object_or_404(Topic.objects.select_related(), pk=id) + if not _can_moderate(topic.forum, request.user): + return HttpResponseRedirect(topic.get_absolute_url()) + + if request.method == "POST": + form = SplitTopicForm(request.user, request.POST) + if form.is_valid(): + if form.split_at: + _split_topic_at(topic, form.post_ids[0], + form.cleaned_data['forums'], + form.cleaned_data['name']) + else: + _split_topic(topic, form.post_ids, + form.cleaned_data['forums'], + form.cleaned_data['name']) + + return HttpResponseRedirect(topic.get_absolute_url()) + else: + form = SplitTopicForm(request.user) + + posts = topic.posts.select_related() + + return render_to_response('forums/mod_split_topic.html', { + 'forum': topic.forum, + 'topic': topic, + 'posts': posts, + 'form': form, + }, + context_instance=RequestContext(request)) + + def _can_moderate(forum, user): """ Determines if a user has permission to moderate a given forum. @@ -659,3 +698,39 @@ tlv.touch() tlv.save() + +def _split_topic_at(topic, post_id, new_forum, new_name): + """ + This function splits the post given by post_id and all posts that come + after it in the given topic to a new topic in a new forum. + It is assumed the caller has been checked for moderator rights. + """ + post = get_object_or_404(Post, id=post_id) + if post.topic == topic: + post_ids = Post.objects.filter(topic=topic, + creation_date__gte=post.creation_date).values_list('id', flat=True) + _split_topic(topic, post_ids, new_forum, new_name) + + +def _split_topic(topic, post_ids, new_forum, new_name): + """ + This function splits the posts given by the post_ids list in the + given topic to a new topic in a new forum. + It is assumed the caller has been checked for moderator rights. + """ + posts = Post.objects.filter(topic=topic, id__in=post_ids) + if len(posts) > 0: + new_topic = Topic(forum=new_forum, name=new_name, user=posts[0].user) + new_topic.save() + for post in posts: + post.topic = new_topic + post.save() + + topic.post_count_update() + topic.save() + new_topic.post_count_update() + new_topic.save() + topic.forum.sync() + topic.forum.save() + new_forum.sync() + new_forum.save() diff -r 535d02d1c017 -r 0ce0104c7df3 gpp/templates/forums/display_post.html --- a/gpp/templates/forums/display_post.html Sun Oct 11 20:27:07 2009 +0000 +++ b/gpp/templates/forums/display_post.html Thu Oct 22 02:57:18 2009 +0000 @@ -19,7 +19,6 @@
{{ post.html|safe }} {% if post.has_been_edited %} -

{{ post.creation_date|date:"M d, Y H:i:s" }} {{ post.update_date|date:"M d, Y H:i:s" }}

Last edited: {{ post.update_date|date:"M d, Y H:i:s" }}

{% endif %}
diff -r 535d02d1c017 -r 0ce0104c7df3 gpp/templates/forums/last_post_info.html --- a/gpp/templates/forums/last_post_info.html Sun Oct 11 20:27:07 2009 +0000 +++ b/gpp/templates/forums/last_post_info.html Thu Oct 22 02:57:18 2009 +0000 @@ -1,6 +1,6 @@ {% if post %} Goto last post -{{ post.update_date|date:"M d, Y H:i"}}
+{{ post.creation_date|date:"M d, Y H:i"}}
{{ post.user.username }} {% else %} No posts diff -r 535d02d1c017 -r 0ce0104c7df3 gpp/templates/forums/topic.html --- a/gpp/templates/forums/topic.html Sun Oct 11 20:27:07 2009 +0000 +++ b/gpp/templates/forums/topic.html Thu Oct 22 02:57:18 2009 +0000 @@ -47,6 +47,8 @@ Move Topic Move this topic + Split Topic + Split this topic {% endif %} diff -r 535d02d1c017 -r 0ce0104c7df3 media/css/base.css --- a/media/css/base.css Sun Oct 11 20:27:07 2009 +0000 +++ b/media/css/base.css Thu Oct 22 02:57:18 2009 +0000 @@ -217,6 +217,17 @@ width:95%; border-bottom: 1px solid black; } +td.forum-post-body-split { + vertical-align: top; + width:90%; + border-bottom: 1px solid black; +} +td.forum-post-split-check { + width:5%; + border-left: 1px solid #ccc; + border-bottom: 1px solid black; + vertical-align: middle; +} div.forum-post-info { padding: 2px; font-size:.8em; diff -r 535d02d1c017 -r 0ce0104c7df3 media/icons/arrow_branch.png Binary file media/icons/arrow_branch.png has changed