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