##// END OF EJS Templates
Added posts count to the profile page.
neko259 -
r185:9f3a047d default
parent child Browse files
Show More
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
@@ -1,266 +1,268 b''
1 # SOME DESCRIPTIVE TITLE.
1 # SOME DESCRIPTIVE TITLE.
2 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
2 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 # This file is distributed under the same license as the PACKAGE package.
3 # This file is distributed under the same license as the PACKAGE package.
4 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5 #
5 #
6 msgid ""
6 msgid ""
7 msgstr ""
7 msgstr ""
8 "Project-Id-Version: PACKAGE VERSION\n"
8 "Project-Id-Version: PACKAGE VERSION\n"
9 "Report-Msgid-Bugs-To: \n"
9 "Report-Msgid-Bugs-To: \n"
10 "POT-Creation-Date: 2013-09-09 21:52+0300\n"
10 "POT-Creation-Date: 2013-09-14 18:46+0300\n"
11 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
11 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
12 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13 "Language-Team: LANGUAGE <LL@li.org>\n"
13 "Language-Team: LANGUAGE <LL@li.org>\n"
14 "Language: ru\n"
14 "Language: ru\n"
15 "MIME-Version: 1.0\n"
15 "MIME-Version: 1.0\n"
16 "Content-Type: text/plain; charset=UTF-8\n"
16 "Content-Type: text/plain; charset=UTF-8\n"
17 "Content-Transfer-Encoding: 8bit\n"
17 "Content-Transfer-Encoding: 8bit\n"
18 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
18 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
19 "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
19 "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
20
20
21 #: templates/boards/404.html:6
21 #: templates/boards/404.html:6
22 msgid "Not found"
22 msgid "Not found"
23 msgstr "Не найдено"
23 msgstr "Не найдено"
24
24
25 #: templates/boards/404.html:12
25 #: templates/boards/404.html:12
26 msgid "This page does not exist"
26 msgid "This page does not exist"
27 msgstr "Этой страницы не существует"
27 msgstr "Этой страницы не существует"
28
28
29 #: templates/boards/authors.html:6
29 #: templates/boards/authors.html:6
30 msgid "Authors"
30 msgid "Authors"
31 msgstr "Авторы"
31 msgstr "Авторы"
32
32
33 #: templates/boards/authors.html:24
33 #: templates/boards/authors.html:24
34 msgid "Distributed under the"
34 msgid "Distributed under the"
35 msgstr "Распространяется под"
35 msgstr "Распространяется под"
36
36
37 #: templates/boards/authors.html:26
37 #: templates/boards/authors.html:26
38 msgid "license"
38 msgid "license"
39 msgstr "лицензией"
39 msgstr "лицензией"
40
40
41 #: templates/boards/authors.html:28
41 #: templates/boards/authors.html:28
42 msgid "Repository"
42 msgid "Repository"
43 msgstr "Репозиторий"
43 msgstr "Репозиторий"
44
44
45 #: templates/boards/base.html:12
45 #: templates/boards/base.html:12
46 msgid "Feed"
46 msgid "Feed"
47 msgstr "Лента"
47 msgstr "Лента"
48
48
49 #: templates/boards/base.html:29
49 #: templates/boards/base.html:29
50 msgid "All threads"
50 msgid "All threads"
51 msgstr "Все темы"
51 msgstr "Все темы"
52
52
53 #: templates/boards/base.html:34
53 #: templates/boards/base.html:34
54 msgid "Tag management"
54 msgid "Tag management"
55 msgstr "Управление тегами"
55 msgstr "Управление тегами"
56
56
57 #: templates/boards/base.html:36
57 #: templates/boards/base.html:36
58 msgid "Settings"
58 msgid "Settings"
59 msgstr "Настройки"
59 msgstr "Настройки"
60
60
61 #: templates/boards/base.html:43 templates/boards/login.html:6
61 #: templates/boards/base.html:43 templates/boards/login.html:6
62 #: templates/boards/login.html.py:21
62 #: templates/boards/login.html.py:21
63 msgid "Login"
63 msgid "Login"
64 msgstr "Вход"
64 msgstr "Вход"
65
65
66 #: templates/boards/base.html:44
66 #: templates/boards/base.html:44
67 msgid "Up"
67 msgid "Up"
68 msgstr "Вверх"
68 msgstr "Вверх"
69
69
70 #: templates/boards/login.html:15
70 #: templates/boards/login.html:15
71 msgid "User ID"
71 msgid "User ID"
72 msgstr "ID пользователя"
72 msgstr "ID пользователя"
73
73
74 #: templates/boards/login.html:24
74 #: templates/boards/login.html:24
75 msgid "Insert your user id above"
75 msgid "Insert your user id above"
76 msgstr "Вставьте свой ID пользователя выше"
76 msgstr "Вставьте свой ID пользователя выше"
77
77
78 #: templates/boards/posting_general.html:18
78 #: templates/boards/posting_general.html:18
79 msgid "Tag: "
79 msgid "Tag: "
80 msgstr "Тег: "
80 msgstr "Тег: "
81
81
82 #: templates/boards/posting_general.html:35
82 #: templates/boards/posting_general.html:43
83 #: templates/boards/posting_general.html:89 templates/boards/thread.html:27
83 #: templates/boards/posting_general.html:99 templates/boards/thread.html:27
84 #: templates/boards/rss/post.html:5
84 #: templates/boards/rss/post.html:5
85 msgid "Post image"
85 msgid "Post image"
86 msgstr "Изображение сообщения"
86 msgstr "Изображение сообщения"
87
87
88 #: templates/boards/posting_general.html:48
88 #: templates/boards/posting_general.html:56
89 msgid "Reply"
89 msgid "Reply"
90 msgstr "Ответ"
90 msgstr "Ответ"
91
91
92 #: templates/boards/posting_general.html:54 templates/boards/thread.html:45
92 #: templates/boards/posting_general.html:61 templates/boards/thread.html:45
93 msgid "Delete"
93 msgid "Delete"
94 msgstr "Удалить"
94 msgstr "Удалить"
95
95
96 #: templates/boards/posting_general.html:63 templates/boards/thread.html:112
96 #: templates/boards/posting_general.html:64 templates/boards/thread.html:48
97 msgid "Ban IP"
98 msgstr "Заблокировать IP"
99
100 #: templates/boards/posting_general.html:73 templates/boards/thread.html:112
97 msgid "replies"
101 msgid "replies"
98 msgstr "ответов"
102 msgstr "ответов"
99
103
100 #: templates/boards/posting_general.html:64 templates/boards/thread.html:113
104 #: templates/boards/posting_general.html:74 templates/boards/thread.html:113
101 msgid "images"
105 msgid "images"
102 msgstr "изображений"
106 msgstr "изображений"
103
107
104 #: templates/boards/posting_general.html:66
108 #: templates/boards/posting_general.html:76
105 #: templates/boards/posting_general.html:139 templates/boards/tags.html:7
109 #: templates/boards/posting_general.html:149 templates/boards/tags.html:7
106 #: templates/boards/thread.html:58 templates/boards/rss/post.html:10
110 #: templates/boards/thread.html:58 templates/boards/rss/post.html:10
107 msgid "Tags"
111 msgid "Tags"
108 msgstr "Теги"
112 msgstr "Теги"
109
113
110 #: templates/boards/posting_general.html:115
114 #: templates/boards/posting_general.html:125
111 msgid "No threads exist. Create the first one!"
115 msgid "No threads exist. Create the first one!"
112 msgstr "Нет тем. Создайте первую!"
116 msgstr "Нет тем. Создайте первую!"
113
117
114 #: templates/boards/posting_general.html:121
118 #: templates/boards/posting_general.html:131
115 msgid "Create new thread"
119 msgid "Create new thread"
116 msgstr "Создать новую тему"
120 msgstr "Создать новую тему"
117
121
118 #: templates/boards/posting_general.html:124 templates/boards/thread.html:77
122 #: templates/boards/posting_general.html:134 templates/boards/thread.html:77
119 msgid "Title"
123 msgid "Title"
120 msgstr "Заголовок"
124 msgstr "Заголовок"
121
125
122 #: templates/boards/posting_general.html:129 templates/boards/thread.html:82
126 #: templates/boards/posting_general.html:139 templates/boards/thread.html:82
123 msgid "Text"
127 msgid "Text"
124 msgstr "Текст"
128 msgstr "Текст"
125
129
126 #: templates/boards/posting_general.html:134 templates/boards/thread.html:87
130 #: templates/boards/posting_general.html:144 templates/boards/thread.html:87
127 msgid "Image"
131 msgid "Image"
128 msgstr "Изображение"
132 msgstr "Изображение"
129
133
130 #: templates/boards/posting_general.html:152 templates/boards/thread.html:101
134 #: templates/boards/posting_general.html:162 templates/boards/thread.html:101
131 msgid "Post"
135 msgid "Post"
132 msgstr "Отправить"
136 msgstr "Отправить"
133
137
134 #: templates/boards/posting_general.html:154
138 #: templates/boards/posting_general.html:164
135 msgid "Tags must be delimited by spaces. Text or image is required."
139 msgid "Tags must be delimited by spaces. Text or image is required."
136 msgstr ""
140 msgstr ""
137 "Теги должны быть разделены пробелами. Текст или изображение обязательны."
141 "Теги должны быть разделены пробелами. Текст или изображение обязательны."
138
142
139 #: templates/boards/posting_general.html:157 templates/boards/thread.html:103
143 #: templates/boards/posting_general.html:167 templates/boards/thread.html:103
140 msgid "Text syntax"
144 msgid "Text syntax"
141 msgstr "Синтаксис текста"
145 msgstr "Синтаксис текста"
142
146
143 #: templates/boards/posting_general.html:167
147 #: templates/boards/posting_general.html:177
144 msgid "Pages:"
148 msgid "Pages:"
145 msgstr "Страницы: "
149 msgstr "Страницы: "
146
150
147 #: templates/boards/settings.html:12
151 #: templates/boards/settings.html:13
148 msgid "User:"
152 msgid "User:"
149 msgstr "Пользователь:"
153 msgstr "Пользователь:"
150
154
151 #: templates/boards/settings.html:14
155 #: templates/boards/settings.html:15
152 msgid "You are moderator."
156 msgid "You are moderator."
153 msgstr "Вы модератор."
157 msgstr "Вы модератор."
154
158
155 #: templates/boards/settings.html:20
159 #: templates/boards/settings.html:18
160 msgid "Posts:"
161 msgstr "Сообщений:"
162
163 #: templates/boards/settings.html:23
156 msgid "Theme"
164 msgid "Theme"
157 msgstr "Тема"
165 msgstr "Тема"
158
166
159 #: templates/boards/settings.html:36
167 #: templates/boards/settings.html:39
160 msgid "Save"
168 msgid "Save"
161 msgstr "Сохранить"
169 msgstr "Сохранить"
162
170
163 #: templates/boards/tags.html:17
171 #: templates/boards/tags.html:17
164 msgid "threads"
172 msgid "threads"
165 msgstr "тем"
173 msgstr "тем"
166
174
167 #: templates/boards/tags.html:20
168 msgid "Remove"
169 msgstr "Удалить"
170
171 #: templates/boards/tags.html:23
172 msgid "Add"
173 msgstr "Добавить"
174
175 #: templates/boards/tags.html:28
175 #: templates/boards/tags.html:28
176 msgid "No tags found."
176 msgid "No tags found."
177 msgstr "Теги не найдены."
177 msgstr "Теги не найдены."
178
178
179 #: templates/boards/thread.html:48
180 msgid "Ban IP"
181 msgstr "Заблокировать IP"
182
183 #: templates/boards/thread.html:74
179 #: templates/boards/thread.html:74
184 msgid "Reply to thread"
180 msgid "Reply to thread"
185 msgstr "Ответить в тему"
181 msgstr "Ответить в тему"
186
182
187 #: templates/boards/thread.html:114
183 #: templates/boards/thread.html:114
188 msgid "Last update: "
184 msgid "Last update: "
189 msgstr "Последнее обновление: "
185 msgstr "Последнее обновление: "
190
186
191 #: templates/boards/staticpages/banned.html:6
187 #: templates/boards/staticpages/banned.html:6
192 msgid "Banned"
188 msgid "Banned"
193 msgstr "Заблокирован"
189 msgstr "Заблокирован"
194
190
195 #: templates/boards/staticpages/banned.html:10
191 #: templates/boards/staticpages/banned.html:11
196 msgid "Your IP address has been banned. Contact the administrator"
192 msgid "Your IP address has been banned. Contact the administrator"
197 msgstr "Ваш IP адрес был заблокирован. Свяжитесь с администратором"
193 msgstr "Ваш IP адрес был заблокирован. Свяжитесь с администратором"
198
194
199 #: templates/boards/staticpages/help.html:6
195 #: templates/boards/staticpages/help.html:6
200 #: templates/boards/staticpages/help.html:10
196 #: templates/boards/staticpages/help.html:10
201 msgid "Syntax"
197 msgid "Syntax"
202 msgstr "Синтаксис"
198 msgstr "Синтаксис"
203
199
204 #: templates/boards/staticpages/help.html:11
200 #: templates/boards/staticpages/help.html:11
205 msgid "2 line breaks for a new line."
201 msgid "2 line breaks for a new line."
206 msgstr "2 перевода строки создают новый абзац."
202 msgstr "2 перевода строки создают новый абзац."
207
203
208 #: templates/boards/staticpages/help.html:12
204 #: templates/boards/staticpages/help.html:12
209 msgid "Italic text"
205 msgid "Italic text"
210 msgstr "Курсивный текст"
206 msgstr "Курсивный текст"
211
207
212 #: templates/boards/staticpages/help.html:13
208 #: templates/boards/staticpages/help.html:13
213 msgid "Bold text"
209 msgid "Bold text"
214 msgstr "Полужирный текст"
210 msgstr "Полужирный текст"
215
211
216 #: templates/boards/staticpages/help.html:14
212 #: templates/boards/staticpages/help.html:14
217 msgid "Spoiler"
213 msgid "Spoiler"
218 msgstr "Спойлер"
214 msgstr "Спойлер"
219
215
220 #: templates/boards/staticpages/help.html:15
216 #: templates/boards/staticpages/help.html:15
221 msgid "Comment"
217 msgid "Comment"
222 msgstr "Комментарий"
218 msgstr "Комментарий"
223
219
224 #: templates/boards/staticpages/help.html:16
220 #: templates/boards/staticpages/help.html:16
225 msgid "Quote"
221 msgid "Quote"
226 msgstr "Цитата"
222 msgstr "Цитата"
227
223
228 #: templates/boards/staticpages/help.html:17
224 #: templates/boards/staticpages/help.html:17
229 msgid "Link to a post"
225 msgid "Link to a post"
230 msgstr "Ссылка на сообщение"
226 msgstr "Ссылка на сообщение"
231
227
228 #~ msgid "Remove"
229 #~ msgstr "Удалить"
230
231 #~ msgid "Add"
232 #~ msgstr "Добавить"
233
232 #~ msgid "Basic markdown syntax."
234 #~ msgid "Basic markdown syntax."
233 #~ msgstr "Базовый синтаксис markdown."
235 #~ msgstr "Базовый синтаксис markdown."
234
236
235 #~ msgid "Example: "
237 #~ msgid "Example: "
236 #~ msgstr "Пример: "
238 #~ msgstr "Пример: "
237
239
238 #~ msgid "italic"
240 #~ msgid "italic"
239 #~ msgstr "курсив"
241 #~ msgstr "курсив"
240
242
241 #~ msgid "bold"
243 #~ msgid "bold"
242 #~ msgstr "полужирный"
244 #~ msgstr "полужирный"
243
245
244 #~ msgid "tags"
246 #~ msgid "tags"
245 #~ msgstr "тегов"
247 #~ msgstr "тегов"
246
248
247 #~ msgid "Get!"
249 #~ msgid "Get!"
248 #~ msgstr "Гет!"
250 #~ msgstr "Гет!"
249
251
250 #~ msgid "View"
252 #~ msgid "View"
251 #~ msgstr "Просмотр"
253 #~ msgstr "Просмотр"
252
254
253 #~ msgid "gets"
255 #~ msgid "gets"
254 #~ msgstr "гетов"
256 #~ msgstr "гетов"
255
257
256 #~ msgid "author"
258 #~ msgid "author"
257 #~ msgstr "автор"
259 #~ msgstr "автор"
258
260
259 #~ msgid "developer"
261 #~ msgid "developer"
260 #~ msgstr "разработчик"
262 #~ msgstr "разработчик"
261
263
262 #~ msgid "javascript developer"
264 #~ msgid "javascript developer"
263 #~ msgstr "разработчик javascript"
265 #~ msgstr "разработчик javascript"
264
266
265 #~ msgid "designer"
267 #~ msgid "designer"
266 #~ msgstr "дизайнер"
268 #~ msgstr "дизайнер"
@@ -1,325 +1,329 b''
1 import os
1 import os
2 from random import random
2 from random import random
3 import re
3 import re
4 import time
4 import time
5 import math
5 import math
6
6
7 from django.db import models
7 from django.db import models
8 from django.db.models import Count
8 from django.db.models import Count
9 from django.http import Http404
9 from django.http import Http404
10 from django.utils import timezone
10 from django.utils import timezone
11 from markupfield.fields import MarkupField
11 from markupfield.fields import MarkupField
12
12
13 from neboard import settings
13 from neboard import settings
14 import thumbs
14 import thumbs
15
15
16 IMAGE_THUMB_SIZE = (200, 150)
16 IMAGE_THUMB_SIZE = (200, 150)
17
17
18 TITLE_MAX_LENGTH = 50
18 TITLE_MAX_LENGTH = 50
19
19
20 DEFAULT_MARKUP_TYPE = 'markdown'
20 DEFAULT_MARKUP_TYPE = 'markdown'
21
21
22 NO_PARENT = -1
22 NO_PARENT = -1
23 NO_IP = '0.0.0.0'
23 NO_IP = '0.0.0.0'
24 UNKNOWN_UA = ''
24 UNKNOWN_UA = ''
25 ALL_PAGES = -1
25 ALL_PAGES = -1
26 OPENING_POST_POPULARITY_WEIGHT = 2
26 OPENING_POST_POPULARITY_WEIGHT = 2
27 IMAGES_DIRECTORY = 'images/'
27 IMAGES_DIRECTORY = 'images/'
28 FILE_EXTENSION_DELIMITER = '.'
28 FILE_EXTENSION_DELIMITER = '.'
29
29
30 RANK_ADMIN = 0
30 RANK_ADMIN = 0
31 RANK_MODERATOR = 10
31 RANK_MODERATOR = 10
32 RANK_USER = 100
32 RANK_USER = 100
33
33
34
34
35 class PostManager(models.Manager):
35 class PostManager(models.Manager):
36
36 def create_post(self, title, text, image=None, thread=None,
37 def create_post(self, title, text, image=None, thread=None,
37 ip=NO_IP, tags=None, user=None):
38 ip=NO_IP, tags=None, user=None):
38 post = self.create(title=title,
39 post = self.create(title=title,
39 text=text,
40 text=text,
40 pub_time=timezone.now(),
41 pub_time=timezone.now(),
41 thread=thread,
42 thread=thread,
42 image=image,
43 image=image,
43 poster_ip=ip,
44 poster_ip=ip,
44 poster_user_agent=UNKNOWN_UA,
45 poster_user_agent=UNKNOWN_UA,
45 last_edit_time=timezone.now(),
46 last_edit_time=timezone.now(),
46 user=user)
47 user=user)
47
48
48 if tags:
49 if tags:
49 map(post.tags.add, tags)
50 map(post.tags.add, tags)
50 for tag in tags:
51 for tag in tags:
51 tag.threads.add(post)
52 tag.threads.add(post)
52
53
53 if thread:
54 if thread:
54 thread.replies.add(post)
55 thread.replies.add(post)
55 thread.bump()
56 thread.bump()
56 else:
57 else:
57 self._delete_old_threads()
58 self._delete_old_threads()
58
59
59 return post
60 return post
60
61
61 def delete_post(self, post):
62 def delete_post(self, post):
62 if post.replies.count() > 0:
63 if post.replies.count() > 0:
63 map(self.delete_post, post.replies.all())
64 map(self.delete_post, post.replies.all())
64 post.delete()
65 post.delete()
65
66
66 def delete_posts_by_ip(self, ip):
67 def delete_posts_by_ip(self, ip):
67 posts = self.filter(poster_ip=ip)
68 posts = self.filter(poster_ip=ip)
68 map(self.delete_post, posts)
69 map(self.delete_post, posts)
69
70
70 def get_threads(self, tag=None, page=ALL_PAGES,
71 def get_threads(self, tag=None, page=ALL_PAGES,
71 order_by='-last_edit_time'):
72 order_by='-last_edit_time'):
72 if tag:
73 if tag:
73 threads = tag.threads
74 threads = tag.threads
74
75
75 # TODO This needs to be uncommented when 'all tags' view won't
76 # TODO This needs to be uncommented when 'all tags' view won't
76 # use this method to get threads for tag
77 # use this method to get threads for tag
77
78
78 # if threads.count() == 0:
79 # if threads.count() == 0:
79 # raise Http404
80 # raise Http404
80 else:
81 else:
81 threads = self.filter(thread=None)
82 threads = self.filter(thread=None)
82
83
83 threads = threads.order_by(order_by)
84 threads = threads.order_by(order_by)
84
85
85 if page != ALL_PAGES:
86 if page != ALL_PAGES:
86 thread_count = threads.count()
87 thread_count = threads.count()
87
88
88 if page < self.get_thread_page_count(tag=tag):
89 if page < self.get_thread_page_count(tag=tag):
89 start_thread = page * settings.THREADS_PER_PAGE
90 start_thread = page * settings.THREADS_PER_PAGE
90 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
91 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
91 thread_count)
92 thread_count)
92 threads = threads[start_thread:end_thread]
93 threads = threads[start_thread:end_thread]
93
94
94 return threads
95 return threads
95
96
96 def get_thread(self, opening_post_id):
97 def get_thread(self, opening_post_id):
97 try:
98 try:
98 opening_post = self.get(id=opening_post_id, thread=None)
99 opening_post = self.get(id=opening_post_id, thread=None)
99 except Post.DoesNotExist:
100 except Post.DoesNotExist:
100 raise Http404
101 raise Http404
101
102
102 if opening_post.replies:
103 if opening_post.replies:
103 thread = [opening_post]
104 thread = [opening_post]
104 thread.extend(opening_post.replies.all())
105 thread.extend(opening_post.replies.all())
105
106
106 return thread
107 return thread
107
108
108 def exists(self, post_id):
109 def exists(self, post_id):
109 posts = self.filter(id=post_id)
110 posts = self.filter(id=post_id)
110
111
111 return posts.count() > 0
112 return posts.count() > 0
112
113
113 def get_thread_page_count(self, tag=None):
114 def get_thread_page_count(self, tag=None):
114 if tag:
115 if tag:
115 threads = self.filter(thread=None, tags=tag)
116 threads = self.filter(thread=None, tags=tag)
116 else:
117 else:
117 threads = self.filter(thread=None)
118 threads = self.filter(thread=None)
118
119
119 return int(math.ceil(threads.count() / float(
120 return int(math.ceil(threads.count() / float(
120 settings.THREADS_PER_PAGE)))
121 settings.THREADS_PER_PAGE)))
121
122
122 def _delete_old_threads(self):
123 def _delete_old_threads(self):
123 """
124 """
124 Preserves maximum thread count. If there are too many threads,
125 Preserves maximum thread count. If there are too many threads,
125 delete the old ones.
126 delete the old ones.
126 """
127 """
127
128
128 # TODO Move old threads to the archive instead of deleting them.
129 # TODO Move old threads to the archive instead of deleting them.
129 # Maybe make some 'old' field in the model to indicate the thread
130 # Maybe make some 'old' field in the model to indicate the thread
130 # must not be shown and be able for replying.
131 # must not be shown and be able for replying.
131
132
132 threads = self.get_threads()
133 threads = self.get_threads()
133 thread_count = len(threads)
134 thread_count = len(threads)
134
135
135 if thread_count > settings.MAX_THREAD_COUNT:
136 if thread_count > settings.MAX_THREAD_COUNT:
136 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
137 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
137 old_threads = threads[thread_count - num_threads_to_delete:]
138 old_threads = threads[thread_count - num_threads_to_delete:]
138
139
139 map(self.delete_post, old_threads)
140 map(self.delete_post, old_threads)
140
141
141
142
142 class TagManager(models.Manager):
143 class TagManager(models.Manager):
143
144
144 def get_not_empty_tags(self):
145 def get_not_empty_tags(self):
145 tags = self.annotate(Count('threads')) \
146 tags = self.annotate(Count('threads')) \
146 .filter(threads__count__gt=0).order_by('name')
147 .filter(threads__count__gt=0).order_by('name')
147
148
148 return tags
149 return tags
149
150
150
151
151 class Tag(models.Model):
152 class Tag(models.Model):
152 """
153 """
153 A tag is a text node assigned to the post. The tag serves as a board
154 A tag is a text node assigned to the post. The tag serves as a board
154 section. There can be multiple tags for each message
155 section. There can be multiple tags for each message
155 """
156 """
156
157
157 objects = TagManager()
158 objects = TagManager()
158
159
159 name = models.CharField(max_length=100)
160 name = models.CharField(max_length=100)
160 threads = models.ManyToManyField('Post', null=True,
161 threads = models.ManyToManyField('Post', null=True,
161 blank=True, related_name='tag+')
162 blank=True, related_name='tag+')
162
163
163 def __unicode__(self):
164 def __unicode__(self):
164 return self.name
165 return self.name
165
166
166 def is_empty(self):
167 def is_empty(self):
167 return self.get_post_count() == 0
168 return self.get_post_count() == 0
168
169
169 def get_post_count(self):
170 def get_post_count(self):
170 return self.threads.count()
171 return self.threads.count()
171
172
172 def get_popularity(self):
173 def get_popularity(self):
173 posts_with_tag = Post.objects.get_threads(tag=self)
174 posts_with_tag = Post.objects.get_threads(tag=self)
174 reply_count = 0
175 reply_count = 0
175 for post in posts_with_tag:
176 for post in posts_with_tag:
176 reply_count += post.get_reply_count()
177 reply_count += post.get_reply_count()
177 reply_count += OPENING_POST_POPULARITY_WEIGHT
178 reply_count += OPENING_POST_POPULARITY_WEIGHT
178
179
179 return reply_count
180 return reply_count
180
181
181
182
182 class Post(models.Model):
183 class Post(models.Model):
183 """A post is a message."""
184 """A post is a message."""
184
185
185 objects = PostManager()
186 objects = PostManager()
186
187
187 def _update_image_filename(self, filename):
188 def _update_image_filename(self, filename):
188 """Get unique image filename"""
189 """Get unique image filename"""
189
190
190 path = IMAGES_DIRECTORY
191 path = IMAGES_DIRECTORY
191 new_name = str(int(time.mktime(time.gmtime())))
192 new_name = str(int(time.mktime(time.gmtime())))
192 new_name += str(int(random() * 1000))
193 new_name += str(int(random() * 1000))
193 new_name += FILE_EXTENSION_DELIMITER
194 new_name += FILE_EXTENSION_DELIMITER
194 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
195 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
195
196
196 return os.path.join(path, new_name)
197 return os.path.join(path, new_name)
197
198
198 title = models.CharField(max_length=TITLE_MAX_LENGTH)
199 title = models.CharField(max_length=TITLE_MAX_LENGTH)
199 pub_time = models.DateTimeField()
200 pub_time = models.DateTimeField()
200 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
201 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
201 escape_html=False)
202 escape_html=False)
202
203
203 image_width = models.IntegerField(default=0)
204 image_width = models.IntegerField(default=0)
204 image_height = models.IntegerField(default=0)
205 image_height = models.IntegerField(default=0)
205
206
206 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
207 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
207 blank=True, sizes=(IMAGE_THUMB_SIZE,),
208 blank=True, sizes=(IMAGE_THUMB_SIZE,),
208 width_field='image_width',
209 width_field='image_width',
209 height_field='image_height')
210 height_field='image_height')
210
211
211 poster_ip = models.GenericIPAddressField()
212 poster_ip = models.GenericIPAddressField()
212 poster_user_agent = models.TextField()
213 poster_user_agent = models.TextField()
213
214
214 # TODO Remove this field after everything has been updated to 'thread'
215 # TODO Remove this field after everything has been updated to 'thread'
215 parent = models.BigIntegerField(default=NO_PARENT)
216 parent = models.BigIntegerField(default=NO_PARENT)
216
217
217 thread = models.ForeignKey('Post', null=True, default=None)
218 thread = models.ForeignKey('Post', null=True, default=None)
218 tags = models.ManyToManyField(Tag)
219 tags = models.ManyToManyField(Tag)
219 last_edit_time = models.DateTimeField()
220 last_edit_time = models.DateTimeField()
220 user = models.ForeignKey('User', null=True, default=None)
221 user = models.ForeignKey('User', null=True, default=None)
221
222
222 replies = models.ManyToManyField('Post', symmetrical=False, null=True,
223 replies = models.ManyToManyField('Post', symmetrical=False, null=True,
223 blank=True, related_name='re+')
224 blank=True, related_name='re+')
224
225
225 def __unicode__(self):
226 def __unicode__(self):
226 return '#' + str(self.id) + ' ' + self.title + ' (' + \
227 return '#' + str(self.id) + ' ' + self.title + ' (' + \
227 self.text.raw[:50] + ')'
228 self.text.raw[:50] + ')'
228
229
229 def get_title(self):
230 def get_title(self):
230 title = self.title
231 title = self.title
231 if len(title) == 0:
232 if len(title) == 0:
232 title = self.text.raw[:20]
233 title = self.text.raw[:20]
233
234
234 return title
235 return title
235
236
236 def get_reply_count(self):
237 def get_reply_count(self):
237 return self.replies.count()
238 return self.replies.count()
238
239
239 def get_images_count(self):
240 def get_images_count(self):
240 images_count = 1 if self.image else 0
241 images_count = 1 if self.image else 0
241 images_count += self.replies.filter(image_width__gt=0).count()
242 images_count += self.replies.filter(image_width__gt=0).count()
242
243
243 return images_count
244 return images_count
244
245
245 def can_bump(self):
246 def can_bump(self):
246 """Check if the thread can be bumped by replying"""
247 """Check if the thread can be bumped by replying"""
247
248
248 post_count = self.get_reply_count() + 1
249 post_count = self.get_reply_count() + 1
249
250
250 return post_count <= settings.MAX_POSTS_PER_THREAD
251 return post_count <= settings.MAX_POSTS_PER_THREAD
251
252
252 def bump(self):
253 def bump(self):
253 """Bump (move to up) thread"""
254 """Bump (move to up) thread"""
254
255
255 if self.can_bump():
256 if self.can_bump():
256 self.last_edit_time = timezone.now()
257 self.last_edit_time = timezone.now()
257 self.save()
258 self.save()
258
259
259 def get_last_replies(self):
260 def get_last_replies(self):
260 if settings.LAST_REPLIES_COUNT > 0:
261 if settings.LAST_REPLIES_COUNT > 0:
261 reply_count = self.get_reply_count()
262 reply_count = self.get_reply_count()
262
263
263 if reply_count > 0:
264 if reply_count > 0:
264 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
265 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
265 reply_count)
266 reply_count)
266 last_replies = self.replies.all()[reply_count -
267 last_replies = self.replies.all()[reply_count -
267 reply_count_to_show:]
268 reply_count_to_show:]
268
269
269 return last_replies
270 return last_replies
270
271
271
272
272 class User(models.Model):
273 class User(models.Model):
273
274
274 user_id = models.CharField(max_length=50)
275 user_id = models.CharField(max_length=50)
275 rank = models.IntegerField()
276 rank = models.IntegerField()
276
277
277 registration_time = models.DateTimeField()
278 registration_time = models.DateTimeField()
278 last_access_time = models.DateTimeField()
279 last_access_time = models.DateTimeField()
279
280
280 fav_tags = models.ManyToManyField(Tag, null=True, blank=True)
281 fav_tags = models.ManyToManyField(Tag, null=True, blank=True)
281 fav_threads = models.ManyToManyField(Post, related_name='+', null=True,
282 fav_threads = models.ManyToManyField(Post, related_name='+', null=True,
282 blank=True)
283 blank=True)
283
284
284 def save_setting(self, name, value):
285 def save_setting(self, name, value):
285 setting, created = Setting.objects.get_or_create(name=name, user=self)
286 setting, created = Setting.objects.get_or_create(name=name, user=self)
286 setting.value = value
287 setting.value = value
287 setting.save()
288 setting.save()
288
289
289 return setting
290 return setting
290
291
291 def get_setting(self, name):
292 def get_setting(self, name):
292 if Setting.objects.filter(name=name, user=self).exists():
293 if Setting.objects.filter(name=name, user=self).exists():
293 setting = Setting.objects.get(name=name, user=self)
294 setting = Setting.objects.get(name=name, user=self)
294 setting_value = setting.value
295 setting_value = setting.value
295 else:
296 else:
296 setting_value = None
297 setting_value = None
297
298
298 return setting_value
299 return setting_value
299
300
300 def is_moderator(self):
301 def is_moderator(self):
301 return RANK_MODERATOR >= self.rank
302 return RANK_MODERATOR >= self.rank
302
303
303 def get_sorted_fav_tags(self):
304 def get_sorted_fav_tags(self):
304 tags = self.fav_tags.annotate(Count('threads'))\
305 tags = self.fav_tags.annotate(Count('threads'))\
305 .filter(threads__count__gt=0).order_by('name')
306 .filter(threads__count__gt=0).order_by('name')
306
307
307 return tags
308 return tags
308
309
310 def get_post_count(self):
311 return Post.objects.filter(user=self).count()
312
309 def __unicode__(self):
313 def __unicode__(self):
310 return self.user_id + '(' + str(self.rank) + ')'
314 return self.user_id + '(' + str(self.rank) + ')'
311
315
312
316
313 class Setting(models.Model):
317 class Setting(models.Model):
314
318
315 name = models.CharField(max_length=50)
319 name = models.CharField(max_length=50)
316 value = models.CharField(max_length=50)
320 value = models.CharField(max_length=50)
317 user = models.ForeignKey(User)
321 user = models.ForeignKey(User)
318
322
319
323
320 class Ban(models.Model):
324 class Ban(models.Model):
321
325
322 ip = models.GenericIPAddressField()
326 ip = models.GenericIPAddressField()
323
327
324 def __unicode__(self):
328 def __unicode__(self):
325 return self.ip
329 return self.ip
@@ -1,41 +1,44 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">
11 <div class="post">
12 <p>
12 {% trans 'User:' %} <b>{{ user.user_id }}</b>.
13 {% trans 'User:' %} <b>{{ user.user_id }}</b>.
13 {% if user.is_moderator %}
14 {% if user.is_moderator %}
14 {% trans 'You are moderator.' %}
15 {% trans 'You are moderator.' %}
15 {% endif %}
16 {% endif %}
17 </p>
18 <p>{% trans 'Posts:' %} {{ user.get_post_count }}</p>
16 </div>
19 </div>
17
20
18 <div class="post-form-w">
21 <div class="post-form-w">
19 <div class="post-form">
22 <div class="post-form">
20 <span class="form-title">{% trans "Theme" %}</span>
23 <span class="form-title">{% trans "Theme" %}</span>
21 <form method="post">{% csrf_token %}
24 <form method="post">{% csrf_token %}
22 {% for choice in form.fields.theme.choices %}
25 {% for choice in form.fields.theme.choices %}
23 <div class="settings_item">
26 <div class="settings_item">
24 <label for="{{ choice.0 }}">
27 <label for="{{ choice.0 }}">
25 <input type="radio" name="theme"
28 <input type="radio" name="theme"
26 id="{{ choice.0 }}"
29 id="{{ choice.0 }}"
27 value="{{ choice.0 }}"
30 value="{{ choice.0 }}"
28 {% ifequal form.initial.theme choice.0 %}
31 {% ifequal form.initial.theme choice.0 %}
29 checked
32 checked
30 {% endifequal %}
33 {% endifequal %}
31 />
34 />
32 {{ choice.1 }}
35 {{ choice.1 }}
33 </label>
36 </label>
34 </div>
37 </div>
35 {% endfor %}
38 {% endfor %}
36 <input type="submit" value="{% trans "Save" %}" />
39 <input type="submit" value="{% trans "Save" %}" />
37 </form>
40 </form>
38 </div>
41 </div>
39 </div>
42 </div>
40
43
41 {% endblock %} No newline at end of file
44 {% endblock %}
@@ -1,356 +1,356 b''
1 import hashlib
1 import hashlib
2 from django.core.urlresolvers import reverse
2 from django.core.urlresolvers import reverse
3 from django.http import HttpResponseRedirect
3 from django.http import HttpResponseRedirect
4 from django.template import RequestContext
4 from django.template import RequestContext
5 from django.shortcuts import render, redirect, get_object_or_404
5 from django.shortcuts import render, redirect, get_object_or_404
6 from django.utils import timezone
6 from django.utils import timezone
7
7
8 from boards import forms
8 from boards import forms
9 import boards
9 import boards
10 from boards import utils
10 from boards import utils
11 from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
11 from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
12 ThreadCaptchaForm, PostCaptchaForm, LoginForm
12 ThreadCaptchaForm, PostCaptchaForm, LoginForm
13
13
14 from boards.models import Post, Tag, Ban, User, RANK_USER, NO_PARENT
14 from boards.models import Post, Tag, Ban, User, RANK_USER, NO_PARENT
15 from boards import authors
15 from boards import authors
16 import neboard
16 import neboard
17
17
18
18
19 def index(request, page=0):
19 def index(request, page=0):
20 context = _init_default_context(request)
20 context = _init_default_context(request)
21
21
22 if utils.need_include_captcha(request):
22 if utils.need_include_captcha(request):
23 threadFormClass = ThreadCaptchaForm
23 threadFormClass = ThreadCaptchaForm
24 kwargs = {'request': request}
24 kwargs = {'request': request}
25 else:
25 else:
26 threadFormClass = ThreadForm
26 threadFormClass = ThreadForm
27 kwargs = {}
27 kwargs = {}
28
28
29 if request.method == 'POST':
29 if request.method == 'POST':
30 form = threadFormClass(request.POST, request.FILES,
30 form = threadFormClass(request.POST, request.FILES,
31 error_class=PlainErrorList, **kwargs)
31 error_class=PlainErrorList, **kwargs)
32 form.session = request.session
32 form.session = request.session
33
33
34 if form.is_valid():
34 if form.is_valid():
35 return _new_post(request, form)
35 return _new_post(request, form)
36 else:
36 else:
37 form = threadFormClass(error_class=PlainErrorList, **kwargs)
37 form = threadFormClass(error_class=PlainErrorList, **kwargs)
38
38
39 threads = []
39 threads = []
40 for thread in Post.objects.get_threads(page=int(page)):
40 for thread in Post.objects.get_threads(page=int(page)):
41 threads.append({'thread': thread,
41 threads.append({'thread': thread,
42 'bumpable': thread.can_bump()})
42 'bumpable': thread.can_bump()})
43
43
44 context['threads'] = None if len(threads) == 0 else threads
44 context['threads'] = None if len(threads) == 0 else threads
45 context['form'] = form
45 context['form'] = form
46 context['pages'] = range(Post.objects.get_thread_page_count())
46 context['pages'] = range(Post.objects.get_thread_page_count())
47
47
48 return render(request, 'boards/posting_general.html',
48 return render(request, 'boards/posting_general.html',
49 context)
49 context)
50
50
51
51
52 def _new_post(request, form, thread_id=boards.models.NO_PARENT):
52 def _new_post(request, form, thread_id=boards.models.NO_PARENT):
53 """Add a new post (in thread or as a reply)."""
53 """Add a new post (in thread or as a reply)."""
54
54
55 ip = _get_client_ip(request)
55 ip = _get_client_ip(request)
56 is_banned = Ban.objects.filter(ip=ip).count() > 0
56 is_banned = Ban.objects.filter(ip=ip).count() > 0
57
57
58 if is_banned:
58 if is_banned:
59 return redirect(you_are_banned)
59 return redirect(you_are_banned)
60
60
61 data = form.cleaned_data
61 data = form.cleaned_data
62
62
63 title = data['title']
63 title = data['title']
64 text = data['text']
64 text = data['text']
65
65
66 if 'image' in data.keys():
66 if 'image' in data.keys():
67 image = data['image']
67 image = data['image']
68 else:
68 else:
69 image = None
69 image = None
70
70
71 tags = []
71 tags = []
72
72
73 new_thread = thread_id == boards.models.NO_PARENT
73 new_thread = thread_id == boards.models.NO_PARENT
74 if new_thread:
74 if new_thread:
75 tag_strings = data['tags']
75 tag_strings = data['tags']
76
76
77 if tag_strings:
77 if tag_strings:
78 tag_strings = tag_strings.split(' ')
78 tag_strings = tag_strings.split(' ')
79 for tag_name in tag_strings:
79 for tag_name in tag_strings:
80 tag_name = tag_name.strip()
80 tag_name = tag_name.strip()
81 if len(tag_name) > 0:
81 if len(tag_name) > 0:
82 tag, created = Tag.objects.get_or_create(name=tag_name)
82 tag, created = Tag.objects.get_or_create(name=tag_name)
83 tags.append(tag)
83 tags.append(tag)
84
84
85 op = None if thread_id == boards.models.NO_PARENT else \
85 op = None if thread_id == boards.models.NO_PARENT else \
86 get_object_or_404(Post, id=thread_id)
86 get_object_or_404(Post, id=thread_id)
87 post = Post.objects.create_post(title=title, text=text, ip=ip,
87 post = Post.objects.create_post(title=title, text=text, ip=ip,
88 thread=op, image=image,
88 thread=op, image=image,
89 tags=tags)
89 tags=tags, user=_get_user(request))
90
90
91 thread_to_show = (post.id if new_thread else thread_id)
91 thread_to_show = (post.id if new_thread else thread_id)
92
92
93 if new_thread:
93 if new_thread:
94 return redirect(thread, post_id=thread_to_show)
94 return redirect(thread, post_id=thread_to_show)
95 else:
95 else:
96 return redirect(reverse(thread, kwargs={'post_id': thread_to_show}) +
96 return redirect(reverse(thread, kwargs={'post_id': thread_to_show}) +
97 '#' + str(post.id))
97 '#' + str(post.id))
98
98
99
99
100 def tag(request, tag_name, page=0):
100 def tag(request, tag_name, page=0):
101 """Get all tag threads (posts without a parent)."""
101 """Get all tag threads (posts without a parent)."""
102
102
103 tag = get_object_or_404(Tag, name=tag_name)
103 tag = get_object_or_404(Tag, name=tag_name)
104 threads = []
104 threads = []
105 for thread in Post.objects.get_threads(tag=tag, page=int(page)):
105 for thread in Post.objects.get_threads(tag=tag, page=int(page)):
106 threads.append({'thread': thread,
106 threads.append({'thread': thread,
107 'bumpable': thread.can_bump()})
107 'bumpable': thread.can_bump()})
108
108
109 if request.method == 'POST':
109 if request.method == 'POST':
110 form = ThreadForm(request.POST, request.FILES,
110 form = ThreadForm(request.POST, request.FILES,
111 error_class=PlainErrorList)
111 error_class=PlainErrorList)
112 if form.is_valid():
112 if form.is_valid():
113 return _new_post(request, form)
113 return _new_post(request, form)
114 else:
114 else:
115 form = forms.ThreadForm(initial={'tags': tag_name},
115 form = forms.ThreadForm(initial={'tags': tag_name},
116 error_class=PlainErrorList)
116 error_class=PlainErrorList)
117
117
118 context = _init_default_context(request)
118 context = _init_default_context(request)
119 context['threads'] = None if len(threads) == 0 else threads
119 context['threads'] = None if len(threads) == 0 else threads
120 context['tag'] = tag
120 context['tag'] = tag
121 context['pages'] = range(Post.objects.get_thread_page_count(tag=tag))
121 context['pages'] = range(Post.objects.get_thread_page_count(tag=tag))
122
122
123 context['form'] = form
123 context['form'] = form
124
124
125 return render(request, 'boards/posting_general.html',
125 return render(request, 'boards/posting_general.html',
126 context)
126 context)
127
127
128
128
129 def thread(request, post_id):
129 def thread(request, post_id):
130 """Get all thread posts"""
130 """Get all thread posts"""
131
131
132 if utils.need_include_captcha(request):
132 if utils.need_include_captcha(request):
133 postFormClass = PostCaptchaForm
133 postFormClass = PostCaptchaForm
134 kwargs = {'request': request}
134 kwargs = {'request': request}
135 else:
135 else:
136 postFormClass = PostForm
136 postFormClass = PostForm
137 kwargs = {}
137 kwargs = {}
138
138
139 if request.method == 'POST':
139 if request.method == 'POST':
140 form = postFormClass(request.POST, request.FILES,
140 form = postFormClass(request.POST, request.FILES,
141 error_class=PlainErrorList, **kwargs)
141 error_class=PlainErrorList, **kwargs)
142 form.session = request.session
142 form.session = request.session
143
143
144 if form.is_valid():
144 if form.is_valid():
145 return _new_post(request, form, post_id)
145 return _new_post(request, form, post_id)
146 else:
146 else:
147 form = postFormClass(error_class=PlainErrorList, **kwargs)
147 form = postFormClass(error_class=PlainErrorList, **kwargs)
148
148
149 posts = Post.objects.get_thread(post_id)
149 posts = Post.objects.get_thread(post_id)
150
150
151 context = _init_default_context(request)
151 context = _init_default_context(request)
152
152
153 context['posts'] = posts
153 context['posts'] = posts
154 context['form'] = form
154 context['form'] = form
155 context['bumpable'] = posts[0].can_bump()
155 context['bumpable'] = posts[0].can_bump()
156
156
157 return render(request, 'boards/thread.html', context)
157 return render(request, 'boards/thread.html', context)
158
158
159
159
160 def login(request):
160 def login(request):
161 """Log in with user id"""
161 """Log in with user id"""
162
162
163 context = _init_default_context(request)
163 context = _init_default_context(request)
164
164
165 if request.method == 'POST':
165 if request.method == 'POST':
166 form = LoginForm(request.POST, request.FILES,
166 form = LoginForm(request.POST, request.FILES,
167 error_class=PlainErrorList)
167 error_class=PlainErrorList)
168 if form.is_valid():
168 if form.is_valid():
169 user = User.objects.get(user_id=form.cleaned_data['user_id'])
169 user = User.objects.get(user_id=form.cleaned_data['user_id'])
170 request.session['user_id'] = user.id
170 request.session['user_id'] = user.id
171 return redirect(index)
171 return redirect(index)
172
172
173 else:
173 else:
174 form = LoginForm()
174 form = LoginForm()
175
175
176 context['form'] = form
176 context['form'] = form
177
177
178 return render(request, 'boards/login.html', context)
178 return render(request, 'boards/login.html', context)
179
179
180
180
181 def settings(request):
181 def settings(request):
182 """User's settings"""
182 """User's settings"""
183
183
184 context = _init_default_context(request)
184 context = _init_default_context(request)
185
185
186 if request.method == 'POST':
186 if request.method == 'POST':
187 form = SettingsForm(request.POST)
187 form = SettingsForm(request.POST)
188 if form.is_valid():
188 if form.is_valid():
189 selected_theme = form.cleaned_data['theme']
189 selected_theme = form.cleaned_data['theme']
190
190
191 user = _get_user(request)
191 user = _get_user(request)
192 user.save_setting('theme', selected_theme)
192 user.save_setting('theme', selected_theme)
193
193
194 return redirect(settings)
194 return redirect(settings)
195 else:
195 else:
196 selected_theme = _get_theme(request)
196 selected_theme = _get_theme(request)
197 form = SettingsForm(initial={'theme': selected_theme})
197 form = SettingsForm(initial={'theme': selected_theme})
198 context['form'] = form
198 context['form'] = form
199
199
200 return render(request, 'boards/settings.html', context)
200 return render(request, 'boards/settings.html', context)
201
201
202
202
203 def all_tags(request):
203 def all_tags(request):
204 """All tags list"""
204 """All tags list"""
205
205
206 context = _init_default_context(request)
206 context = _init_default_context(request)
207 context['all_tags'] = Tag.objects.get_not_empty_tags()
207 context['all_tags'] = Tag.objects.get_not_empty_tags()
208
208
209 return render(request, 'boards/tags.html', context)
209 return render(request, 'boards/tags.html', context)
210
210
211
211
212 def jump_to_post(request, post_id):
212 def jump_to_post(request, post_id):
213 """Determine thread in which the requested post is and open it's page"""
213 """Determine thread in which the requested post is and open it's page"""
214
214
215 post = get_object_or_404(Post, id=post_id)
215 post = get_object_or_404(Post, id=post_id)
216
216
217 if not post.thread:
217 if not post.thread:
218 return redirect(thread, post_id=post.id)
218 return redirect(thread, post_id=post.id)
219 else:
219 else:
220 return redirect(reverse(thread, kwargs={'post_id': post.thread.id})
220 return redirect(reverse(thread, kwargs={'post_id': post.thread.id})
221 + '#' + str(post.id))
221 + '#' + str(post.id))
222
222
223
223
224 def authors(request):
224 def authors(request):
225 context = _init_default_context(request)
225 context = _init_default_context(request)
226 context['authors'] = boards.authors.authors
226 context['authors'] = boards.authors.authors
227
227
228 return render(request, 'boards/authors.html', context)
228 return render(request, 'boards/authors.html', context)
229
229
230
230
231 def delete(request, post_id):
231 def delete(request, post_id):
232 user = _get_user(request)
232 user = _get_user(request)
233 post = get_object_or_404(Post, id=post_id)
233 post = get_object_or_404(Post, id=post_id)
234
234
235 if user.is_moderator():
235 if user.is_moderator():
236 # TODO Show confirmation page before deletion
236 # TODO Show confirmation page before deletion
237 Post.objects.delete_post(post)
237 Post.objects.delete_post(post)
238
238
239 if not post.thread:
239 if not post.thread:
240 return _redirect_to_next(request)
240 return _redirect_to_next(request)
241 else:
241 else:
242 return redirect(thread, post_id=post.thread.id)
242 return redirect(thread, post_id=post.thread.id)
243
243
244
244
245 def ban(request, post_id):
245 def ban(request, post_id):
246 user = _get_user(request)
246 user = _get_user(request)
247 post = get_object_or_404(Post, id=post_id)
247 post = get_object_or_404(Post, id=post_id)
248
248
249 if user.is_moderator():
249 if user.is_moderator():
250 # TODO Show confirmation page before ban
250 # TODO Show confirmation page before ban
251 Ban.objects.get_or_create(ip=post.poster_ip)
251 Ban.objects.get_or_create(ip=post.poster_ip)
252
252
253 return _redirect_to_next(request)
253 return _redirect_to_next(request)
254
254
255
255
256 def you_are_banned(request):
256 def you_are_banned(request):
257 context = _init_default_context(request)
257 context = _init_default_context(request)
258 return render(request, 'boards/staticpages/banned.html', context)
258 return render(request, 'boards/staticpages/banned.html', context)
259
259
260
260
261 def page_404(request):
261 def page_404(request):
262 context = _init_default_context(request)
262 context = _init_default_context(request)
263 return render(request, 'boards/404.html', context)
263 return render(request, 'boards/404.html', context)
264
264
265
265
266 def tag_subscribe(request, tag_name):
266 def tag_subscribe(request, tag_name):
267 user = _get_user(request)
267 user = _get_user(request)
268 tag = get_object_or_404(Tag, name=tag_name)
268 tag = get_object_or_404(Tag, name=tag_name)
269
269
270 if not tag in user.fav_tags.all():
270 if not tag in user.fav_tags.all():
271 user.fav_tags.add(tag)
271 user.fav_tags.add(tag)
272
272
273 return _redirect_to_next(request)
273 return _redirect_to_next(request)
274
274
275
275
276 def tag_unsubscribe(request, tag_name):
276 def tag_unsubscribe(request, tag_name):
277 user = _get_user(request)
277 user = _get_user(request)
278 tag = get_object_or_404(Tag, name=tag_name)
278 tag = get_object_or_404(Tag, name=tag_name)
279
279
280 if tag in user.fav_tags.all():
280 if tag in user.fav_tags.all():
281 user.fav_tags.remove(tag)
281 user.fav_tags.remove(tag)
282
282
283 return _redirect_to_next(request)
283 return _redirect_to_next(request)
284
284
285
285
286 def static_page(request, name):
286 def static_page(request, name):
287 context = _init_default_context(request)
287 context = _init_default_context(request)
288 return render(request, 'boards/staticpages/' + name + '.html', context)
288 return render(request, 'boards/staticpages/' + name + '.html', context)
289
289
290
290
291 def _get_theme(request, user=None):
291 def _get_theme(request, user=None):
292 """Get user's CSS theme"""
292 """Get user's CSS theme"""
293
293
294 if not user:
294 if not user:
295 user = _get_user(request)
295 user = _get_user(request)
296 theme = user.get_setting('theme')
296 theme = user.get_setting('theme')
297 if not theme:
297 if not theme:
298 theme = neboard.settings.DEFAULT_THEME
298 theme = neboard.settings.DEFAULT_THEME
299
299
300 return theme
300 return theme
301
301
302
302
303 def _get_client_ip(request):
303 def _get_client_ip(request):
304 x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
304 x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
305 if x_forwarded_for:
305 if x_forwarded_for:
306 ip = x_forwarded_for.split(',')[-1].strip()
306 ip = x_forwarded_for.split(',')[-1].strip()
307 else:
307 else:
308 ip = request.META.get('REMOTE_ADDR')
308 ip = request.META.get('REMOTE_ADDR')
309 return ip
309 return ip
310
310
311
311
312 def _init_default_context(request):
312 def _init_default_context(request):
313 """Create context with default values that are used in most views"""
313 """Create context with default values that are used in most views"""
314
314
315 context = RequestContext(request)
315 context = RequestContext(request)
316
316
317 user = _get_user(request)
317 user = _get_user(request)
318 context['user'] = user
318 context['user'] = user
319 context['tags'] = user.get_sorted_fav_tags()
319 context['tags'] = user.get_sorted_fav_tags()
320 context['theme'] = _get_theme(request, user)
320 context['theme'] = _get_theme(request, user)
321 context['moderator'] = user.is_moderator()
321 context['moderator'] = user.is_moderator()
322
322
323 return context
323 return context
324
324
325
325
326 def _get_user(request):
326 def _get_user(request):
327 """Get current user from the session"""
327 """Get current user from the session"""
328
328
329 session = request.session
329 session = request.session
330 if not 'user_id' in session:
330 if not 'user_id' in session:
331 request.session.save()
331 request.session.save()
332
332
333 md5 = hashlib.md5()
333 md5 = hashlib.md5()
334 md5.update(session.session_key)
334 md5.update(session.session_key)
335 new_id = md5.hexdigest()
335 new_id = md5.hexdigest()
336
336
337 time_now = timezone.now()
337 time_now = timezone.now()
338 user = User.objects.create(user_id=new_id, rank=RANK_USER,
338 user = User.objects.create(user_id=new_id, rank=RANK_USER,
339 registration_time=time_now,
339 registration_time=time_now,
340 last_access_time=time_now)
340 last_access_time=time_now)
341
341
342 session['user_id'] = user.id
342 session['user_id'] = user.id
343 else:
343 else:
344 user = User.objects.get(id=session['user_id'])
344 user = User.objects.get(id=session['user_id'])
345 user.last_access_time = timezone.now()
345 user.last_access_time = timezone.now()
346 user.save()
346 user.save()
347
347
348 return user
348 return user
349
349
350
350
351 def _redirect_to_next(request):
351 def _redirect_to_next(request):
352 if 'next' in request.GET:
352 if 'next' in request.GET:
353 next_page = request.GET['next']
353 next_page = request.GET['next']
354 return HttpResponseRedirect(next_page)
354 return HttpResponseRedirect(next_page)
355 else:
355 else:
356 return redirect(index)
356 return redirect(index)
General Comments 0
You need to be logged in to leave comments. Login now