##// END OF EJS Templates
Speed up tags ordering in the navigation panel.
neko259 -
r149:71e2d696 default
parent child Browse files
Show More
@@ -1,326 +1,329 b''
1 import os
1 import os
2 from random import random
2 from random import random
3 import re
3 import re
4 import time
4 import time
5 import math
5 import math
6
6
7 from django.db import models
7 from django.db import models
8 from django.http import Http404
8 from django.http import Http404
9 from django.utils import timezone
9 from django.utils import timezone
10 from markupfield.fields import MarkupField
10 from markupfield.fields import MarkupField
11 from threading import Thread
11 from threading import Thread
12
12
13 from neboard import settings
13 from neboard import settings
14 import thumbs
14 import thumbs
15
15
16 IMAGE_THUMB_SIZE = (200, 150)
16 IMAGE_THUMB_SIZE = (200, 150)
17
17
18 TITLE_MAX_LENGTH = 50
18 TITLE_MAX_LENGTH = 50
19
19
20 DEFAULT_MARKUP_TYPE = 'markdown'
20 DEFAULT_MARKUP_TYPE = 'markdown'
21
21
22 NO_PARENT = -1
22 NO_PARENT = -1
23 NO_IP = '0.0.0.0'
23 NO_IP = '0.0.0.0'
24 UNKNOWN_UA = ''
24 UNKNOWN_UA = ''
25 ALL_PAGES = -1
25 ALL_PAGES = -1
26 OPENING_POST_POPULARITY_WEIGHT = 2
26 OPENING_POST_POPULARITY_WEIGHT = 2
27 IMAGES_DIRECTORY = 'images/'
27 IMAGES_DIRECTORY = 'images/'
28 FILE_EXTENSION_DELIMITER = '.'
28 FILE_EXTENSION_DELIMITER = '.'
29
29
30 RANK_ADMIN = 0
30 RANK_ADMIN = 0
31 RANK_MODERATOR = 10
31 RANK_MODERATOR = 10
32 RANK_USER = 100
32 RANK_USER = 100
33
33
34
34
35 class PostManager(models.Manager):
35 class PostManager(models.Manager):
36 def create_post(self, title, text, image=None, parent_id=NO_PARENT,
36 def create_post(self, title, text, image=None, parent_id=NO_PARENT,
37 ip=NO_IP, tags=None, user=None):
37 ip=NO_IP, tags=None, user=None):
38 post = self.create(title=title,
38 post = self.create(title=title,
39 text=text,
39 text=text,
40 pub_time=timezone.now(),
40 pub_time=timezone.now(),
41 parent=parent_id,
41 parent=parent_id,
42 image=image,
42 image=image,
43 poster_ip=ip,
43 poster_ip=ip,
44 poster_user_agent=UNKNOWN_UA,
44 poster_user_agent=UNKNOWN_UA,
45 last_edit_time=timezone.now(),
45 last_edit_time=timezone.now(),
46 user=user)
46 user=user)
47
47
48 if tags:
48 if tags:
49 map(post.tags.add, tags)
49 map(post.tags.add, tags)
50
50
51 if parent_id != NO_PARENT:
51 if parent_id != NO_PARENT:
52 self._bump_thread(parent_id)
52 self._bump_thread(parent_id)
53 else:
53 else:
54 self._delete_old_threads()
54 self._delete_old_threads()
55
55
56 return post
56 return post
57
57
58 def delete_post(self, post):
58 def delete_post(self, post):
59 children = self.filter(parent=post.id)
59 children = self.filter(parent=post.id)
60
60
61 map(self.delete_post, children)
61 map(self.delete_post, children)
62 post.delete()
62 post.delete()
63
63
64 def delete_posts_by_ip(self, ip):
64 def delete_posts_by_ip(self, ip):
65 posts = self.filter(poster_ip=ip)
65 posts = self.filter(poster_ip=ip)
66 map(self.delete_post, posts)
66 map(self.delete_post, posts)
67
67
68 def get_threads(self, tag=None, page=ALL_PAGES,
68 def get_threads(self, tag=None, page=ALL_PAGES,
69 order_by='-last_edit_time'):
69 order_by='-last_edit_time'):
70 if tag:
70 if tag:
71 threads = self.filter(parent=NO_PARENT, tags=tag)
71 threads = self.filter(parent=NO_PARENT, tags=tag)
72
72
73 # TODO Throw error 404 if no threads for tag found?
73 # TODO Throw error 404 if no threads for tag found?
74 else:
74 else:
75 threads = self.filter(parent=NO_PARENT)
75 threads = self.filter(parent=NO_PARENT)
76
76
77 threads = threads.order_by(order_by)
77 threads = threads.order_by(order_by)
78
78
79 if page != ALL_PAGES:
79 if page != ALL_PAGES:
80 thread_count = len(threads)
80 thread_count = len(threads)
81
81
82 if page < self.get_thread_page_count(tag=tag):
82 if page < self.get_thread_page_count(tag=tag):
83 start_thread = page * settings.THREADS_PER_PAGE
83 start_thread = page * settings.THREADS_PER_PAGE
84 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
84 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
85 thread_count)
85 thread_count)
86 threads = threads[start_thread:end_thread]
86 threads = threads[start_thread:end_thread]
87
87
88 return threads
88 return threads
89
89
90 def get_thread(self, opening_post_id):
90 def get_thread(self, opening_post_id):
91 try:
91 try:
92 opening_post = self.get(id=opening_post_id, parent=NO_PARENT)
92 opening_post = self.get(id=opening_post_id, parent=NO_PARENT)
93 except Post.DoesNotExist:
93 except Post.DoesNotExist:
94 raise Http404
94 raise Http404
95
95
96 if opening_post.parent == NO_PARENT:
96 if opening_post.parent == NO_PARENT:
97 replies = self.filter(parent=opening_post_id)
97 replies = self.filter(parent=opening_post_id)
98
98
99 thread = [opening_post]
99 thread = [opening_post]
100 thread.extend(replies)
100 thread.extend(replies)
101
101
102 return thread
102 return thread
103
103
104 def exists(self, post_id):
104 def exists(self, post_id):
105 posts = self.filter(id=post_id)
105 posts = self.filter(id=post_id)
106
106
107 return posts.count() > 0
107 return posts.count() > 0
108
108
109 def get_thread_page_count(self, tag=None):
109 def get_thread_page_count(self, tag=None):
110 if tag:
110 if tag:
111 threads = self.filter(parent=NO_PARENT, tags=tag)
111 threads = self.filter(parent=NO_PARENT, tags=tag)
112 else:
112 else:
113 threads = self.filter(parent=NO_PARENT)
113 threads = self.filter(parent=NO_PARENT)
114
114
115 return int(math.ceil(threads.count() / float(
115 return int(math.ceil(threads.count() / float(
116 settings.THREADS_PER_PAGE)))
116 settings.THREADS_PER_PAGE)))
117
117
118 def _delete_old_threads(self):
118 def _delete_old_threads(self):
119 """
119 """
120 Preserves maximum thread count. If there are too many threads,
120 Preserves maximum thread count. If there are too many threads,
121 delete the old ones.
121 delete the old ones.
122 """
122 """
123
123
124 # TODO Move old threads to the archive instead of deleting them.
124 # TODO Move old threads to the archive instead of deleting them.
125 # Maybe make some 'old' field in the model to indicate the thread
125 # Maybe make some 'old' field in the model to indicate the thread
126 # must not be shown and be able for replying.
126 # must not be shown and be able for replying.
127
127
128 threads = self.get_threads()
128 threads = self.get_threads()
129 thread_count = len(threads)
129 thread_count = len(threads)
130
130
131 if thread_count > settings.MAX_THREAD_COUNT:
131 if thread_count > settings.MAX_THREAD_COUNT:
132 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
132 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
133 old_threads = threads[thread_count - num_threads_to_delete:]
133 old_threads = threads[thread_count - num_threads_to_delete:]
134
134
135 map(self.delete_post, old_threads)
135 map(self.delete_post, old_threads)
136
136
137 def _bump_thread(self, thread_id):
137 def _bump_thread(self, thread_id):
138 thread = self.get(id=thread_id)
138 thread = self.get(id=thread_id)
139
139
140 if thread.can_bump():
140 if thread.can_bump():
141 thread.last_edit_time = timezone.now()
141 thread.last_edit_time = timezone.now()
142 thread.save()
142 thread.save()
143
143
144
144
145 class TagManager(models.Manager):
145 class TagManager(models.Manager):
146 def get_not_empty_tags(self):
146 def get_not_empty_tags(self):
147 all_tags = self.all().order_by('name')
147 all_tags = self.all().order_by('name')
148 tags = []
148 tags = []
149 for tag in all_tags:
149 for tag in all_tags:
150 if not tag.is_empty():
150 if not tag.is_empty():
151 tags.append(tag)
151 tags.append(tag)
152
152
153 return tags
153 return tags
154
154
155 def get_popular_tags(self):
155 def get_popular_tags(self):
156 all_tags = self.get_not_empty_tags()
156 all_tags = self.get_not_empty_tags()
157
157
158 sorted_tags = sorted(all_tags, key=lambda tag: tag.get_popularity(),
158 sorted_tags = sorted(all_tags, key=lambda tag: tag.get_popularity(),
159 reverse=True)
159 reverse=True)
160
160
161 return sorted_tags[:settings.POPULAR_TAGS]
161 return sorted_tags[:settings.POPULAR_TAGS]
162
162
163
163
164 class Tag(models.Model):
164 class Tag(models.Model):
165 """
165 """
166 A tag is a text node assigned to the post. The tag serves as a board
166 A tag is a text node assigned to the post. The tag serves as a board
167 section. There can be multiple tags for each message
167 section. There can be multiple tags for each message
168 """
168 """
169
169
170 objects = TagManager()
170 objects = TagManager()
171
171
172 name = models.CharField(max_length=100)
172 name = models.CharField(max_length=100)
173
173
174 def __unicode__(self):
174 def __unicode__(self):
175 return self.name
175 return self.name
176
176
177 def is_empty(self):
177 def is_empty(self):
178 return self.get_post_count() == 0
178 return self.get_post_count() == 0
179
179
180 def get_post_count(self):
180 def get_post_count(self):
181 posts_with_tag = Post.objects.get_threads(tag=self)
181 posts_with_tag = Post.objects.get_threads(tag=self)
182 return posts_with_tag.count()
182 return posts_with_tag.count()
183
183
184 def get_popularity(self):
184 def get_popularity(self):
185 posts_with_tag = Post.objects.get_threads(tag=self)
185 posts_with_tag = Post.objects.get_threads(tag=self)
186 reply_count = 0
186 reply_count = 0
187 for post in posts_with_tag:
187 for post in posts_with_tag:
188 reply_count += post.get_reply_count()
188 reply_count += post.get_reply_count()
189 reply_count += OPENING_POST_POPULARITY_WEIGHT
189 reply_count += OPENING_POST_POPULARITY_WEIGHT
190
190
191 return reply_count
191 return reply_count
192
192
193
193
194 class Post(models.Model):
194 class Post(models.Model):
195 """A post is a message."""
195 """A post is a message."""
196
196
197 objects = PostManager()
197 objects = PostManager()
198
198
199 def _update_image_filename(self, filename):
199 def _update_image_filename(self, filename):
200 """Get unique image filename"""
200 """Get unique image filename"""
201
201
202 path = IMAGES_DIRECTORY
202 path = IMAGES_DIRECTORY
203 new_name = str(int(time.mktime(time.gmtime())))
203 new_name = str(int(time.mktime(time.gmtime())))
204 new_name += str(int(random() * 1000))
204 new_name += str(int(random() * 1000))
205 new_name += FILE_EXTENSION_DELIMITER
205 new_name += FILE_EXTENSION_DELIMITER
206 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
206 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
207
207
208 return os.path.join(path, new_name)
208 return os.path.join(path, new_name)
209
209
210 title = models.CharField(max_length=TITLE_MAX_LENGTH)
210 title = models.CharField(max_length=TITLE_MAX_LENGTH)
211 pub_time = models.DateTimeField()
211 pub_time = models.DateTimeField()
212 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
212 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
213 escape_html=False)
213 escape_html=False)
214
214
215 image_width = models.IntegerField(default=0)
215 image_width = models.IntegerField(default=0)
216 image_height = models.IntegerField(default=0)
216 image_height = models.IntegerField(default=0)
217
217
218 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
218 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
219 blank=True, sizes=(IMAGE_THUMB_SIZE,),
219 blank=True, sizes=(IMAGE_THUMB_SIZE,),
220 width_field='image_width',
220 width_field='image_width',
221 height_field='image_height')
221 height_field='image_height')
222
222
223 poster_ip = models.GenericIPAddressField()
223 poster_ip = models.GenericIPAddressField()
224 poster_user_agent = models.TextField()
224 poster_user_agent = models.TextField()
225 parent = models.BigIntegerField()
225 parent = models.BigIntegerField()
226 tags = models.ManyToManyField(Tag)
226 tags = models.ManyToManyField(Tag)
227 last_edit_time = models.DateTimeField()
227 last_edit_time = models.DateTimeField()
228 user = models.ForeignKey('User', null=True, default=None)
228 user = models.ForeignKey('User', null=True, default=None)
229
229
230 def __unicode__(self):
230 def __unicode__(self):
231 return '#' + str(self.id) + ' ' + self.title + ' (' + \
231 return '#' + str(self.id) + ' ' + self.title + ' (' + \
232 self.text.raw[:50] + ')'
232 self.text.raw[:50] + ')'
233
233
234 def get_title(self):
234 def get_title(self):
235 title = self.title
235 title = self.title
236 if len(title) == 0:
236 if len(title) == 0:
237 title = self.text.raw[:20]
237 title = self.text.raw[:20]
238
238
239 return title
239 return title
240
240
241 def _get_replies(self):
241 def _get_replies(self):
242 return Post.objects.filter(parent=self.id)
242 return Post.objects.filter(parent=self.id)
243
243
244 def get_reply_count(self):
244 def get_reply_count(self):
245 return self._get_replies().count()
245 return self._get_replies().count()
246
246
247 def get_images_count(self):
247 def get_images_count(self):
248 images_count = 1 if self.image else 0
248 images_count = 1 if self.image else 0
249 for reply in self._get_replies():
249 for reply in self._get_replies():
250 if reply.image:
250 if reply.image:
251 images_count += 1
251 images_count += 1
252
252
253 return images_count
253 return images_count
254
254
255 def can_bump(self):
255 def can_bump(self):
256 """Check if the thread can be bumped by replying"""
256 """Check if the thread can be bumped by replying"""
257
257
258 replies_count = len(Post.objects.get_thread(self.id))
258 replies_count = len(Post.objects.get_thread(self.id))
259
259
260 return replies_count <= settings.MAX_POSTS_PER_THREAD
260 return replies_count <= settings.MAX_POSTS_PER_THREAD
261
261
262 def get_last_replies(self):
262 def get_last_replies(self):
263 if settings.LAST_REPLIES_COUNT > 0:
263 if settings.LAST_REPLIES_COUNT > 0:
264 reply_count = self.get_reply_count()
264 reply_count = self.get_reply_count()
265
265
266 if reply_count > 0:
266 if reply_count > 0:
267 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
267 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
268 reply_count)
268 reply_count)
269 last_replies = self._get_replies()[reply_count
269 last_replies = self._get_replies()[reply_count
270 - reply_count_to_show:]
270 - reply_count_to_show:]
271
271
272 return last_replies
272 return last_replies
273
273
274
274
275 class User(models.Model):
275 class User(models.Model):
276
276
277 user_id = models.CharField(max_length=50)
277 user_id = models.CharField(max_length=50)
278 rank = models.IntegerField()
278 rank = models.IntegerField()
279
279
280 registration_time = models.DateTimeField()
280 registration_time = models.DateTimeField()
281 last_access_time = models.DateTimeField()
281 last_access_time = models.DateTimeField()
282
282
283 fav_tags = models.ManyToManyField(Tag, null=True, blank=True)
283 fav_tags = models.ManyToManyField(Tag, null=True, blank=True)
284 fav_threads = models.ManyToManyField(Post, related_name='+', null=True,
284 fav_threads = models.ManyToManyField(Post, related_name='+', null=True,
285 blank=True)
285 blank=True)
286
286
287 def save_setting(self, name, value):
287 def save_setting(self, name, value):
288 setting, created = Setting.objects.get_or_create(name=name, user=self)
288 setting, created = Setting.objects.get_or_create(name=name, user=self)
289 setting.value = value
289 setting.value = value
290 setting.save()
290 setting.save()
291
291
292 return setting
292 return setting
293
293
294 def get_setting(self, name):
294 def get_setting(self, name):
295 settings = Setting.objects.filter(name=name, user=self)
295 settings = Setting.objects.filter(name=name, user=self)
296 if len(settings) > 0:
296 if len(settings) > 0:
297 setting = settings[0]
297 setting = settings[0]
298 else:
298 else:
299 setting = None
299 setting = None
300
300
301 if setting:
301 if setting:
302 setting_value = setting.value
302 setting_value = setting.value
303 else:
303 else:
304 setting_value = None
304 setting_value = None
305
305
306 return setting_value
306 return setting_value
307
307
308 def is_moderator(self):
308 def is_moderator(self):
309 return RANK_MODERATOR >= self.rank
309 return RANK_MODERATOR >= self.rank
310
310
311 def get_sorted_fav_tags(self):
312 return self.fav_tags.order_by('name')
313
311 def __unicode__(self):
314 def __unicode__(self):
312 return self.user_id + '(' + self.rank + ')'
315 return self.user_id + '(' + self.rank + ')'
313
316
314
317
315 class Setting(models.Model):
318 class Setting(models.Model):
316
319
317 name = models.CharField(max_length=50)
320 name = models.CharField(max_length=50)
318 value = models.CharField(max_length=50)
321 value = models.CharField(max_length=50)
319 user = models.ForeignKey(User)
322 user = models.ForeignKey(User)
320
323
321
324
322 class Ban(models.Model):
325 class Ban(models.Model):
323 ip = models.GenericIPAddressField()
326 ip = models.GenericIPAddressField()
324
327
325 def __unicode__(self):
328 def __unicode__(self):
326 return self.ip
329 return self.ip
@@ -1,323 +1,323 b''
1 import hashlib
1 import hashlib
2 from django.core.urlresolvers import reverse
2 from django.core.urlresolvers import reverse
3 from django.template import RequestContext
3 from django.template import RequestContext
4 from django.shortcuts import render, redirect, get_object_or_404
4 from django.shortcuts import render, redirect, get_object_or_404
5 from django.utils import timezone
5 from django.utils import timezone
6
6
7 from boards import forms
7 from boards import forms
8 import boards
8 import boards
9 from boards import utils
9 from boards import utils
10 from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
10 from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
11 ThreadCaptchaForm, PostCaptchaForm, LoginForm
11 ThreadCaptchaForm, PostCaptchaForm, LoginForm
12
12
13 from boards.models import Post, Tag, Ban, User, RANK_USER, NO_PARENT
13 from boards.models import Post, Tag, Ban, User, RANK_USER, NO_PARENT
14 from boards import authors
14 from boards import authors
15 import neboard
15 import neboard
16
16
17
17
18 def index(request, page=0):
18 def index(request, page=0):
19 context = _init_default_context(request)
19 context = _init_default_context(request)
20
20
21 if utils.need_include_captcha(request):
21 if utils.need_include_captcha(request):
22 threadFormClass = ThreadCaptchaForm
22 threadFormClass = ThreadCaptchaForm
23 kwargs = {'request': request}
23 kwargs = {'request': request}
24 else:
24 else:
25 threadFormClass = ThreadForm
25 threadFormClass = ThreadForm
26 kwargs = {}
26 kwargs = {}
27
27
28 if request.method == 'POST':
28 if request.method == 'POST':
29 form = threadFormClass(request.POST, request.FILES,
29 form = threadFormClass(request.POST, request.FILES,
30 error_class=PlainErrorList, **kwargs)
30 error_class=PlainErrorList, **kwargs)
31
31
32 if form.is_valid():
32 if form.is_valid():
33 return _new_post(request, form)
33 return _new_post(request, form)
34 else:
34 else:
35 form = threadFormClass(error_class=PlainErrorList, **kwargs)
35 form = threadFormClass(error_class=PlainErrorList, **kwargs)
36
36
37 threads = Post.objects.get_threads(page=int(page))
37 threads = Post.objects.get_threads(page=int(page))
38
38
39 context['threads'] = None if len(threads) == 0 else threads
39 context['threads'] = None if len(threads) == 0 else threads
40 context['form'] = form
40 context['form'] = form
41 context['pages'] = range(Post.objects.get_thread_page_count())
41 context['pages'] = range(Post.objects.get_thread_page_count())
42
42
43 return render(request, 'boards/posting_general.html',
43 return render(request, 'boards/posting_general.html',
44 context)
44 context)
45
45
46
46
47 def _new_post(request, form, thread_id=boards.models.NO_PARENT):
47 def _new_post(request, form, thread_id=boards.models.NO_PARENT):
48 """Add a new post (in thread or as a reply)."""
48 """Add a new post (in thread or as a reply)."""
49
49
50 ip = _get_client_ip(request)
50 ip = _get_client_ip(request)
51 is_banned = Ban.objects.filter(ip=ip).count() > 0
51 is_banned = Ban.objects.filter(ip=ip).count() > 0
52
52
53 if is_banned:
53 if is_banned:
54 return redirect(you_are_banned)
54 return redirect(you_are_banned)
55
55
56 data = form.cleaned_data
56 data = form.cleaned_data
57
57
58 title = data['title']
58 title = data['title']
59 text = data['text']
59 text = data['text']
60
60
61 if 'image' in data.keys():
61 if 'image' in data.keys():
62 image = data['image']
62 image = data['image']
63 else:
63 else:
64 image = None
64 image = None
65
65
66 tags = []
66 tags = []
67
67
68 new_thread = thread_id == boards.models.NO_PARENT
68 new_thread = thread_id == boards.models.NO_PARENT
69 if new_thread:
69 if new_thread:
70 tag_strings = data['tags']
70 tag_strings = data['tags']
71
71
72 if tag_strings:
72 if tag_strings:
73 tag_strings = tag_strings.split(' ')
73 tag_strings = tag_strings.split(' ')
74 for tag_name in tag_strings:
74 for tag_name in tag_strings:
75 tag_name = tag_name.strip()
75 tag_name = tag_name.strip()
76 if len(tag_name) > 0:
76 if len(tag_name) > 0:
77 tag, created = Tag.objects.get_or_create(name=tag_name)
77 tag, created = Tag.objects.get_or_create(name=tag_name)
78 tags.append(tag)
78 tags.append(tag)
79
79
80 # TODO Add a possibility to define a link image instead of an image file.
80 # TODO Add a possibility to define a link image instead of an image file.
81 # If a link is given, download the image automatically.
81 # If a link is given, download the image automatically.
82
82
83 post = Post.objects.create_post(title=title, text=text, ip=ip,
83 post = Post.objects.create_post(title=title, text=text, ip=ip,
84 parent_id=thread_id, image=image,
84 parent_id=thread_id, image=image,
85 tags=tags)
85 tags=tags)
86
86
87 thread_to_show = (post.id if new_thread else thread_id)
87 thread_to_show = (post.id if new_thread else thread_id)
88
88
89 if new_thread:
89 if new_thread:
90 return redirect(thread, post_id=thread_to_show)
90 return redirect(thread, post_id=thread_to_show)
91 else:
91 else:
92 return redirect(reverse(thread,
92 return redirect(reverse(thread,
93 kwargs={'post_id': thread_to_show}) + '#'
93 kwargs={'post_id': thread_to_show}) + '#'
94 + str(post.id))
94 + str(post.id))
95
95
96
96
97 def tag(request, tag_name, page=0):
97 def tag(request, tag_name, page=0):
98 """Get all tag threads (posts without a parent)."""
98 """Get all tag threads (posts without a parent)."""
99
99
100 tag = get_object_or_404(Tag, name=tag_name)
100 tag = get_object_or_404(Tag, name=tag_name)
101 threads = Post.objects.get_threads(tag=tag, page=int(page))
101 threads = Post.objects.get_threads(tag=tag, page=int(page))
102
102
103 if request.method == 'POST':
103 if request.method == 'POST':
104 form = ThreadForm(request.POST, request.FILES,
104 form = ThreadForm(request.POST, request.FILES,
105 error_class=PlainErrorList)
105 error_class=PlainErrorList)
106 if form.is_valid():
106 if form.is_valid():
107 return _new_post(request, form)
107 return _new_post(request, form)
108 else:
108 else:
109 form = forms.ThreadForm(initial={'tags': tag_name},
109 form = forms.ThreadForm(initial={'tags': tag_name},
110 error_class=PlainErrorList)
110 error_class=PlainErrorList)
111
111
112 context = _init_default_context(request)
112 context = _init_default_context(request)
113 context['threads'] = None if len(threads) == 0 else threads
113 context['threads'] = None if len(threads) == 0 else threads
114 context['tag'] = tag_name
114 context['tag'] = tag_name
115 context['pages'] = range(Post.objects.get_thread_page_count(tag=tag))
115 context['pages'] = range(Post.objects.get_thread_page_count(tag=tag))
116
116
117 context['form'] = form
117 context['form'] = form
118
118
119 return render(request, 'boards/posting_general.html',
119 return render(request, 'boards/posting_general.html',
120 context)
120 context)
121
121
122
122
123 def thread(request, post_id):
123 def thread(request, post_id):
124 """Get all thread posts"""
124 """Get all thread posts"""
125
125
126 if utils.need_include_captcha(request):
126 if utils.need_include_captcha(request):
127 postFormClass = PostCaptchaForm
127 postFormClass = PostCaptchaForm
128 kwargs = {'request': request}
128 kwargs = {'request': request}
129 else:
129 else:
130 postFormClass = PostForm
130 postFormClass = PostForm
131 kwargs = {}
131 kwargs = {}
132
132
133 if request.method == 'POST':
133 if request.method == 'POST':
134 form = postFormClass(request.POST, request.FILES,
134 form = postFormClass(request.POST, request.FILES,
135 error_class=PlainErrorList, **kwargs)
135 error_class=PlainErrorList, **kwargs)
136 if form.is_valid():
136 if form.is_valid():
137 return _new_post(request, form, post_id)
137 return _new_post(request, form, post_id)
138 else:
138 else:
139 form = postFormClass(error_class=PlainErrorList, **kwargs)
139 form = postFormClass(error_class=PlainErrorList, **kwargs)
140
140
141 posts = Post.objects.get_thread(post_id)
141 posts = Post.objects.get_thread(post_id)
142
142
143 context = _init_default_context(request)
143 context = _init_default_context(request)
144
144
145 context['posts'] = posts
145 context['posts'] = posts
146 context['form'] = form
146 context['form'] = form
147
147
148 return render(request, 'boards/thread.html', context)
148 return render(request, 'boards/thread.html', context)
149
149
150
150
151 def login(request):
151 def login(request):
152 """Log in with user id"""
152 """Log in with user id"""
153
153
154 context = _init_default_context(request)
154 context = _init_default_context(request)
155
155
156 if request.method == 'POST':
156 if request.method == 'POST':
157 form = LoginForm(request.POST, request.FILES,
157 form = LoginForm(request.POST, request.FILES,
158 error_class=PlainErrorList)
158 error_class=PlainErrorList)
159 if form.is_valid():
159 if form.is_valid():
160 user = User.objects.get(user_id=form.cleaned_data['user_id'])
160 user = User.objects.get(user_id=form.cleaned_data['user_id'])
161 request.session['user_id'] = user.id
161 request.session['user_id'] = user.id
162 return redirect(index)
162 return redirect(index)
163
163
164 else:
164 else:
165 form = LoginForm()
165 form = LoginForm()
166
166
167 context['form'] = form
167 context['form'] = form
168
168
169 return render(request, 'boards/login.html', context)
169 return render(request, 'boards/login.html', context)
170
170
171
171
172 def settings(request):
172 def settings(request):
173 """User's settings"""
173 """User's settings"""
174
174
175 context = _init_default_context(request)
175 context = _init_default_context(request)
176
176
177 if request.method == 'POST':
177 if request.method == 'POST':
178 form = SettingsForm(request.POST)
178 form = SettingsForm(request.POST)
179 if form.is_valid():
179 if form.is_valid():
180 selected_theme = form.cleaned_data['theme']
180 selected_theme = form.cleaned_data['theme']
181
181
182 user = _get_user(request)
182 user = _get_user(request)
183 user.save_setting('theme', selected_theme)
183 user.save_setting('theme', selected_theme)
184
184
185 return redirect(settings)
185 return redirect(settings)
186 else:
186 else:
187 selected_theme = _get_theme(request)
187 selected_theme = _get_theme(request)
188 form = SettingsForm(initial={'theme': selected_theme})
188 form = SettingsForm(initial={'theme': selected_theme})
189 context['form'] = form
189 context['form'] = form
190
190
191 return render(request, 'boards/settings.html', context)
191 return render(request, 'boards/settings.html', context)
192
192
193
193
194 def all_tags(request):
194 def all_tags(request):
195 """All tags list"""
195 """All tags list"""
196
196
197 context = _init_default_context(request)
197 context = _init_default_context(request)
198 context['all_tags'] = Tag.objects.get_not_empty_tags()
198 context['all_tags'] = Tag.objects.get_not_empty_tags()
199
199
200 return render(request, 'boards/tags.html', context)
200 return render(request, 'boards/tags.html', context)
201
201
202
202
203 def jump_to_post(request, post_id):
203 def jump_to_post(request, post_id):
204 """Determine thread in which the requested post is and open it's page"""
204 """Determine thread in which the requested post is and open it's page"""
205
205
206 post = get_object_or_404(Post, id=post_id)
206 post = get_object_or_404(Post, id=post_id)
207
207
208 if boards.models.NO_PARENT == post.parent:
208 if boards.models.NO_PARENT == post.parent:
209 return redirect(thread, post_id=post.id)
209 return redirect(thread, post_id=post.id)
210 else:
210 else:
211 parent_thread = get_object_or_404(Post, id=post.parent)
211 parent_thread = get_object_or_404(Post, id=post.parent)
212 return redirect(reverse(thread, kwargs={'post_id': parent_thread.id})
212 return redirect(reverse(thread, kwargs={'post_id': parent_thread.id})
213 + '#' + str(post.id))
213 + '#' + str(post.id))
214
214
215
215
216 def authors(request):
216 def authors(request):
217 context = _init_default_context(request)
217 context = _init_default_context(request)
218 context['authors'] = boards.authors.authors
218 context['authors'] = boards.authors.authors
219
219
220 return render(request, 'boards/authors.html', context)
220 return render(request, 'boards/authors.html', context)
221
221
222
222
223 def delete(request, post_id):
223 def delete(request, post_id):
224 user = _get_user(request)
224 user = _get_user(request)
225 post = get_object_or_404(Post, id=post_id)
225 post = get_object_or_404(Post, id=post_id)
226
226
227 if user.is_moderator():
227 if user.is_moderator():
228 # TODO Show confirmation page before deletion
228 # TODO Show confirmation page before deletion
229 Post.objects.delete_post(post)
229 Post.objects.delete_post(post)
230
230
231 if NO_PARENT == post.parent:
231 if NO_PARENT == post.parent:
232 return redirect(index)
232 return redirect(index)
233 else:
233 else:
234 return redirect(thread, post_id=post.parent)
234 return redirect(thread, post_id=post.parent)
235
235
236
236
237 def you_are_banned(request):
237 def you_are_banned(request):
238 context = _init_default_context(request)
238 context = _init_default_context(request)
239 return render(request, 'boards/banned.html', context)
239 return render(request, 'boards/banned.html', context)
240
240
241
241
242 def page_404(request):
242 def page_404(request):
243 context = _init_default_context(request)
243 context = _init_default_context(request)
244 return render(request, 'boards/404.html', context)
244 return render(request, 'boards/404.html', context)
245
245
246
246
247 def tag_subscribe(request, tag_name):
247 def tag_subscribe(request, tag_name):
248 user = _get_user(request)
248 user = _get_user(request)
249 tag = get_object_or_404(Tag, name=tag_name)
249 tag = get_object_or_404(Tag, name=tag_name)
250
250
251 if not tag in user.fav_tags.all():
251 if not tag in user.fav_tags.all():
252 user.fav_tags.add(tag)
252 user.fav_tags.add(tag)
253
253
254 return redirect(all_tags)
254 return redirect(all_tags)
255
255
256
256
257 def tag_unsubscribe(request, tag_name):
257 def tag_unsubscribe(request, tag_name):
258 user = _get_user(request)
258 user = _get_user(request)
259 tag = get_object_or_404(Tag, name=tag_name)
259 tag = get_object_or_404(Tag, name=tag_name)
260
260
261 if tag in user.fav_tags.all():
261 if tag in user.fav_tags.all():
262 user.fav_tags.remove(tag)
262 user.fav_tags.remove(tag)
263
263
264 return redirect(all_tags)
264 return redirect(all_tags)
265
265
266
266
267 def _get_theme(request, user=None):
267 def _get_theme(request, user=None):
268 """Get user's CSS theme"""
268 """Get user's CSS theme"""
269
269
270 if not user:
270 if not user:
271 user = _get_user(request)
271 user = _get_user(request)
272 theme = user.get_setting('theme')
272 theme = user.get_setting('theme')
273 if not theme:
273 if not theme:
274 theme = neboard.settings.DEFAULT_THEME
274 theme = neboard.settings.DEFAULT_THEME
275
275
276 return theme
276 return theme
277
277
278
278
279 def _get_client_ip(request):
279 def _get_client_ip(request):
280 x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
280 x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
281 if x_forwarded_for:
281 if x_forwarded_for:
282 ip = x_forwarded_for.split(',')[-1].strip()
282 ip = x_forwarded_for.split(',')[-1].strip()
283 else:
283 else:
284 ip = request.META.get('REMOTE_ADDR')
284 ip = request.META.get('REMOTE_ADDR')
285 return ip
285 return ip
286
286
287
287
288 def _init_default_context(request):
288 def _init_default_context(request):
289 """Create context with default values that are used in most views"""
289 """Create context with default values that are used in most views"""
290
290
291 context = RequestContext(request)
291 context = RequestContext(request)
292
292
293 user = _get_user(request)
293 user = _get_user(request)
294 context['user'] = user
294 context['user'] = user
295 context['tags'] = sorted(user.fav_tags.all(), key=lambda tag: tag.name)
295 context['tags'] = user.get_sorted_fav_tags()
296 context['theme'] = _get_theme(request, user)
296 context['theme'] = _get_theme(request, user)
297
297
298 return context
298 return context
299
299
300
300
301 def _get_user(request):
301 def _get_user(request):
302 """Get current user from the session"""
302 """Get current user from the session"""
303
303
304 session = request.session
304 session = request.session
305 if not 'user_id' in session:
305 if not 'user_id' in session:
306 request.session.save()
306 request.session.save()
307
307
308 md5 = hashlib.md5()
308 md5 = hashlib.md5()
309 md5.update(session.session_key)
309 md5.update(session.session_key)
310 new_id = md5.hexdigest()
310 new_id = md5.hexdigest()
311
311
312 time_now = timezone.now()
312 time_now = timezone.now()
313 user = User.objects.create(user_id=new_id, rank=RANK_USER,
313 user = User.objects.create(user_id=new_id, rank=RANK_USER,
314 registration_time=time_now,
314 registration_time=time_now,
315 last_access_time=time_now)
315 last_access_time=time_now)
316
316
317 session['user_id'] = user.id
317 session['user_id'] = user.id
318 else:
318 else:
319 user = User.objects.get(id=session['user_id'])
319 user = User.objects.get(id=session['user_id'])
320 user.last_access_time = timezone.now()
320 user.last_access_time = timezone.now()
321 user.save()
321 user.save()
322
322
323 return user
323 return user
General Comments 0
You need to be logged in to leave comments. Login now