##// END OF EJS Templates
Using django paginator instead of manual pagination
neko259 -
r493:1e58394d 1.6-dev
parent child Browse files
Show More
@@ -1,393 +1,371 b''
1 from datetime import datetime, timedelta
1 from datetime import datetime, timedelta
2 from datetime import time as dtime
2 from datetime import time as dtime
3 import os
3 import os
4 from random import random
4 from random import random
5 import time
5 import time
6 import math
6 import math
7 import re
7 import re
8 from django.core.cache import cache
8 from django.core.cache import cache
9 from django.core.paginator import Paginator
9
10
10 from django.db import models
11 from django.db import models
11 from django.http import Http404
12 from django.http import Http404
12 from django.utils import timezone
13 from django.utils import timezone
13 from markupfield.fields import MarkupField
14 from markupfield.fields import MarkupField
14
15
15 from neboard import settings
16 from neboard import settings
16 from boards import thumbs
17 from boards import thumbs
17
18
18 MAX_TITLE_LENGTH = 50
19 MAX_TITLE_LENGTH = 50
19
20
20 APP_LABEL_BOARDS = 'boards'
21 APP_LABEL_BOARDS = 'boards'
21
22
22 CACHE_KEY_PPD = 'ppd'
23 CACHE_KEY_PPD = 'ppd'
23
24
24 POSTS_PER_DAY_RANGE = range(7)
25 POSTS_PER_DAY_RANGE = range(7)
25
26
26 BAN_REASON_AUTO = 'Auto'
27 BAN_REASON_AUTO = 'Auto'
27
28
28 IMAGE_THUMB_SIZE = (200, 150)
29 IMAGE_THUMB_SIZE = (200, 150)
29
30
30 TITLE_MAX_LENGTH = 50
31 TITLE_MAX_LENGTH = 50
31
32
32 DEFAULT_MARKUP_TYPE = 'markdown'
33 DEFAULT_MARKUP_TYPE = 'markdown'
33
34
34 NO_PARENT = -1
35 NO_PARENT = -1
35 NO_IP = '0.0.0.0'
36 NO_IP = '0.0.0.0'
36 UNKNOWN_UA = ''
37 UNKNOWN_UA = ''
37 ALL_PAGES = -1
38 ALL_PAGES = -1
38 IMAGES_DIRECTORY = 'images/'
39 IMAGES_DIRECTORY = 'images/'
39 FILE_EXTENSION_DELIMITER = '.'
40 FILE_EXTENSION_DELIMITER = '.'
40
41
41 SETTING_MODERATE = "moderate"
42 SETTING_MODERATE = "moderate"
42
43
43 REGEX_REPLY = re.compile('>>(\d+)')
44 REGEX_REPLY = re.compile('>>(\d+)')
44
45
45
46
46 class PostManager(models.Manager):
47 class PostManager(models.Manager):
47
48
48 def create_post(self, title, text, image=None, thread=None,
49 def create_post(self, title, text, image=None, thread=None,
49 ip=NO_IP, tags=None, user=None):
50 ip=NO_IP, tags=None, user=None):
50 """
51 """
51 Create new post
52 Create new post
52 """
53 """
53
54
54 posting_time = timezone.now()
55 posting_time = timezone.now()
55 if not thread:
56 if not thread:
56 thread = Thread.objects.create(bump_time=posting_time,
57 thread = Thread.objects.create(bump_time=posting_time,
57 last_edit_time=posting_time)
58 last_edit_time=posting_time)
58 else:
59 else:
59 thread.bump()
60 thread.bump()
60 thread.last_edit_time = posting_time
61 thread.last_edit_time = posting_time
61 thread.save()
62 thread.save()
62
63
63 post = self.create(title=title,
64 post = self.create(title=title,
64 text=text,
65 text=text,
65 pub_time=posting_time,
66 pub_time=posting_time,
66 thread_new=thread,
67 thread_new=thread,
67 image=image,
68 image=image,
68 poster_ip=ip,
69 poster_ip=ip,
69 poster_user_agent=UNKNOWN_UA, # TODO Get UA at
70 poster_user_agent=UNKNOWN_UA, # TODO Get UA at
70 # last!
71 # last!
71 last_edit_time=posting_time,
72 last_edit_time=posting_time,
72 user=user)
73 user=user)
73
74
74 thread.replies.add(post)
75 thread.replies.add(post)
75 if tags:
76 if tags:
76 linked_tags = []
77 linked_tags = []
77 for tag in tags:
78 for tag in tags:
78 tag_linked_tags = tag.get_linked_tags()
79 tag_linked_tags = tag.get_linked_tags()
79 if len(tag_linked_tags) > 0:
80 if len(tag_linked_tags) > 0:
80 linked_tags.extend(tag_linked_tags)
81 linked_tags.extend(tag_linked_tags)
81
82
82 tags.extend(linked_tags)
83 tags.extend(linked_tags)
83 map(thread.add_tag, tags)
84 map(thread.add_tag, tags)
84
85
85 self._delete_old_threads()
86 self._delete_old_threads()
86 self.connect_replies(post)
87 self.connect_replies(post)
87
88
88 return post
89 return post
89
90
90 def delete_post(self, post):
91 def delete_post(self, post):
91 """
92 """
92 Delete post and update or delete its thread
93 Delete post and update or delete its thread
93 """
94 """
94
95
95 thread = post.thread_new
96 thread = post.thread_new
96
97
97 if thread.get_opening_post() == self:
98 if thread.get_opening_post() == self:
98 thread.replies.delete()
99 thread.replies.delete()
99
100
100 thread.delete()
101 thread.delete()
101 else:
102 else:
102 thread.last_edit_time = timezone.now()
103 thread.last_edit_time = timezone.now()
103 thread.save()
104 thread.save()
104
105
105 post.delete()
106 post.delete()
106
107
107 def delete_posts_by_ip(self, ip):
108 def delete_posts_by_ip(self, ip):
108 """
109 """
109 Delete all posts of the author with same IP
110 Delete all posts of the author with same IP
110 """
111 """
111
112
112 posts = self.filter(poster_ip=ip)
113 posts = self.filter(poster_ip=ip)
113 map(self.delete_post, posts)
114 map(self.delete_post, posts)
114
115
115 # TODO Move this method to thread manager
116 # TODO Move this method to thread manager
116 def get_threads(self, tag=None, page=ALL_PAGES,
117 def get_threads(self, tag=None, page=ALL_PAGES,
117 order_by='-bump_time', archived=False):
118 order_by='-bump_time', archived=False):
118 if tag:
119 if tag:
119 threads = tag.threads
120 threads = tag.threads
120
121
121 if not threads.exists():
122 if not threads.exists():
122 raise Http404
123 raise Http404
123 else:
124 else:
124 threads = Thread.objects.all()
125 threads = Thread.objects.all()
125
126
126 threads = threads.filter(archived=archived).order_by(order_by)
127 threads = threads.filter(archived=archived).order_by(order_by)
127
128
128 if page != ALL_PAGES:
129 if page != ALL_PAGES:
129 thread_count = threads.count()
130 threads = Paginator(threads, settings.THREADS_PER_PAGE).page(
130
131 page).object_list
131 if page < self._get_page_count(thread_count):
132 start_thread = page * settings.THREADS_PER_PAGE
133 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
134 thread_count)
135 threads = threads[start_thread:end_thread]
136
132
137 return threads
133 return threads
138
134
139 # TODO Move this method to thread manager
135 # TODO Move this method to thread manager
140 def get_thread_page_count(self, tag=None, archived=False):
141 if tag:
142 threads = Thread.objects.filter(tags=tag)
143 else:
144 threads = Thread.objects.all()
145
146 threads = threads.filter(archived=archived)
147
148 return self._get_page_count(threads.count())
149
150 # TODO Move this method to thread manager
151 def _delete_old_threads(self):
136 def _delete_old_threads(self):
152 """
137 """
153 Preserves maximum thread count. If there are too many threads,
138 Preserves maximum thread count. If there are too many threads,
154 archive the old ones.
139 archive the old ones.
155 """
140 """
156
141
157 threads = self.get_threads()
142 threads = self.get_threads()
158 thread_count = threads.count()
143 thread_count = threads.count()
159
144
160 if thread_count > settings.MAX_THREAD_COUNT:
145 if thread_count > settings.MAX_THREAD_COUNT:
161 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
146 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
162 old_threads = threads[thread_count - num_threads_to_delete:]
147 old_threads = threads[thread_count - num_threads_to_delete:]
163
148
164 for thread in old_threads:
149 for thread in old_threads:
165 thread.archived = True
150 thread.archived = True
166 thread.last_edit_time = timezone.now()
151 thread.last_edit_time = timezone.now()
167 thread.save()
152 thread.save()
168
153
169 def connect_replies(self, post):
154 def connect_replies(self, post):
170 """
155 """
171 Connect replies to a post to show them as a reflink map
156 Connect replies to a post to show them as a reflink map
172 """
157 """
173
158
174 for reply_number in re.finditer(REGEX_REPLY, post.text.raw):
159 for reply_number in re.finditer(REGEX_REPLY, post.text.raw):
175 post_id = reply_number.group(1)
160 post_id = reply_number.group(1)
176 ref_post = self.filter(id=post_id)
161 ref_post = self.filter(id=post_id)
177 if ref_post.count() > 0:
162 if ref_post.count() > 0:
178 referenced_post = ref_post[0]
163 referenced_post = ref_post[0]
179 referenced_post.referenced_posts.add(post)
164 referenced_post.referenced_posts.add(post)
180 referenced_post.last_edit_time = post.pub_time
165 referenced_post.last_edit_time = post.pub_time
181 referenced_post.save()
166 referenced_post.save()
182
167
183 def _get_page_count(self, thread_count):
184 """
185 Get number of pages that will be needed for all threads
186 """
187
188 return int(math.ceil(thread_count / float(settings.THREADS_PER_PAGE)))
189
190 def get_posts_per_day(self):
168 def get_posts_per_day(self):
191 """
169 """
192 Get average count of posts per day for the last 7 days
170 Get average count of posts per day for the last 7 days
193 """
171 """
194
172
195 today = datetime.now().date()
173 today = datetime.now().date()
196 ppd = cache.get(CACHE_KEY_PPD + str(today))
174 ppd = cache.get(CACHE_KEY_PPD + str(today))
197 if ppd:
175 if ppd:
198 return ppd
176 return ppd
199
177
200 posts_per_days = []
178 posts_per_days = []
201 for i in POSTS_PER_DAY_RANGE:
179 for i in POSTS_PER_DAY_RANGE:
202 day_end = today - timedelta(i + 1)
180 day_end = today - timedelta(i + 1)
203 day_start = today - timedelta(i + 2)
181 day_start = today - timedelta(i + 2)
204
182
205 day_time_start = timezone.make_aware(datetime.combine(day_start,
183 day_time_start = timezone.make_aware(datetime.combine(day_start,
206 dtime()), timezone.get_current_timezone())
184 dtime()), timezone.get_current_timezone())
207 day_time_end = timezone.make_aware(datetime.combine(day_end,
185 day_time_end = timezone.make_aware(datetime.combine(day_end,
208 dtime()), timezone.get_current_timezone())
186 dtime()), timezone.get_current_timezone())
209
187
210 posts_per_days.append(float(self.filter(
188 posts_per_days.append(float(self.filter(
211 pub_time__lte=day_time_end,
189 pub_time__lte=day_time_end,
212 pub_time__gte=day_time_start).count()))
190 pub_time__gte=day_time_start).count()))
213
191
214 ppd = (sum(posts_per_day for posts_per_day in posts_per_days) /
192 ppd = (sum(posts_per_day for posts_per_day in posts_per_days) /
215 len(posts_per_days))
193 len(posts_per_days))
216 cache.set(CACHE_KEY_PPD, ppd)
194 cache.set(CACHE_KEY_PPD, ppd)
217 return ppd
195 return ppd
218
196
219
197
220 class Post(models.Model):
198 class Post(models.Model):
221 """A post is a message."""
199 """A post is a message."""
222
200
223 objects = PostManager()
201 objects = PostManager()
224
202
225 class Meta:
203 class Meta:
226 app_label = APP_LABEL_BOARDS
204 app_label = APP_LABEL_BOARDS
227
205
228 # TODO Save original file name to some field
206 # TODO Save original file name to some field
229 def _update_image_filename(self, filename):
207 def _update_image_filename(self, filename):
230 """Get unique image filename"""
208 """Get unique image filename"""
231
209
232 path = IMAGES_DIRECTORY
210 path = IMAGES_DIRECTORY
233 new_name = str(int(time.mktime(time.gmtime())))
211 new_name = str(int(time.mktime(time.gmtime())))
234 new_name += str(int(random() * 1000))
212 new_name += str(int(random() * 1000))
235 new_name += FILE_EXTENSION_DELIMITER
213 new_name += FILE_EXTENSION_DELIMITER
236 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
214 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
237
215
238 return os.path.join(path, new_name)
216 return os.path.join(path, new_name)
239
217
240 title = models.CharField(max_length=TITLE_MAX_LENGTH)
218 title = models.CharField(max_length=TITLE_MAX_LENGTH)
241 pub_time = models.DateTimeField()
219 pub_time = models.DateTimeField()
242 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
220 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
243 escape_html=False)
221 escape_html=False)
244
222
245 image_width = models.IntegerField(default=0)
223 image_width = models.IntegerField(default=0)
246 image_height = models.IntegerField(default=0)
224 image_height = models.IntegerField(default=0)
247
225
248 image_pre_width = models.IntegerField(default=0)
226 image_pre_width = models.IntegerField(default=0)
249 image_pre_height = models.IntegerField(default=0)
227 image_pre_height = models.IntegerField(default=0)
250
228
251 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
229 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
252 blank=True, sizes=(IMAGE_THUMB_SIZE,),
230 blank=True, sizes=(IMAGE_THUMB_SIZE,),
253 width_field='image_width',
231 width_field='image_width',
254 height_field='image_height',
232 height_field='image_height',
255 preview_width_field='image_pre_width',
233 preview_width_field='image_pre_width',
256 preview_height_field='image_pre_height')
234 preview_height_field='image_pre_height')
257
235
258 poster_ip = models.GenericIPAddressField()
236 poster_ip = models.GenericIPAddressField()
259 poster_user_agent = models.TextField()
237 poster_user_agent = models.TextField()
260
238
261 thread = models.ForeignKey('Post', null=True, default=None)
239 thread = models.ForeignKey('Post', null=True, default=None)
262 thread_new = models.ForeignKey('Thread', null=True, default=None)
240 thread_new = models.ForeignKey('Thread', null=True, default=None)
263 last_edit_time = models.DateTimeField()
241 last_edit_time = models.DateTimeField()
264 user = models.ForeignKey('User', null=True, default=None)
242 user = models.ForeignKey('User', null=True, default=None)
265
243
266 referenced_posts = models.ManyToManyField('Post', symmetrical=False,
244 referenced_posts = models.ManyToManyField('Post', symmetrical=False,
267 null=True,
245 null=True,
268 blank=True, related_name='rfp+')
246 blank=True, related_name='rfp+')
269
247
270 def __unicode__(self):
248 def __unicode__(self):
271 return '#' + str(self.id) + ' ' + self.title + ' (' + \
249 return '#' + str(self.id) + ' ' + self.title + ' (' + \
272 self.text.raw[:50] + ')'
250 self.text.raw[:50] + ')'
273
251
274 def get_title(self):
252 def get_title(self):
275 title = self.title
253 title = self.title
276 if len(title) == 0:
254 if len(title) == 0:
277 title = self.text.rendered[:MAX_TITLE_LENGTH]
255 title = self.text.rendered[:MAX_TITLE_LENGTH]
278
256
279 return title
257 return title
280
258
281 def get_sorted_referenced_posts(self):
259 def get_sorted_referenced_posts(self):
282 return self.referenced_posts.order_by('id')
260 return self.referenced_posts.order_by('id')
283
261
284 def is_referenced(self):
262 def is_referenced(self):
285 return self.referenced_posts.all().exists()
263 return self.referenced_posts.all().exists()
286
264
287 def is_opening(self):
265 def is_opening(self):
288 return self.thread_new.get_replies()[0] == self
266 return self.thread_new.get_replies()[0] == self
289
267
290
268
291 class Thread(models.Model):
269 class Thread(models.Model):
292
270
293 class Meta:
271 class Meta:
294 app_label = APP_LABEL_BOARDS
272 app_label = APP_LABEL_BOARDS
295
273
296 tags = models.ManyToManyField('Tag')
274 tags = models.ManyToManyField('Tag')
297 bump_time = models.DateTimeField()
275 bump_time = models.DateTimeField()
298 last_edit_time = models.DateTimeField()
276 last_edit_time = models.DateTimeField()
299 replies = models.ManyToManyField('Post', symmetrical=False, null=True,
277 replies = models.ManyToManyField('Post', symmetrical=False, null=True,
300 blank=True, related_name='tre+')
278 blank=True, related_name='tre+')
301 archived = models.BooleanField(default=False)
279 archived = models.BooleanField(default=False)
302
280
303 def get_tags(self):
281 def get_tags(self):
304 """
282 """
305 Get a sorted tag list
283 Get a sorted tag list
306 """
284 """
307
285
308 return self.tags.order_by('name')
286 return self.tags.order_by('name')
309
287
310 def bump(self):
288 def bump(self):
311 """
289 """
312 Bump (move to up) thread
290 Bump (move to up) thread
313 """
291 """
314
292
315 if self.can_bump():
293 if self.can_bump():
316 self.bump_time = timezone.now()
294 self.bump_time = timezone.now()
317
295
318 def get_reply_count(self):
296 def get_reply_count(self):
319 return self.replies.count()
297 return self.replies.count()
320
298
321 def get_images_count(self):
299 def get_images_count(self):
322 return self.replies.filter(image_width__gt=0).count()
300 return self.replies.filter(image_width__gt=0).count()
323
301
324 def can_bump(self):
302 def can_bump(self):
325 """
303 """
326 Check if the thread can be bumped by replying
304 Check if the thread can be bumped by replying
327 """
305 """
328
306
329 if self.archived:
307 if self.archived:
330 return False
308 return False
331
309
332 post_count = self.get_reply_count()
310 post_count = self.get_reply_count()
333
311
334 return post_count < settings.MAX_POSTS_PER_THREAD
312 return post_count < settings.MAX_POSTS_PER_THREAD
335
313
336 def delete_with_posts(self):
314 def delete_with_posts(self):
337 """
315 """
338 Completely delete thread and all its posts
316 Completely delete thread and all its posts
339 """
317 """
340
318
341 if self.replies.count() > 0:
319 if self.replies.count() > 0:
342 self.replies.all().delete()
320 self.replies.all().delete()
343
321
344 self.delete()
322 self.delete()
345
323
346 def get_last_replies(self):
324 def get_last_replies(self):
347 """
325 """
348 Get last replies, not including opening post
326 Get last replies, not including opening post
349 """
327 """
350
328
351 if settings.LAST_REPLIES_COUNT > 0:
329 if settings.LAST_REPLIES_COUNT > 0:
352 reply_count = self.get_reply_count()
330 reply_count = self.get_reply_count()
353
331
354 if reply_count > 0:
332 if reply_count > 0:
355 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
333 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
356 reply_count - 1)
334 reply_count - 1)
357 last_replies = self.replies.all().order_by('pub_time')[
335 last_replies = self.replies.all().order_by('pub_time')[
358 reply_count - reply_count_to_show:]
336 reply_count - reply_count_to_show:]
359
337
360 return last_replies
338 return last_replies
361
339
362 def get_replies(self):
340 def get_replies(self):
363 """
341 """
364 Get sorted thread posts
342 Get sorted thread posts
365 """
343 """
366
344
367 return self.replies.all().order_by('pub_time')
345 return self.replies.all().order_by('pub_time')
368
346
369 def add_tag(self, tag):
347 def add_tag(self, tag):
370 """
348 """
371 Connect thread to a tag and tag to a thread
349 Connect thread to a tag and tag to a thread
372 """
350 """
373
351
374 self.tags.add(tag)
352 self.tags.add(tag)
375 tag.threads.add(self)
353 tag.threads.add(self)
376
354
377 def get_opening_post(self):
355 def get_opening_post(self):
378 """
356 """
379 Get first post of the thread
357 Get first post of the thread
380 """
358 """
381
359
382 return self.get_replies()[0]
360 return self.get_replies()[0]
383
361
384 def __unicode__(self):
362 def __unicode__(self):
385 return str(self.get_replies()[0].id)
363 return str(self.get_replies()[0].id)
386
364
387 def get_pub_time(self):
365 def get_pub_time(self):
388 """
366 """
389 Thread does not have its own pub time, so we need to get it from
367 Thread does not have its own pub time, so we need to get it from
390 the opening post
368 the opening post
391 """
369 """
392
370
393 return self.get_opening_post().pub_time
371 return self.get_opening_post().pub_time
@@ -1,145 +1,145 b''
1 {% extends "boards/base.html" %}
1 {% extends "boards/base.html" %}
2
2
3 {% load i18n %}
3 {% load i18n %}
4 {% load cache %}
4 {% load cache %}
5 {% load board %}
5 {% load board %}
6 {% load static %}
6 {% load static %}
7
7
8 {% block head %}
8 {% block head %}
9 <title>Neboard</title>
9 <title>Neboard - {% trans 'Archive' %}</title>
10
10
11 {% if prev_page %}
11 {% if current_page.has_previous %}
12 <link rel="next" href="
12 <link rel="prev" href="
13 {% if tag %}
13 {% if tag %}
14 {% url "tag" tag_name=tag page=prev_page %}
14 {% url "tag" tag_name=tag page=current_page.previous_page_number %}
15 {% else %}
15 {% else %}
16 {% url "index" page=prev_page %}
16 {% url "index" page=current_page.previous_page_number %}
17 {% endif %}
17 {% endif %}
18 " />
18 " />
19 {% endif %}
19 {% endif %}
20 {% if next_page %}
20 {% if current_page.has_next %}
21 <link rel="next" href="
21 <link rel="next" href="
22 {% if tag %}
22 {% if tag %}
23 {% url "tag" tag_name=tag page=next_page %}
23 {% url "tag" tag_name=tag page=current_page.next_page_number %}
24 {% else %}
24 {% else %}
25 {% url "index" page=next_page %}
25 {% url "index" page=current_page.next_page_number %}
26 {% endif %}
26 {% endif %}
27 " />
27 " />
28 {% endif %}
28 {% endif %}
29
29
30 {% endblock %}
30 {% endblock %}
31
31
32 {% block content %}
32 {% block content %}
33
33
34 {% get_current_language as LANGUAGE_CODE %}
34 {% get_current_language as LANGUAGE_CODE %}
35
35
36 {% if threads %}
36 {% if threads %}
37 {% if prev_page %}
37 {% if current_page.has_previous %}
38 <div class="page_link">
38 <div class="page_link">
39 <a href="{% url "archive" page=prev_page %}">{% trans "Previous page" %}</a>
39 <a href="{% url "archive" page=current_page.previous_page_number %}">{% trans "Previous page" %}</a>
40 </div>
40 </div>
41 {% endif %}
41 {% endif %}
42
42
43 {% for thread in threads %}
43 {% for thread in threads %}
44 {% cache 600 thread_short thread.id thread.thread.last_edit_time moderator LANGUAGE_CODE %}
44 {% cache 600 thread_short thread.id thread.thread.last_edit_time moderator LANGUAGE_CODE %}
45 <div class="thread">
45 <div class="thread">
46 <div class="post archive_post" id="{{ thread.op.id }}">
46 <div class="post archive_post" id="{{ thread.op.id }}">
47 {% if thread.op.image %}
47 {% if thread.op.image %}
48 <div class="image">
48 <div class="image">
49 <a class="thumb"
49 <a class="thumb"
50 href="{{ thread.op.image.url }}"><img
50 href="{{ thread.op.image.url }}"><img
51 src="{{ thread.op.image.url_200x150 }}"
51 src="{{ thread.op.image.url_200x150 }}"
52 alt="{{ thread.op.id }}"
52 alt="{{ thread.op.id }}"
53 width="{{ thread.op.image_pre_width }}"
53 width="{{ thread.op.image_pre_width }}"
54 height="{{ thread.op.image_pre_height }}"
54 height="{{ thread.op.image_pre_height }}"
55 data-width="{{ thread.op.image_width }}"
55 data-width="{{ thread.op.image_width }}"
56 data-height="{{ thread.op.image_height }}"/>
56 data-height="{{ thread.op.image_height }}"/>
57 </a>
57 </a>
58 </div>
58 </div>
59 {% endif %}
59 {% endif %}
60 <div class="message">
60 <div class="message">
61 <div class="post-info">
61 <div class="post-info">
62 <span class="title">{{ thread.op.title }}</span>
62 <span class="title">{{ thread.op.title }}</span>
63 <a class="post_id" href="{% url 'thread' thread.op.id %}"
63 <a class="post_id" href="{% url 'thread' thread.op.id %}"
64 > ({{ thread.op.id }})</a>
64 > ({{ thread.op.id }})</a>
65 [{{ thread.op.pub_time }}] β€” [{{ thread.thread.last_edit_time }}]
65 [{{ thread.op.pub_time }}] β€” [{{ thread.thread.last_edit_time }}]
66
66
67 [<a class="link" href="
67 [<a class="link" href="
68 {% url 'thread' thread.op.id %}">{% trans "Open" %}</a>]
68 {% url 'thread' thread.op.id %}">{% trans "Open" %}</a>]
69
69
70 {% if moderator %}
70 {% if moderator %}
71 <span class="moderator_info">
71 <span class="moderator_info">
72 [<a href="
72 [<a href="
73 {% url 'delete' post_id=thread.op.id %}?next={{ request.path }}"
73 {% url 'delete' post_id=thread.op.id %}?next={{ request.path }}"
74 >{% trans 'Delete' %}</a>]
74 >{% trans 'Delete' %}</a>]
75 ({{ thread.op.poster_ip }})
75 ({{ thread.op.poster_ip }})
76 [<a href="
76 [<a href="
77 {% url 'ban' post_id=thread.op.id %}?next={{ request.path }}"
77 {% url 'ban' post_id=thread.op.id %}?next={{ request.path }}"
78 >{% trans 'Ban IP' %}</a>]
78 >{% trans 'Ban IP' %}</a>]
79 </span>
79 </span>
80 {% endif %}
80 {% endif %}
81 </div>
81 </div>
82 {% autoescape off %}
82 {% autoescape off %}
83 {{ thread.op.text.rendered|truncatewords_html:50 }}
83 {{ thread.op.text.rendered|truncatewords_html:50 }}
84 {% endautoescape %}
84 {% endautoescape %}
85 {% if thread.op.is_referenced %}
85 {% if thread.op.is_referenced %}
86 <div class="refmap">
86 <div class="refmap">
87 {% trans "Replies" %}:
87 {% trans "Replies" %}:
88 {% for ref_post in thread.op.get_sorted_referenced_posts %}
88 {% for ref_post in thread.op.get_sorted_referenced_posts %}
89 <a href="{% post_url ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a
89 <a href="{% post_url ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a
90 >{% if not forloop.last %},{% endif %}
90 >{% if not forloop.last %},{% endif %}
91 {% endfor %}
91 {% endfor %}
92 </div>
92 </div>
93 {% endif %}
93 {% endif %}
94 </div>
94 </div>
95 <div class="metadata">
95 <div class="metadata">
96 {{ thread.thread.get_images_count }} {% trans 'images' %},
96 {{ thread.thread.get_images_count }} {% trans 'images' %},
97 {{ thread.thread.get_reply_count }} {% trans 'replies' %}.
97 {{ thread.thread.get_reply_count }} {% trans 'replies' %}.
98 {% if thread.thread.tags %}
98 {% if thread.thread.tags %}
99 <span class="tags">
99 <span class="tags">
100 {% for tag in thread.thread.get_tags %}
100 {% for tag in thread.thread.get_tags %}
101 <a class="tag" href="
101 <a class="tag" href="
102 {% url 'tag' tag_name=tag.name %}">
102 {% url 'tag' tag_name=tag.name %}">
103 #{{ tag.name }}</a
103 #{{ tag.name }}</a
104 >{% if not forloop.last %},{% endif %}
104 >{% if not forloop.last %},{% endif %}
105 {% endfor %}
105 {% endfor %}
106 </span>
106 </span>
107 {% endif %}
107 {% endif %}
108 </div>
108 </div>
109 </div>
109 </div>
110 </div>
110 </div>
111 {% endcache %}
111 {% endcache %}
112 {% endfor %}
112 {% endfor %}
113
113
114 {% if next_page %}
114 {% if current_page.has_next %}
115 <div class="page_link">
115 <div class="page_link">
116 <a href="{% url "archive" page=next_page %}">{% trans "Next page" %}</a>
116 <a href="{% url "archive" page=current_page.next_page_number %}">{% trans "Next page" %}</a>
117 </div>
117 </div>
118 {% endif %}
118 {% endif %}
119 {% else %}
119 {% else %}
120 <div class="post">
120 <div class="post">
121 {% trans 'No threads exist. Create the first one!' %}</div>
121 {% trans 'No threads exist. Create the first one!' %}</div>
122 {% endif %}
122 {% endif %}
123
123
124 {% endblock %}
124 {% endblock %}
125
125
126 {% block metapanel %}
126 {% block metapanel %}
127
127
128 <span class="metapanel">
128 <span class="metapanel">
129 <b><a href="{% url "authors" %}">Neboard</a> 1.5 Aker</b>
129 <b><a href="{% url "authors" %}">Neboard</a> 1.6 Amon</b>
130 {% trans "Pages:" %}[
130 {% trans "Pages:" %}[
131 {% for page in pages %}
131 {% for page in paginator.page_range %}
132 <a
132 <a
133 {% ifequal page current_page %}
133 {% ifequal page current_page.number %}
134 class="current_page"
134 class="current_page"
135 {% endifequal %}
135 {% endifequal %}
136 href="
136 href="
137 {% url "archive" page=page %}
137 {% url "archive" page=page %}
138 ">{{ page }}</a>
138 ">{{ page }}</a>
139 {% if not forloop.last %},{% endif %}
139 {% if not forloop.last %},{% endif %}
140 {% endfor %}
140 {% endfor %}
141 ]
141 ]
142 [<a href="rss/">RSS</a>]
142 [<a href="rss/">RSS</a>]
143 </span>
143 </span>
144
144
145 {% endblock %}
145 {% endblock %}
@@ -1,254 +1,254 b''
1 {% extends "boards/base.html" %}
1 {% extends "boards/base.html" %}
2
2
3 {% load i18n %}
3 {% load i18n %}
4 {% load cache %}
4 {% load cache %}
5 {% load board %}
5 {% load board %}
6 {% load static %}
6 {% load static %}
7
7
8 {% block head %}
8 {% block head %}
9 {% if tag %}
9 {% if tag %}
10 <title>Neboard - {{ tag.name }}</title>
10 <title>Neboard - {{ tag.name }}</title>
11 {% else %}
11 {% else %}
12 <title>Neboard</title>
12 <title>Neboard</title>
13 {% endif %}
13 {% endif %}
14
14
15 {% if prev_page %}
15 {% if current_page.has_previous %}
16 <link rel="next" href="
16 <link rel="prev" href="
17 {% if tag %}
17 {% if tag %}
18 {% url "tag" tag_name=tag page=prev_page %}
18 {% url "tag" tag_name=tag page=current_page.previous_page_number %}
19 {% else %}
19 {% else %}
20 {% url "index" page=prev_page %}
20 {% url "index" page=current_page.previous_page_number %}
21 {% endif %}
21 {% endif %}
22 " />
22 " />
23 {% endif %}
23 {% endif %}
24 {% if next_page %}
24 {% if current_page.has_next %}
25 <link rel="next" href="
25 <link rel="next" href="
26 {% if tag %}
26 {% if tag %}
27 {% url "tag" tag_name=tag page=next_page %}
27 {% url "tag" tag_name=tag page=current_page.next_page_number %}
28 {% else %}
28 {% else %}
29 {% url "index" page=next_page %}
29 {% url "index" page=current_page.next_page_number %}
30 {% endif %}
30 {% endif %}
31 " />
31 " />
32 {% endif %}
32 {% endif %}
33
33
34 {% endblock %}
34 {% endblock %}
35
35
36 {% block content %}
36 {% block content %}
37
37
38 {% get_current_language as LANGUAGE_CODE %}
38 {% get_current_language as LANGUAGE_CODE %}
39
39
40 {% if tag %}
40 {% if tag %}
41 <div class="tag_info">
41 <div class="tag_info">
42 <h2>
42 <h2>
43 {% if tag in user.fav_tags.all %}
43 {% if tag in user.fav_tags.all %}
44 <a href="{% url 'tag_unsubscribe' tag.name %}?next={{ request.path }}"
44 <a href="{% url 'tag_unsubscribe' tag.name %}?next={{ request.path }}"
45 class="fav">β˜…</a>
45 class="fav">β˜…</a>
46 {% else %}
46 {% else %}
47 <a href="{% url 'tag_subscribe' tag.name %}?next={{ request.path }}"
47 <a href="{% url 'tag_subscribe' tag.name %}?next={{ request.path }}"
48 class="not_fav">β˜…</a>
48 class="not_fav">β˜…</a>
49 {% endif %}
49 {% endif %}
50 #{{ tag.name }}
50 #{{ tag.name }}
51 </h2>
51 </h2>
52 </div>
52 </div>
53 {% endif %}
53 {% endif %}
54
54
55 {% if threads %}
55 {% if threads %}
56 {% if prev_page %}
56 {% if current_page.has_previous %}
57 <div class="page_link">
57 <div class="page_link">
58 <a href="
58 <a href="
59 {% if tag %}
59 {% if tag %}
60 {% url "tag" tag_name=tag page=prev_page %}
60 {% url "tag" tag_name=tag page=current_page.previous_page_number %}
61 {% else %}
61 {% else %}
62 {% url "index" page=prev_page %}
62 {% url "index" page=current_page.previous_page_number %}
63 {% endif %}
63 {% endif %}
64 ">{% trans "Previous page" %}</a>
64 ">{% trans "Previous page" %}</a>
65 </div>
65 </div>
66 {% endif %}
66 {% endif %}
67
67
68 {% for thread in threads %}
68 {% for thread in threads %}
69 {% cache 600 thread_short thread.id thread.thread.last_edit_time moderator LANGUAGE_CODE %}
69 {% cache 600 thread_short thread.id thread.thread.last_edit_time moderator LANGUAGE_CODE %}
70 <div class="thread">
70 <div class="thread">
71 {% if thread.bumpable %}
71 {% if thread.bumpable %}
72 <div class="post" id="{{ thread.op.id }}">
72 <div class="post" id="{{ thread.op.id }}">
73 {% else %}
73 {% else %}
74 <div class="post dead_post" id="{{ thread.op.id }}">
74 <div class="post dead_post" id="{{ thread.op.id }}">
75 {% endif %}
75 {% endif %}
76 {% if thread.op.image %}
76 {% if thread.op.image %}
77 <div class="image">
77 <div class="image">
78 <a class="thumb"
78 <a class="thumb"
79 href="{{ thread.op.image.url }}"><img
79 href="{{ thread.op.image.url }}"><img
80 src="{{ thread.op.image.url_200x150 }}"
80 src="{{ thread.op.image.url_200x150 }}"
81 alt="{{ thread.op.id }}"
81 alt="{{ thread.op.id }}"
82 width="{{ thread.op.image_pre_width }}"
82 width="{{ thread.op.image_pre_width }}"
83 height="{{ thread.op.image_pre_height }}"
83 height="{{ thread.op.image_pre_height }}"
84 data-width="{{ thread.op.image_width }}"
84 data-width="{{ thread.op.image_width }}"
85 data-height="{{ thread.op.image_height }}"/>
85 data-height="{{ thread.op.image_height }}"/>
86 </a>
86 </a>
87 </div>
87 </div>
88 {% endif %}
88 {% endif %}
89 <div class="message">
89 <div class="message">
90 <div class="post-info">
90 <div class="post-info">
91 <span class="title">{{ thread.op.title }}</span>
91 <span class="title">{{ thread.op.title }}</span>
92 <a class="post_id" href="{% url 'thread' thread.op.id %}"
92 <a class="post_id" href="{% url 'thread' thread.op.id %}"
93 > ({{ thread.op.id }})</a>
93 > ({{ thread.op.id }})</a>
94 [{{ thread.op.pub_time }}]
94 [{{ thread.op.pub_time }}]
95 [<a class="link" href="
95 [<a class="link" href="
96 {% url 'thread' thread.op.id %}#form"
96 {% url 'thread' thread.op.id %}#form"
97 >{% trans "Reply" %}</a>]
97 >{% trans "Reply" %}</a>]
98
98
99 {% if moderator %}
99 {% if moderator %}
100 <span class="moderator_info">
100 <span class="moderator_info">
101 [<a href="
101 [<a href="
102 {% url 'delete' post_id=thread.op.id %}?next={{ request.path }}"
102 {% url 'delete' post_id=thread.op.id %}?next={{ request.path }}"
103 >{% trans 'Delete' %}</a>]
103 >{% trans 'Delete' %}</a>]
104 ({{ thread.op.poster_ip }})
104 ({{ thread.op.poster_ip }})
105 [<a href="
105 [<a href="
106 {% url 'ban' post_id=thread.op.id %}?next={{ request.path }}"
106 {% url 'ban' post_id=thread.op.id %}?next={{ request.path }}"
107 >{% trans 'Ban IP' %}</a>]
107 >{% trans 'Ban IP' %}</a>]
108 </span>
108 </span>
109 {% endif %}
109 {% endif %}
110 </div>
110 </div>
111 {% autoescape off %}
111 {% autoescape off %}
112 {{ thread.op.text.rendered|truncatewords_html:50 }}
112 {{ thread.op.text.rendered|truncatewords_html:50 }}
113 {% endautoescape %}
113 {% endautoescape %}
114 {% if thread.op.is_referenced %}
114 {% if thread.op.is_referenced %}
115 <div class="refmap">
115 <div class="refmap">
116 {% trans "Replies" %}:
116 {% trans "Replies" %}:
117 {% for ref_post in thread.op.get_sorted_referenced_posts %}
117 {% for ref_post in thread.op.get_sorted_referenced_posts %}
118 <a href="{% post_url ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a
118 <a href="{% post_url ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a
119 >{% if not forloop.last %},{% endif %}
119 >{% if not forloop.last %},{% endif %}
120 {% endfor %}
120 {% endfor %}
121 </div>
121 </div>
122 {% endif %}
122 {% endif %}
123 </div>
123 </div>
124 <div class="metadata">
124 <div class="metadata">
125 {{ thread.thread.get_images_count }} {% trans 'images' %}.
125 {{ thread.thread.get_images_count }} {% trans 'images' %}.
126 {% if thread.thread.tags %}
126 {% if thread.thread.tags %}
127 <span class="tags">
127 <span class="tags">
128 {% for tag in thread.thread.get_tags %}
128 {% for tag in thread.thread.get_tags %}
129 <a class="tag" href="
129 <a class="tag" href="
130 {% url 'tag' tag_name=tag.name %}">
130 {% url 'tag' tag_name=tag.name %}">
131 #{{ tag.name }}</a
131 #{{ tag.name }}</a
132 >{% if not forloop.last %},{% endif %}
132 >{% if not forloop.last %},{% endif %}
133 {% endfor %}
133 {% endfor %}
134 </span>
134 </span>
135 {% endif %}
135 {% endif %}
136 </div>
136 </div>
137 </div>
137 </div>
138 {% if thread.last_replies.exists %}
138 {% if thread.last_replies.exists %}
139 {% if thread.skipped_replies %}
139 {% if thread.skipped_replies %}
140 <div class="skipped_replies">
140 <div class="skipped_replies">
141 <a href="{% url 'thread' thread.op.id %}">
141 <a href="{% url 'thread' thread.op.id %}">
142 {% blocktrans with count=thread.skipped_replies %}Skipped {{ count }} replies. Open thread to see all replies.{% endblocktrans %}
142 {% blocktrans with count=thread.skipped_replies %}Skipped {{ count }} replies. Open thread to see all replies.{% endblocktrans %}
143 </a>
143 </a>
144 </div>
144 </div>
145 {% endif %}
145 {% endif %}
146 <div class="last-replies">
146 <div class="last-replies">
147 {% for post in thread.last_replies %}
147 {% for post in thread.last_replies %}
148 {% if thread.bumpable %}
148 {% if thread.bumpable %}
149 <div class="post" id="{{ post.id }}">
149 <div class="post" id="{{ post.id }}">
150 {% else %}
150 {% else %}
151 <div class="post dead_post" id="{{ post.id }}">
151 <div class="post dead_post" id="{{ post.id }}">
152 {% endif %}
152 {% endif %}
153 {% if post.image %}
153 {% if post.image %}
154 <div class="image">
154 <div class="image">
155 <a class="thumb"
155 <a class="thumb"
156 href="{{ post.image.url }}"><img
156 href="{{ post.image.url }}"><img
157 src=" {{ post.image.url_200x150 }}"
157 src=" {{ post.image.url_200x150 }}"
158 alt="{{ post.id }}"
158 alt="{{ post.id }}"
159 width="{{ post.image_pre_width }}"
159 width="{{ post.image_pre_width }}"
160 height="{{ post.image_pre_height }}"
160 height="{{ post.image_pre_height }}"
161 data-width="{{ post.image_width }}"
161 data-width="{{ post.image_width }}"
162 data-height="{{ post.image_height }}"/>
162 data-height="{{ post.image_height }}"/>
163 </a>
163 </a>
164 </div>
164 </div>
165 {% endif %}
165 {% endif %}
166 <div class="message">
166 <div class="message">
167 <div class="post-info">
167 <div class="post-info">
168 <span class="title">{{ post.title }}</span>
168 <span class="title">{{ post.title }}</span>
169 <a class="post_id" href="
169 <a class="post_id" href="
170 {% url 'thread' thread.op.id %}#{{ post.id }}">
170 {% url 'thread' thread.op.id %}#{{ post.id }}">
171 ({{ post.id }})</a>
171 ({{ post.id }})</a>
172 [{{ post.pub_time }}]
172 [{{ post.pub_time }}]
173 </div>
173 </div>
174 {% autoescape off %}
174 {% autoescape off %}
175 {{ post.text.rendered|truncatewords_html:50 }}
175 {{ post.text.rendered|truncatewords_html:50 }}
176 {% endautoescape %}
176 {% endautoescape %}
177 </div>
177 </div>
178 {% if post.is_referenced %}
178 {% if post.is_referenced %}
179 <div class="refmap">
179 <div class="refmap">
180 {% trans "Replies" %}:
180 {% trans "Replies" %}:
181 {% for ref_post in post.get_sorted_referenced_posts %}
181 {% for ref_post in post.get_sorted_referenced_posts %}
182 <a href="{% post_url ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a
182 <a href="{% post_url ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a
183 >{% if not forloop.last %},{% endif %}
183 >{% if not forloop.last %},{% endif %}
184 {% endfor %}
184 {% endfor %}
185 </div>
185 </div>
186 {% endif %}
186 {% endif %}
187 </div>
187 </div>
188 {% endfor %}
188 {% endfor %}
189 </div>
189 </div>
190 {% endif %}
190 {% endif %}
191 </div>
191 </div>
192 {% endcache %}
192 {% endcache %}
193 {% endfor %}
193 {% endfor %}
194
194
195 {% if next_page %}
195 {% if current_page.has_next %}
196 <div class="page_link">
196 <div class="page_link">
197 <a href="
197 <a href="
198 {% if tag %}
198 {% if tag %}
199 {% url "tag" tag_name=tag page=next_page %}
199 {% url "tag" tag_name=tag page=current_page.next_page_number %}
200 {% else %}
200 {% else %}
201 {% url "index" page=next_page %}
201 {% url "index" page=current_page.next_page_number %}
202 {% endif %}
202 {% endif %}
203 ">{% trans "Next page" %}</a>
203 ">{% trans "Next page" %}</a>
204 </div>
204 </div>
205 {% endif %}
205 {% endif %}
206 {% else %}
206 {% else %}
207 <div class="post">
207 <div class="post">
208 {% trans 'No threads exist. Create the first one!' %}</div>
208 {% trans 'No threads exist. Create the first one!' %}</div>
209 {% endif %}
209 {% endif %}
210
210
211 <div class="post-form-w">
211 <div class="post-form-w">
212 <script src="{% static 'js/panel.js' %}"></script>
212 <script src="{% static 'js/panel.js' %}"></script>
213 <div class="post-form">
213 <div class="post-form">
214 <div class="form-title">{% trans "Create new thread" %}</div>
214 <div class="form-title">{% trans "Create new thread" %}</div>
215 <form enctype="multipart/form-data" method="post">{% csrf_token %}
215 <form enctype="multipart/form-data" method="post">{% csrf_token %}
216 {{ form.as_div }}
216 {{ form.as_div }}
217 <div class="form-submit">
217 <div class="form-submit">
218 <input type="submit" value="{% trans "Post" %}"/>
218 <input type="submit" value="{% trans "Post" %}"/>
219 </div>
219 </div>
220 </form>
220 </form>
221 <div>
221 <div>
222 {% trans 'Tags must be delimited by spaces. Text or image is required.' %}
222 {% trans 'Tags must be delimited by spaces. Text or image is required.' %}
223 </div>
223 </div>
224 <div><a href="{% url "staticpage" name="help" %}">
224 <div><a href="{% url "staticpage" name="help" %}">
225 {% trans 'Text syntax' %}</a></div>
225 {% trans 'Text syntax' %}</a></div>
226 </div>
226 </div>
227 </div>
227 </div>
228
228
229 {% endblock %}
229 {% endblock %}
230
230
231 {% block metapanel %}
231 {% block metapanel %}
232
232
233 <span class="metapanel">
233 <span class="metapanel">
234 <b><a href="{% url "authors" %}">Neboard</a> 1.6 Amon</b>
234 <b><a href="{% url "authors" %}">Neboard</a> 1.6 Amon</b>
235 {% trans "Pages:" %}[
235 {% trans "Pages:" %}[
236 {% for page in pages %}
236 {% for page in paginator.page_range %}
237 <a
237 <a
238 {% ifequal page current_page %}
238 {% ifequal page current_page.number %}
239 class="current_page"
239 class="current_page"
240 {% endifequal %}
240 {% endifequal %}
241 href="
241 href="
242 {% if tag %}
242 {% if tag %}
243 {% url "tag" tag_name=tag page=page %}
243 {% url "tag" tag_name=tag page=page %}
244 {% else %}
244 {% else %}
245 {% url "index" page=page %}
245 {% url "index" page=page %}
246 {% endif %}
246 {% endif %}
247 ">{{ page }}</a>
247 ">{{ page }}</a>
248 {% if not forloop.last %},{% endif %}
248 {% if not forloop.last %},{% endif %}
249 {% endfor %}
249 {% endfor %}
250 ]
250 ]
251 [<a href="rss/">RSS</a>]
251 [<a href="rss/">RSS</a>]
252 </span>
252 </span>
253
253
254 {% endblock %}
254 {% endblock %}
@@ -1,604 +1,601 b''
1 from datetime import datetime, timedelta
1 from datetime import datetime, timedelta
2
2
3 from django.db.models import Count
3 from django.db.models import Count
4
4
5
5
6 OLD_USER_AGE_DAYS = 90
6 OLD_USER_AGE_DAYS = 90
7
7
8 __author__ = 'neko259'
8 __author__ = 'neko259'
9
9
10 import hashlib
10 import hashlib
11 import string
11 import string
12 import time
12 import time
13 import re
13 import re
14
14
15 from django.core import serializers
15 from django.core import serializers
16 from django.core.urlresolvers import reverse
16 from django.core.urlresolvers import reverse
17 from django.http import HttpResponseRedirect, Http404
17 from django.http import HttpResponseRedirect, Http404
18 from django.http.response import HttpResponse
18 from django.http.response import HttpResponse
19 from django.template import RequestContext
19 from django.template import RequestContext
20 from django.shortcuts import render, redirect, get_object_or_404
20 from django.shortcuts import render, redirect, get_object_or_404
21 from django.utils import timezone
21 from django.utils import timezone
22 from django.db import transaction
22 from django.db import transaction
23 from django.views.decorators.cache import cache_page
23 from django.views.decorators.cache import cache_page
24 from django.views.i18n import javascript_catalog
24 from django.views.i18n import javascript_catalog
25 from django.core.paginator import Paginator
25
26
26 from boards import forms
27 from boards import forms
27 import boards
28 import boards
28 from boards import utils
29 from boards import utils
29 from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
30 from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
30 ThreadCaptchaForm, PostCaptchaForm, LoginForm, ModeratorSettingsForm
31 ThreadCaptchaForm, PostCaptchaForm, LoginForm, ModeratorSettingsForm
31 from boards.models import Post, Tag, Ban, User
32 from boards.models import Post, Tag, Ban, User, Thread
32 from boards.models.post import SETTING_MODERATE, REGEX_REPLY
33 from boards.models.post import SETTING_MODERATE, REGEX_REPLY
33 from boards.models.user import RANK_USER
34 from boards.models.user import RANK_USER
34 from boards import authors
35 from boards import authors
35 from boards.utils import get_client_ip
36 from boards.utils import get_client_ip
36 import neboard
37 import neboard
37
38
38
39
39 BAN_REASON_SPAM = 'Autoban: spam bot'
40 BAN_REASON_SPAM = 'Autoban: spam bot'
40 MODE_GALLERY = 'gallery'
41 MODE_GALLERY = 'gallery'
41 MODE_NORMAL = 'normal'
42 MODE_NORMAL = 'normal'
42
43
44 DEFAULT_PAGE = 1
43
45
44 def index(request, page=0):
46
47 def index(request, page=DEFAULT_PAGE):
45 context = _init_default_context(request)
48 context = _init_default_context(request)
46
49
47 if utils.need_include_captcha(request):
50 if utils.need_include_captcha(request):
48 threadFormClass = ThreadCaptchaForm
51 threadFormClass = ThreadCaptchaForm
49 kwargs = {'request': request}
52 kwargs = {'request': request}
50 else:
53 else:
51 threadFormClass = ThreadForm
54 threadFormClass = ThreadForm
52 kwargs = {}
55 kwargs = {}
53
56
54 if request.method == 'POST':
57 if request.method == 'POST':
55 form = threadFormClass(request.POST, request.FILES,
58 form = threadFormClass(request.POST, request.FILES,
56 error_class=PlainErrorList, **kwargs)
59 error_class=PlainErrorList, **kwargs)
57 form.session = request.session
60 form.session = request.session
58
61
59 if form.is_valid():
62 if form.is_valid():
60 return _new_post(request, form)
63 return _new_post(request, form)
61 if form.need_to_ban:
64 if form.need_to_ban:
62 # Ban user because he is suspected to be a bot
65 # Ban user because he is suspected to be a bot
63 _ban_current_user(request)
66 _ban_current_user(request)
64 else:
67 else:
65 form = threadFormClass(error_class=PlainErrorList, **kwargs)
68 form = threadFormClass(error_class=PlainErrorList, **kwargs)
66
69
67 threads = []
70 threads = []
68 for thread_to_show in Post.objects.get_threads(page=int(page)):
71 for thread_to_show in Post.objects.get_threads(page=int(page)):
69 threads.append(_get_template_thread(thread_to_show))
72 threads.append(_get_template_thread(thread_to_show))
70
73
71 # TODO Make this generic for tag and threads list pages
74 # TODO Make this generic for tag and threads list pages
72 context['threads'] = None if len(threads) == 0 else threads
75 context['threads'] = None if len(threads) == 0 else threads
73 context['form'] = form
76 context['form'] = form
74 context['current_page'] = int(page)
75
77
76 page_count = Post.objects.get_thread_page_count()
78 paginator = Paginator(Thread.objects.filter(archived=False),
77 context['pages'] = range(page_count) if page_count > 0 else [0]
79 neboard.settings.THREADS_PER_PAGE)
78 page = int(page)
80 _get_page_context(paginator, context, page)
79 if page < page_count - 1:
80 context['next_page'] = str(page + 1)
81 if page > 0:
82 context['prev_page'] = str(page - 1)
83
81
84 return render(request, 'boards/posting_general.html',
82 return render(request, 'boards/posting_general.html',
85 context)
83 context)
86
84
87
85
88 def archive(request, page=0):
86 def archive(request, page=DEFAULT_PAGE):
89 context = _init_default_context(request)
87 context = _init_default_context(request)
90
88
91 threads = []
89 threads = []
92 for thread_to_show in Post.objects.get_threads(page=int(page),
90 for thread_to_show in Post.objects.get_threads(page=int(page),
93 archived=True):
91 archived=True):
94 threads.append(_get_template_thread(thread_to_show))
92 threads.append(_get_template_thread(thread_to_show))
95
93
96 context['threads'] = threads
94 context['threads'] = threads
97 context['current_page'] = int(page)
98
95
99 page_count = Post.objects.get_thread_page_count(archived=True)
96 paginator = Paginator(Thread.objects.filter(archived=True),
100 context['pages'] = range(page_count) if page_count > 0 else [0]
97 neboard.settings.THREADS_PER_PAGE)
101 page = int(page)
98 _get_page_context(paginator, context, page)
102 if page < page_count - 1:
103 context['next_page'] = str(page + 1)
104 if page > 0:
105 context['prev_page'] = str(page - 1)
106
99
107 return render(request, 'boards/archive.html', context)
100 return render(request, 'boards/archive.html', context)
108
101
109
102
110 @transaction.atomic
103 @transaction.atomic
111 def _new_post(request, form, opening_post=None):
104 def _new_post(request, form, opening_post=None):
112 """Add a new post (in thread or as a reply)."""
105 """Add a new post (in thread or as a reply)."""
113
106
114 ip = get_client_ip(request)
107 ip = get_client_ip(request)
115 is_banned = Ban.objects.filter(ip=ip).exists()
108 is_banned = Ban.objects.filter(ip=ip).exists()
116
109
117 if is_banned:
110 if is_banned:
118 return redirect(you_are_banned)
111 return redirect(you_are_banned)
119
112
120 data = form.cleaned_data
113 data = form.cleaned_data
121
114
122 title = data['title']
115 title = data['title']
123 text = data['text']
116 text = data['text']
124
117
125 text = _remove_invalid_links(text)
118 text = _remove_invalid_links(text)
126
119
127 if 'image' in data.keys():
120 if 'image' in data.keys():
128 image = data['image']
121 image = data['image']
129 else:
122 else:
130 image = None
123 image = None
131
124
132 tags = []
125 tags = []
133
126
134 if not opening_post:
127 if not opening_post:
135 tag_strings = data['tags']
128 tag_strings = data['tags']
136
129
137 if tag_strings:
130 if tag_strings:
138 tag_strings = tag_strings.split(' ')
131 tag_strings = tag_strings.split(' ')
139 for tag_name in tag_strings:
132 for tag_name in tag_strings:
140 tag_name = string.lower(tag_name.strip())
133 tag_name = string.lower(tag_name.strip())
141 if len(tag_name) > 0:
134 if len(tag_name) > 0:
142 tag, created = Tag.objects.get_or_create(name=tag_name)
135 tag, created = Tag.objects.get_or_create(name=tag_name)
143 tags.append(tag)
136 tags.append(tag)
144 post_thread = None
137 post_thread = None
145 else:
138 else:
146 post_thread = opening_post.thread_new
139 post_thread = opening_post.thread_new
147
140
148 post = Post.objects.create_post(title=title, text=text, ip=ip,
141 post = Post.objects.create_post(title=title, text=text, ip=ip,
149 thread=post_thread, image=image,
142 thread=post_thread, image=image,
150 tags=tags, user=_get_user(request))
143 tags=tags, user=_get_user(request))
151
144
152 thread_to_show = (opening_post.id if opening_post else post.id)
145 thread_to_show = (opening_post.id if opening_post else post.id)
153
146
154 if opening_post:
147 if opening_post:
155 return redirect(reverse(thread, kwargs={'post_id': thread_to_show}) +
148 return redirect(reverse(thread, kwargs={'post_id': thread_to_show}) +
156 '#' + str(post.id))
149 '#' + str(post.id))
157 else:
150 else:
158 return redirect(thread, post_id=thread_to_show)
151 return redirect(thread, post_id=thread_to_show)
159
152
160
153
161 def tag(request, tag_name, page=0):
154 def tag(request, tag_name, page=DEFAULT_PAGE):
162 """
155 """
163 Get all tag threads. Threads are split in pages, so some page is
156 Get all tag threads. Threads are split in pages, so some page is
164 requested. Default page is 0.
157 requested.
165 """
158 """
166
159
167 tag = get_object_or_404(Tag, name=tag_name)
160 tag = get_object_or_404(Tag, name=tag_name)
168 threads = []
161 threads = []
169 for thread_to_show in Post.objects.get_threads(page=int(page), tag=tag):
162 for thread_to_show in Post.objects.get_threads(page=int(page), tag=tag):
170 threads.append(_get_template_thread(thread_to_show))
163 threads.append(_get_template_thread(thread_to_show))
171
164
172 if request.method == 'POST':
165 if request.method == 'POST':
173 form = ThreadForm(request.POST, request.FILES,
166 form = ThreadForm(request.POST, request.FILES,
174 error_class=PlainErrorList)
167 error_class=PlainErrorList)
175 form.session = request.session
168 form.session = request.session
176
169
177 if form.is_valid():
170 if form.is_valid():
178 return _new_post(request, form)
171 return _new_post(request, form)
179 if form.need_to_ban:
172 if form.need_to_ban:
180 # Ban user because he is suspected to be a bot
173 # Ban user because he is suspected to be a bot
181 _ban_current_user(request)
174 _ban_current_user(request)
182 else:
175 else:
183 form = forms.ThreadForm(initial={'tags': tag_name},
176 form = forms.ThreadForm(initial={'tags': tag_name},
184 error_class=PlainErrorList)
177 error_class=PlainErrorList)
185
178
186 context = _init_default_context(request)
179 context = _init_default_context(request)
187 context['threads'] = None if len(threads) == 0 else threads
180 context['threads'] = None if len(threads) == 0 else threads
188 context['tag'] = tag
181 context['tag'] = tag
189 context['current_page'] = int(page)
190
182
191 page_count = Post.objects.get_thread_page_count(tag=tag)
183 paginator = Paginator(Post.objects.get_threads(tag=tag),
192 context['pages'] = range(page_count)
184 neboard.settings.THREADS_PER_PAGE)
193 page = int(page)
185 _get_page_context(paginator, context, page)
194 if page < page_count - 1:
195 context['next_page'] = str(page + 1)
196 if page > 0:
197 context['prev_page'] = str(page - 1)
198
186
199 context['form'] = form
187 context['form'] = form
200
188
201 return render(request, 'boards/posting_general.html',
189 return render(request, 'boards/posting_general.html',
202 context)
190 context)
203
191
204
192
205 def thread(request, post_id, mode=MODE_NORMAL):
193 def thread(request, post_id, mode=MODE_NORMAL):
206 """Get all thread posts"""
194 """Get all thread posts"""
207
195
208 if utils.need_include_captcha(request):
196 if utils.need_include_captcha(request):
209 postFormClass = PostCaptchaForm
197 postFormClass = PostCaptchaForm
210 kwargs = {'request': request}
198 kwargs = {'request': request}
211 else:
199 else:
212 postFormClass = PostForm
200 postFormClass = PostForm
213 kwargs = {}
201 kwargs = {}
214
202
215 opening_post = get_object_or_404(Post, id=post_id)
203 opening_post = get_object_or_404(Post, id=post_id)
216
204
217 # If this is not OP, don't show it as it is
205 # If this is not OP, don't show it as it is
218 if not opening_post.is_opening():
206 if not opening_post.is_opening():
219 raise Http404
207 raise Http404
220
208
221 if request.method == 'POST' and not opening_post.thread_new.archived:
209 if request.method == 'POST' and not opening_post.thread_new.archived:
222 form = postFormClass(request.POST, request.FILES,
210 form = postFormClass(request.POST, request.FILES,
223 error_class=PlainErrorList, **kwargs)
211 error_class=PlainErrorList, **kwargs)
224 form.session = request.session
212 form.session = request.session
225
213
226 if form.is_valid():
214 if form.is_valid():
227 return _new_post(request, form, opening_post)
215 return _new_post(request, form, opening_post)
228 if form.need_to_ban:
216 if form.need_to_ban:
229 # Ban user because he is suspected to be a bot
217 # Ban user because he is suspected to be a bot
230 _ban_current_user(request)
218 _ban_current_user(request)
231 else:
219 else:
232 form = postFormClass(error_class=PlainErrorList, **kwargs)
220 form = postFormClass(error_class=PlainErrorList, **kwargs)
233
221
234 thread_to_show = opening_post.thread_new
222 thread_to_show = opening_post.thread_new
235
223
236 context = _init_default_context(request)
224 context = _init_default_context(request)
237
225
238 posts = thread_to_show.get_replies()
226 posts = thread_to_show.get_replies()
239 context['form'] = form
227 context['form'] = form
240 context["last_update"] = _datetime_to_epoch(thread_to_show.last_edit_time)
228 context["last_update"] = _datetime_to_epoch(thread_to_show.last_edit_time)
241 context["thread"] = thread_to_show
229 context["thread"] = thread_to_show
242
230
243 if MODE_NORMAL == mode:
231 if MODE_NORMAL == mode:
244 context['bumpable'] = thread_to_show.can_bump()
232 context['bumpable'] = thread_to_show.can_bump()
245 if context['bumpable']:
233 if context['bumpable']:
246 context['posts_left'] = neboard.settings.MAX_POSTS_PER_THREAD - posts \
234 context['posts_left'] = neboard.settings.MAX_POSTS_PER_THREAD - posts \
247 .count()
235 .count()
248 context['bumplimit_progress'] = str(
236 context['bumplimit_progress'] = str(
249 float(context['posts_left']) /
237 float(context['posts_left']) /
250 neboard.settings.MAX_POSTS_PER_THREAD * 100)
238 neboard.settings.MAX_POSTS_PER_THREAD * 100)
251
239
252 context['posts'] = posts
240 context['posts'] = posts
253
241
254 document = 'boards/thread.html'
242 document = 'boards/thread.html'
255 elif MODE_GALLERY == mode:
243 elif MODE_GALLERY == mode:
256 context['posts'] = posts.filter(image_width__gt=0)
244 context['posts'] = posts.filter(image_width__gt=0)
257
245
258 document = 'boards/thread_gallery.html'
246 document = 'boards/thread_gallery.html'
259 else:
247 else:
260 raise Http404
248 raise Http404
261
249
262 return render(request, document, context)
250 return render(request, document, context)
263
251
264
252
265 def login(request):
253 def login(request):
266 """Log in with user id"""
254 """Log in with user id"""
267
255
268 context = _init_default_context(request)
256 context = _init_default_context(request)
269
257
270 if request.method == 'POST':
258 if request.method == 'POST':
271 form = LoginForm(request.POST, request.FILES,
259 form = LoginForm(request.POST, request.FILES,
272 error_class=PlainErrorList)
260 error_class=PlainErrorList)
273 form.session = request.session
261 form.session = request.session
274
262
275 if form.is_valid():
263 if form.is_valid():
276 user = User.objects.get(user_id=form.cleaned_data['user_id'])
264 user = User.objects.get(user_id=form.cleaned_data['user_id'])
277 request.session['user_id'] = user.id
265 request.session['user_id'] = user.id
278 return redirect(index)
266 return redirect(index)
279
267
280 else:
268 else:
281 form = LoginForm()
269 form = LoginForm()
282
270
283 context['form'] = form
271 context['form'] = form
284
272
285 return render(request, 'boards/login.html', context)
273 return render(request, 'boards/login.html', context)
286
274
287
275
288 def settings(request):
276 def settings(request):
289 """User's settings"""
277 """User's settings"""
290
278
291 context = _init_default_context(request)
279 context = _init_default_context(request)
292 user = _get_user(request)
280 user = _get_user(request)
293 is_moderator = user.is_moderator()
281 is_moderator = user.is_moderator()
294
282
295 if request.method == 'POST':
283 if request.method == 'POST':
296 with transaction.atomic():
284 with transaction.atomic():
297 if is_moderator:
285 if is_moderator:
298 form = ModeratorSettingsForm(request.POST,
286 form = ModeratorSettingsForm(request.POST,
299 error_class=PlainErrorList)
287 error_class=PlainErrorList)
300 else:
288 else:
301 form = SettingsForm(request.POST, error_class=PlainErrorList)
289 form = SettingsForm(request.POST, error_class=PlainErrorList)
302
290
303 if form.is_valid():
291 if form.is_valid():
304 selected_theme = form.cleaned_data['theme']
292 selected_theme = form.cleaned_data['theme']
305
293
306 user.save_setting('theme', selected_theme)
294 user.save_setting('theme', selected_theme)
307
295
308 if is_moderator:
296 if is_moderator:
309 moderate = form.cleaned_data['moderate']
297 moderate = form.cleaned_data['moderate']
310 user.save_setting(SETTING_MODERATE, moderate)
298 user.save_setting(SETTING_MODERATE, moderate)
311
299
312 return redirect(settings)
300 return redirect(settings)
313 else:
301 else:
314 selected_theme = _get_theme(request)
302 selected_theme = _get_theme(request)
315
303
316 if is_moderator:
304 if is_moderator:
317 form = ModeratorSettingsForm(initial={'theme': selected_theme,
305 form = ModeratorSettingsForm(initial={'theme': selected_theme,
318 'moderate': context['moderator']},
306 'moderate': context['moderator']},
319 error_class=PlainErrorList)
307 error_class=PlainErrorList)
320 else:
308 else:
321 form = SettingsForm(initial={'theme': selected_theme},
309 form = SettingsForm(initial={'theme': selected_theme},
322 error_class=PlainErrorList)
310 error_class=PlainErrorList)
323
311
324 context['form'] = form
312 context['form'] = form
325
313
326 return render(request, 'boards/settings.html', context)
314 return render(request, 'boards/settings.html', context)
327
315
328
316
329 def all_tags(request):
317 def all_tags(request):
330 """All tags list"""
318 """All tags list"""
331
319
332 context = _init_default_context(request)
320 context = _init_default_context(request)
333 context['all_tags'] = Tag.objects.get_not_empty_tags()
321 context['all_tags'] = Tag.objects.get_not_empty_tags()
334
322
335 return render(request, 'boards/tags.html', context)
323 return render(request, 'boards/tags.html', context)
336
324
337
325
338 def jump_to_post(request, post_id):
326 def jump_to_post(request, post_id):
339 """Determine thread in which the requested post is and open it's page"""
327 """Determine thread in which the requested post is and open it's page"""
340
328
341 post = get_object_or_404(Post, id=post_id)
329 post = get_object_or_404(Post, id=post_id)
342
330
343 if not post.thread:
331 if not post.thread:
344 return redirect(thread, post_id=post.id)
332 return redirect(thread, post_id=post.id)
345 else:
333 else:
346 return redirect(reverse(thread, kwargs={'post_id': post.thread.id})
334 return redirect(reverse(thread, kwargs={'post_id': post.thread.id})
347 + '#' + str(post.id))
335 + '#' + str(post.id))
348
336
349
337
350 def authors(request):
338 def authors(request):
351 """Show authors list"""
339 """Show authors list"""
352
340
353 context = _init_default_context(request)
341 context = _init_default_context(request)
354 context['authors'] = boards.authors.authors
342 context['authors'] = boards.authors.authors
355
343
356 return render(request, 'boards/authors.html', context)
344 return render(request, 'boards/authors.html', context)
357
345
358
346
359 @transaction.atomic
347 @transaction.atomic
360 def delete(request, post_id):
348 def delete(request, post_id):
361 """Delete post"""
349 """Delete post"""
362
350
363 user = _get_user(request)
351 user = _get_user(request)
364 post = get_object_or_404(Post, id=post_id)
352 post = get_object_or_404(Post, id=post_id)
365
353
366 if user.is_moderator():
354 if user.is_moderator():
367 # TODO Show confirmation page before deletion
355 # TODO Show confirmation page before deletion
368 Post.objects.delete_post(post)
356 Post.objects.delete_post(post)
369
357
370 if not post.thread:
358 if not post.thread:
371 return _redirect_to_next(request)
359 return _redirect_to_next(request)
372 else:
360 else:
373 return redirect(thread, post_id=post.thread.id)
361 return redirect(thread, post_id=post.thread.id)
374
362
375
363
376 @transaction.atomic
364 @transaction.atomic
377 def ban(request, post_id):
365 def ban(request, post_id):
378 """Ban user"""
366 """Ban user"""
379
367
380 user = _get_user(request)
368 user = _get_user(request)
381 post = get_object_or_404(Post, id=post_id)
369 post = get_object_or_404(Post, id=post_id)
382
370
383 if user.is_moderator():
371 if user.is_moderator():
384 # TODO Show confirmation page before ban
372 # TODO Show confirmation page before ban
385 ban, created = Ban.objects.get_or_create(ip=post.poster_ip)
373 ban, created = Ban.objects.get_or_create(ip=post.poster_ip)
386 if created:
374 if created:
387 ban.reason = 'Banned for post ' + str(post_id)
375 ban.reason = 'Banned for post ' + str(post_id)
388 ban.save()
376 ban.save()
389
377
390 return _redirect_to_next(request)
378 return _redirect_to_next(request)
391
379
392
380
393 def you_are_banned(request):
381 def you_are_banned(request):
394 """Show the page that notifies that user is banned"""
382 """Show the page that notifies that user is banned"""
395
383
396 context = _init_default_context(request)
384 context = _init_default_context(request)
397
385
398 ban = get_object_or_404(Ban, ip=utils.get_client_ip(request))
386 ban = get_object_or_404(Ban, ip=utils.get_client_ip(request))
399 context['ban_reason'] = ban.reason
387 context['ban_reason'] = ban.reason
400 return render(request, 'boards/staticpages/banned.html', context)
388 return render(request, 'boards/staticpages/banned.html', context)
401
389
402
390
403 def page_404(request):
391 def page_404(request):
404 """Show page 404 (not found error)"""
392 """Show page 404 (not found error)"""
405
393
406 context = _init_default_context(request)
394 context = _init_default_context(request)
407 return render(request, 'boards/404.html', context)
395 return render(request, 'boards/404.html', context)
408
396
409
397
410 @transaction.atomic
398 @transaction.atomic
411 def tag_subscribe(request, tag_name):
399 def tag_subscribe(request, tag_name):
412 """Add tag to favorites"""
400 """Add tag to favorites"""
413
401
414 user = _get_user(request)
402 user = _get_user(request)
415 tag = get_object_or_404(Tag, name=tag_name)
403 tag = get_object_or_404(Tag, name=tag_name)
416
404
417 if not tag in user.fav_tags.all():
405 if not tag in user.fav_tags.all():
418 user.add_tag(tag)
406 user.add_tag(tag)
419
407
420 return _redirect_to_next(request)
408 return _redirect_to_next(request)
421
409
422
410
423 @transaction.atomic
411 @transaction.atomic
424 def tag_unsubscribe(request, tag_name):
412 def tag_unsubscribe(request, tag_name):
425 """Remove tag from favorites"""
413 """Remove tag from favorites"""
426
414
427 user = _get_user(request)
415 user = _get_user(request)
428 tag = get_object_or_404(Tag, name=tag_name)
416 tag = get_object_or_404(Tag, name=tag_name)
429
417
430 if tag in user.fav_tags.all():
418 if tag in user.fav_tags.all():
431 user.remove_tag(tag)
419 user.remove_tag(tag)
432
420
433 return _redirect_to_next(request)
421 return _redirect_to_next(request)
434
422
435
423
436 def static_page(request, name):
424 def static_page(request, name):
437 """Show a static page that needs only tags list and a CSS"""
425 """Show a static page that needs only tags list and a CSS"""
438
426
439 context = _init_default_context(request)
427 context = _init_default_context(request)
440 return render(request, 'boards/staticpages/' + name + '.html', context)
428 return render(request, 'boards/staticpages/' + name + '.html', context)
441
429
442
430
443 def api_get_post(request, post_id):
431 def api_get_post(request, post_id):
444 """
432 """
445 Get the JSON of a post. This can be
433 Get the JSON of a post. This can be
446 used as and API for external clients.
434 used as and API for external clients.
447 """
435 """
448
436
449 post = get_object_or_404(Post, id=post_id)
437 post = get_object_or_404(Post, id=post_id)
450
438
451 json = serializers.serialize("json", [post], fields=(
439 json = serializers.serialize("json", [post], fields=(
452 "pub_time", "_text_rendered", "title", "text", "image",
440 "pub_time", "_text_rendered", "title", "text", "image",
453 "image_width", "image_height", "replies", "tags"
441 "image_width", "image_height", "replies", "tags"
454 ))
442 ))
455
443
456 return HttpResponse(content=json)
444 return HttpResponse(content=json)
457
445
458
446
459 @cache_page(86400)
447 @cache_page(86400)
460 def cached_js_catalog(request, domain='djangojs', packages=None):
448 def cached_js_catalog(request, domain='djangojs', packages=None):
461 return javascript_catalog(request, domain, packages)
449 return javascript_catalog(request, domain, packages)
462
450
463
451
464 def _get_theme(request, user=None):
452 def _get_theme(request, user=None):
465 """Get user's CSS theme"""
453 """Get user's CSS theme"""
466
454
467 if not user:
455 if not user:
468 user = _get_user(request)
456 user = _get_user(request)
469 theme = user.get_setting('theme')
457 theme = user.get_setting('theme')
470 if not theme:
458 if not theme:
471 theme = neboard.settings.DEFAULT_THEME
459 theme = neboard.settings.DEFAULT_THEME
472
460
473 return theme
461 return theme
474
462
475
463
476 def _init_default_context(request):
464 def _init_default_context(request):
477 """Create context with default values that are used in most views"""
465 """Create context with default values that are used in most views"""
478
466
479 context = RequestContext(request)
467 context = RequestContext(request)
480
468
481 user = _get_user(request)
469 user = _get_user(request)
482 context['user'] = user
470 context['user'] = user
483 context['tags'] = user.get_sorted_fav_tags()
471 context['tags'] = user.get_sorted_fav_tags()
484 context['posts_per_day'] = float(Post.objects.get_posts_per_day())
472 context['posts_per_day'] = float(Post.objects.get_posts_per_day())
485
473
486 theme = _get_theme(request, user)
474 theme = _get_theme(request, user)
487 context['theme'] = theme
475 context['theme'] = theme
488 context['theme_css'] = 'css/' + theme + '/base_page.css'
476 context['theme_css'] = 'css/' + theme + '/base_page.css'
489
477
490 # This shows the moderator panel
478 # This shows the moderator panel
491 moderate = user.get_setting(SETTING_MODERATE)
479 moderate = user.get_setting(SETTING_MODERATE)
492 if moderate == 'True':
480 if moderate == 'True':
493 context['moderator'] = user.is_moderator()
481 context['moderator'] = user.is_moderator()
494 else:
482 else:
495 context['moderator'] = False
483 context['moderator'] = False
496
484
497 return context
485 return context
498
486
499
487
500 def _get_user(request):
488 def _get_user(request):
501 """
489 """
502 Get current user from the session. If the user does not exist, create
490 Get current user from the session. If the user does not exist, create
503 a new one.
491 a new one.
504 """
492 """
505
493
506 session = request.session
494 session = request.session
507 if not 'user_id' in session:
495 if not 'user_id' in session:
508 request.session.save()
496 request.session.save()
509
497
510 md5 = hashlib.md5()
498 md5 = hashlib.md5()
511 md5.update(session.session_key)
499 md5.update(session.session_key)
512 new_id = md5.hexdigest()
500 new_id = md5.hexdigest()
513
501
514 while User.objects.filter(user_id=new_id).exists():
502 while User.objects.filter(user_id=new_id).exists():
515 md5.update(str(timezone.now()))
503 md5.update(str(timezone.now()))
516 new_id = md5.hexdigest()
504 new_id = md5.hexdigest()
517
505
518 time_now = timezone.now()
506 time_now = timezone.now()
519 user = User.objects.create(user_id=new_id, rank=RANK_USER,
507 user = User.objects.create(user_id=new_id, rank=RANK_USER,
520 registration_time=time_now)
508 registration_time=time_now)
521
509
522 _delete_old_users()
510 _delete_old_users()
523
511
524 session['user_id'] = user.id
512 session['user_id'] = user.id
525 else:
513 else:
526 user = User.objects.get(id=session['user_id'])
514 user = User.objects.get(id=session['user_id'])
527
515
528 return user
516 return user
529
517
530
518
531 def _redirect_to_next(request):
519 def _redirect_to_next(request):
532 """
520 """
533 If a 'next' parameter was specified, redirect to the next page. This is
521 If a 'next' parameter was specified, redirect to the next page. This is
534 used when the user is required to return to some page after the current
522 used when the user is required to return to some page after the current
535 view has finished its work.
523 view has finished its work.
536 """
524 """
537
525
538 if 'next' in request.GET:
526 if 'next' in request.GET:
539 next_page = request.GET['next']
527 next_page = request.GET['next']
540 return HttpResponseRedirect(next_page)
528 return HttpResponseRedirect(next_page)
541 else:
529 else:
542 return redirect(index)
530 return redirect(index)
543
531
544
532
545 @transaction.atomic
533 @transaction.atomic
546 def _ban_current_user(request):
534 def _ban_current_user(request):
547 """Add current user to the IP ban list"""
535 """Add current user to the IP ban list"""
548
536
549 ip = utils.get_client_ip(request)
537 ip = utils.get_client_ip(request)
550 ban, created = Ban.objects.get_or_create(ip=ip)
538 ban, created = Ban.objects.get_or_create(ip=ip)
551 if created:
539 if created:
552 ban.can_read = False
540 ban.can_read = False
553 ban.reason = BAN_REASON_SPAM
541 ban.reason = BAN_REASON_SPAM
554 ban.save()
542 ban.save()
555
543
556
544
557 def _remove_invalid_links(text):
545 def _remove_invalid_links(text):
558 """
546 """
559 Replace invalid links in posts so that they won't be parsed.
547 Replace invalid links in posts so that they won't be parsed.
560 Invalid links are links to non-existent posts
548 Invalid links are links to non-existent posts
561 """
549 """
562
550
563 for reply_number in re.finditer(REGEX_REPLY, text):
551 for reply_number in re.finditer(REGEX_REPLY, text):
564 post_id = reply_number.group(1)
552 post_id = reply_number.group(1)
565 post = Post.objects.filter(id=post_id)
553 post = Post.objects.filter(id=post_id)
566 if not post.exists():
554 if not post.exists():
567 text = string.replace(text, '>>' + post_id, post_id)
555 text = string.replace(text, '>>' + post_id, post_id)
568
556
569 return text
557 return text
570
558
571
559
572 def _datetime_to_epoch(datetime):
560 def _datetime_to_epoch(datetime):
573 return int(time.mktime(timezone.localtime(
561 return int(time.mktime(timezone.localtime(
574 datetime,timezone.get_current_timezone()).timetuple())
562 datetime,timezone.get_current_timezone()).timetuple())
575 * 1000000 + datetime.microsecond)
563 * 1000000 + datetime.microsecond)
576
564
577
565
578 def _get_template_thread(thread_to_show):
566 def _get_template_thread(thread_to_show):
579 """Get template values for thread"""
567 """Get template values for thread"""
580
568
581 last_replies = thread_to_show.get_last_replies()
569 last_replies = thread_to_show.get_last_replies()
582 skipped_replies_count = thread_to_show.get_replies().count() \
570 skipped_replies_count = thread_to_show.get_replies().count() \
583 - len(last_replies) - 1
571 - len(last_replies) - 1
584 return {
572 return {
585 'thread': thread_to_show,
573 'thread': thread_to_show,
586 'op': thread_to_show.get_replies()[0],
574 'op': thread_to_show.get_replies()[0],
587 'bumpable': thread_to_show.can_bump(),
575 'bumpable': thread_to_show.can_bump(),
588 'last_replies': last_replies,
576 'last_replies': last_replies,
589 'skipped_replies': skipped_replies_count,
577 'skipped_replies': skipped_replies_count,
590 }
578 }
591
579
592
580
593 def _delete_old_users():
581 def _delete_old_users():
594 """
582 """
595 Delete users with no favorite tags and posted messages. These can be spam
583 Delete users with no favorite tags and posted messages. These can be spam
596 bots or just old user accounts
584 bots or just old user accounts
597 """
585 """
598
586
599 old_registration_date = datetime.now().date() - timedelta(OLD_USER_AGE_DAYS)
587 old_registration_date = datetime.now().date() - timedelta(OLD_USER_AGE_DAYS)
600
588
601 for user in User.objects.annotate(tags_count=Count('fav_tags')).filter(
589 for user in User.objects.annotate(tags_count=Count('fav_tags')).filter(
602 tags_count=0).filter(registration_time__lt=old_registration_date):
590 tags_count=0).filter(registration_time__lt=old_registration_date):
603 if not Post.objects.filter(user=user).exists():
591 if not Post.objects.filter(user=user).exists():
604 user.delete()
592 user.delete()
593
594
595 def _get_page_context(paginator, context, page):
596 """
597 Get pagination context variables
598 """
599
600 context['paginator'] = paginator
601 context['current_page'] = paginator.page(int(page))
General Comments 0
You need to be logged in to leave comments. Login now