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