##// END OF EJS Templates
Small refactoring. Added index view test
neko259 -
r415:cc6ec42b default
parent child Browse files
Show More
@@ -1,224 +1,233 b''
1 # coding=utf-8
1 # coding=utf-8
2 from django.test import TestCase
2 from django.test import TestCase
3 from django.test.client import Client
3 from django.test.client import Client
4 import time
4 import time
5
5
6 from boards.models import Post, Tag
6 from boards.models import Post, Tag
7 from neboard import settings
7 from neboard import settings
8
8
9 PAGE_404 = 'boards/404.html'
9 PAGE_404 = 'boards/404.html'
10
10
11 TEST_TEXT = 'test text'
11 TEST_TEXT = 'test text'
12
12
13 NEW_THREAD_PAGE = '/'
13 NEW_THREAD_PAGE = '/'
14 THREAD_PAGE_ONE = '/thread/1/'
14 THREAD_PAGE_ONE = '/thread/1/'
15 THREAD_PAGE = '/thread/'
15 THREAD_PAGE = '/thread/'
16 TAG_PAGE = '/tag/'
16 TAG_PAGE = '/tag/'
17 HTTP_CODE_REDIRECT = 302
17 HTTP_CODE_REDIRECT = 302
18 HTTP_CODE_OK = 200
18 HTTP_CODE_OK = 200
19 HTTP_CODE_NOT_FOUND = 404
19 HTTP_CODE_NOT_FOUND = 404
20
20
21
21
22 class PostTests(TestCase):
22 class PostTests(TestCase):
23
23
24 def _create_post(self):
24 def _create_post(self):
25 return Post.objects.create_post(title='title',
25 return Post.objects.create_post(title='title',
26 text='text')
26 text='text')
27
27
28 def test_post_add(self):
28 def test_post_add(self):
29 """Test adding post"""
29 """Test adding post"""
30
30
31 post = self._create_post()
31 post = self._create_post()
32
32
33 self.assertIsNotNone(post, 'No post was created')
33 self.assertIsNotNone(post, 'No post was created')
34
34
35 def test_delete_post(self):
35 def test_delete_post(self):
36 """Test post deletion"""
36 """Test post deletion"""
37
37
38 post = self._create_post()
38 post = self._create_post()
39 post_id = post.id
39 post_id = post.id
40
40
41 Post.objects.delete_post(post)
41 Post.objects.delete_post(post)
42
42
43 self.assertFalse(Post.objects.filter(id=post_id).exists())
43 self.assertFalse(Post.objects.filter(id=post_id).exists())
44
44
45 def test_delete_thread(self):
45 def test_delete_thread(self):
46 """Test thread deletion"""
46 """Test thread deletion"""
47
47
48 opening_post = self._create_post()
48 opening_post = self._create_post()
49 thread = opening_post.thread_new
49 thread = opening_post.thread_new
50 reply = Post.objects.create_post("", "", thread=thread)
50 reply = Post.objects.create_post("", "", thread=thread)
51
51
52 thread.delete_with_posts()
52 thread.delete_with_posts()
53
53
54 self.assertFalse(Post.objects.filter(id=reply.id).exists())
54 self.assertFalse(Post.objects.filter(id=reply.id).exists())
55
55
56 def test_post_to_thread(self):
56 def test_post_to_thread(self):
57 """Test adding post to a thread"""
57 """Test adding post to a thread"""
58
58
59 op = self._create_post()
59 op = self._create_post()
60 post = Post.objects.create_post("", "", thread=op.thread_new)
60 post = Post.objects.create_post("", "", thread=op.thread_new)
61
61
62 self.assertIsNotNone(post, 'Reply to thread wasn\'t created')
62 self.assertIsNotNone(post, 'Reply to thread wasn\'t created')
63 self.assertEqual(op.thread_new.last_edit_time, post.pub_time,
63 self.assertEqual(op.thread_new.last_edit_time, post.pub_time,
64 'Post\'s create time doesn\'t match thread last edit'
64 'Post\'s create time doesn\'t match thread last edit'
65 ' time')
65 ' time')
66
66
67 def test_delete_posts_by_ip(self):
67 def test_delete_posts_by_ip(self):
68 """Test deleting posts with the given ip"""
68 """Test deleting posts with the given ip"""
69
69
70 post = self._create_post()
70 post = self._create_post()
71 post_id = post.id
71 post_id = post.id
72
72
73 Post.objects.delete_posts_by_ip('0.0.0.0')
73 Post.objects.delete_posts_by_ip('0.0.0.0')
74
74
75 self.assertFalse(Post.objects.filter(id=post_id).exists())
75 self.assertFalse(Post.objects.filter(id=post_id).exists())
76
76
77 def test_get_thread(self):
77 def test_get_thread(self):
78 """Test getting all posts of a thread"""
78 """Test getting all posts of a thread"""
79
79
80 opening_post = self._create_post()
80 opening_post = self._create_post()
81
81
82 for i in range(0, 2):
82 for i in range(0, 2):
83 Post.objects.create_post('title', 'text',
83 Post.objects.create_post('title', 'text',
84 thread=opening_post.thread_new)
84 thread=opening_post.thread_new)
85
85
86 thread = opening_post.thread_new
86 thread = opening_post.thread_new
87
87
88 self.assertEqual(3, thread.replies.count())
88 self.assertEqual(3, thread.replies.count())
89
89
90 def test_create_post_with_tag(self):
90 def test_create_post_with_tag(self):
91 """Test adding tag to post"""
91 """Test adding tag to post"""
92
92
93 tag = Tag.objects.create(name='test_tag')
93 tag = Tag.objects.create(name='test_tag')
94 post = Post.objects.create_post(title='title', text='text', tags=[tag])
94 post = Post.objects.create_post(title='title', text='text', tags=[tag])
95
95
96 thread = post.thread_new
96 thread = post.thread_new
97 self.assertIsNotNone(post, 'Post not created')
97 self.assertIsNotNone(post, 'Post not created')
98 self.assertTrue(tag in thread.tags.all(), 'Tag not added to thread')
98 self.assertTrue(tag in thread.tags.all(), 'Tag not added to thread')
99 self.assertTrue(thread in tag.threads.all(), 'Thread not added to tag')
99 self.assertTrue(thread in tag.threads.all(), 'Thread not added to tag')
100
100
101 def test_thread_max_count(self):
101 def test_thread_max_count(self):
102 """Test deletion of old posts when the max thread count is reached"""
102 """Test deletion of old posts when the max thread count is reached"""
103
103
104 for i in range(settings.MAX_THREAD_COUNT + 1):
104 for i in range(settings.MAX_THREAD_COUNT + 1):
105 self._create_post()
105 self._create_post()
106
106
107 self.assertEqual(settings.MAX_THREAD_COUNT,
107 self.assertEqual(settings.MAX_THREAD_COUNT,
108 len(Post.objects.get_threads()))
108 len(Post.objects.get_threads()))
109
109
110 def test_pages(self):
110 def test_pages(self):
111 """Test that the thread list is properly split into pages"""
111 """Test that the thread list is properly split into pages"""
112
112
113 for i in range(settings.MAX_THREAD_COUNT):
113 for i in range(settings.MAX_THREAD_COUNT):
114 self._create_post()
114 self._create_post()
115
115
116 all_threads = Post.objects.get_threads()
116 all_threads = Post.objects.get_threads()
117
117
118 posts_in_second_page = Post.objects.get_threads(page=1)
118 posts_in_second_page = Post.objects.get_threads(page=1)
119 first_post = posts_in_second_page[0]
119 first_post = posts_in_second_page[0]
120
120
121 self.assertEqual(all_threads[settings.THREADS_PER_PAGE].id,
121 self.assertEqual(all_threads[settings.THREADS_PER_PAGE].id,
122 first_post.id)
122 first_post.id)
123
123
124 def test_linked_tag(self):
124 def test_linked_tag(self):
125 """Test adding a linked tag"""
125 """Test adding a linked tag"""
126
126
127 linked_tag = Tag.objects.create(name=u'tag1')
127 linked_tag = Tag.objects.create(name=u'tag1')
128 tag = Tag.objects.create(name=u'tag2', linked=linked_tag)
128 tag = Tag.objects.create(name=u'tag2', linked=linked_tag)
129
129
130 post = Post.objects.create_post("", "", tags=[tag])
130 post = Post.objects.create_post("", "", tags=[tag])
131
131
132 self.assertTrue(linked_tag in post.thread_new.tags.all(),
132 self.assertTrue(linked_tag in post.thread_new.tags.all(),
133 'Linked tag was not added')
133 'Linked tag was not added')
134
134
135
135
136 class PagesTest(TestCase):
136 class PagesTest(TestCase):
137
137
138 def test_404(self):
138 def test_404(self):
139 """Test receiving error 404 when opening a non-existent page"""
139 """Test receiving error 404 when opening a non-existent page"""
140
140
141 tag_name = u'test_tag'
141 tag_name = u'test_tag'
142 tag = Tag.objects.create(name=tag_name)
142 tag = Tag.objects.create(name=tag_name)
143 client = Client()
143 client = Client()
144
144
145 Post.objects.create_post('title', TEST_TEXT, tags=[tag])
145 Post.objects.create_post('title', TEST_TEXT, tags=[tag])
146
146
147 existing_post_id = Post.objects.all()[0].id
147 existing_post_id = Post.objects.all()[0].id
148 response_existing = client.get(THREAD_PAGE + str(existing_post_id) +
148 response_existing = client.get(THREAD_PAGE + str(existing_post_id) +
149 '/')
149 '/')
150 self.assertEqual(HTTP_CODE_OK, response_existing.status_code,
150 self.assertEqual(HTTP_CODE_OK, response_existing.status_code,
151 u'Cannot open existing thread')
151 u'Cannot open existing thread')
152
152
153 response_not_existing = client.get(THREAD_PAGE + str(
153 response_not_existing = client.get(THREAD_PAGE + str(
154 existing_post_id + 1) + '/')
154 existing_post_id + 1) + '/')
155 self.assertEqual(PAGE_404,
155 self.assertEqual(PAGE_404,
156 response_not_existing.templates[0].name,
156 response_not_existing.templates[0].name,
157 u'Not existing thread is opened')
157 u'Not existing thread is opened')
158
158
159 response_existing = client.get(TAG_PAGE + tag_name + '/')
159 response_existing = client.get(TAG_PAGE + tag_name + '/')
160 self.assertEqual(HTTP_CODE_OK,
160 self.assertEqual(HTTP_CODE_OK,
161 response_existing.status_code,
161 response_existing.status_code,
162 u'Cannot open existing tag')
162 u'Cannot open existing tag')
163
163
164 response_not_existing = client.get(TAG_PAGE + u'not_tag' + '/')
164 response_not_existing = client.get(TAG_PAGE + u'not_tag' + '/')
165 self.assertEqual(PAGE_404,
165 self.assertEqual(PAGE_404,
166 response_not_existing.templates[0].name,
166 response_not_existing.templates[0].name,
167 u'Not existing tag is opened')
167 u'Not existing tag is opened')
168
168
169 reply_id = Post.objects.create_post('', TEST_TEXT,
169 reply_id = Post.objects.create_post('', TEST_TEXT,
170 thread=Post.objects.all()[0]
170 thread=Post.objects.all()[0]
171 .thread)
171 .thread)
172 response_not_existing = client.get(THREAD_PAGE + str(
172 response_not_existing = client.get(THREAD_PAGE + str(
173 reply_id) + '/')
173 reply_id) + '/')
174 self.assertEqual(PAGE_404,
174 self.assertEqual(PAGE_404,
175 response_not_existing.templates[0].name,
175 response_not_existing.templates[0].name,
176 u'Reply is opened as a thread')
176 u'Reply is opened as a thread')
177
177
178
178
179 class FormTest(TestCase):
179 class FormTest(TestCase):
180 def test_post_validation(self):
180 def test_post_validation(self):
181 """Test the validation of the post form"""
182
183 # Disable captcha for the test
181 # Disable captcha for the test
184 captcha_enabled = settings.ENABLE_CAPTCHA
182 captcha_enabled = settings.ENABLE_CAPTCHA
185 settings.ENABLE_CAPTCHA = False
183 settings.ENABLE_CAPTCHA = False
186
184
187 client = Client()
185 client = Client()
188
186
189 valid_tags = u'tag1 tag_2 Ρ‚Π΅Π³_3'
187 valid_tags = u'tag1 tag_2 Ρ‚Π΅Π³_3'
190 invalid_tags = u'$%_356 ---'
188 invalid_tags = u'$%_356 ---'
191
189
192 response = client.post(NEW_THREAD_PAGE, {'title': 'test title',
190 response = client.post(NEW_THREAD_PAGE, {'title': 'test title',
193 'text': TEST_TEXT,
191 'text': TEST_TEXT,
194 'tags': valid_tags})
192 'tags': valid_tags})
195 self.assertEqual(response.status_code, HTTP_CODE_REDIRECT,
193 self.assertEqual(response.status_code, HTTP_CODE_REDIRECT,
196 msg='Posting new message failed: got code ' +
194 msg='Posting new message failed: got code ' +
197 str(response.status_code))
195 str(response.status_code))
198
196
199 self.assertEqual(1, Post.objects.count(),
197 self.assertEqual(1, Post.objects.count(),
200 msg='No posts were created')
198 msg='No posts were created')
201
199
202 client.post(NEW_THREAD_PAGE, {'text': TEST_TEXT,
200 client.post(NEW_THREAD_PAGE, {'text': TEST_TEXT,
203 'tags': invalid_tags})
201 'tags': invalid_tags})
204 self.assertEqual(1, Post.objects.count(), msg='The validation passed '
202 self.assertEqual(1, Post.objects.count(), msg='The validation passed '
205 'where it should fail')
203 'where it should fail')
206
204
207 # Change posting delay so we don't have to wait for 30 seconds or more
205 # Change posting delay so we don't have to wait for 30 seconds or more
208 old_posting_delay = settings.POSTING_DELAY
206 old_posting_delay = settings.POSTING_DELAY
209 # Wait fot the posting delay or we won't be able to post
207 # Wait fot the posting delay or we won't be able to post
210 settings.POSTING_DELAY = 1
208 settings.POSTING_DELAY = 1
211 time.sleep(settings.POSTING_DELAY + 1)
209 time.sleep(settings.POSTING_DELAY + 1)
212 response = client.post(THREAD_PAGE_ONE, {'text': TEST_TEXT,
210 response = client.post(THREAD_PAGE_ONE, {'text': TEST_TEXT,
213 'tags': valid_tags})
211 'tags': valid_tags})
214 self.assertEqual(HTTP_CODE_REDIRECT, response.status_code,
212 self.assertEqual(HTTP_CODE_REDIRECT, response.status_code,
215 msg=u'Posting new message failed: got code ' +
213 msg=u'Posting new message failed: got code ' +
216 str(response.status_code))
214 str(response.status_code))
217 # Restore posting delay
215 # Restore posting delay
218 settings.POSTING_DELAY = old_posting_delay
216 settings.POSTING_DELAY = old_posting_delay
219
217
220 self.assertEqual(2, Post.objects.count(),
218 self.assertEqual(2, Post.objects.count(),
221 msg=u'No posts were created')
219 msg=u'No posts were created')
222
220
223 # Restore captcha setting
221 # Restore captcha setting
224 settings.ENABLE_CAPTCHA = captcha_enabled
222 settings.ENABLE_CAPTCHA = captcha_enabled
223
224
225 class ViewTest(TestCase):
226 def test_index(self):
227 client = Client()
228
229 response = client.get('/')
230 self.assertEqual(HTTP_CODE_OK, response.status_code, 'Index page not '
231 'opened')
232 self.assertEqual('boards/posting_general.html', response.templates[0]
233 .name, 'Index page should open posting_general template') No newline at end of file
@@ -1,574 +1,571 b''
1 import hashlib
1 import hashlib
2 import json
2 import json
3 import string
3 import string
4 import time
4 import time
5 from datetime import datetime
5 from datetime import datetime
6 import re
6 import re
7
7
8 from django.core import serializers
8 from django.core import serializers
9 from django.core.urlresolvers import reverse
9 from django.core.urlresolvers import reverse
10 from django.http import HttpResponseRedirect
10 from django.http import HttpResponseRedirect
11 from django.http.response import HttpResponse
11 from django.http.response import HttpResponse
12 from django.template import RequestContext
12 from django.template import RequestContext
13 from django.shortcuts import render, redirect, get_object_or_404
13 from django.shortcuts import render, redirect, get_object_or_404
14 from django.utils import timezone
14 from django.utils import timezone
15 from django.db import transaction
15 from django.db import transaction
16
16
17 from boards import forms
17 from boards import forms
18 import boards
18 import boards
19 from boards import utils
19 from boards import utils
20 from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
20 from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
21 ThreadCaptchaForm, PostCaptchaForm, LoginForm, ModeratorSettingsForm
21 ThreadCaptchaForm, PostCaptchaForm, LoginForm, ModeratorSettingsForm
22 from boards.models import Post, Tag, Ban, User
22 from boards.models import Post, Tag, Ban, User
23 from boards.models.post import SETTING_MODERATE, REGEX_REPLY
23 from boards.models.post import SETTING_MODERATE, REGEX_REPLY
24 from boards.models.user import RANK_USER
24 from boards.models.user import RANK_USER
25 from boards import authors
25 from boards import authors
26 from boards.utils import get_client_ip
26 from boards.utils import get_client_ip
27 import neboard
27 import neboard
28
28
29
29
30 BAN_REASON_SPAM = 'Autoban: spam bot'
30 BAN_REASON_SPAM = 'Autoban: spam bot'
31
31
32
32
33 def index(request, page=0):
33 def index(request, page=0):
34 context = _init_default_context(request)
34 context = _init_default_context(request)
35
35
36 if utils.need_include_captcha(request):
36 if utils.need_include_captcha(request):
37 threadFormClass = ThreadCaptchaForm
37 threadFormClass = ThreadCaptchaForm
38 kwargs = {'request': request}
38 kwargs = {'request': request}
39 else:
39 else:
40 threadFormClass = ThreadForm
40 threadFormClass = ThreadForm
41 kwargs = {}
41 kwargs = {}
42
42
43 if request.method == 'POST':
43 if request.method == 'POST':
44 form = threadFormClass(request.POST, request.FILES,
44 form = threadFormClass(request.POST, request.FILES,
45 error_class=PlainErrorList, **kwargs)
45 error_class=PlainErrorList, **kwargs)
46 form.session = request.session
46 form.session = request.session
47
47
48 if form.is_valid():
48 if form.is_valid():
49 return _new_post(request, form)
49 return _new_post(request, form)
50 if form.need_to_ban:
50 if form.need_to_ban:
51 # Ban user because he is suspected to be a bot
51 # Ban user because he is suspected to be a bot
52 _ban_current_user(request)
52 _ban_current_user(request)
53 else:
53 else:
54 form = threadFormClass(error_class=PlainErrorList, **kwargs)
54 form = threadFormClass(error_class=PlainErrorList, **kwargs)
55
55
56 threads = []
56 threads = []
57 for thread_to_show in Post.objects.get_threads(page=int(page)):
57 for thread_to_show in Post.objects.get_threads(page=int(page)):
58 last_replies = thread_to_show.get_last_replies()
58 threads.append(_get_template_thread(thread_to_show))
59 skipped_replies_count = thread_to_show.get_replies().count() \
60 - len(last_replies) - 1
61 threads.append({
62 'thread': thread_to_show,
63 'op': thread_to_show.get_replies()[0],
64 'bumpable': thread_to_show.can_bump(),
65 'last_replies': last_replies,
66 'skipped_replies': skipped_replies_count,
67 })
68
59
69 # TODO Make this generic for tag and threads list pages
60 # TODO Make this generic for tag and threads list pages
70 context['threads'] = None if len(threads) == 0 else threads
61 context['threads'] = None if len(threads) == 0 else threads
71 context['form'] = form
62 context['form'] = form
72
63
73 page_count = Post.objects.get_thread_page_count()
64 page_count = Post.objects.get_thread_page_count()
74 context['pages'] = range(page_count)
65 context['pages'] = range(page_count)
75 page = int(page)
66 page = int(page)
76 if page < page_count - 1:
67 if page < page_count - 1:
77 context['next_page'] = str(page + 1)
68 context['next_page'] = str(page + 1)
78 if page > 0:
69 if page > 0:
79 context['prev_page'] = str(page - 1)
70 context['prev_page'] = str(page - 1)
80
71
81 return render(request, 'boards/posting_general.html',
72 return render(request, 'boards/posting_general.html',
82 context)
73 context)
83
74
84
75
85 @transaction.atomic
76 @transaction.atomic
86 def _new_post(request, form, opening_post=None):
77 def _new_post(request, form, opening_post=None):
87 """Add a new post (in thread or as a reply)."""
78 """Add a new post (in thread or as a reply)."""
88
79
89 ip = get_client_ip(request)
80 ip = get_client_ip(request)
90 is_banned = Ban.objects.filter(ip=ip).exists()
81 is_banned = Ban.objects.filter(ip=ip).exists()
91
82
92 if is_banned:
83 if is_banned:
93 return redirect(you_are_banned)
84 return redirect(you_are_banned)
94
85
95 data = form.cleaned_data
86 data = form.cleaned_data
96
87
97 title = data['title']
88 title = data['title']
98 text = data['text']
89 text = data['text']
99
90
100 text = _remove_invalid_links(text)
91 text = _remove_invalid_links(text)
101
92
102 if 'image' in data.keys():
93 if 'image' in data.keys():
103 image = data['image']
94 image = data['image']
104 else:
95 else:
105 image = None
96 image = None
106
97
107 tags = []
98 tags = []
108
99
109 if not opening_post:
100 if not opening_post:
110 tag_strings = data['tags']
101 tag_strings = data['tags']
111
102
112 if tag_strings:
103 if tag_strings:
113 tag_strings = tag_strings.split(' ')
104 tag_strings = tag_strings.split(' ')
114 for tag_name in tag_strings:
105 for tag_name in tag_strings:
115 tag_name = string.lower(tag_name.strip())
106 tag_name = string.lower(tag_name.strip())
116 if len(tag_name) > 0:
107 if len(tag_name) > 0:
117 tag, created = Tag.objects.get_or_create(name=tag_name)
108 tag, created = Tag.objects.get_or_create(name=tag_name)
118 tags.append(tag)
109 tags.append(tag)
119 post_thread = None
110 post_thread = None
120 else:
111 else:
121 post_thread = opening_post.thread_new
112 post_thread = opening_post.thread_new
122
113
123 post = Post.objects.create_post(title=title, text=text, ip=ip,
114 post = Post.objects.create_post(title=title, text=text, ip=ip,
124 thread=post_thread, image=image,
115 thread=post_thread, image=image,
125 tags=tags, user=_get_user(request))
116 tags=tags, user=_get_user(request))
126
117
127 thread_to_show = (opening_post.id if opening_post else post.id)
118 thread_to_show = (opening_post.id if opening_post else post.id)
128
119
129 if opening_post:
120 if opening_post:
130 return redirect(reverse(thread, kwargs={'post_id': thread_to_show}) +
121 return redirect(reverse(thread, kwargs={'post_id': thread_to_show}) +
131 '#' + str(post.id))
122 '#' + str(post.id))
132 else:
123 else:
133 return redirect(thread, post_id=thread_to_show)
124 return redirect(thread, post_id=thread_to_show)
134
125
135
126
136 def tag(request, tag_name, page=0):
127 def tag(request, tag_name, page=0):
137 """
128 """
138 Get all tag threads. Threads are split in pages, so some page is
129 Get all tag threads. Threads are split in pages, so some page is
139 requested. Default page is 0.
130 requested. Default page is 0.
140 """
131 """
141
132
142 tag = get_object_or_404(Tag, name=tag_name)
133 tag = get_object_or_404(Tag, name=tag_name)
143 threads = []
134 threads = []
144 for thread_to_show in Post.objects.get_threads(page=int(page), tag=tag):
135 for thread_to_show in Post.objects.get_threads(page=int(page), tag=tag):
145 last_replies = thread_to_show.get_last_replies()
136 threads.append(_get_template_thread(thread_to_show))
146 skipped_replies_count = thread_to_show.get_replies().count() \
147 - len(last_replies) - 1
148 threads.append({
149 'thread': thread_to_show,
150 'op': thread_to_show.get_replies()[0],
151 'bumpable': thread_to_show.can_bump(),
152 'last_replies': last_replies,
153 'skipped_replies': skipped_replies_count,
154 })
155
137
156 if request.method == 'POST':
138 if request.method == 'POST':
157 form = ThreadForm(request.POST, request.FILES,
139 form = ThreadForm(request.POST, request.FILES,
158 error_class=PlainErrorList)
140 error_class=PlainErrorList)
159 form.session = request.session
141 form.session = request.session
160
142
161 if form.is_valid():
143 if form.is_valid():
162 return _new_post(request, form)
144 return _new_post(request, form)
163 if form.need_to_ban:
145 if form.need_to_ban:
164 # Ban user because he is suspected to be a bot
146 # Ban user because he is suspected to be a bot
165 _ban_current_user(request)
147 _ban_current_user(request)
166 else:
148 else:
167 form = forms.ThreadForm(initial={'tags': tag_name},
149 form = forms.ThreadForm(initial={'tags': tag_name},
168 error_class=PlainErrorList)
150 error_class=PlainErrorList)
169
151
170 context = _init_default_context(request)
152 context = _init_default_context(request)
171 context['threads'] = None if len(threads) == 0 else threads
153 context['threads'] = None if len(threads) == 0 else threads
172 context['tag'] = tag
154 context['tag'] = tag
173
155
174 page_count = Post.objects.get_thread_page_count(tag=tag)
156 page_count = Post.objects.get_thread_page_count(tag=tag)
175 context['pages'] = range(page_count)
157 context['pages'] = range(page_count)
176 page = int(page)
158 page = int(page)
177 if page < page_count - 1:
159 if page < page_count - 1:
178 context['next_page'] = str(page + 1)
160 context['next_page'] = str(page + 1)
179 if page > 0:
161 if page > 0:
180 context['prev_page'] = str(page - 1)
162 context['prev_page'] = str(page - 1)
181
163
182 context['form'] = form
164 context['form'] = form
183
165
184 return render(request, 'boards/posting_general.html',
166 return render(request, 'boards/posting_general.html',
185 context)
167 context)
186
168
187
169
188 def thread(request, post_id):
170 def thread(request, post_id):
189 """Get all thread posts"""
171 """Get all thread posts"""
190
172
191 if utils.need_include_captcha(request):
173 if utils.need_include_captcha(request):
192 postFormClass = PostCaptchaForm
174 postFormClass = PostCaptchaForm
193 kwargs = {'request': request}
175 kwargs = {'request': request}
194 else:
176 else:
195 postFormClass = PostForm
177 postFormClass = PostForm
196 kwargs = {}
178 kwargs = {}
197
179
198 if request.method == 'POST':
180 if request.method == 'POST':
199 form = postFormClass(request.POST, request.FILES,
181 form = postFormClass(request.POST, request.FILES,
200 error_class=PlainErrorList, **kwargs)
182 error_class=PlainErrorList, **kwargs)
201 form.session = request.session
183 form.session = request.session
202
184
203 opening_post = get_object_or_404(Post, id=post_id)
185 opening_post = get_object_or_404(Post, id=post_id)
204 if form.is_valid():
186 if form.is_valid():
205 return _new_post(request, form, opening_post)
187 return _new_post(request, form, opening_post)
206 if form.need_to_ban:
188 if form.need_to_ban:
207 # Ban user because he is suspected to be a bot
189 # Ban user because he is suspected to be a bot
208 _ban_current_user(request)
190 _ban_current_user(request)
209 else:
191 else:
210 form = postFormClass(error_class=PlainErrorList, **kwargs)
192 form = postFormClass(error_class=PlainErrorList, **kwargs)
211
193
212 thread_to_show = get_object_or_404(Post, id=post_id).thread_new
194 thread_to_show = get_object_or_404(Post, id=post_id).thread_new
213
195
214 context = _init_default_context(request)
196 context = _init_default_context(request)
215
197
216 posts = thread_to_show.get_replies()
198 posts = thread_to_show.get_replies()
217 context['form'] = form
199 context['form'] = form
218 context['bumpable'] = thread_to_show.can_bump()
200 context['bumpable'] = thread_to_show.can_bump()
219 if context['bumpable']:
201 if context['bumpable']:
220 context['posts_left'] = neboard.settings.MAX_POSTS_PER_THREAD - posts\
202 context['posts_left'] = neboard.settings.MAX_POSTS_PER_THREAD - posts\
221 .count()
203 .count()
222 context['bumplimit_progress'] = str(
204 context['bumplimit_progress'] = str(
223 float(context['posts_left']) /
205 float(context['posts_left']) /
224 neboard.settings.MAX_POSTS_PER_THREAD * 100)
206 neboard.settings.MAX_POSTS_PER_THREAD * 100)
225 context["last_update"] = _datetime_to_epoch(thread_to_show.last_edit_time)
207 context["last_update"] = _datetime_to_epoch(thread_to_show.last_edit_time)
226 context["thread"] = thread_to_show
208 context["thread"] = thread_to_show
227
209
228 return render(request, 'boards/thread.html', context)
210 return render(request, 'boards/thread.html', context)
229
211
230
212
231 def login(request):
213 def login(request):
232 """Log in with user id"""
214 """Log in with user id"""
233
215
234 context = _init_default_context(request)
216 context = _init_default_context(request)
235
217
236 if request.method == 'POST':
218 if request.method == 'POST':
237 form = LoginForm(request.POST, request.FILES,
219 form = LoginForm(request.POST, request.FILES,
238 error_class=PlainErrorList)
220 error_class=PlainErrorList)
239 form.session = request.session
221 form.session = request.session
240
222
241 if form.is_valid():
223 if form.is_valid():
242 user = User.objects.get(user_id=form.cleaned_data['user_id'])
224 user = User.objects.get(user_id=form.cleaned_data['user_id'])
243 request.session['user_id'] = user.id
225 request.session['user_id'] = user.id
244 return redirect(index)
226 return redirect(index)
245
227
246 else:
228 else:
247 form = LoginForm()
229 form = LoginForm()
248
230
249 context['form'] = form
231 context['form'] = form
250
232
251 return render(request, 'boards/login.html', context)
233 return render(request, 'boards/login.html', context)
252
234
253
235
254 def settings(request):
236 def settings(request):
255 """User's settings"""
237 """User's settings"""
256
238
257 context = _init_default_context(request)
239 context = _init_default_context(request)
258 user = _get_user(request)
240 user = _get_user(request)
259 is_moderator = user.is_moderator()
241 is_moderator = user.is_moderator()
260
242
261 if request.method == 'POST':
243 if request.method == 'POST':
262 with transaction.atomic():
244 with transaction.atomic():
263 if is_moderator:
245 if is_moderator:
264 form = ModeratorSettingsForm(request.POST,
246 form = ModeratorSettingsForm(request.POST,
265 error_class=PlainErrorList)
247 error_class=PlainErrorList)
266 else:
248 else:
267 form = SettingsForm(request.POST, error_class=PlainErrorList)
249 form = SettingsForm(request.POST, error_class=PlainErrorList)
268
250
269 if form.is_valid():
251 if form.is_valid():
270 selected_theme = form.cleaned_data['theme']
252 selected_theme = form.cleaned_data['theme']
271
253
272 user.save_setting('theme', selected_theme)
254 user.save_setting('theme', selected_theme)
273
255
274 if is_moderator:
256 if is_moderator:
275 moderate = form.cleaned_data['moderate']
257 moderate = form.cleaned_data['moderate']
276 user.save_setting(SETTING_MODERATE, moderate)
258 user.save_setting(SETTING_MODERATE, moderate)
277
259
278 return redirect(settings)
260 return redirect(settings)
279 else:
261 else:
280 selected_theme = _get_theme(request)
262 selected_theme = _get_theme(request)
281
263
282 if is_moderator:
264 if is_moderator:
283 form = ModeratorSettingsForm(initial={'theme': selected_theme,
265 form = ModeratorSettingsForm(initial={'theme': selected_theme,
284 'moderate': context['moderator']},
266 'moderate': context['moderator']},
285 error_class=PlainErrorList)
267 error_class=PlainErrorList)
286 else:
268 else:
287 form = SettingsForm(initial={'theme': selected_theme},
269 form = SettingsForm(initial={'theme': selected_theme},
288 error_class=PlainErrorList)
270 error_class=PlainErrorList)
289
271
290 context['form'] = form
272 context['form'] = form
291
273
292 return render(request, 'boards/settings.html', context)
274 return render(request, 'boards/settings.html', context)
293
275
294
276
295 def all_tags(request):
277 def all_tags(request):
296 """All tags list"""
278 """All tags list"""
297
279
298 context = _init_default_context(request)
280 context = _init_default_context(request)
299 context['all_tags'] = Tag.objects.get_not_empty_tags()
281 context['all_tags'] = Tag.objects.get_not_empty_tags()
300
282
301 return render(request, 'boards/tags.html', context)
283 return render(request, 'boards/tags.html', context)
302
284
303
285
304 def jump_to_post(request, post_id):
286 def jump_to_post(request, post_id):
305 """Determine thread in which the requested post is and open it's page"""
287 """Determine thread in which the requested post is and open it's page"""
306
288
307 post = get_object_or_404(Post, id=post_id)
289 post = get_object_or_404(Post, id=post_id)
308
290
309 if not post.thread:
291 if not post.thread:
310 return redirect(thread, post_id=post.id)
292 return redirect(thread, post_id=post.id)
311 else:
293 else:
312 return redirect(reverse(thread, kwargs={'post_id': post.thread.id})
294 return redirect(reverse(thread, kwargs={'post_id': post.thread.id})
313 + '#' + str(post.id))
295 + '#' + str(post.id))
314
296
315
297
316 def authors(request):
298 def authors(request):
317 """Show authors list"""
299 """Show authors list"""
318
300
319 context = _init_default_context(request)
301 context = _init_default_context(request)
320 context['authors'] = boards.authors.authors
302 context['authors'] = boards.authors.authors
321
303
322 return render(request, 'boards/authors.html', context)
304 return render(request, 'boards/authors.html', context)
323
305
324
306
325 @transaction.atomic
307 @transaction.atomic
326 def delete(request, post_id):
308 def delete(request, post_id):
327 """Delete post"""
309 """Delete post"""
328
310
329 user = _get_user(request)
311 user = _get_user(request)
330 post = get_object_or_404(Post, id=post_id)
312 post = get_object_or_404(Post, id=post_id)
331
313
332 if user.is_moderator():
314 if user.is_moderator():
333 # TODO Show confirmation page before deletion
315 # TODO Show confirmation page before deletion
334 Post.objects.delete_post(post)
316 Post.objects.delete_post(post)
335
317
336 if not post.thread:
318 if not post.thread:
337 return _redirect_to_next(request)
319 return _redirect_to_next(request)
338 else:
320 else:
339 return redirect(thread, post_id=post.thread.id)
321 return redirect(thread, post_id=post.thread.id)
340
322
341
323
342 @transaction.atomic
324 @transaction.atomic
343 def ban(request, post_id):
325 def ban(request, post_id):
344 """Ban user"""
326 """Ban user"""
345
327
346 user = _get_user(request)
328 user = _get_user(request)
347 post = get_object_or_404(Post, id=post_id)
329 post = get_object_or_404(Post, id=post_id)
348
330
349 if user.is_moderator():
331 if user.is_moderator():
350 # TODO Show confirmation page before ban
332 # TODO Show confirmation page before ban
351 ban, created = Ban.objects.get_or_create(ip=post.poster_ip)
333 ban, created = Ban.objects.get_or_create(ip=post.poster_ip)
352 if created:
334 if created:
353 ban.reason = 'Banned for post ' + str(post_id)
335 ban.reason = 'Banned for post ' + str(post_id)
354 ban.save()
336 ban.save()
355
337
356 return _redirect_to_next(request)
338 return _redirect_to_next(request)
357
339
358
340
359 def you_are_banned(request):
341 def you_are_banned(request):
360 """Show the page that notifies that user is banned"""
342 """Show the page that notifies that user is banned"""
361
343
362 context = _init_default_context(request)
344 context = _init_default_context(request)
363
345
364 ban = get_object_or_404(Ban, ip=utils.get_client_ip(request))
346 ban = get_object_or_404(Ban, ip=utils.get_client_ip(request))
365 context['ban_reason'] = ban.reason
347 context['ban_reason'] = ban.reason
366 return render(request, 'boards/staticpages/banned.html', context)
348 return render(request, 'boards/staticpages/banned.html', context)
367
349
368
350
369 def page_404(request):
351 def page_404(request):
370 """Show page 404 (not found error)"""
352 """Show page 404 (not found error)"""
371
353
372 context = _init_default_context(request)
354 context = _init_default_context(request)
373 return render(request, 'boards/404.html', context)
355 return render(request, 'boards/404.html', context)
374
356
375
357
376 @transaction.atomic
358 @transaction.atomic
377 def tag_subscribe(request, tag_name):
359 def tag_subscribe(request, tag_name):
378 """Add tag to favorites"""
360 """Add tag to favorites"""
379
361
380 user = _get_user(request)
362 user = _get_user(request)
381 tag = get_object_or_404(Tag, name=tag_name)
363 tag = get_object_or_404(Tag, name=tag_name)
382
364
383 if not tag in user.fav_tags.all():
365 if not tag in user.fav_tags.all():
384 user.add_tag(tag)
366 user.add_tag(tag)
385
367
386 return _redirect_to_next(request)
368 return _redirect_to_next(request)
387
369
388
370
389 @transaction.atomic
371 @transaction.atomic
390 def tag_unsubscribe(request, tag_name):
372 def tag_unsubscribe(request, tag_name):
391 """Remove tag from favorites"""
373 """Remove tag from favorites"""
392
374
393 user = _get_user(request)
375 user = _get_user(request)
394 tag = get_object_or_404(Tag, name=tag_name)
376 tag = get_object_or_404(Tag, name=tag_name)
395
377
396 if tag in user.fav_tags.all():
378 if tag in user.fav_tags.all():
397 user.remove_tag(tag)
379 user.remove_tag(tag)
398
380
399 return _redirect_to_next(request)
381 return _redirect_to_next(request)
400
382
401
383
402 def static_page(request, name):
384 def static_page(request, name):
403 """Show a static page that needs only tags list and a CSS"""
385 """Show a static page that needs only tags list and a CSS"""
404
386
405 context = _init_default_context(request)
387 context = _init_default_context(request)
406 return render(request, 'boards/staticpages/' + name + '.html', context)
388 return render(request, 'boards/staticpages/' + name + '.html', context)
407
389
408
390
409 def api_get_post(request, post_id):
391 def api_get_post(request, post_id):
410 """
392 """
411 Get the JSON of a post. This can be
393 Get the JSON of a post. This can be
412 used as and API for external clients.
394 used as and API for external clients.
413 """
395 """
414
396
415 post = get_object_or_404(Post, id=post_id)
397 post = get_object_or_404(Post, id=post_id)
416
398
417 json = serializers.serialize("json", [post], fields=(
399 json = serializers.serialize("json", [post], fields=(
418 "pub_time", "_text_rendered", "title", "text", "image",
400 "pub_time", "_text_rendered", "title", "text", "image",
419 "image_width", "image_height", "replies", "tags"
401 "image_width", "image_height", "replies", "tags"
420 ))
402 ))
421
403
422 return HttpResponse(content=json)
404 return HttpResponse(content=json)
423
405
424
406
425 @transaction.atomic
407 @transaction.atomic
426 def api_get_threaddiff(request, thread_id, last_update_time):
408 def api_get_threaddiff(request, thread_id, last_update_time):
427 """Get posts that were changed or added since time"""
409 """Get posts that were changed or added since time"""
428
410
429 thread = get_object_or_404(Post, id=thread_id).thread_new
411 thread = get_object_or_404(Post, id=thread_id).thread_new
430
412
431 filter_time = datetime.fromtimestamp(float(last_update_time) / 1000000,
413 filter_time = datetime.fromtimestamp(float(last_update_time) / 1000000,
432 timezone.get_current_timezone())
414 timezone.get_current_timezone())
433
415
434 json_data = {
416 json_data = {
435 'added': [],
417 'added': [],
436 'updated': [],
418 'updated': [],
437 'last_update': None,
419 'last_update': None,
438 }
420 }
439 added_posts = Post.objects.filter(thread_new=thread,
421 added_posts = Post.objects.filter(thread_new=thread,
440 pub_time__gt=filter_time)\
422 pub_time__gt=filter_time)\
441 .order_by('pub_time')
423 .order_by('pub_time')
442 updated_posts = Post.objects.filter(thread_new=thread,
424 updated_posts = Post.objects.filter(thread_new=thread,
443 pub_time__lte=filter_time,
425 pub_time__lte=filter_time,
444 last_edit_time__gt=filter_time)
426 last_edit_time__gt=filter_time)
445 for post in added_posts:
427 for post in added_posts:
446 json_data['added'].append(get_post(request, post.id).content.strip())
428 json_data['added'].append(get_post(request, post.id).content.strip())
447 for post in updated_posts:
429 for post in updated_posts:
448 json_data['updated'].append(get_post(request, post.id).content.strip())
430 json_data['updated'].append(get_post(request, post.id).content.strip())
449 json_data['last_update'] = _datetime_to_epoch(thread.last_edit_time)
431 json_data['last_update'] = _datetime_to_epoch(thread.last_edit_time)
450
432
451 return HttpResponse(content=json.dumps(json_data))
433 return HttpResponse(content=json.dumps(json_data))
452
434
453
435
454 def get_post(request, post_id):
436 def get_post(request, post_id):
455 """Get the html of a post. Used for popups."""
437 """Get the html of a post. Used for popups."""
456
438
457 post = get_object_or_404(Post, id=post_id)
439 post = get_object_or_404(Post, id=post_id)
458 thread = post.thread_new
440 thread = post.thread_new
459
441
460 context = RequestContext(request)
442 context = RequestContext(request)
461 context["post"] = post
443 context["post"] = post
462 context["can_bump"] = thread.can_bump()
444 context["can_bump"] = thread.can_bump()
463 if "truncated" in request.GET:
445 if "truncated" in request.GET:
464 context["truncated"] = True
446 context["truncated"] = True
465
447
466 return render(request, 'boards/post.html', context)
448 return render(request, 'boards/post.html', context)
467
449
468
450
469 def _get_theme(request, user=None):
451 def _get_theme(request, user=None):
470 """Get user's CSS theme"""
452 """Get user's CSS theme"""
471
453
472 if not user:
454 if not user:
473 user = _get_user(request)
455 user = _get_user(request)
474 theme = user.get_setting('theme')
456 theme = user.get_setting('theme')
475 if not theme:
457 if not theme:
476 theme = neboard.settings.DEFAULT_THEME
458 theme = neboard.settings.DEFAULT_THEME
477
459
478 return theme
460 return theme
479
461
480
462
481 def _init_default_context(request):
463 def _init_default_context(request):
482 """Create context with default values that are used in most views"""
464 """Create context with default values that are used in most views"""
483
465
484 context = RequestContext(request)
466 context = RequestContext(request)
485
467
486 user = _get_user(request)
468 user = _get_user(request)
487 context['user'] = user
469 context['user'] = user
488 context['tags'] = user.get_sorted_fav_tags()
470 context['tags'] = user.get_sorted_fav_tags()
489 context['posts_per_day'] = float(Post.objects.get_posts_per_day())
471 context['posts_per_day'] = float(Post.objects.get_posts_per_day())
490
472
491 theme = _get_theme(request, user)
473 theme = _get_theme(request, user)
492 context['theme'] = theme
474 context['theme'] = theme
493 context['theme_css'] = 'css/' + theme + '/base_page.css'
475 context['theme_css'] = 'css/' + theme + '/base_page.css'
494
476
495 # This shows the moderator panel
477 # This shows the moderator panel
496 moderate = user.get_setting(SETTING_MODERATE)
478 moderate = user.get_setting(SETTING_MODERATE)
497 if moderate == 'True':
479 if moderate == 'True':
498 context['moderator'] = user.is_moderator()
480 context['moderator'] = user.is_moderator()
499 else:
481 else:
500 context['moderator'] = False
482 context['moderator'] = False
501
483
502 return context
484 return context
503
485
504
486
505 def _get_user(request):
487 def _get_user(request):
506 """
488 """
507 Get current user from the session. If the user does not exist, create
489 Get current user from the session. If the user does not exist, create
508 a new one.
490 a new one.
509 """
491 """
510
492
511 session = request.session
493 session = request.session
512 if not 'user_id' in session:
494 if not 'user_id' in session:
513 request.session.save()
495 request.session.save()
514
496
515 md5 = hashlib.md5()
497 md5 = hashlib.md5()
516 md5.update(session.session_key)
498 md5.update(session.session_key)
517 new_id = md5.hexdigest()
499 new_id = md5.hexdigest()
518
500
519 time_now = timezone.now()
501 time_now = timezone.now()
520 user = User.objects.create(user_id=new_id, rank=RANK_USER,
502 user = User.objects.create(user_id=new_id, rank=RANK_USER,
521 registration_time=time_now)
503 registration_time=time_now)
522
504
523 session['user_id'] = user.id
505 session['user_id'] = user.id
524 else:
506 else:
525 user = User.objects.get(id=session['user_id'])
507 user = User.objects.get(id=session['user_id'])
526
508
527 return user
509 return user
528
510
529
511
530 def _redirect_to_next(request):
512 def _redirect_to_next(request):
531 """
513 """
532 If a 'next' parameter was specified, redirect to the next page. This is
514 If a 'next' parameter was specified, redirect to the next page. This is
533 used when the user is required to return to some page after the current
515 used when the user is required to return to some page after the current
534 view has finished its work.
516 view has finished its work.
535 """
517 """
536
518
537 if 'next' in request.GET:
519 if 'next' in request.GET:
538 next_page = request.GET['next']
520 next_page = request.GET['next']
539 return HttpResponseRedirect(next_page)
521 return HttpResponseRedirect(next_page)
540 else:
522 else:
541 return redirect(index)
523 return redirect(index)
542
524
543
525
544 @transaction.atomic
526 @transaction.atomic
545 def _ban_current_user(request):
527 def _ban_current_user(request):
546 """Add current user to the IP ban list"""
528 """Add current user to the IP ban list"""
547
529
548 ip = utils.get_client_ip(request)
530 ip = utils.get_client_ip(request)
549 ban, created = Ban.objects.get_or_create(ip=ip)
531 ban, created = Ban.objects.get_or_create(ip=ip)
550 if created:
532 if created:
551 ban.can_read = False
533 ban.can_read = False
552 ban.reason = BAN_REASON_SPAM
534 ban.reason = BAN_REASON_SPAM
553 ban.save()
535 ban.save()
554
536
555
537
556 def _remove_invalid_links(text):
538 def _remove_invalid_links(text):
557 """
539 """
558 Replace invalid links in posts so that they won't be parsed.
540 Replace invalid links in posts so that they won't be parsed.
559 Invalid links are links to non-existent posts
541 Invalid links are links to non-existent posts
560 """
542 """
561
543
562 for reply_number in re.finditer(REGEX_REPLY, text):
544 for reply_number in re.finditer(REGEX_REPLY, text):
563 post_id = reply_number.group(1)
545 post_id = reply_number.group(1)
564 post = Post.objects.filter(id=post_id)
546 post = Post.objects.filter(id=post_id)
565 if not post.exists():
547 if not post.exists():
566 text = string.replace(text, '>>' + post_id, post_id)
548 text = string.replace(text, '>>' + post_id, post_id)
567
549
568 return text
550 return text
569
551
570
552
571 def _datetime_to_epoch(datetime):
553 def _datetime_to_epoch(datetime):
572 return int(time.mktime(timezone.localtime(
554 return int(time.mktime(timezone.localtime(
573 datetime,timezone.get_current_timezone()).timetuple())
555 datetime,timezone.get_current_timezone()).timetuple())
574 * 1000000 + datetime.microsecond)
556 * 1000000 + datetime.microsecond)
557
558
559 def _get_template_thread(thread_to_show):
560 """Get template values for thread"""
561
562 last_replies = thread_to_show.get_last_replies()
563 skipped_replies_count = thread_to_show.get_replies().count() \
564 - len(last_replies) - 1
565 return {
566 'thread': thread_to_show,
567 'op': thread_to_show.get_replies()[0],
568 'bumpable': thread_to_show.can_bump(),
569 'last_replies': last_replies,
570 'skipped_replies': skipped_replies_count,
571 }
General Comments 0
You need to be logged in to leave comments. Login now