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