##// END OF EJS Templates
Fixed another linked tags issue
neko259 -
r288:fadeaeb6 default
parent child Browse files
Show More
@@ -1,354 +1,358 b''
1 import os
1 import os
2 from random import random
2 from random import random
3 import time
3 import time
4 import math
4 import math
5
5
6 from django.db import models
6 from django.db import models
7 from django.db.models import Count
7 from django.db.models import Count
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
11
12 from neboard import settings
12 from neboard import settings
13 import thumbs
13 import thumbs
14
14
15 IMAGE_THUMB_SIZE = (200, 150)
15 IMAGE_THUMB_SIZE = (200, 150)
16
16
17 TITLE_MAX_LENGTH = 50
17 TITLE_MAX_LENGTH = 50
18
18
19 DEFAULT_MARKUP_TYPE = 'markdown'
19 DEFAULT_MARKUP_TYPE = 'markdown'
20
20
21 NO_PARENT = -1
21 NO_PARENT = -1
22 NO_IP = '0.0.0.0'
22 NO_IP = '0.0.0.0'
23 UNKNOWN_UA = ''
23 UNKNOWN_UA = ''
24 ALL_PAGES = -1
24 ALL_PAGES = -1
25 OPENING_POST_POPULARITY_WEIGHT = 2
25 OPENING_POST_POPULARITY_WEIGHT = 2
26 IMAGES_DIRECTORY = 'images/'
26 IMAGES_DIRECTORY = 'images/'
27 FILE_EXTENSION_DELIMITER = '.'
27 FILE_EXTENSION_DELIMITER = '.'
28
28
29 RANK_ADMIN = 0
29 RANK_ADMIN = 0
30 RANK_MODERATOR = 10
30 RANK_MODERATOR = 10
31 RANK_USER = 100 \
31 RANK_USER = 100 \
32
32
33 SETTING_MODERATE = "moderate"
33 SETTING_MODERATE = "moderate"
34
34
35
35
36 class PostManager(models.Manager):
36 class PostManager(models.Manager):
37
37
38 def create_post(self, title, text, image=None, thread=None,
38 def create_post(self, title, text, image=None, thread=None,
39 ip=NO_IP, tags=None, user=None):
39 ip=NO_IP, tags=None, user=None):
40 post = self.create(title=title,
40 post = self.create(title=title,
41 text=text,
41 text=text,
42 pub_time=timezone.now(),
42 pub_time=timezone.now(),
43 thread=thread,
43 thread=thread,
44 image=image,
44 image=image,
45 poster_ip=ip,
45 poster_ip=ip,
46 poster_user_agent=UNKNOWN_UA,
46 poster_user_agent=UNKNOWN_UA,
47 last_edit_time=timezone.now(),
47 last_edit_time=timezone.now(),
48 bump_time=timezone.now(),
48 bump_time=timezone.now(),
49 user=user)
49 user=user)
50
50
51 if tags:
51 if tags:
52 map(post.tags.add, tags)
52 map(post.tags.add, tags)
53 for tag in tags:
53 for tag in tags:
54 tag.threads.add(post)
54 tag.threads.add(post)
55
55
56 if thread:
56 if thread:
57 thread.replies.add(post)
57 thread.replies.add(post)
58 thread.bump()
58 thread.bump()
59 thread.last_edit_time = timezone.now()
59 thread.last_edit_time = timezone.now()
60 thread.save()
60 thread.save()
61 else:
61 else:
62 self._delete_old_threads()
62 self._delete_old_threads()
63
63
64 return post
64 return post
65
65
66 def delete_post(self, post):
66 def delete_post(self, post):
67 if post.replies.count() > 0:
67 if post.replies.count() > 0:
68 map(self.delete_post, post.replies.all())
68 map(self.delete_post, post.replies.all())
69
69
70 # Update thread's last edit time (used as cache key)
70 # Update thread's last edit time (used as cache key)
71 thread = post.thread
71 thread = post.thread
72 if thread:
72 if thread:
73 thread.last_edit_time = timezone.now()
73 thread.last_edit_time = timezone.now()
74 thread.save()
74 thread.save()
75
75
76 post.delete()
76 post.delete()
77
77
78 def delete_posts_by_ip(self, ip):
78 def delete_posts_by_ip(self, ip):
79 posts = self.filter(poster_ip=ip)
79 posts = self.filter(poster_ip=ip)
80 map(self.delete_post, posts)
80 map(self.delete_post, posts)
81
81
82 def get_threads(self, tag=None, page=ALL_PAGES,
82 def get_threads(self, tag=None, page=ALL_PAGES,
83 order_by='-bump_time'):
83 order_by='-bump_time'):
84 if tag:
84 if tag:
85 threads = tag.threads
85 threads = tag.threads
86
86
87 if threads.count() == 0:
87 if threads.count() == 0:
88 raise Http404
88 raise Http404
89 else:
89 else:
90 threads = self.filter(thread=None)
90 threads = self.filter(thread=None)
91
91
92 threads = threads.order_by(order_by)
92 threads = threads.order_by(order_by)
93
93
94 if page != ALL_PAGES:
94 if page != ALL_PAGES:
95 thread_count = threads.count()
95 thread_count = threads.count()
96
96
97 if page < self.get_thread_page_count(tag=tag):
97 if page < self.get_thread_page_count(tag=tag):
98 start_thread = page * settings.THREADS_PER_PAGE
98 start_thread = page * settings.THREADS_PER_PAGE
99 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
99 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
100 thread_count)
100 thread_count)
101 threads = threads[start_thread:end_thread]
101 threads = threads[start_thread:end_thread]
102
102
103 return threads
103 return threads
104
104
105 def get_thread(self, opening_post_id):
105 def get_thread(self, opening_post_id):
106 try:
106 try:
107 opening_post = self.get(id=opening_post_id, thread=None)
107 opening_post = self.get(id=opening_post_id, thread=None)
108 except Post.DoesNotExist:
108 except Post.DoesNotExist:
109 raise Http404
109 raise Http404
110
110
111 if opening_post.replies:
111 if opening_post.replies:
112 thread = [opening_post]
112 thread = [opening_post]
113 thread.extend(opening_post.replies.all().order_by('pub_time'))
113 thread.extend(opening_post.replies.all().order_by('pub_time'))
114
114
115 return thread
115 return thread
116
116
117 def exists(self, post_id):
117 def exists(self, post_id):
118 posts = self.filter(id=post_id)
118 posts = self.filter(id=post_id)
119
119
120 return posts.count() > 0
120 return posts.count() > 0
121
121
122 def get_thread_page_count(self, tag=None):
122 def get_thread_page_count(self, tag=None):
123 if tag:
123 if tag:
124 threads = self.filter(thread=None, tags=tag)
124 threads = self.filter(thread=None, tags=tag)
125 else:
125 else:
126 threads = self.filter(thread=None)
126 threads = self.filter(thread=None)
127
127
128 return int(math.ceil(threads.count() / float(
128 return int(math.ceil(threads.count() / float(
129 settings.THREADS_PER_PAGE)))
129 settings.THREADS_PER_PAGE)))
130
130
131 def _delete_old_threads(self):
131 def _delete_old_threads(self):
132 """
132 """
133 Preserves maximum thread count. If there are too many threads,
133 Preserves maximum thread count. If there are too many threads,
134 delete the old ones.
134 delete the old ones.
135 """
135 """
136
136
137 # TODO Move old threads to the archive instead of deleting them.
137 # TODO Move old threads to the archive instead of deleting them.
138 # Maybe make some 'old' field in the model to indicate the thread
138 # Maybe make some 'old' field in the model to indicate the thread
139 # must not be shown and be able for replying.
139 # must not be shown and be able for replying.
140
140
141 threads = self.get_threads()
141 threads = self.get_threads()
142 thread_count = threads.count()
142 thread_count = threads.count()
143
143
144 if thread_count > settings.MAX_THREAD_COUNT:
144 if thread_count > settings.MAX_THREAD_COUNT:
145 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
145 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
146 old_threads = threads[thread_count - num_threads_to_delete:]
146 old_threads = threads[thread_count - num_threads_to_delete:]
147
147
148 map(self.delete_post, old_threads)
148 map(self.delete_post, old_threads)
149
149
150
150
151 class TagManager(models.Manager):
151 class TagManager(models.Manager):
152
152
153 def get_not_empty_tags(self):
153 def get_not_empty_tags(self):
154 tags = self.annotate(Count('threads')) \
154 tags = self.annotate(Count('threads')) \
155 .filter(threads__count__gt=0).order_by('name')
155 .filter(threads__count__gt=0).order_by('name')
156
156
157 return tags
157 return tags
158
158
159
159
160 class Tag(models.Model):
160 class Tag(models.Model):
161 """
161 """
162 A tag is a text node assigned to the post. The tag serves as a board
162 A tag is a text node assigned to the post. The tag serves as a board
163 section. There can be multiple tags for each message
163 section. There can be multiple tags for each message
164 """
164 """
165
165
166 objects = TagManager()
166 objects = TagManager()
167
167
168 name = models.CharField(max_length=100)
168 name = models.CharField(max_length=100)
169 threads = models.ManyToManyField('Post', null=True,
169 threads = models.ManyToManyField('Post', null=True,
170 blank=True, related_name='tag+')
170 blank=True, related_name='tag+')
171 linked = models.ForeignKey('Tag', null=True, blank=True)
171 linked = models.ForeignKey('Tag', null=True, blank=True)
172
172
173 def __unicode__(self):
173 def __unicode__(self):
174 return self.name
174 return self.name
175
175
176 def is_empty(self):
176 def is_empty(self):
177 return self.get_post_count() == 0
177 return self.get_post_count() == 0
178
178
179 def get_post_count(self):
179 def get_post_count(self):
180 return self.threads.count()
180 return self.threads.count()
181
181
182 def get_popularity(self):
182 def get_popularity(self):
183 posts_with_tag = Post.objects.get_threads(tag=self)
183 posts_with_tag = Post.objects.get_threads(tag=self)
184 reply_count = 0
184 reply_count = 0
185 for post in posts_with_tag:
185 for post in posts_with_tag:
186 reply_count += post.get_reply_count()
186 reply_count += post.get_reply_count()
187 reply_count += OPENING_POST_POPULARITY_WEIGHT
187 reply_count += OPENING_POST_POPULARITY_WEIGHT
188
188
189 return reply_count
189 return reply_count
190
190
191 def get_linked_tags(self, tag_list=[]):
191 def get_linked_tags(self):
192 tag_list = []
193 self.get_linked_tags_list(tag_list)
194
195 return tag_list
196
197 def get_linked_tags_list(self, tag_list=[]):
192 """
198 """
193 Returns the list of tags linked to current. The list can be got
199 Returns the list of tags linked to current. The list can be got
194 through returned value or tag_list parameter
200 through returned value or tag_list parameter
195 """
201 """
196
202
197 linked_tag = self.linked
203 linked_tag = self.linked
198
204
199 if linked_tag and not (linked_tag in tag_list):
205 if linked_tag and not (linked_tag in tag_list):
200 tag_list.append(linked_tag)
206 tag_list.append(linked_tag)
201
207
202 linked_tag.get_linked_tags(tag_list)
208 linked_tag.get_linked_tags_list(tag_list)
203
204 return tag_list
205
209
206
210
207 class Post(models.Model):
211 class Post(models.Model):
208 """A post is a message."""
212 """A post is a message."""
209
213
210 objects = PostManager()
214 objects = PostManager()
211
215
212 def _update_image_filename(self, filename):
216 def _update_image_filename(self, filename):
213 """Get unique image filename"""
217 """Get unique image filename"""
214
218
215 path = IMAGES_DIRECTORY
219 path = IMAGES_DIRECTORY
216 new_name = str(int(time.mktime(time.gmtime())))
220 new_name = str(int(time.mktime(time.gmtime())))
217 new_name += str(int(random() * 1000))
221 new_name += str(int(random() * 1000))
218 new_name += FILE_EXTENSION_DELIMITER
222 new_name += FILE_EXTENSION_DELIMITER
219 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
223 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
220
224
221 return os.path.join(path, new_name)
225 return os.path.join(path, new_name)
222
226
223 title = models.CharField(max_length=TITLE_MAX_LENGTH)
227 title = models.CharField(max_length=TITLE_MAX_LENGTH)
224 pub_time = models.DateTimeField()
228 pub_time = models.DateTimeField()
225 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
229 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
226 escape_html=False)
230 escape_html=False)
227
231
228 image_width = models.IntegerField(default=0)
232 image_width = models.IntegerField(default=0)
229 image_height = models.IntegerField(default=0)
233 image_height = models.IntegerField(default=0)
230
234
231 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
235 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
232 blank=True, sizes=(IMAGE_THUMB_SIZE,),
236 blank=True, sizes=(IMAGE_THUMB_SIZE,),
233 width_field='image_width',
237 width_field='image_width',
234 height_field='image_height')
238 height_field='image_height')
235
239
236 poster_ip = models.GenericIPAddressField()
240 poster_ip = models.GenericIPAddressField()
237 poster_user_agent = models.TextField()
241 poster_user_agent = models.TextField()
238
242
239 thread = models.ForeignKey('Post', null=True, default=None)
243 thread = models.ForeignKey('Post', null=True, default=None)
240 tags = models.ManyToManyField(Tag)
244 tags = models.ManyToManyField(Tag)
241 last_edit_time = models.DateTimeField()
245 last_edit_time = models.DateTimeField()
242 bump_time = models.DateTimeField()
246 bump_time = models.DateTimeField()
243 user = models.ForeignKey('User', null=True, default=None)
247 user = models.ForeignKey('User', null=True, default=None)
244
248
245 replies = models.ManyToManyField('Post', symmetrical=False, null=True,
249 replies = models.ManyToManyField('Post', symmetrical=False, null=True,
246 blank=True, related_name='re+')
250 blank=True, related_name='re+')
247
251
248 def __unicode__(self):
252 def __unicode__(self):
249 return '#' + str(self.id) + ' ' + self.title + ' (' + \
253 return '#' + str(self.id) + ' ' + self.title + ' (' + \
250 self.text.raw[:50] + ')'
254 self.text.raw[:50] + ')'
251
255
252 def get_title(self):
256 def get_title(self):
253 title = self.title
257 title = self.title
254 if len(title) == 0:
258 if len(title) == 0:
255 title = self.text.raw[:20]
259 title = self.text.raw[:20]
256
260
257 return title
261 return title
258
262
259 def get_reply_count(self):
263 def get_reply_count(self):
260 return self.replies.count()
264 return self.replies.count()
261
265
262 def get_images_count(self):
266 def get_images_count(self):
263 images_count = 1 if self.image else 0
267 images_count = 1 if self.image else 0
264 images_count += self.replies.filter(image_width__gt=0).count()
268 images_count += self.replies.filter(image_width__gt=0).count()
265
269
266 return images_count
270 return images_count
267
271
268 def can_bump(self):
272 def can_bump(self):
269 """Check if the thread can be bumped by replying"""
273 """Check if the thread can be bumped by replying"""
270
274
271 post_count = self.get_reply_count() + 1
275 post_count = self.get_reply_count() + 1
272
276
273 return post_count <= settings.MAX_POSTS_PER_THREAD
277 return post_count <= settings.MAX_POSTS_PER_THREAD
274
278
275 def bump(self):
279 def bump(self):
276 """Bump (move to up) thread"""
280 """Bump (move to up) thread"""
277
281
278 if self.can_bump():
282 if self.can_bump():
279 self.bump_time = timezone.now()
283 self.bump_time = timezone.now()
280
284
281 def get_last_replies(self):
285 def get_last_replies(self):
282 if settings.LAST_REPLIES_COUNT > 0:
286 if settings.LAST_REPLIES_COUNT > 0:
283 reply_count = self.get_reply_count()
287 reply_count = self.get_reply_count()
284
288
285 if reply_count > 0:
289 if reply_count > 0:
286 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
290 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
287 reply_count)
291 reply_count)
288 last_replies = self.replies.all().order_by('pub_time')[reply_count -
292 last_replies = self.replies.all().order_by('pub_time')[reply_count -
289 reply_count_to_show:]
293 reply_count_to_show:]
290
294
291 return last_replies
295 return last_replies
292
296
293
297
294 class User(models.Model):
298 class User(models.Model):
295
299
296 user_id = models.CharField(max_length=50)
300 user_id = models.CharField(max_length=50)
297 rank = models.IntegerField()
301 rank = models.IntegerField()
298
302
299 registration_time = models.DateTimeField()
303 registration_time = models.DateTimeField()
300
304
301 fav_tags = models.ManyToManyField(Tag, null=True, blank=True)
305 fav_tags = models.ManyToManyField(Tag, null=True, blank=True)
302 fav_threads = models.ManyToManyField(Post, related_name='+', null=True,
306 fav_threads = models.ManyToManyField(Post, related_name='+', null=True,
303 blank=True)
307 blank=True)
304
308
305 def save_setting(self, name, value):
309 def save_setting(self, name, value):
306 setting, created = Setting.objects.get_or_create(name=name, user=self)
310 setting, created = Setting.objects.get_or_create(name=name, user=self)
307 setting.value = str(value)
311 setting.value = str(value)
308 setting.save()
312 setting.save()
309
313
310 return setting
314 return setting
311
315
312 def get_setting(self, name):
316 def get_setting(self, name):
313 if Setting.objects.filter(name=name, user=self).exists():
317 if Setting.objects.filter(name=name, user=self).exists():
314 setting = Setting.objects.get(name=name, user=self)
318 setting = Setting.objects.get(name=name, user=self)
315 setting_value = setting.value
319 setting_value = setting.value
316 else:
320 else:
317 setting_value = None
321 setting_value = None
318
322
319 return setting_value
323 return setting_value
320
324
321 def is_moderator(self):
325 def is_moderator(self):
322 return RANK_MODERATOR >= self.rank
326 return RANK_MODERATOR >= self.rank
323
327
324 def get_sorted_fav_tags(self):
328 def get_sorted_fav_tags(self):
325 tags = self.fav_tags.annotate(Count('threads'))\
329 tags = self.fav_tags.annotate(Count('threads'))\
326 .filter(threads__count__gt=0).order_by('name')
330 .filter(threads__count__gt=0).order_by('name')
327
331
328 return tags
332 return tags
329
333
330 def get_post_count(self):
334 def get_post_count(self):
331 return Post.objects.filter(user=self).count()
335 return Post.objects.filter(user=self).count()
332
336
333 def __unicode__(self):
337 def __unicode__(self):
334 return self.user_id + '(' + str(self.rank) + ')'
338 return self.user_id + '(' + str(self.rank) + ')'
335
339
336 def get_last_access_time(self):
340 def get_last_access_time(self):
337 posts = Post.objects.filter(user=self)
341 posts = Post.objects.filter(user=self)
338 if posts.count() > 0:
342 if posts.count() > 0:
339 return posts.latest('pub_time').pub_time
343 return posts.latest('pub_time').pub_time
340
344
341
345
342 class Setting(models.Model):
346 class Setting(models.Model):
343
347
344 name = models.CharField(max_length=50)
348 name = models.CharField(max_length=50)
345 value = models.CharField(max_length=50)
349 value = models.CharField(max_length=50)
346 user = models.ForeignKey(User)
350 user = models.ForeignKey(User)
347
351
348
352
349 class Ban(models.Model):
353 class Ban(models.Model):
350
354
351 ip = models.GenericIPAddressField()
355 ip = models.GenericIPAddressField()
352
356
353 def __unicode__(self):
357 def __unicode__(self):
354 return self.ip
358 return self.ip
@@ -1,459 +1,459 b''
1 import hashlib
1 import hashlib
2 import string
2 import string
3 from django.core import serializers
3 from django.core import serializers
4 from django.core.urlresolvers import reverse
4 from django.core.urlresolvers import reverse
5 from django.http import HttpResponseRedirect
5 from django.http import HttpResponseRedirect
6 from django.http.response import HttpResponse
6 from django.http.response import HttpResponse
7 from django.template import RequestContext
7 from django.template import RequestContext
8 from django.shortcuts import render, redirect, get_object_or_404
8 from django.shortcuts import render, redirect, get_object_or_404
9 from django.utils import timezone
9 from django.utils import timezone
10
10
11 from boards import forms
11 from boards import forms
12 import boards
12 import boards
13 from boards import utils
13 from boards import utils
14 from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
14 from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
15 ThreadCaptchaForm, PostCaptchaForm, LoginForm, ModeratorSettingsForm
15 ThreadCaptchaForm, PostCaptchaForm, LoginForm, ModeratorSettingsForm
16
16
17 from boards.models import Post, Tag, Ban, User, RANK_USER, SETTING_MODERATE
17 from boards.models import Post, Tag, Ban, User, RANK_USER, SETTING_MODERATE
18 from boards import authors
18 from boards import authors
19 from boards.utils import get_client_ip
19 from boards.utils import get_client_ip
20 import neboard
20 import neboard
21
21
22
22
23 def index(request, page=0):
23 def index(request, page=0):
24 context = _init_default_context(request)
24 context = _init_default_context(request)
25
25
26 if utils.need_include_captcha(request):
26 if utils.need_include_captcha(request):
27 threadFormClass = ThreadCaptchaForm
27 threadFormClass = ThreadCaptchaForm
28 kwargs = {'request': request}
28 kwargs = {'request': request}
29 else:
29 else:
30 threadFormClass = ThreadForm
30 threadFormClass = ThreadForm
31 kwargs = {}
31 kwargs = {}
32
32
33 if request.method == 'POST':
33 if request.method == 'POST':
34 form = threadFormClass(request.POST, request.FILES,
34 form = threadFormClass(request.POST, request.FILES,
35 error_class=PlainErrorList, **kwargs)
35 error_class=PlainErrorList, **kwargs)
36 form.session = request.session
36 form.session = request.session
37
37
38 if form.is_valid():
38 if form.is_valid():
39 return _new_post(request, form)
39 return _new_post(request, form)
40 if form.need_to_ban:
40 if form.need_to_ban:
41 # Ban user because he is suspected to be a bot
41 # Ban user because he is suspected to be a bot
42 _ban_current_user(request)
42 _ban_current_user(request)
43 else:
43 else:
44 form = threadFormClass(error_class=PlainErrorList, **kwargs)
44 form = threadFormClass(error_class=PlainErrorList, **kwargs)
45
45
46 threads = []
46 threads = []
47 for thread in Post.objects.get_threads(page=int(page)):
47 for thread in Post.objects.get_threads(page=int(page)):
48 threads.append({'thread': thread,
48 threads.append({'thread': thread,
49 'bumpable': thread.can_bump()})
49 'bumpable': thread.can_bump()})
50
50
51 context['threads'] = None if len(threads) == 0 else threads
51 context['threads'] = None if len(threads) == 0 else threads
52 context['form'] = form
52 context['form'] = form
53 context['pages'] = range(Post.objects.get_thread_page_count())
53 context['pages'] = range(Post.objects.get_thread_page_count())
54
54
55 return render(request, 'boards/posting_general.html',
55 return render(request, 'boards/posting_general.html',
56 context)
56 context)
57
57
58
58
59 def _new_post(request, form, thread_id=boards.models.NO_PARENT):
59 def _new_post(request, form, thread_id=boards.models.NO_PARENT):
60 """Add a new post (in thread or as a reply)."""
60 """Add a new post (in thread or as a reply)."""
61
61
62 ip = get_client_ip(request)
62 ip = get_client_ip(request)
63 is_banned = Ban.objects.filter(ip=ip).exists()
63 is_banned = Ban.objects.filter(ip=ip).exists()
64
64
65 if is_banned:
65 if is_banned:
66 return redirect(you_are_banned)
66 return redirect(you_are_banned)
67
67
68 data = form.cleaned_data
68 data = form.cleaned_data
69
69
70 title = data['title']
70 title = data['title']
71 text = data['text']
71 text = data['text']
72
72
73 if 'image' in data.keys():
73 if 'image' in data.keys():
74 image = data['image']
74 image = data['image']
75 else:
75 else:
76 image = None
76 image = None
77
77
78 tags = []
78 tags = []
79
79
80 new_thread = thread_id == boards.models.NO_PARENT
80 new_thread = thread_id == boards.models.NO_PARENT
81 if new_thread:
81 if new_thread:
82 tag_strings = data['tags']
82 tag_strings = data['tags']
83
83
84 if tag_strings:
84 if tag_strings:
85 tag_strings = tag_strings.split(' ')
85 tag_strings = tag_strings.split(' ')
86 for tag_name in tag_strings:
86 for tag_name in tag_strings:
87 tag_name = string.lower(tag_name.strip())
87 tag_name = string.lower(tag_name.strip())
88 if len(tag_name) > 0:
88 if len(tag_name) > 0:
89 tag, created = Tag.objects.get_or_create(name=tag_name)
89 tag, created = Tag.objects.get_or_create(name=tag_name)
90 tags.append(tag)
90 tags.append(tag)
91
91
92 linked_tags = tag.get_linked_tags()
92 linked_tags = tag.get_linked_tags()
93 if len(linked_tags) > 0:
93 if len(linked_tags) > 0:
94 tags.extend(linked_tags)
94 tags.extend(linked_tags)
95
95
96 op = None if thread_id == boards.models.NO_PARENT else \
96 op = None if thread_id == boards.models.NO_PARENT else \
97 get_object_or_404(Post, id=thread_id)
97 get_object_or_404(Post, id=thread_id)
98 post = Post.objects.create_post(title=title, text=text, ip=ip,
98 post = Post.objects.create_post(title=title, text=text, ip=ip,
99 thread=op, image=image,
99 thread=op, image=image,
100 tags=tags, user=_get_user(request))
100 tags=tags, user=_get_user(request))
101
101
102 thread_to_show = (post.id if new_thread else thread_id)
102 thread_to_show = (post.id if new_thread else thread_id)
103
103
104 if new_thread:
104 if new_thread:
105 return redirect(thread, post_id=thread_to_show)
105 return redirect(thread, post_id=thread_to_show)
106 else:
106 else:
107 return redirect(reverse(thread, kwargs={'post_id': thread_to_show}) +
107 return redirect(reverse(thread, kwargs={'post_id': thread_to_show}) +
108 '#' + str(post.id))
108 '#' + str(post.id))
109
109
110
110
111 def tag(request, tag_name, page=0):
111 def tag(request, tag_name, page=0):
112 """
112 """
113 Get all tag threads. Threads are split in pages, so some page is
113 Get all tag threads. Threads are split in pages, so some page is
114 requested. Default page is 0.
114 requested. Default page is 0.
115 """
115 """
116
116
117 tag = get_object_or_404(Tag, name=tag_name)
117 tag = get_object_or_404(Tag, name=tag_name)
118 threads = []
118 threads = []
119 for thread in Post.objects.get_threads(tag=tag, page=int(page)):
119 for thread in Post.objects.get_threads(tag=tag, page=int(page)):
120 threads.append({'thread': thread,
120 threads.append({'thread': thread,
121 'bumpable': thread.can_bump()})
121 'bumpable': thread.can_bump()})
122
122
123 if request.method == 'POST':
123 if request.method == 'POST':
124 form = ThreadForm(request.POST, request.FILES,
124 form = ThreadForm(request.POST, request.FILES,
125 error_class=PlainErrorList)
125 error_class=PlainErrorList)
126 form.session = request.session
126 form.session = request.session
127
127
128 if form.is_valid():
128 if form.is_valid():
129 return _new_post(request, form)
129 return _new_post(request, form)
130 if form.need_to_ban:
130 if form.need_to_ban:
131 # Ban user because he is suspected to be a bot
131 # Ban user because he is suspected to be a bot
132 _ban_current_user(request)
132 _ban_current_user(request)
133 else:
133 else:
134 form = forms.ThreadForm(initial={'tags': tag_name},
134 form = forms.ThreadForm(initial={'tags': tag_name},
135 error_class=PlainErrorList)
135 error_class=PlainErrorList)
136
136
137 context = _init_default_context(request)
137 context = _init_default_context(request)
138 context['threads'] = None if len(threads) == 0 else threads
138 context['threads'] = None if len(threads) == 0 else threads
139 context['tag'] = tag
139 context['tag'] = tag
140 context['pages'] = range(Post.objects.get_thread_page_count(tag=tag))
140 context['pages'] = range(Post.objects.get_thread_page_count(tag=tag))
141
141
142 context['form'] = form
142 context['form'] = form
143
143
144 return render(request, 'boards/posting_general.html',
144 return render(request, 'boards/posting_general.html',
145 context)
145 context)
146
146
147
147
148 def thread(request, post_id):
148 def thread(request, post_id):
149 """Get all thread posts"""
149 """Get all thread posts"""
150
150
151 if utils.need_include_captcha(request):
151 if utils.need_include_captcha(request):
152 postFormClass = PostCaptchaForm
152 postFormClass = PostCaptchaForm
153 kwargs = {'request': request}
153 kwargs = {'request': request}
154 else:
154 else:
155 postFormClass = PostForm
155 postFormClass = PostForm
156 kwargs = {}
156 kwargs = {}
157
157
158 if request.method == 'POST':
158 if request.method == 'POST':
159 form = postFormClass(request.POST, request.FILES,
159 form = postFormClass(request.POST, request.FILES,
160 error_class=PlainErrorList, **kwargs)
160 error_class=PlainErrorList, **kwargs)
161 form.session = request.session
161 form.session = request.session
162
162
163 if form.is_valid():
163 if form.is_valid():
164 return _new_post(request, form, post_id)
164 return _new_post(request, form, post_id)
165 if form.need_to_ban:
165 if form.need_to_ban:
166 # Ban user because he is suspected to be a bot
166 # Ban user because he is suspected to be a bot
167 _ban_current_user(request)
167 _ban_current_user(request)
168 else:
168 else:
169 form = postFormClass(error_class=PlainErrorList, **kwargs)
169 form = postFormClass(error_class=PlainErrorList, **kwargs)
170
170
171 posts = Post.objects.get_thread(post_id)
171 posts = Post.objects.get_thread(post_id)
172
172
173 context = _init_default_context(request)
173 context = _init_default_context(request)
174
174
175 context['posts'] = posts
175 context['posts'] = posts
176 context['form'] = form
176 context['form'] = form
177 context['bumpable'] = posts[0].can_bump()
177 context['bumpable'] = posts[0].can_bump()
178 if context['bumpable']:
178 if context['bumpable']:
179 context['posts_left'] = neboard.settings.MAX_POSTS_PER_THREAD - len(
179 context['posts_left'] = neboard.settings.MAX_POSTS_PER_THREAD - len(
180 posts)
180 posts)
181 context['bumplimit_progress'] = float(context['posts_left']) / \
181 context['bumplimit_progress'] = float(context['posts_left']) / \
182 neboard.settings.MAX_POSTS_PER_THREAD * 100
182 neboard.settings.MAX_POSTS_PER_THREAD * 100
183
183
184 return render(request, 'boards/thread.html', context)
184 return render(request, 'boards/thread.html', context)
185
185
186
186
187 def login(request):
187 def login(request):
188 """Log in with user id"""
188 """Log in with user id"""
189
189
190 context = _init_default_context(request)
190 context = _init_default_context(request)
191
191
192 if request.method == 'POST':
192 if request.method == 'POST':
193 form = LoginForm(request.POST, request.FILES,
193 form = LoginForm(request.POST, request.FILES,
194 error_class=PlainErrorList)
194 error_class=PlainErrorList)
195 form.session = request.session
195 form.session = request.session
196
196
197 if form.is_valid():
197 if form.is_valid():
198 user = User.objects.get(user_id=form.cleaned_data['user_id'])
198 user = User.objects.get(user_id=form.cleaned_data['user_id'])
199 request.session['user_id'] = user.id
199 request.session['user_id'] = user.id
200 return redirect(index)
200 return redirect(index)
201
201
202 else:
202 else:
203 form = LoginForm()
203 form = LoginForm()
204
204
205 context['form'] = form
205 context['form'] = form
206
206
207 return render(request, 'boards/login.html', context)
207 return render(request, 'boards/login.html', context)
208
208
209
209
210 def settings(request):
210 def settings(request):
211 """User's settings"""
211 """User's settings"""
212
212
213 context = _init_default_context(request)
213 context = _init_default_context(request)
214 user = _get_user(request)
214 user = _get_user(request)
215 is_moderator = user.is_moderator()
215 is_moderator = user.is_moderator()
216
216
217 if request.method == 'POST':
217 if request.method == 'POST':
218 if is_moderator:
218 if is_moderator:
219 form = ModeratorSettingsForm(request.POST,
219 form = ModeratorSettingsForm(request.POST,
220 error_class=PlainErrorList)
220 error_class=PlainErrorList)
221 else:
221 else:
222 form = SettingsForm(request.POST, error_class=PlainErrorList)
222 form = SettingsForm(request.POST, error_class=PlainErrorList)
223
223
224 if form.is_valid():
224 if form.is_valid():
225 selected_theme = form.cleaned_data['theme']
225 selected_theme = form.cleaned_data['theme']
226
226
227 user.save_setting('theme', selected_theme)
227 user.save_setting('theme', selected_theme)
228
228
229 if is_moderator:
229 if is_moderator:
230 moderate = form.cleaned_data['moderate']
230 moderate = form.cleaned_data['moderate']
231 user.save_setting(SETTING_MODERATE, moderate)
231 user.save_setting(SETTING_MODERATE, moderate)
232
232
233 return redirect(settings)
233 return redirect(settings)
234 else:
234 else:
235 selected_theme = _get_theme(request)
235 selected_theme = _get_theme(request)
236
236
237 if is_moderator:
237 if is_moderator:
238 form = ModeratorSettingsForm(initial={'theme': selected_theme,
238 form = ModeratorSettingsForm(initial={'theme': selected_theme,
239 'moderate': context['moderator']},
239 'moderate': context['moderator']},
240 error_class=PlainErrorList)
240 error_class=PlainErrorList)
241 else:
241 else:
242 form = SettingsForm(initial={'theme': selected_theme},
242 form = SettingsForm(initial={'theme': selected_theme},
243 error_class=PlainErrorList)
243 error_class=PlainErrorList)
244
244
245 context['form'] = form
245 context['form'] = form
246
246
247 return render(request, 'boards/settings.html', context)
247 return render(request, 'boards/settings.html', context)
248
248
249
249
250 def all_tags(request):
250 def all_tags(request):
251 """All tags list"""
251 """All tags list"""
252
252
253 context = _init_default_context(request)
253 context = _init_default_context(request)
254 context['all_tags'] = Tag.objects.get_not_empty_tags()
254 context['all_tags'] = Tag.objects.get_not_empty_tags()
255
255
256 return render(request, 'boards/tags.html', context)
256 return render(request, 'boards/tags.html', context)
257
257
258
258
259 def jump_to_post(request, post_id):
259 def jump_to_post(request, post_id):
260 """Determine thread in which the requested post is and open it's page"""
260 """Determine thread in which the requested post is and open it's page"""
261
261
262 post = get_object_or_404(Post, id=post_id)
262 post = get_object_or_404(Post, id=post_id)
263
263
264 if not post.thread:
264 if not post.thread:
265 return redirect(thread, post_id=post.id)
265 return redirect(thread, post_id=post.id)
266 else:
266 else:
267 return redirect(reverse(thread, kwargs={'post_id': post.thread.id})
267 return redirect(reverse(thread, kwargs={'post_id': post.thread.id})
268 + '#' + str(post.id))
268 + '#' + str(post.id))
269
269
270
270
271 def authors(request):
271 def authors(request):
272 """Show authors list"""
272 """Show authors list"""
273
273
274 context = _init_default_context(request)
274 context = _init_default_context(request)
275 context['authors'] = boards.authors.authors
275 context['authors'] = boards.authors.authors
276
276
277 return render(request, 'boards/authors.html', context)
277 return render(request, 'boards/authors.html', context)
278
278
279
279
280 def delete(request, post_id):
280 def delete(request, post_id):
281 """Delete post"""
281 """Delete post"""
282
282
283 user = _get_user(request)
283 user = _get_user(request)
284 post = get_object_or_404(Post, id=post_id)
284 post = get_object_or_404(Post, id=post_id)
285
285
286 if user.is_moderator():
286 if user.is_moderator():
287 # TODO Show confirmation page before deletion
287 # TODO Show confirmation page before deletion
288 Post.objects.delete_post(post)
288 Post.objects.delete_post(post)
289
289
290 if not post.thread:
290 if not post.thread:
291 return _redirect_to_next(request)
291 return _redirect_to_next(request)
292 else:
292 else:
293 return redirect(thread, post_id=post.thread.id)
293 return redirect(thread, post_id=post.thread.id)
294
294
295
295
296 def ban(request, post_id):
296 def ban(request, post_id):
297 """Ban user"""
297 """Ban user"""
298
298
299 user = _get_user(request)
299 user = _get_user(request)
300 post = get_object_or_404(Post, id=post_id)
300 post = get_object_or_404(Post, id=post_id)
301
301
302 if user.is_moderator():
302 if user.is_moderator():
303 # TODO Show confirmation page before ban
303 # TODO Show confirmation page before ban
304 Ban.objects.get_or_create(ip=post.poster_ip)
304 Ban.objects.get_or_create(ip=post.poster_ip)
305
305
306 return _redirect_to_next(request)
306 return _redirect_to_next(request)
307
307
308
308
309 def you_are_banned(request):
309 def you_are_banned(request):
310 """Show the page that notifies that user is banned"""
310 """Show the page that notifies that user is banned"""
311
311
312 context = _init_default_context(request)
312 context = _init_default_context(request)
313 return render(request, 'boards/staticpages/banned.html', context)
313 return render(request, 'boards/staticpages/banned.html', context)
314
314
315
315
316 def page_404(request):
316 def page_404(request):
317 """Show page 404 (not found error)"""
317 """Show page 404 (not found error)"""
318
318
319 context = _init_default_context(request)
319 context = _init_default_context(request)
320 return render(request, 'boards/404.html', context)
320 return render(request, 'boards/404.html', context)
321
321
322
322
323 def tag_subscribe(request, tag_name):
323 def tag_subscribe(request, tag_name):
324 """Add tag to favorites"""
324 """Add tag to favorites"""
325
325
326 user = _get_user(request)
326 user = _get_user(request)
327 tag = get_object_or_404(Tag, name=tag_name)
327 tag = get_object_or_404(Tag, name=tag_name)
328
328
329 if not tag in user.fav_tags.all():
329 if not tag in user.fav_tags.all():
330 user.fav_tags.add(tag)
330 user.fav_tags.add(tag)
331
331
332 return _redirect_to_next(request)
332 return _redirect_to_next(request)
333
333
334
334
335 def tag_unsubscribe(request, tag_name):
335 def tag_unsubscribe(request, tag_name):
336 """Remove tag from favorites"""
336 """Remove tag from favorites"""
337
337
338 user = _get_user(request)
338 user = _get_user(request)
339 tag = get_object_or_404(Tag, name=tag_name)
339 tag = get_object_or_404(Tag, name=tag_name)
340
340
341 if tag in user.fav_tags.all():
341 if tag in user.fav_tags.all():
342 user.fav_tags.remove(tag)
342 user.fav_tags.remove(tag)
343
343
344 return _redirect_to_next(request)
344 return _redirect_to_next(request)
345
345
346
346
347 def static_page(request, name):
347 def static_page(request, name):
348 """Show a static page that needs only tags list and a CSS"""
348 """Show a static page that needs only tags list and a CSS"""
349
349
350 context = _init_default_context(request)
350 context = _init_default_context(request)
351 return render(request, 'boards/staticpages/' + name + '.html', context)
351 return render(request, 'boards/staticpages/' + name + '.html', context)
352
352
353
353
354 def api_get_post(request, post_id):
354 def api_get_post(request, post_id):
355 """
355 """
356 Get the JSON of a post. This can be
356 Get the JSON of a post. This can be
357 used as and API for external clients.
357 used as and API for external clients.
358 """
358 """
359
359
360 post = get_object_or_404(Post, id=post_id)
360 post = get_object_or_404(Post, id=post_id)
361
361
362 json = serializers.serialize("json", [post], fields=(
362 json = serializers.serialize("json", [post], fields=(
363 "pub_time", "_text_rendered", "title", "text", "image",
363 "pub_time", "_text_rendered", "title", "text", "image",
364 "image_width", "image_height", "replies", "tags"
364 "image_width", "image_height", "replies", "tags"
365 ))
365 ))
366
366
367 return HttpResponse(content=json)
367 return HttpResponse(content=json)
368
368
369
369
370 def get_post(request, post_id):
370 def get_post(request, post_id):
371 """Get the html of a post. Used for popups."""
371 """Get the html of a post. Used for popups."""
372
372
373 post = get_object_or_404(Post, id=post_id)
373 post = get_object_or_404(Post, id=post_id)
374
374
375 context = RequestContext(request)
375 context = RequestContext(request)
376 context["post"] = post
376 context["post"] = post
377
377
378 return render(request, 'boards/post.html', context)
378 return render(request, 'boards/post.html', context)
379
379
380
380
381 def _get_theme(request, user=None):
381 def _get_theme(request, user=None):
382 """Get user's CSS theme"""
382 """Get user's CSS theme"""
383
383
384 if not user:
384 if not user:
385 user = _get_user(request)
385 user = _get_user(request)
386 theme = user.get_setting('theme')
386 theme = user.get_setting('theme')
387 if not theme:
387 if not theme:
388 theme = neboard.settings.DEFAULT_THEME
388 theme = neboard.settings.DEFAULT_THEME
389
389
390 return theme
390 return theme
391
391
392
392
393 def _init_default_context(request):
393 def _init_default_context(request):
394 """Create context with default values that are used in most views"""
394 """Create context with default values that are used in most views"""
395
395
396 context = RequestContext(request)
396 context = RequestContext(request)
397
397
398 user = _get_user(request)
398 user = _get_user(request)
399 context['user'] = user
399 context['user'] = user
400 context['tags'] = user.get_sorted_fav_tags()
400 context['tags'] = user.get_sorted_fav_tags()
401
401
402 theme = _get_theme(request, user)
402 theme = _get_theme(request, user)
403 context['theme'] = theme
403 context['theme'] = theme
404 context['theme_css'] = 'css/' + theme + '/base_page.css'
404 context['theme_css'] = 'css/' + theme + '/base_page.css'
405
405
406 # This shows the moderator panel
406 # This shows the moderator panel
407 moderate = user.get_setting(SETTING_MODERATE)
407 moderate = user.get_setting(SETTING_MODERATE)
408 if moderate == 'True':
408 if moderate == 'True':
409 context['moderator'] = user.is_moderator()
409 context['moderator'] = user.is_moderator()
410 else:
410 else:
411 context['moderator'] = False
411 context['moderator'] = False
412
412
413 return context
413 return context
414
414
415
415
416 def _get_user(request):
416 def _get_user(request):
417 """
417 """
418 Get current user from the session. If the user does not exist, create
418 Get current user from the session. If the user does not exist, create
419 a new one.
419 a new one.
420 """
420 """
421
421
422 session = request.session
422 session = request.session
423 if not 'user_id' in session:
423 if not 'user_id' in session:
424 request.session.save()
424 request.session.save()
425
425
426 md5 = hashlib.md5()
426 md5 = hashlib.md5()
427 md5.update(session.session_key)
427 md5.update(session.session_key)
428 new_id = md5.hexdigest()
428 new_id = md5.hexdigest()
429
429
430 time_now = timezone.now()
430 time_now = timezone.now()
431 user = User.objects.create(user_id=new_id, rank=RANK_USER,
431 user = User.objects.create(user_id=new_id, rank=RANK_USER,
432 registration_time=time_now)
432 registration_time=time_now)
433
433
434 session['user_id'] = user.id
434 session['user_id'] = user.id
435 else:
435 else:
436 user = User.objects.get(id=session['user_id'])
436 user = User.objects.get(id=session['user_id'])
437
437
438 return user
438 return user
439
439
440
440
441 def _redirect_to_next(request):
441 def _redirect_to_next(request):
442 """
442 """
443 If a 'next' parameter was specified, redirect to the next page. This is
443 If a 'next' parameter was specified, redirect to the next page. This is
444 used when the user is required to return to some page after the current
444 used when the user is required to return to some page after the current
445 view has finished its work.
445 view has finished its work.
446 """
446 """
447
447
448 if 'next' in request.GET:
448 if 'next' in request.GET:
449 next_page = request.GET['next']
449 next_page = request.GET['next']
450 return HttpResponseRedirect(next_page)
450 return HttpResponseRedirect(next_page)
451 else:
451 else:
452 return redirect(index)
452 return redirect(index)
453
453
454
454
455 def _ban_current_user(request):
455 def _ban_current_user(request):
456 """Add current user to the IP ban list"""
456 """Add current user to the IP ban list"""
457
457
458 ip = utils.get_client_ip(request)
458 ip = utils.get_client_ip(request)
459 Ban.objects.get_or_create(ip=ip)
459 Ban.objects.get_or_create(ip=ip)
General Comments 0
You need to be logged in to leave comments. Login now