diff --git a/boards/templates/boards/base.html b/boards/templates/boards/base.html
--- a/boards/templates/boards/base.html
+++ b/boards/templates/boards/base.html
@@ -30,7 +30,7 @@
{% trans "All threads" %}
{% for tag in tags %}
{% autoescape off %}
- {{ tag.get_view }}{% if not forloop.last %},{% endif %}
+ {{ tag.get_view }},
{% endautoescape %}
{% endfor %}
{% trans 'Normal mode' %},
- {% trans 'Gallery mode' %}
+ {% trans 'Gallery mode' %}
{% if bumpable %}
diff --git a/boards/templates/boards/thread_gallery.html b/boards/templates/boards/thread_gallery.html
--- a/boards/templates/boards/thread_gallery.html
+++ b/boards/templates/boards/thread_gallery.html
@@ -17,7 +17,7 @@
{% cache 600 thread_gallery_view thread.id thread.last_edit_time LANGUAGE_CODE request.get_host %}
diff --git a/boards/urls.py b/boards/urls.py
--- a/boards/urls.py
+++ b/boards/urls.py
@@ -29,10 +29,10 @@ urlpatterns = patterns('',
tag_threads.TagView.as_view(), name='tag'),
# /boards/thread/
- url(r'^thread/(?P
\w+)/$', views.thread.ThreadView.as_view(),
+ url(r'^thread/(?P\w+)/$', views.thread.normal.NormalThreadView.as_view(),
name='thread'),
- url(r'^thread/(?P\w+)/mode/(?P\w+)/$', views.thread.ThreadView
- .as_view(), name='thread_mode'),
+ url(r'^thread/(?P\w+)/mode/gallery/$', views.thread.gallery.GalleryThreadView.as_view(),
+ name='thread_gallery'),
url(r'^settings/$', settings.SettingsView.as_view(), name='settings'),
url(r'^tags/$', all_tags.AllTagsView.as_view(), name='tags'),
diff --git a/boards/views/thread.py b/boards/views/thread.py
deleted file mode 100644
--- a/boards/views/thread.py
+++ /dev/null
@@ -1,142 +0,0 @@
-from django.core.urlresolvers import reverse
-from django.db import transaction
-from django.http import Http404
-from django.shortcuts import get_object_or_404, render, redirect
-from django.views.generic.edit import FormMixin
-
-from boards import utils, settings
-from boards.forms import PostForm, PlainErrorList
-from boards.models import Post, Ban
-from boards.views.banned import BannedView
-from boards.views.base import BaseBoardView, CONTEXT_FORM
-from boards.views.posting_mixin import PostMixin
-import neboard
-
-TEMPLATE_GALLERY = 'boards/thread_gallery.html'
-TEMPLATE_NORMAL = 'boards/thread.html'
-
-CONTEXT_POSTS = 'posts'
-CONTEXT_OP = 'opening_post'
-CONTEXT_BUMPLIMIT_PRG = 'bumplimit_progress'
-CONTEXT_POSTS_LEFT = 'posts_left'
-CONTEXT_LASTUPDATE = "last_update"
-CONTEXT_MAX_REPLIES = 'max_replies'
-CONTEXT_THREAD = 'thread'
-CONTEXT_BUMPABLE = 'bumpable'
-CONTEXT_WS_TOKEN = 'ws_token'
-CONTEXT_WS_PROJECT = 'ws_project'
-CONTEXT_WS_HOST = 'ws_host'
-CONTEXT_WS_PORT = 'ws_port'
-
-FORM_TITLE = 'title'
-FORM_TEXT = 'text'
-FORM_IMAGE = 'image'
-
-MODE_GALLERY = 'gallery'
-MODE_NORMAL = 'normal'
-
-
-class ThreadView(BaseBoardView, PostMixin, FormMixin):
-
- def get(self, request, post_id, mode=MODE_NORMAL, form=None):
- try:
- opening_post = Post.objects.filter(id=post_id).only('thread_new')[0]
- except IndexError:
- raise Http404
-
- # If this is not OP, don't show it as it is
- if not opening_post or not opening_post.is_opening():
- raise Http404
-
- if not form:
- form = PostForm(error_class=PlainErrorList)
-
- thread_to_show = opening_post.get_thread()
-
- params = dict()
-
- params[CONTEXT_FORM] = form
- params[CONTEXT_LASTUPDATE] = str(utils.datetime_to_epoch(
- thread_to_show.last_edit_time))
- params[CONTEXT_THREAD] = thread_to_show
- params[CONTEXT_MAX_REPLIES] = settings.MAX_POSTS_PER_THREAD
-
- if settings.WEBSOCKETS_ENABLED:
- params[CONTEXT_WS_TOKEN] = utils.get_websocket_token(
- timestamp=params[CONTEXT_LASTUPDATE])
- params[CONTEXT_WS_PROJECT] = neboard.settings.CENTRIFUGE_PROJECT_ID
- params[CONTEXT_WS_HOST] = request.get_host().split(':')[0]
- params[CONTEXT_WS_PORT] = neboard.settings.CENTRIFUGE_PORT
-
- # TODO Move this to subclasses: NormalThreadView, GalleryThreadView etc
- if MODE_NORMAL == mode:
- bumpable = thread_to_show.can_bump()
- params[CONTEXT_BUMPABLE] = bumpable
- if bumpable:
- left_posts = settings.MAX_POSTS_PER_THREAD \
- - thread_to_show.get_reply_count()
- params[CONTEXT_POSTS_LEFT] = left_posts
- params[CONTEXT_BUMPLIMIT_PRG] = str(
- float(left_posts) / settings.MAX_POSTS_PER_THREAD * 100)
-
- params[CONTEXT_OP] = opening_post
-
- document = TEMPLATE_NORMAL
- elif MODE_GALLERY == mode:
- params[CONTEXT_POSTS] = thread_to_show.get_replies_with_images(
- view_fields_only=True)
-
- document = TEMPLATE_GALLERY
- else:
- raise Http404
-
- return render(request, document, params)
-
- def post(self, request, post_id, mode=MODE_NORMAL):
- opening_post = get_object_or_404(Post, id=post_id)
-
- # If this is not OP, don't show it as it is
- if not opening_post.is_opening():
- raise Http404
-
- if not opening_post.get_thread().archived:
- form = PostForm(request.POST, request.FILES,
- error_class=PlainErrorList)
- form.session = request.session
-
- if form.is_valid():
- return self.new_post(request, form, opening_post)
- if form.need_to_ban:
- # Ban user because he is suspected to be a bot
- self._ban_current_user(request)
-
- return self.get(request, post_id, mode, form)
-
- def new_post(self, request, form, opening_post=None, html_response=True):
- """Add a new post (in thread or as a reply)."""
-
- ip = utils.get_client_ip(request)
-
- data = form.cleaned_data
-
- title = data[FORM_TITLE]
- text = data[FORM_TEXT]
- image = data.get(FORM_IMAGE)
-
- text = self._remove_invalid_links(text)
-
- post_thread = opening_post.get_thread()
-
- post = Post.objects.create_post(title=title, text=text, image=image,
- thread=post_thread, ip=ip)
- post.send_to_websocket(request)
-
- thread_to_show = (opening_post.id if opening_post else post.id)
-
- if html_response:
- if opening_post:
- return redirect(
- reverse('thread', kwargs={'post_id': thread_to_show})
- + '#' + str(post.id))
- else:
- return post
diff --git a/boards/views/thread/__init__.py b/boards/views/thread/__init__.py
new file mode 100644
--- /dev/null
+++ b/boards/views/thread/__init__.py
@@ -0,0 +1,3 @@
+from boards.views.thread.thread import ThreadView
+from boards.views.thread.normal import NormalThreadView
+from boards.views.thread.gallery import GalleryThreadView
diff --git a/boards/views/thread/gallery.py b/boards/views/thread/gallery.py
new file mode 100644
--- /dev/null
+++ b/boards/views/thread/gallery.py
@@ -0,0 +1,19 @@
+from boards.views.thread import ThreadView
+
+TEMPLATE_GALLERY = 'boards/thread_gallery.html'
+
+CONTEXT_POSTS = 'posts'
+
+
+class GalleryThreadView(ThreadView):
+
+ def get_template(self):
+ return TEMPLATE_GALLERY
+
+ def get_data(self, thread):
+ params = dict()
+
+ params[CONTEXT_POSTS] = thread.get_replies_with_images(
+ view_fields_only=True)
+
+ return params
diff --git a/boards/views/thread/normal.py b/boards/views/thread/normal.py
new file mode 100644
--- /dev/null
+++ b/boards/views/thread/normal.py
@@ -0,0 +1,38 @@
+from boards import settings
+from boards.views.thread import ThreadView
+
+TEMPLATE_NORMAL = 'boards/thread.html'
+
+CONTEXT_OP = 'opening_post'
+CONTEXT_BUMPLIMIT_PRG = 'bumplimit_progress'
+CONTEXT_POSTS_LEFT = 'posts_left'
+CONTEXT_BUMPABLE = 'bumpable'
+
+FORM_TITLE = 'title'
+FORM_TEXT = 'text'
+FORM_IMAGE = 'image'
+
+MODE_GALLERY = 'gallery'
+MODE_NORMAL = 'normal'
+
+
+class NormalThreadView(ThreadView):
+
+ def get_template(self):
+ return TEMPLATE_NORMAL
+
+ def get_data(self, thread):
+ params = dict()
+
+ bumpable = thread.can_bump()
+ params[CONTEXT_BUMPABLE] = bumpable
+ if bumpable:
+ left_posts = settings.MAX_POSTS_PER_THREAD \
+ - thread.get_reply_count()
+ params[CONTEXT_POSTS_LEFT] = left_posts
+ params[CONTEXT_BUMPLIMIT_PRG] = str(
+ float(left_posts) / settings.MAX_POSTS_PER_THREAD * 100)
+
+ params[CONTEXT_OP] = thread.get_opening_post()
+
+ return params
diff --git a/boards/views/thread/thread.py b/boards/views/thread/thread.py
new file mode 100644
--- /dev/null
+++ b/boards/views/thread/thread.py
@@ -0,0 +1,135 @@
+from django.core.urlresolvers import reverse
+from django.db import transaction
+from django.http import Http404
+from django.shortcuts import get_object_or_404, render, redirect
+from django.views.generic.edit import FormMixin
+
+from boards import utils, settings
+from boards.forms import PostForm, PlainErrorList
+from boards.models import Post, Ban
+from boards.views.banned import BannedView
+from boards.views.base import BaseBoardView, CONTEXT_FORM
+from boards.views.posting_mixin import PostMixin
+import neboard
+
+TEMPLATE_GALLERY = 'boards/thread_gallery.html'
+TEMPLATE_NORMAL = 'boards/thread.html'
+
+CONTEXT_POSTS = 'posts'
+CONTEXT_OP = 'opening_post'
+CONTEXT_BUMPLIMIT_PRG = 'bumplimit_progress'
+CONTEXT_POSTS_LEFT = 'posts_left'
+CONTEXT_LASTUPDATE = "last_update"
+CONTEXT_MAX_REPLIES = 'max_replies'
+CONTEXT_THREAD = 'thread'
+CONTEXT_BUMPABLE = 'bumpable'
+CONTEXT_WS_TOKEN = 'ws_token'
+CONTEXT_WS_PROJECT = 'ws_project'
+CONTEXT_WS_HOST = 'ws_host'
+CONTEXT_WS_PORT = 'ws_port'
+
+FORM_TITLE = 'title'
+FORM_TEXT = 'text'
+FORM_IMAGE = 'image'
+
+
+class ThreadView(BaseBoardView, PostMixin, FormMixin):
+
+ def get(self, request, post_id, form=None):
+ try:
+ opening_post = Post.objects.filter(id=post_id).only('thread_new')[0]
+ except IndexError:
+ raise Http404
+
+ # If this is not OP, don't show it as it is
+ if not opening_post or not opening_post.is_opening():
+ raise Http404
+
+ if not form:
+ form = PostForm(error_class=PlainErrorList)
+
+ thread_to_show = opening_post.get_thread()
+
+ params = dict()
+
+ params[CONTEXT_FORM] = form
+ params[CONTEXT_LASTUPDATE] = str(utils.datetime_to_epoch(
+ thread_to_show.last_edit_time))
+ params[CONTEXT_THREAD] = thread_to_show
+ params[CONTEXT_MAX_REPLIES] = settings.MAX_POSTS_PER_THREAD
+
+ if settings.WEBSOCKETS_ENABLED:
+ params[CONTEXT_WS_TOKEN] = utils.get_websocket_token(
+ timestamp=params[CONTEXT_LASTUPDATE])
+ params[CONTEXT_WS_PROJECT] = neboard.settings.CENTRIFUGE_PROJECT_ID
+ params[CONTEXT_WS_HOST] = request.get_host().split(':')[0]
+ params[CONTEXT_WS_PORT] = neboard.settings.CENTRIFUGE_PORT
+
+ params.update(self.get_data(thread_to_show))
+
+ return render(request, self.get_template(), params)
+
+ def post(self, request, post_id):
+ opening_post = get_object_or_404(Post, id=post_id)
+
+ # If this is not OP, don't show it as it is
+ if not opening_post.is_opening():
+ raise Http404
+
+ if not opening_post.get_thread().archived:
+ form = PostForm(request.POST, request.FILES,
+ error_class=PlainErrorList)
+ form.session = request.session
+
+ if form.is_valid():
+ return self.new_post(request, form, opening_post)
+ if form.need_to_ban:
+ # Ban user because he is suspected to be a bot
+ self._ban_current_user(request)
+
+ return self.get(request, post_id, form)
+
+ def new_post(self, request, form, opening_post=None, html_response=True):
+ """
+ Adds a new post (in thread or as a reply).
+ """
+
+ ip = utils.get_client_ip(request)
+
+ data = form.cleaned_data
+
+ title = data[FORM_TITLE]
+ text = data[FORM_TEXT]
+ image = data.get(FORM_IMAGE)
+
+ text = self._remove_invalid_links(text)
+
+ post_thread = opening_post.get_thread()
+
+ post = Post.objects.create_post(title=title, text=text, image=image,
+ thread=post_thread, ip=ip)
+ post.send_to_websocket(request)
+
+ thread_to_show = (opening_post.id if opening_post else post.id)
+
+ if html_response:
+ if opening_post:
+ return redirect(
+ reverse('thread', kwargs={'post_id': thread_to_show})
+ + '#' + str(post.id))
+ else:
+ return post
+
+ def get_data(self, thread):
+ """
+ Returns context params for the view.
+ """
+
+ pass
+
+ def get_template(self):
+ """
+ Gets template to show the thread mode on.
+ """
+
+ pass