##// END OF EJS Templates
Fixed links to not existing messages
neko259 -
r388:2d312aa9 default
parent child Browse files
Show More
@@ -1,560 +1,560 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 80 @transaction.commit_on_success
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 311 @transaction.commit_on_success
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 328 @transaction.commit_on_success
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 362 @transaction.commit_on_success
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 375 @transaction.commit_on_success
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 411 def api_get_threaddiff(request, thread_id, last_update_time):
412 412 """Get posts that were changed or added since time"""
413 413
414 414 thread = get_object_or_404(Post, id=thread_id)
415 415
416 416 filter_time = datetime.fromtimestamp(float(last_update_time) / 1000000,
417 417 timezone.get_current_timezone())
418 418
419 419 json_data = {
420 420 'added': [],
421 421 'updated': [],
422 422 'last_update': None,
423 423 }
424 424 added_posts = Post.objects.filter(thread=thread,
425 425 pub_time__gt=filter_time)\
426 426 .order_by('pub_time')
427 427 updated_posts = Post.objects.filter(thread=thread,
428 428 pub_time__lte=filter_time,
429 429 last_edit_time__gt=filter_time)
430 430 for post in added_posts:
431 431 json_data['added'].append(get_post(request, post.id).content.strip())
432 432 for post in updated_posts:
433 433 json_data['updated'].append(get_post(request, post.id).content.strip())
434 434 json_data['last_update'] = _datetime_to_epoch(thread.last_edit_time)
435 435
436 436 return HttpResponse(content=json.dumps(json_data))
437 437
438 438
439 439 def get_post(request, post_id):
440 440 """Get the html of a post. Used for popups."""
441 441
442 442 post = get_object_or_404(Post, id=post_id)
443 443 thread = post.thread
444 444 if not thread:
445 445 thread = post
446 446
447 447 context = RequestContext(request)
448 448 context["post"] = post
449 449 context["can_bump"] = thread.can_bump()
450 450 if "truncated" in request.GET:
451 451 context["truncated"] = True
452 452
453 453 return render(request, 'boards/post.html', context)
454 454
455 455
456 456 def _get_theme(request, user=None):
457 457 """Get user's CSS theme"""
458 458
459 459 if not user:
460 460 user = _get_user(request)
461 461 theme = user.get_setting('theme')
462 462 if not theme:
463 463 theme = neboard.settings.DEFAULT_THEME
464 464
465 465 return theme
466 466
467 467
468 468 def _init_default_context(request):
469 469 """Create context with default values that are used in most views"""
470 470
471 471 context = RequestContext(request)
472 472
473 473 user = _get_user(request)
474 474 context['user'] = user
475 475 context['tags'] = user.get_sorted_fav_tags()
476 476
477 477 theme = _get_theme(request, user)
478 478 context['theme'] = theme
479 479 context['theme_css'] = 'css/' + theme + '/base_page.css'
480 480
481 481 # This shows the moderator panel
482 482 moderate = user.get_setting(SETTING_MODERATE)
483 483 if moderate == 'True':
484 484 context['moderator'] = user.is_moderator()
485 485 else:
486 486 context['moderator'] = False
487 487
488 488 return context
489 489
490 490
491 491 def _get_user(request):
492 492 """
493 493 Get current user from the session. If the user does not exist, create
494 494 a new one.
495 495 """
496 496
497 497 session = request.session
498 498 if not 'user_id' in session:
499 499 request.session.save()
500 500
501 501 md5 = hashlib.md5()
502 502 md5.update(session.session_key)
503 503 new_id = md5.hexdigest()
504 504
505 505 time_now = timezone.now()
506 506 user = User.objects.create(user_id=new_id, rank=RANK_USER,
507 507 registration_time=time_now)
508 508
509 509 session['user_id'] = user.id
510 510 else:
511 511 user = User.objects.get(id=session['user_id'])
512 512
513 513 return user
514 514
515 515
516 516 def _redirect_to_next(request):
517 517 """
518 518 If a 'next' parameter was specified, redirect to the next page. This is
519 519 used when the user is required to return to some page after the current
520 520 view has finished its work.
521 521 """
522 522
523 523 if 'next' in request.GET:
524 524 next_page = request.GET['next']
525 525 return HttpResponseRedirect(next_page)
526 526 else:
527 527 return redirect(index)
528 528
529 529
530 530 @transaction.commit_on_success
531 531 def _ban_current_user(request):
532 532 """Add current user to the IP ban list"""
533 533
534 534 ip = utils.get_client_ip(request)
535 535 ban, created = Ban.objects.get_or_create(ip=ip)
536 536 if created:
537 537 ban.can_read = False
538 538 ban.reason = BAN_REASON_SPAM
539 539 ban.save()
540 540
541 541
542 542 def _remove_invalid_links(text):
543 543 """
544 544 Replace invalid links in posts so that they won't be parsed.
545 545 Invalid links are links to non-existent posts
546 546 """
547 547
548 548 for reply_number in re.finditer(REGEX_REPLY, text):
549 549 post_id = reply_number.group(1)
550 550 post = Post.objects.filter(id=post_id)
551 551 if not post.exists():
552 text = string.replace(text, '>>' + id, id)
552 text = string.replace(text, '>>' + post_id, post_id)
553 553
554 554 return text
555 555
556 556
557 557 def _datetime_to_epoch(datetime):
558 558 return int(time.mktime(timezone.localtime(
559 559 datetime,timezone.get_current_timezone()).timetuple())
560 560 * 1000000 + datetime.microsecond)
General Comments 0
You need to be logged in to leave comments. Login now