##// END OF EJS Templates
Added django management command to clean old users. Cleaned up failing tests. Fixed error when trying to get a non-existent post
neko259 -
r689:3d81370e 1.8-dev
parent child Browse files
Show More
@@ -0,0 +1,1 b''
1 __author__ = 'vurdalak'
@@ -0,0 +1,1 b''
1 __author__ = 'vurdalak'
@@ -0,0 +1,29 b''
1 from datetime import datetime, timedelta
2 from django.core.management import BaseCommand
3 from django.db import transaction
4 from django.db.models import Count
5 from boards.models import User, Post
6
7 __author__ = 'neko259'
8
9 OLD_USER_AGE_DAYS = 90
10
11
12 class Command(BaseCommand):
13 help = 'Removes empty users (that don\'t have posts or tags'
14
15 @transaction.atomic
16 def handle(self, *args, **options):
17 old_registration_date = datetime.now().date() - timedelta(
18 OLD_USER_AGE_DAYS)
19
20 old_users = User.objects.annotate(tags_count=Count('fav_tags')).filter(
21 tags_count=0).filter(registration_time__lt=old_registration_date)
22 deleted_users = 0
23 for user in old_users:
24 if not Post.objects.filter(user=user).exists():
25 self.stdout.write('Deleting user %s' % user.user_id)
26 user.delete()
27 deleted_users += 1
28
29 self.stdout.write('Deleted %d users' % deleted_users) No newline at end of file
@@ -1,260 +1,259 b''
1 1 # coding=utf-8
2 2 import time
3 3 import logging
4 4 from django.core.paginator import Paginator
5 5
6 6 from django.test import TestCase
7 7 from django.test.client import Client
8 8 from django.core.urlresolvers import reverse, NoReverseMatch
9 9
10 10 from boards.models import Post, Tag, Thread
11 11 from boards import urls
12 12 from neboard import settings
13 13
14 14 PAGE_404 = 'boards/404.html'
15 15
16 16 TEST_TEXT = 'test text'
17 17
18 18 NEW_THREAD_PAGE = '/'
19 19 THREAD_PAGE_ONE = '/thread/1/'
20 20 THREAD_PAGE = '/thread/'
21 21 TAG_PAGE = '/tag/'
22 22 HTTP_CODE_REDIRECT = 302
23 23 HTTP_CODE_OK = 200
24 24 HTTP_CODE_NOT_FOUND = 404
25 25
26 26 logger = logging.getLogger(__name__)
27 27
28 28
29 29 class PostTests(TestCase):
30 30
31 31 def _create_post(self):
32 32 return Post.objects.create_post(title='title',
33 33 text='text')
34 34
35 35 def test_post_add(self):
36 36 """Test adding post"""
37 37
38 38 post = self._create_post()
39 39
40 40 self.assertIsNotNone(post, 'No post was created')
41 41
42 42 def test_delete_post(self):
43 43 """Test post deletion"""
44 44
45 45 post = self._create_post()
46 46 post_id = post.id
47 47
48 48 Post.objects.delete_post(post)
49 49
50 50 self.assertFalse(Post.objects.filter(id=post_id).exists())
51 51
52 52 def test_delete_thread(self):
53 53 """Test thread deletion"""
54 54
55 55 opening_post = self._create_post()
56 56 thread = opening_post.get_thread()
57 57 reply = Post.objects.create_post("", "", thread=thread)
58 58
59 59 thread.delete_with_posts()
60 60
61 61 self.assertFalse(Post.objects.filter(id=reply.id).exists())
62 62
63 63 def test_post_to_thread(self):
64 64 """Test adding post to a thread"""
65 65
66 66 op = self._create_post()
67 67 post = Post.objects.create_post("", "", thread=op.get_thread())
68 68
69 69 self.assertIsNotNone(post, 'Reply to thread wasn\'t created')
70 70 self.assertEqual(op.get_thread().last_edit_time, post.pub_time,
71 71 'Post\'s create time doesn\'t match thread last edit'
72 72 ' time')
73 73
74 74 def test_delete_posts_by_ip(self):
75 75 """Test deleting posts with the given ip"""
76 76
77 77 post = self._create_post()
78 78 post_id = post.id
79 79
80 80 Post.objects.delete_posts_by_ip('0.0.0.0')
81 81
82 82 self.assertFalse(Post.objects.filter(id=post_id).exists())
83 83
84 84 def test_get_thread(self):
85 85 """Test getting all posts of a thread"""
86 86
87 87 opening_post = self._create_post()
88 88
89 89 for i in range(0, 2):
90 90 Post.objects.create_post('title', 'text',
91 91 thread=opening_post.get_thread())
92 92
93 93 thread = opening_post.get_thread()
94 94
95 95 self.assertEqual(3, thread.replies.count())
96 96
97 97 def test_create_post_with_tag(self):
98 98 """Test adding tag to post"""
99 99
100 100 tag = Tag.objects.create(name='test_tag')
101 101 post = Post.objects.create_post(title='title', text='text', tags=[tag])
102 102
103 103 thread = post.get_thread()
104 104 self.assertIsNotNone(post, 'Post not created')
105 105 self.assertTrue(tag in thread.tags.all(), 'Tag not added to thread')
106 106 self.assertTrue(thread in tag.threads.all(), 'Thread not added to tag')
107 107
108 108 def test_thread_max_count(self):
109 109 """Test deletion of old posts when the max thread count is reached"""
110 110
111 111 for i in range(settings.MAX_THREAD_COUNT + 1):
112 112 self._create_post()
113 113
114 114 self.assertEqual(settings.MAX_THREAD_COUNT,
115 115 len(Thread.objects.filter(archived=False)))
116 116
117 117 def test_pages(self):
118 118 """Test that the thread list is properly split into pages"""
119 119
120 120 for i in range(settings.MAX_THREAD_COUNT):
121 121 self._create_post()
122 122
123 123 all_threads = Thread.objects.filter(archived=False)
124 124
125 125 paginator = Paginator(Thread.objects.filter(archived=False),
126 126 settings.THREADS_PER_PAGE)
127 127 posts_in_second_page = paginator.page(2).object_list
128 128 first_post = posts_in_second_page[0]
129 129
130 130 self.assertEqual(all_threads[settings.THREADS_PER_PAGE].id,
131 131 first_post.id)
132 132
133 133 def test_linked_tag(self):
134 134 """Test adding a linked tag"""
135 135
136 136 linked_tag = Tag.objects.create(name=u'tag1')
137 137 tag = Tag.objects.create(name=u'tag2', linked=linked_tag)
138 138
139 139 post = Post.objects.create_post("", "", tags=[tag])
140 140
141 141 self.assertTrue(linked_tag in post.get_thread().tags.all(),
142 142 'Linked tag was not added')
143 143
144 144
145 145 class PagesTest(TestCase):
146 146
147 147 def test_404(self):
148 148 """Test receiving error 404 when opening a non-existent page"""
149 149
150 150 tag_name = u'test_tag'
151 151 tag = Tag.objects.create(name=tag_name)
152 152 client = Client()
153 153
154 154 Post.objects.create_post('title', TEST_TEXT, tags=[tag])
155 155
156 156 existing_post_id = Post.objects.all()[0].id
157 157 response_existing = client.get(THREAD_PAGE + str(existing_post_id) +
158 158 '/')
159 159 self.assertEqual(HTTP_CODE_OK, response_existing.status_code,
160 160 u'Cannot open existing thread')
161 161
162 162 response_not_existing = client.get(THREAD_PAGE + str(
163 163 existing_post_id + 1) + '/')
164 self.assertEqual(PAGE_404,
165 response_not_existing.templates[0].name,
164 self.assertEqual(PAGE_404, response_not_existing.templates[0].name,
166 165 u'Not existing thread is opened')
167 166
168 167 response_existing = client.get(TAG_PAGE + tag_name + '/')
169 168 self.assertEqual(HTTP_CODE_OK,
170 169 response_existing.status_code,
171 170 u'Cannot open existing tag')
172 171
173 172 response_not_existing = client.get(TAG_PAGE + u'not_tag' + '/')
174 173 self.assertEqual(PAGE_404,
175 174 response_not_existing.templates[0].name,
176 175 u'Not existing tag is opened')
177 176
178 177 reply_id = Post.objects.create_post('', TEST_TEXT,
179 178 thread=Post.objects.all()[0]
180 .thread)
179 .get_thread())
181 180 response_not_existing = client.get(THREAD_PAGE + str(
182 181 reply_id) + '/')
183 182 self.assertEqual(PAGE_404,
184 183 response_not_existing.templates[0].name,
185 184 u'Reply is opened as a thread')
186 185
187 186
188 187 class FormTest(TestCase):
189 188 def test_post_validation(self):
190 189 # Disable captcha for the test
191 190 captcha_enabled = settings.ENABLE_CAPTCHA
192 191 settings.ENABLE_CAPTCHA = False
193 192
194 193 client = Client()
195 194
196 195 valid_tags = u'tag1 tag_2 тег_3'
197 196 invalid_tags = u'$%_356 ---'
198 197
199 198 response = client.post(NEW_THREAD_PAGE, {'title': 'test title',
200 199 'text': TEST_TEXT,
201 200 'tags': valid_tags})
202 201 self.assertEqual(response.status_code, HTTP_CODE_REDIRECT,
203 202 msg='Posting new message failed: got code ' +
204 203 str(response.status_code))
205 204
206 205 self.assertEqual(1, Post.objects.count(),
207 206 msg='No posts were created')
208 207
209 208 client.post(NEW_THREAD_PAGE, {'text': TEST_TEXT,
210 209 'tags': invalid_tags})
211 210 self.assertEqual(1, Post.objects.count(), msg='The validation passed '
212 211 'where it should fail')
213 212
214 213 # Change posting delay so we don't have to wait for 30 seconds or more
215 214 old_posting_delay = settings.POSTING_DELAY
216 215 # Wait fot the posting delay or we won't be able to post
217 216 settings.POSTING_DELAY = 1
218 217 time.sleep(settings.POSTING_DELAY + 1)
219 218 response = client.post(THREAD_PAGE_ONE, {'text': TEST_TEXT,
220 219 'tags': valid_tags})
221 220 self.assertEqual(HTTP_CODE_REDIRECT, response.status_code,
222 221 msg=u'Posting new message failed: got code ' +
223 222 str(response.status_code))
224 223 # Restore posting delay
225 224 settings.POSTING_DELAY = old_posting_delay
226 225
227 226 self.assertEqual(2, Post.objects.count(),
228 227 msg=u'No posts were created')
229 228
230 229 # Restore captcha setting
231 230 settings.ENABLE_CAPTCHA = captcha_enabled
232 231
233 232
234 233 class ViewTest(TestCase):
235 234
236 235 def test_all_views(self):
237 236 '''
238 237 Try opening all views defined in ulrs.py that don't need additional
239 238 parameters
240 239 '''
241 240
242 241 client = Client()
243 242 for url in urls.urlpatterns:
244 243 try:
245 244 view_name = url.name
246 245 logger.debug('Testing view %s' % view_name)
247 246
248 247 try:
249 248 response = client.get(reverse(view_name))
250 249
251 250 self.assertEqual(HTTP_CODE_OK, response.status_code,
252 251 '%s view not opened' % view_name)
253 252 except NoReverseMatch:
254 253 # This view just needs additional arguments
255 254 pass
256 255 except Exception, e:
257 256 self.fail('Got exception %s at %s view' % (e, view_name))
258 257 except AttributeError:
259 258 # This is normal, some views do not have names
260 259 pass
@@ -1,248 +1,245 b''
1 1 from datetime import datetime
2 2 import json
3 3 import logging
4 4 from django.db import transaction
5 5 from django.http import HttpResponse
6 6 from django.shortcuts import get_object_or_404, render
7 7 from django.template import RequestContext
8 8 from django.utils import timezone
9 9 from django.core import serializers
10 10
11 11 from boards.forms import PostForm, PlainErrorList
12 12 from boards.models import Post, Thread, Tag
13 13 from boards.utils import datetime_to_epoch
14 14 from boards.views.thread import ThreadView
15 15
16 16 __author__ = 'neko259'
17 17
18 18 PARAMETER_TRUNCATED = 'truncated'
19 19 PARAMETER_TAG = 'tag'
20 20 PARAMETER_OFFSET = 'offset'
21 21 PARAMETER_DIFF_TYPE = 'type'
22 22
23 23 DIFF_TYPE_HTML = 'html'
24 24 DIFF_TYPE_JSON = 'json'
25 25
26 26 STATUS_OK = 'ok'
27 27 STATUS_ERROR = 'error'
28 28
29 29 logger = logging.getLogger(__name__)
30 30
31 31
32 32 @transaction.atomic
33 33 def api_get_threaddiff(request, thread_id, last_update_time):
34 34 """
35 35 Gets posts that were changed or added since time
36 36 """
37 37
38 38 thread = get_object_or_404(Post, id=thread_id).get_thread()
39 39
40 logger.info('Getting thread #%s diff since %s' % (thread_id,
41 last_update_time))
42
43 40 filter_time = datetime.fromtimestamp(float(last_update_time) / 1000000,
44 41 timezone.get_current_timezone())
45 42
46 43 json_data = {
47 44 'added': [],
48 45 'updated': [],
49 46 'last_update': None,
50 47 }
51 48 added_posts = Post.objects.filter(thread_new=thread,
52 49 pub_time__gt=filter_time) \
53 50 .order_by('pub_time')
54 51 updated_posts = Post.objects.filter(thread_new=thread,
55 52 pub_time__lte=filter_time,
56 53 last_edit_time__gt=filter_time)
57 54
58 55 diff_type = DIFF_TYPE_HTML
59 56 if PARAMETER_DIFF_TYPE in request.GET:
60 57 diff_type = request.GET[PARAMETER_DIFF_TYPE]
61 58
62 59 for post in added_posts:
63 60 json_data['added'].append(_get_post_data(post.id, diff_type, request))
64 61 for post in updated_posts:
65 62 json_data['updated'].append(_get_post_data(post.id, diff_type, request))
66 63 json_data['last_update'] = datetime_to_epoch(thread.last_edit_time)
67 64
68 65 return HttpResponse(content=json.dumps(json_data))
69 66
70 67
71 68 def api_add_post(request, opening_post_id):
72 69 """
73 70 Adds a post and return the JSON response for it
74 71 """
75 72
76 73 opening_post = get_object_or_404(Post, id=opening_post_id)
77 74
78 75 logger.info('Adding post via api...')
79 76
80 77 status = STATUS_OK
81 78 errors = []
82 79
83 80 if request.method == 'POST':
84 81 form = PostForm(request.POST, request.FILES, error_class=PlainErrorList)
85 82 form.session = request.session
86 83
87 84 if form.need_to_ban:
88 85 # Ban user because he is suspected to be a bot
89 86 # _ban_current_user(request)
90 87 status = STATUS_ERROR
91 88 if form.is_valid():
92 89 post = ThreadView().new_post(request, form, opening_post,
93 90 html_response=False)
94 91 if not post:
95 92 status = STATUS_ERROR
96 93 else:
97 94 logger.info('Added post #%d via api.' % post.id)
98 95 else:
99 96 status = STATUS_ERROR
100 97 errors = form.as_json_errors()
101 98
102 99 response = {
103 100 'status': status,
104 101 'errors': errors,
105 102 }
106 103
107 104 return HttpResponse(content=json.dumps(response))
108 105
109 106
110 107 def get_post(request, post_id):
111 108 """
112 109 Gets the html of a post. Used for popups. Post can be truncated if used
113 110 in threads list with 'truncated' get parameter.
114 111 """
115 112
116 113 logger.info('Getting post #%s' % post_id)
117 114
118 115 post = get_object_or_404(Post, id=post_id)
119 116
120 117 context = RequestContext(request)
121 118 context['post'] = post
122 119 if PARAMETER_TRUNCATED in request.GET:
123 120 context[PARAMETER_TRUNCATED] = True
124 121
125 122 return render(request, 'boards/api_post.html', context)
126 123
127 124
128 125 # TODO Test this
129 126 def api_get_threads(request, count):
130 127 """
131 128 Gets the JSON thread opening posts list.
132 129 Parameters that can be used for filtering:
133 130 tag, offset (from which thread to get results)
134 131 """
135 132
136 133 if PARAMETER_TAG in request.GET:
137 134 tag_name = request.GET[PARAMETER_TAG]
138 135 if tag_name is not None:
139 136 tag = get_object_or_404(Tag, name=tag_name)
140 137 threads = tag.threads.filter(archived=False)
141 138 else:
142 139 threads = Thread.objects.filter(archived=False)
143 140
144 141 if PARAMETER_OFFSET in request.GET:
145 142 offset = request.GET[PARAMETER_OFFSET]
146 143 offset = int(offset) if offset is not None else 0
147 144 else:
148 145 offset = 0
149 146
150 147 threads = threads.order_by('-bump_time')
151 148 threads = threads[offset:offset + int(count)]
152 149
153 150 opening_posts = []
154 151 for thread in threads:
155 152 opening_post = thread.get_opening_post()
156 153
157 154 # TODO Add tags, replies and images count
158 155 opening_posts.append(_get_post_data(opening_post.id,
159 156 include_last_update=True))
160 157
161 158 return HttpResponse(content=json.dumps(opening_posts))
162 159
163 160
164 161 # TODO Test this
165 162 def api_get_tags(request):
166 163 """
167 164 Gets all tags or user tags.
168 165 """
169 166
170 167 # TODO Get favorite tags for the given user ID
171 168
172 169 tags = Tag.objects.get_not_empty_tags()
173 170 tag_names = []
174 171 for tag in tags:
175 172 tag_names.append(tag.name)
176 173
177 174 return HttpResponse(content=json.dumps(tag_names))
178 175
179 176
180 177 # TODO The result can be cached by the thread last update time
181 178 # TODO Test this
182 179 def api_get_thread_posts(request, opening_post_id):
183 180 """
184 181 Gets the JSON array of thread posts
185 182 """
186 183
187 184 opening_post = get_object_or_404(Post, id=opening_post_id)
188 185 thread = opening_post.get_thread()
189 186 posts = thread.get_replies()
190 187
191 188 json_data = {
192 189 'posts': [],
193 190 'last_update': None,
194 191 }
195 192 json_post_list = []
196 193
197 194 for post in posts:
198 195 json_post_list.append(_get_post_data(post.id))
199 196 json_data['last_update'] = datetime_to_epoch(thread.last_edit_time)
200 197 json_data['posts'] = json_post_list
201 198
202 199 return HttpResponse(content=json.dumps(json_data))
203 200
204 201
205 202 def api_get_post(request, post_id):
206 203 """
207 204 Gets the JSON of a post. This can be
208 205 used as and API for external clients.
209 206 """
210 207
211 208 post = get_object_or_404(Post, id=post_id)
212 209
213 210 json = serializers.serialize("json", [post], fields=(
214 211 "pub_time", "_text_rendered", "title", "text", "image",
215 212 "image_width", "image_height", "replies", "tags"
216 213 ))
217 214
218 215 return HttpResponse(content=json)
219 216
220 217
221 218 def get_tag_popularity(request, tag_name):
222 219 tag = get_object_or_404(Tag, name=tag_name)
223 220
224 221 json_data = []
225 222 json_data['popularity'] = tag.get_popularity()
226 223
227 224 return HttpResponse(content=json.dumps(json_data))
228 225
229 226
230 227 # TODO Add pub time and replies
231 228 def _get_post_data(post_id, format_type=DIFF_TYPE_JSON, request=None,
232 229 include_last_update=False):
233 230 if format_type == DIFF_TYPE_HTML:
234 231 return get_post(request, post_id).content.strip()
235 232 elif format_type == DIFF_TYPE_JSON:
236 233 post = get_object_or_404(Post, id=post_id)
237 234 post_json = {
238 235 'id': post.id,
239 236 'title': post.title,
240 237 'text': post.text.rendered,
241 238 }
242 239 if post.image:
243 240 post_json['image'] = post.image.url
244 241 post_json['image_preview'] = post.image.url_200x150
245 242 if include_last_update:
246 243 post_json['bump_time'] = datetime_to_epoch(
247 244 post.thread_new.bump_time)
248 245 return post_json
@@ -1,127 +1,107 b''
1 1 from datetime import datetime, timedelta
2 2 import hashlib
3 3 from django.db import transaction
4 4 from django.db.models import Count
5 5 from django.template import RequestContext
6 6 from django.utils import timezone
7 7 from django.views.generic import View
8 8 from boards import utils
9 9 from boards.models import User, Post
10 10 from boards.models.post import SETTING_MODERATE
11 11 from boards.models.user import RANK_USER, Ban
12 12 import neboard
13 13
14 14 BAN_REASON_SPAM = 'Autoban: spam bot'
15 15
16 OLD_USER_AGE_DAYS = 90
17
18 16 PARAMETER_FORM = 'form'
19 17
20 18
21 19 class BaseBoardView(View):
22 20
23 21 def get_context_data(self, **kwargs):
24 22 request = kwargs['request']
25 23 context = self._default_context(request)
26 24
27 25 context['version'] = neboard.settings.VERSION
28 26 context['site_name'] = neboard.settings.SITE_NAME
29 27
30 28 return context
31 29
32 30 def _default_context(self, request):
33 31 """Create context with default values that are used in most views"""
34 32
35 33 context = RequestContext(request)
36 34
37 35 user = self._get_user(request)
38 36 context['user'] = user
39 37 context['tags'] = user.fav_tags.all()
40 38 context['posts_per_day'] = float(Post.objects.get_posts_per_day())
41 39
42 40 theme = self._get_theme(request, user)
43 41 context['theme'] = theme
44 42 context['theme_css'] = 'css/' + theme + '/base_page.css'
45 43
46 44 # This shows the moderator panel
47 45 moderate = user.get_setting(SETTING_MODERATE)
48 46 if moderate == 'True':
49 47 context['moderator'] = user.is_moderator()
50 48 else:
51 49 context['moderator'] = False
52 50
53 51 return context
54 52
55 53 def _get_user(self, request):
56 54 """
57 55 Get current user from the session. If the user does not exist, create
58 56 a new one.
59 57 """
60 58
61 59 session = request.session
62 60 if not 'user_id' in session:
63 61 request.session.save()
64 62
65 63 md5 = hashlib.md5()
66 64 md5.update(session.session_key)
67 65 new_id = md5.hexdigest()
68 66
69 67 while User.objects.filter(user_id=new_id).exists():
70 68 md5.update(str(timezone.now()))
71 69 new_id = md5.hexdigest()
72 70
73 71 time_now = timezone.now()
74 72 user = User.objects.create(user_id=new_id, rank=RANK_USER,
75 73 registration_time=time_now)
76 74
77 # TODO Move this to manage.py commands
78 #self._delete_old_users()
79
80 75 session['user_id'] = user.id
81 76 else:
82 77 user = User.objects.select_related('fav_tags').get(
83 78 id=session['user_id'])
84 79
85 80 return user
86 81
87 82 def _get_theme(self, request, user=None):
88 83 """
89 84 Get user's CSS theme
90 85 """
91 86
92 87 if not user:
93 88 user = self._get_user(request)
94 89 theme = user.get_setting('theme')
95 90 if not theme:
96 91 theme = neboard.settings.DEFAULT_THEME
97 92
98 93 return theme
99 94
100 def _delete_old_users(self):
101 """
102 Delete users with no favorite tags and posted messages. These can be spam
103 bots or just old user accounts
104 """
105
106 old_registration_date = datetime.now().date() - timedelta(
107 OLD_USER_AGE_DAYS)
108
109 for user in User.objects.annotate(tags_count=Count('fav_tags')).filter(
110 tags_count=0).filter(
111 registration_time__lt=old_registration_date):
112 if not Post.objects.filter(user=user).exists():
113 user.delete()
114
115 95 @transaction.atomic
116 96 def _ban_current_user(self, request):
117 97 """
118 98 Add current user to the IP ban list
119 99 """
120 100
121 101 ip = utils.get_client_ip(request)
122 102 ban, created = Ban.objects.get_or_create(ip=ip)
123 103 if created:
124 104 ban.can_read = False
125 105 ban.reason = BAN_REASON_SPAM
126 106 ban.save()
127 107
@@ -1,129 +1,132 b''
1 1 import string
2 2 from django.core.urlresolvers import reverse
3 3 from django.db import transaction
4 4 from django.http import Http404
5 5 from django.shortcuts import get_object_or_404, render, redirect
6 6 from django.views.generic.edit import FormMixin
7 7 from boards import utils
8 8 from boards.forms import PostForm, PlainErrorList
9 9 from boards.models import Post, Ban, Tag
10 10 from boards.views.banned import BannedView
11 11 from boards.views.base import BaseBoardView, PARAMETER_FORM
12 12 from boards.views.posting_mixin import PostMixin
13 13 import neboard
14 14
15 15 MODE_GALLERY = 'gallery'
16 16 MODE_NORMAL = 'normal'
17 17
18 18 PARAMETER_MAX_REPLIES = 'max_replies'
19 19 PARAMETER_THREAD = 'thread'
20 20 PARAMETER_BUMPABLE = 'bumpable'
21 21
22 22
23 23 class ThreadView(BaseBoardView, PostMixin, FormMixin):
24 24
25 25 def get(self, request, post_id, mode=MODE_NORMAL, form=None):
26 try:
26 27 opening_post = Post.objects.filter(id=post_id).only('thread_new')[0]
28 except IndexError:
29 raise Http404
27 30
28 31 # If this is not OP, don't show it as it is
29 32 if not opening_post or not opening_post.is_opening():
30 33 raise Http404
31 34
32 35 if not form:
33 36 form = PostForm(error_class=PlainErrorList)
34 37
35 38 thread_to_show = opening_post.get_thread()
36 39
37 40 context = self.get_context_data(request=request)
38 41
39 42 context[PARAMETER_FORM] = form
40 43 context["last_update"] = utils.datetime_to_epoch(
41 44 thread_to_show.last_edit_time)
42 45 context[PARAMETER_THREAD] = thread_to_show
43 46 context[PARAMETER_MAX_REPLIES] = neboard.settings.MAX_POSTS_PER_THREAD
44 47
45 48 if MODE_NORMAL == mode:
46 49 context[PARAMETER_BUMPABLE] = thread_to_show.can_bump()
47 50 if context[PARAMETER_BUMPABLE]:
48 51 context['posts_left'] = neboard.settings.MAX_POSTS_PER_THREAD \
49 52 - thread_to_show.get_reply_count()
50 53 context['bumplimit_progress'] = str(
51 54 float(context['posts_left']) /
52 55 neboard.settings.MAX_POSTS_PER_THREAD * 100)
53 56
54 57 context['opening_post'] = opening_post
55 58
56 59 document = 'boards/thread.html'
57 60 elif MODE_GALLERY == mode:
58 61 posts = thread_to_show.get_replies(view_fields_only=True)
59 62 context['posts'] = posts.filter(image_width__gt=0)
60 63
61 64 document = 'boards/thread_gallery.html'
62 65 else:
63 66 raise Http404
64 67
65 68 return render(request, document, context)
66 69
67 70 def post(self, request, post_id, mode=MODE_NORMAL):
68 71 opening_post = get_object_or_404(Post, id=post_id)
69 72
70 73 # If this is not OP, don't show it as it is
71 74 if not opening_post.is_opening():
72 75 raise Http404
73 76
74 77 if not opening_post.get_thread().archived:
75 78 form = PostForm(request.POST, request.FILES,
76 79 error_class=PlainErrorList)
77 80 form.session = request.session
78 81
79 82 if form.is_valid():
80 83 return self.new_post(request, form, opening_post)
81 84 if form.need_to_ban:
82 85 # Ban user because he is suspected to be a bot
83 86 self._ban_current_user(request)
84 87
85 88 return self.get(request, post_id, mode, form)
86 89
87 90 @transaction.atomic
88 91 def new_post(self, request, form, opening_post=None, html_response=True):
89 92 """Add a new post (in thread or as a reply)."""
90 93
91 94 ip = utils.get_client_ip(request)
92 95 is_banned = Ban.objects.filter(ip=ip).exists()
93 96
94 97 if is_banned:
95 98 if html_response:
96 99 return redirect(BannedView().as_view())
97 100 else:
98 101 return None
99 102
100 103 data = form.cleaned_data
101 104
102 105 title = data['title']
103 106 text = data['text']
104 107
105 108 text = self._remove_invalid_links(text)
106 109
107 110 if 'image' in data.keys():
108 111 image = data['image']
109 112 else:
110 113 image = None
111 114
112 115 tags = []
113 116
114 117 post_thread = opening_post.get_thread()
115 118
116 119 post = Post.objects.create_post(title=title, text=text, ip=ip,
117 120 thread=post_thread, image=image,
118 121 tags=tags,
119 122 user=self._get_user(request))
120 123
121 124 thread_to_show = (opening_post.id if opening_post else post.id)
122 125
123 126 if html_response:
124 127 if opening_post:
125 128 return redirect(reverse(
126 129 'thread',
127 130 kwargs={'post_id': thread_to_show}) + '#' + str(post.id))
128 131 else:
129 132 return post
General Comments 0
You need to be logged in to leave comments. Login now