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