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