##// END OF EJS Templates
Partly merged with default branch
neko259 -
r1157:d41f2b1c merge decentral
parent child Browse files
Show More
@@ -0,0 +1,33 b''
1 [Version]
2 Version = 2.7.0 Chani
3 SiteName = Neboard
4
5 [Cache]
6 # Timeout for caching, if cache is used
7 CacheTimeout = 600
8
9 [Forms]
10 # Max post length in characters
11 MaxTextLength = 30000
12 MaxImageSize = 8000000
13 LimitPostingSpeed = false
14
15 [Messages]
16 # Thread bumplimit
17 MaxPostsPerThread = 10
18 # Old posts will be archived or deleted if this value is reached
19 MaxThreadCount = 5
20
21 [View]
22 DefaultTheme = md
23 DefaultImageViewer = simple
24 LastRepliesCount = 3
25 ThreadsPerPage = 3
26
27 [Storage]
28 # Enable archiving threads instead of deletion when the thread limit is reached
29 ArchiveThreads = true
30
31 [External]
32 # Thread update
33 WebsocketsEnabled = false
@@ -0,0 +1,23 b''
1 # -*- coding: utf-8 -*-
2 from __future__ import unicode_literals
3
4 from django.db import models, migrations
5
6
7 class Migration(migrations.Migration):
8
9 dependencies = [
10 ('boards', '0017_auto_20150503_1847'),
11 ]
12
13 operations = [
14 migrations.CreateModel(
15 name='Banner',
16 fields=[
17 ('id', models.AutoField(serialize=False, primary_key=True, verbose_name='ID', auto_created=True)),
18 ('title', models.TextField()),
19 ('text', models.TextField()),
20 ('post', models.ForeignKey(to='boards.Post')),
21 ],
22 ),
23 ]
@@ -0,0 +1,10 b''
1 from django.db import models
2
3
4 class Banner(models.Model):
5 title = models.TextField()
6 text = models.TextField()
7 post = models.ForeignKey('Post')
8
9 def __str__(self):
10 return self.title
@@ -0,0 +1,55 b''
1 from boards import utils
2
3
4 PARAMETER_TRUNCATED = 'truncated'
5
6 DIFF_TYPE_HTML = 'html'
7 DIFF_TYPE_JSON = 'json'
8
9
10 class Exporter():
11 @staticmethod
12 def export(post, request, include_last_update) -> str:
13 pass
14
15
16 class HtmlExporter(Exporter):
17 @staticmethod
18 def export(post, request, include_last_update):
19 if request is not None and PARAMETER_TRUNCATED in request.GET:
20 truncated = True
21 reply_link = False
22 else:
23 truncated = False
24 reply_link = True
25
26 return post.get_view(truncated=truncated, reply_link=reply_link,
27 moderator=utils.is_moderator(request))
28
29
30 class JsonExporter(Exporter):
31 @staticmethod
32 def export(post, request, include_last_update):
33 post_json = {
34 'id': post.id,
35 'title': post.title,
36 'text': post.get_raw_text(),
37 }
38 if post.images.exists():
39 post_image = post.get_first_image()
40 post_json['image'] = post_image.image.url
41 post_json['image_preview'] = post_image.image.url_200x150
42 if include_last_update:
43 post_json['bump_time'] = utils.datetime_to_epoch(
44 post.get_thread().bump_time)
45 return post_json
46
47
48 EXPORTERS = {
49 DIFF_TYPE_HTML: HtmlExporter,
50 DIFF_TYPE_JSON: JsonExporter,
51 }
52
53
54 def get_exporter(export_type: str) -> Exporter:
55 return EXPORTERS[export_type]()
@@ -28,3 +28,4 b' 119fafc5381b933bf30d97be0b278349f6135075'
28 d528d76d3242cced614fa11bb63f3d342e4e1d09 2.5.2
28 d528d76d3242cced614fa11bb63f3d342e4e1d09 2.5.2
29 1b631781ced34fbdeec032e7674bc4e131724699 2.6.0
29 1b631781ced34fbdeec032e7674bc4e131724699 2.6.0
30 0f2ef17dc0de678ada279bf7eedf6c5585f1fd7a 2.6.1
30 0f2ef17dc0de678ada279bf7eedf6c5585f1fd7a 2.6.1
31 d53fc814a424d7fd90f23025c87b87baa164450e 2.7.0
@@ -1,5 +1,5 b''
1 from django.contrib import admin
1 from django.contrib import admin
2 from boards.models import Post, Tag, Ban, Thread, KeyPair
2 from boards.models import Post, Tag, Ban, Thread, KeyPair, Banner
3 from django.utils.translation import ugettext_lazy as _
3 from django.utils.translation import ugettext_lazy as _
4
4
5
5
@@ -64,3 +64,8 b' class BanAdmin(admin.ModelAdmin):'
64 list_display = ('ip', 'can_read')
64 list_display = ('ip', 'can_read')
65 list_filter = ('can_read',)
65 list_filter = ('can_read',)
66 search_fields = ('ip',)
66 search_fields = ('ip',)
67
68
69 @admin.register(Banner)
70 class BannerAdmin(admin.ModelAdmin):
71 list_display = ('title', 'text')
@@ -51,11 +51,12 b' def user_and_ui_processor(request):'
51 # This shows the moderator panel
51 # This shows the moderator panel
52 context[CONTEXT_MODERATOR] = utils.is_moderator(request)
52 context[CONTEXT_MODERATOR] = utils.is_moderator(request)
53
53
54 context[CONTEXT_VERSION] = settings.VERSION
54 context[CONTEXT_VERSION] = settings.get('Version', 'Version')
55 context[CONTEXT_SITE_NAME] = settings.SITE_NAME
55 context[CONTEXT_SITE_NAME] = settings.get('Version', 'SiteName')
56
56
57 context[CONTEXT_IMAGE_VIEWER] = settings_manager.get_setting(
57 context[CONTEXT_IMAGE_VIEWER] = settings_manager.get_setting(
58 SETTING_IMAGE_VIEWER, default=settings.DEFAULT_IMAGE_VIEWER)
58 SETTING_IMAGE_VIEWER,
59 default=settings.get('View', 'DefaultImageViewer'))
59
60
60 get_notifications(context, request)
61 get_notifications(context, request)
61
62
@@ -172,11 +172,10 b' class PostForm(NeboardForm):'
172 def clean_text(self):
172 def clean_text(self):
173 text = self.cleaned_data['text'].strip()
173 text = self.cleaned_data['text'].strip()
174 if text:
174 if text:
175 if len(text) > board_settings.MAX_TEXT_LENGTH:
175 max_length = board_settings.get_int('Forms', 'MaxTextLength')
176 if len(text) > max_length:
176 raise forms.ValidationError(_('Text must have less than %s '
177 raise forms.ValidationError(_('Text must have less than %s '
177 'characters') %
178 'characters') % str(max_length))
178 str(board_settings
179 .MAX_TEXT_LENGTH))
180 return text
179 return text
181
180
182 def clean_image(self):
181 def clean_image(self):
@@ -256,7 +255,7 b' class PostForm(NeboardForm):'
256
255
257 posting_delay = settings.POSTING_DELAY
256 posting_delay = settings.POSTING_DELAY
258
257
259 if board_settings.LIMIT_POSTING_SPEED:
258 if board_settings.get_bool('Forms', 'LimitPostingSpeed'):
260 now = time.time()
259 now = time.time()
261
260
262 current_delay = 0
261 current_delay = 0
@@ -283,10 +282,11 b' class PostForm(NeboardForm):'
283 self.session[LAST_POST_TIME] = now
282 self.session[LAST_POST_TIME] = now
284
283
285 def validate_image_size(self, size: int):
284 def validate_image_size(self, size: int):
286 if size > board_settings.MAX_IMAGE_SIZE:
285 max_size = board_settings.get_int('Forms', 'MaxImageSize')
286 if size > max_size:
287 raise forms.ValidationError(
287 raise forms.ValidationError(
288 _('Image must be less than %s bytes')
288 _('Image must be less than %s bytes')
289 % str(board_settings.MAX_IMAGE_SIZE))
289 % str(max_size))
290
290
291 def _get_image_from_url(self, url: str) -> SimpleUploadedFile:
291 def _get_image_from_url(self, url: str) -> SimpleUploadedFile:
292 """
292 """
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
@@ -7,7 +7,7 b' 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: 2015-04-21 21:10+0300\n"
10 "POT-Creation-Date: 2015-05-07 13:10+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"
@@ -68,79 +68,79 b' msgstr "\xd0\x9f\xd0\xbe\xd0\xb8\xd1\x81\xd0\xba"'
68 msgid "Please wait %s seconds before sending message"
68 msgid "Please wait %s seconds before sending message"
69 msgstr "Пожалуйста подождите %s секунд перед отправкой сообщения"
69 msgstr "Пожалуйста подождите %s секунд перед отправкой сообщения"
70
70
71 #: forms.py:142
71 #: forms.py:144
72 msgid "Image"
72 msgid "Image"
73 msgstr "Изображение"
73 msgstr "Изображение"
74
74
75 #: forms.py:145
75 #: forms.py:147
76 msgid "Image URL"
76 msgid "Image URL"
77 msgstr "URL изображения"
77 msgstr "URL изображения"
78
78
79 #: forms.py:151
79 #: forms.py:153
80 msgid "e-mail"
80 msgid "e-mail"
81 msgstr ""
81 msgstr ""
82
82
83 #: forms.py:154
83 #: forms.py:156
84 msgid "Additional threads"
84 msgid "Additional threads"
85 msgstr "Дополнительные темы"
85 msgstr "Дополнительные темы"
86
86
87 #: forms.py:165
87 #: forms.py:167
88 #, python-format
88 #, python-format
89 msgid "Title must have less than %s characters"
89 msgid "Title must have less than %s characters"
90 msgstr "Заголовок должен иметь меньше %s символов"
90 msgstr "Заголовок должен иметь меньше %s символов"
91
91
92 #: forms.py:174
92 #: forms.py:176
93 #, python-format
93 #, python-format
94 msgid "Text must have less than %s characters"
94 msgid "Text must have less than %s characters"
95 msgstr "Текст должен быть короче %s символов"
95 msgstr "Текст должен быть короче %s символов"
96
96
97 #: forms.py:196
97 #: forms.py:198
98 msgid "Invalid URL"
98 msgid "Invalid URL"
99 msgstr "Неверный URL"
99 msgstr "Неверный URL"
100
100
101 #: forms.py:217
101 #: forms.py:219
102 msgid "Invalid additional thread list"
102 msgid "Invalid additional thread list"
103 msgstr "Неверный список дополнительных тем"
103 msgstr "Неверный список дополнительных тем"
104
104
105 #: forms.py:249
105 #: forms.py:251
106 msgid "Either text or image must be entered."
106 msgid "Either text or image must be entered."
107 msgstr "Текст или картинка должны быть введены."
107 msgstr "Текст или картинка должны быть введены."
108
108
109 #: forms.py:286
109 #: forms.py:288
110 #, python-format
110 #, python-format
111 msgid "Image must be less than %s bytes"
111 msgid "Image must be less than %s bytes"
112 msgstr "Изображение должно быть менее %s байт"
112 msgstr "Изображение должно быть менее %s байт"
113
113
114 #: forms.py:333 templates/boards/posting_general.html:145
114 #: forms.py:335 templates/boards/posting_general.html:129
115 #: templates/boards/rss/post.html:10 templates/boards/tags.html:7
115 #: templates/boards/rss/post.html:10 templates/boards/tags.html:7
116 msgid "Tags"
116 msgid "Tags"
117 msgstr "Метки"
117 msgstr "Метки"
118
118
119 #: forms.py:340
119 #: forms.py:342
120 msgid "Inappropriate characters in tags."
120 msgid "Inappropriate characters in tags."
121 msgstr "Недопустимые символы в метках."
121 msgstr "Недопустимые символы в метках."
122
122
123 #: forms.py:354
123 #: forms.py:356
124 msgid "Need at least one of the tags: "
124 msgid "Need at least one of the tags: "
125 msgstr "Нужна хотя бы одна из меток: "
125 msgstr "Нужна хотя бы одна из меток: "
126
126
127 #: forms.py:367
127 #: forms.py:369
128 msgid "Theme"
128 msgid "Theme"
129 msgstr "Тема"
129 msgstr "Тема"
130
130
131 #: forms.py:368
131 #: forms.py:370
132 msgid "Image view mode"
132 msgid "Image view mode"
133 msgstr "Режим просмотра изображений"
133 msgstr "Режим просмотра изображений"
134
134
135 #: forms.py:369
135 #: forms.py:371
136 msgid "User name"
136 msgid "User name"
137 msgstr "Имя пользователя"
137 msgstr "Имя пользователя"
138
138
139 #: forms.py:370
139 #: forms.py:372
140 msgid "Time zone"
140 msgid "Time zone"
141 msgstr "Часовой пояс"
141 msgstr "Часовой пояс"
142
142
143 #: forms.py:376
143 #: forms.py:378
144 msgid "Inappropriate characters."
144 msgid "Inappropriate characters."
145 msgstr "Недопустимые символы."
145 msgstr "Недопустимые символы."
146
146
@@ -189,7 +189,7 b' msgstr "\xd0\xa3\xd0\xbf\xd1\x80\xd0\xb0\xd0\xb2\xd0\xbb\xd0\xb5\xd0\xbd\xd0\xb8\xd0\xb5 \xd0\xbc\xd0\xb5\xd1\x82\xd0\xba\xd0\xb0\xd0\xbc\xd0\xb8"'
189 msgid "Notifications"
189 msgid "Notifications"
190 msgstr "Уведомления"
190 msgstr "Уведомления"
191
191
192 #: templates/boards/base.html:52 templates/boards/settings.html:9
192 #: templates/boards/base.html:52 templates/boards/settings.html:8
193 msgid "Settings"
193 msgid "Settings"
194 msgstr "Настройки"
194 msgstr "Настройки"
195
195
@@ -207,12 +207,12 b' msgid "Up"'
207 msgstr "Вверх"
207 msgstr "Вверх"
208
208
209 #: templates/boards/notifications.html:17
209 #: templates/boards/notifications.html:17
210 #: templates/boards/posting_general.html:80 templates/search/search.html:26
210 #: templates/boards/posting_general.html:70 templates/search/search.html:26
211 msgid "Previous page"
211 msgid "Previous page"
212 msgstr "Предыдущая страница"
212 msgstr "Предыдущая страница"
213
213
214 #: templates/boards/notifications.html:27
214 #: templates/boards/notifications.html:27
215 #: templates/boards/posting_general.html:118 templates/search/search.html:37
215 #: templates/boards/posting_general.html:102 templates/search/search.html:37
216 msgid "Next page"
216 msgid "Next page"
217 msgstr "Следующая страница"
217 msgstr "Следующая страница"
218
218
@@ -244,44 +244,49 b' msgstr "\xd1\x81\xd0\xbe\xd0\xbe\xd0\xb1\xd1\x89\xd0\xb5\xd0\xbd\xd0\xb8\xd0\xb9"'
244 msgid "images"
244 msgid "images"
245 msgstr "изображений"
245 msgstr "изображений"
246
246
247 #: templates/boards/posting_general.html:64
247 #: templates/boards/posting_general.html:35
248 #| msgid "messages"
249 msgid "Related message"
250 msgstr "Связанное сообщение"
251
252 #: templates/boards/posting_general.html:60
248 msgid "Edit tag"
253 msgid "Edit tag"
249 msgstr "Изменить метку"
254 msgstr "Изменить метку"
250
255
251 #: templates/boards/posting_general.html:67
256 #: templates/boards/posting_general.html:63
252 #, python-format
257 #, python-format
253 msgid "This tag has %(thread_count)s threads and %(post_count)s posts."
258 msgid "This tag has %(thread_count)s threads and %(post_count)s posts."
254 msgstr "С этой меткой есть %(thread_count)s тем и %(post_count)s сообщений."
259 msgstr "С этой меткой есть %(thread_count)s тем и %(post_count)s сообщений."
255
260
256 #: templates/boards/posting_general.html:94
261 #: templates/boards/posting_general.html:84
257 #, python-format
262 #, python-format
258 msgid "Skipped %(count)s replies. Open thread to see all replies."
263 msgid "Skipped %(count)s replies. Open thread to see all replies."
259 msgstr "Пропущено %(count)s ответов. Откройте тред, чтобы увидеть все ответы."
264 msgstr "Пропущено %(count)s ответов. Откройте тред, чтобы увидеть все ответы."
260
265
261 #: templates/boards/posting_general.html:123
266 #: templates/boards/posting_general.html:107
262 msgid "No threads exist. Create the first one!"
267 msgid "No threads exist. Create the first one!"
263 msgstr "Нет тем. Создайте первую!"
268 msgstr "Нет тем. Создайте первую!"
264
269
265 #: templates/boards/posting_general.html:129
270 #: templates/boards/posting_general.html:113
266 msgid "Create new thread"
271 msgid "Create new thread"
267 msgstr "Создать новую тему"
272 msgstr "Создать новую тему"
268
273
269 #: templates/boards/posting_general.html:134 templates/boards/preview.html:16
274 #: templates/boards/posting_general.html:118 templates/boards/preview.html:16
270 #: templates/boards/thread_normal.html:43
275 #: templates/boards/thread_normal.html:43
271 msgid "Post"
276 msgid "Post"
272 msgstr "Отправить"
277 msgstr "Отправить"
273
278
274 #: templates/boards/posting_general.html:139
279 #: templates/boards/posting_general.html:123
275 msgid "Tags must be delimited by spaces. Text or image is required."
280 msgid "Tags must be delimited by spaces. Text or image is required."
276 msgstr ""
281 msgstr ""
277 "Метки должны быть разделены пробелами. Текст или изображение обязательны."
282 "Метки должны быть разделены пробелами. Текст или изображение обязательны."
278
283
279 #: templates/boards/posting_general.html:142
284 #: templates/boards/posting_general.html:126
280 #: templates/boards/thread_normal.html:48
285 #: templates/boards/thread_normal.html:48
281 msgid "Text syntax"
286 msgid "Text syntax"
282 msgstr "Синтаксис текста"
287 msgstr "Синтаксис текста"
283
288
284 #: templates/boards/posting_general.html:159
289 #: templates/boards/posting_general.html:143
285 msgid "Pages:"
290 msgid "Pages:"
286 msgstr "Страницы: "
291 msgstr "Страницы: "
287
292
@@ -293,19 +298,19 b' msgstr "\xd0\x9f\xd1\x80\xd0\xb5\xd0\xb4\xd0\xbf\xd1\x80\xd0\xbe\xd1\x81\xd0\xbc\xd0\xbe\xd1\x82\xd1\x80"'
293 msgid "Post image"
298 msgid "Post image"
294 msgstr "Изображение сообщения"
299 msgstr "Изображение сообщения"
295
300
296 #: templates/boards/settings.html:17
301 #: templates/boards/settings.html:16
297 msgid "You are moderator."
302 msgid "You are moderator."
298 msgstr "Вы модератор."
303 msgstr "Вы модератор."
299
304
300 #: templates/boards/settings.html:21
305 #: templates/boards/settings.html:20
301 msgid "Hidden tags:"
306 msgid "Hidden tags:"
302 msgstr "Скрытые метки:"
307 msgstr "Скрытые метки:"
303
308
304 #: templates/boards/settings.html:29
309 #: templates/boards/settings.html:28
305 msgid "No hidden tags."
310 msgid "No hidden tags."
306 msgstr "Нет скрытых меток."
311 msgstr "Нет скрытых меток."
307
312
308 #: templates/boards/settings.html:38
313 #: templates/boards/settings.html:37
309 msgid "Save"
314 msgid "Save"
310 msgstr "Сохранить"
315 msgstr "Сохранить"
311
316
@@ -377,7 +382,7 b' msgstr "\xd0\x9d\xd0\xbe\xd1\x80\xd0\xbc\xd0\xb0\xd0\xbb\xd1\x8c\xd0\xbd\xd1\x8b\xd0\xb9 \xd1\x80\xd0\xb5\xd0\xb6\xd0\xb8\xd0\xbc"'
377 msgid "Gallery mode"
382 msgid "Gallery mode"
378 msgstr "Режим галереи"
383 msgstr "Режим галереи"
379
384
380 #: templates/boards/thread_gallery.html:50
385 #: templates/boards/thread_gallery.html:41
381 msgid "No images."
386 msgid "No images."
382 msgstr "Нет изображений."
387 msgstr "Нет изображений."
383
388
@@ -401,8 +406,3 b' msgstr "\xd0\x9e\xd0\xb1\xd0\xbd\xd0\xbe\xd0\xb2\xd0\xb8\xd1\x82\xd1\x8c"'
401 msgid "Ok"
406 msgid "Ok"
402 msgstr "Ок"
407 msgstr "Ок"
403
408
404 #~ msgid "Wait %s seconds after last posting"
405 #~ msgstr "Подождите %s секунд после последнего постинга"
406
407 #~ msgid "tag1 several_words_tag"
408 #~ msgstr "метка1 метка_из_нескольких_слов"
@@ -7,3 +7,4 b' from boards.models.thread import Thread'
7 from boards.models.post import Post
7 from boards.models.post import Post
8 from boards.models.tag import Tag
8 from boards.models.tag import Tag
9 from boards.models.user import Ban
9 from boards.models.user import Ban
10 from boards.models.banner import Banner
1 NO CONTENT: file renamed from boards/models/post.py to boards/models/post/__init__.py
NO CONTENT: file renamed from boards/models/post.py to boards/models/post/__init__.py
@@ -56,9 +56,13 b' class Tag(models.Model, Viewable):'
56 def get_thread_count(self) -> int:
56 def get_thread_count(self) -> int:
57 return self.get_threads().count()
57 return self.get_threads().count()
58
58
59 # TODO Remove this and use get_absolute_url
59 def get_url(self):
60 def get_url(self):
60 return reverse('tag', kwargs={'tag_name': self.name})
61 return reverse('tag', kwargs={'tag_name': self.name})
61
62
63 def get_absolute_url(self):
64 return self.get_url()
65
62 def get_threads(self):
66 def get_threads(self):
63 return self.thread_set.order_by('-bump_time')
67 return self.thread_set.order_by('-bump_time')
64
68
@@ -67,7 +71,7 b' class Tag(models.Model, Viewable):'
67
71
68 def get_view(self):
72 def get_view(self):
69 link = '<a class="tag" href="{}">{}</a>'.format(
73 link = '<a class="tag" href="{}">{}</a>'.format(
70 self.get_url(), self.name)
74 self.get_absolute_url(), self.name)
71 if self.is_required():
75 if self.is_required():
72 link = '<b>{}</b>'.format(link)
76 link = '<b>{}</b>'.format(link)
73 return link
77 return link
@@ -34,12 +34,13 b' class ThreadManager(models.Manager):'
34 threads = Thread.objects.filter(archived=False).order_by('-bump_time')
34 threads = Thread.objects.filter(archived=False).order_by('-bump_time')
35 thread_count = threads.count()
35 thread_count = threads.count()
36
36
37 if thread_count > settings.MAX_THREAD_COUNT:
37 max_thread_count = settings.get_int('Messages', 'MaxThreadCount')
38 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
38 if thread_count > max_thread_count:
39 num_threads_to_delete = thread_count - max_thread_count
39 old_threads = threads[thread_count - num_threads_to_delete:]
40 old_threads = threads[thread_count - num_threads_to_delete:]
40
41
41 for thread in old_threads:
42 for thread in old_threads:
42 if settings.ARCHIVE_THREADS:
43 if settings.get_bool('Storage', 'ArchiveThreads'):
43 self._archive_thread(thread)
44 self._archive_thread(thread)
44 else:
45 else:
45 thread.delete()
46 thread.delete()
@@ -55,7 +56,7 b' class ThreadManager(models.Manager):'
55
56
56
57
57 def get_thread_max_posts():
58 def get_thread_max_posts():
58 return settings.MAX_POSTS_PER_THREAD
59 return settings.get_int('Messages', 'MaxPostsPerThread')
59
60
60
61
61 class Thread(models.Model):
62 class Thread(models.Model):
@@ -122,11 +123,13 b' class Thread(models.Model):'
122 Gets several last replies, not including opening post
123 Gets several last replies, not including opening post
123 """
124 """
124
125
125 if settings.LAST_REPLIES_COUNT > 0:
126 last_replies_count = settings.get_int('View', 'LastRepliesCount')
127
128 if last_replies_count > 0:
126 reply_count = self.get_reply_count()
129 reply_count = self.get_reply_count()
127
130
128 if reply_count > 0:
131 if reply_count > 0:
129 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
132 reply_count_to_show = min(last_replies_count,
130 reply_count - 1)
133 reply_count - 1)
131 replies = self.get_replies()
134 replies = self.get_replies()
132 last_replies = replies[reply_count - reply_count_to_show:]
135 last_replies = replies[reply_count - reply_count_to_show:]
@@ -138,7 +141,7 b' class Thread(models.Model):'
138 Gets number of posts between opening post and last replies.
141 Gets number of posts between opening post and last replies.
139 """
142 """
140 reply_count = self.get_reply_count()
143 reply_count = self.get_reply_count()
141 last_replies_count = min(settings.LAST_REPLIES_COUNT,
144 last_replies_count = min(settings.get_int('View', 'LastRepliesCount'),
142 reply_count - 1)
145 reply_count - 1)
143 return reply_count - last_replies_count - 1
146 return reply_count - last_replies_count - 1
144
147
@@ -222,7 +225,7 b' class Thread(models.Model):'
222 post.threads.update(last_edit_time=self.last_edit_time)
225 post.threads.update(last_edit_time=self.last_edit_time)
223
226
224 def notify_clients(self):
227 def notify_clients(self):
225 if not settings.WEBSOCKETS_ENABLED:
228 if not settings.get_bool('External', 'WebsocketsEnabled'):
226 return
229 return
227
230
228 client = Client()
231 client = Client()
@@ -232,3 +235,6 b' class Thread(models.Model):'
232 WS_NOTIFICATION_TYPE: WS_NOTIFICATION_TYPE_NEW_POST,
235 WS_NOTIFICATION_TYPE: WS_NOTIFICATION_TYPE_NEW_POST,
233 })
236 })
234 client.send()
237 client.send()
238
239 def get_absolute_url(self):
240 return self.get_opening_post().get_absolute_url()
@@ -10,7 +10,7 b' from boards import settings'
10 # TODO Make tests for all of these
10 # TODO Make tests for all of these
11 class AllThreadsFeed(Feed):
11 class AllThreadsFeed(Feed):
12
12
13 title = settings.SITE_NAME + ' - All threads'
13 title = settings.get('Version', 'SiteName') + ' - All threads'
14 link = '/'
14 link = '/'
15 description_template = 'boards/rss/post.html'
15 description_template = 'boards/rss/post.html'
16
16
@@ -1,2 +1,18 b''
1 from boards.default_settings import *
1 import configparser
2
3
4 config = configparser.ConfigParser()
5 config.read('boards/config/default_settings.ini')
6 config.read('boards/config/settings.ini')
7
2
8
9 def get(section, name):
10 return config[section][name]
11
12
13 def get_int(section, name):
14 return int(get(section, name))
15
16
17 def get_bool(section, name):
18 return get(section, name) == 'true'
@@ -28,6 +28,8 b" var CLASS_POST = '.post'"
28 var POST_ADDED = 0;
28 var POST_ADDED = 0;
29 var POST_UPDATED = 1;
29 var POST_UPDATED = 1;
30
30
31 var JS_AUTOUPDATE_PERIOD = 20000;
32
31 var wsUser = '';
33 var wsUser = '';
32
34
33 var unreadPosts = 0;
35 var unreadPosts = 0;
@@ -48,36 +50,39 b' function connectWebsocket() {'
48 var wsHost = metapanel.getAttribute('data-ws-host');
50 var wsHost = metapanel.getAttribute('data-ws-host');
49 var wsPort = metapanel.getAttribute('data-ws-port');
51 var wsPort = metapanel.getAttribute('data-ws-port');
50
52
51 if (wsHost.length > 0 && wsPort.length > 0)
53 if (wsHost.length > 0 && wsPort.length > 0) {
52 var centrifuge = new Centrifuge({
54 var centrifuge = new Centrifuge({
53 "url": 'ws://' + wsHost + ':' + wsPort + "/connection/websocket",
55 "url": 'ws://' + wsHost + ':' + wsPort + "/connection/websocket",
54 "project": metapanel.getAttribute('data-ws-project'),
56 "project": metapanel.getAttribute('data-ws-project'),
55 "user": wsUser,
57 "user": wsUser,
56 "timestamp": metapanel.getAttribute('data-ws-token-time'),
58 "timestamp": metapanel.getAttribute('data-ws-token-time'),
57 "token": metapanel.getAttribute('data-ws-token'),
59 "token": metapanel.getAttribute('data-ws-token'),
58 "debug": false
60 "debug": false
59 });
61 });
60
62
61 centrifuge.on('error', function(error_message) {
63 centrifuge.on('error', function(error_message) {
62 console.log("Error connecting to websocket server.");
64 console.log("Error connecting to websocket server.");
63 console.log(error_message);
65 console.log(error_message);
64 return false;
66 return false;
65 });
66
67 centrifuge.on('connect', function() {
68 var channelName = 'thread:' + threadId;
69 centrifuge.subscribe(channelName, function(message) {
70 getThreadDiff();
71 });
67 });
72
68
73 // For the case we closed the browser and missed some updates
69 centrifuge.on('connect', function() {
74 getThreadDiff();
70 var channelName = 'thread:' + threadId;
75 $('#autoupdate').hide();
71 centrifuge.subscribe(channelName, function(message) {
76 });
72 getThreadDiff();
73 });
77
74
78 centrifuge.connect();
75 // For the case we closed the browser and missed some updates
76 getThreadDiff();
77 $('#autoupdate').hide();
78 });
79
79
80 return true;
80 centrifuge.connect();
81
82 return true;
83 } else {
84 return false;
85 }
81 }
86 }
82
87
83 /**
88 /**
@@ -193,8 +198,21 b' function isPageBottom() {'
193 return scroll == 1
198 return scroll == 1
194 }
199 }
195
200
201 function enableJsUpdate() {
202 setInterval(getThreadDiff, JS_AUTOUPDATE_PERIOD);
203 return true;
204 }
205
196 function initAutoupdate() {
206 function initAutoupdate() {
197 return connectWebsocket();
207 if (location.protocol === 'https:') {
208 return enableJsUpdate();
209 } else {
210 if (connectWebsocket()) {
211 return true;
212 } else {
213 return enableJsUpdate();
214 }
215 }
198 }
216 }
199
217
200 function getReplyCount() {
218 function getReplyCount() {
@@ -28,6 +28,14 b''
28 {% get_current_language as LANGUAGE_CODE %}
28 {% get_current_language as LANGUAGE_CODE %}
29 {% get_current_timezone as TIME_ZONE %}
29 {% get_current_timezone as TIME_ZONE %}
30
30
31 {% for banner in banners %}
32 <div class="post">
33 <div class="title">{{ banner.title }}</div>
34 <div>{{ banner.text }}</div>
35 <div>{% trans 'Related message' %}: <a href="{{ banner.post.get_url }}">>>{{ banner.post.id }}</a></div>
36 </div>
37 {% endfor %}
38
31 {% if tag %}
39 {% if tag %}
32 <div class="tag_info">
40 <div class="tag_info">
33 <h2>
41 <h2>
@@ -1,7 +1,6 b''
1 {% extends "boards/base.html" %}
1 {% extends "boards/base.html" %}
2
2
3 {% load i18n %}
3 {% load i18n %}
4 {% load humanize %}
5 {% load tz %}
4 {% load tz %}
6
5
7 {% block head %}
6 {% block head %}
@@ -91,26 +91,26 b' class PostTests(TestCase):'
91 def test_thread_max_count(self):
91 def test_thread_max_count(self):
92 """Test deletion of old posts when the max thread count is reached"""
92 """Test deletion of old posts when the max thread count is reached"""
93
93
94 for i in range(settings.MAX_THREAD_COUNT + 1):
94 for i in range(settings.get_int('Messages', 'MaxThreadCount') + 1):
95 self._create_post()
95 self._create_post()
96
96
97 self.assertEqual(settings.MAX_THREAD_COUNT,
97 self.assertEqual(settings.get_int('Messages', 'MaxThreadCount'),
98 len(Thread.objects.filter(archived=False)))
98 len(Thread.objects.filter(archived=False)))
99
99
100 def test_pages(self):
100 def test_pages(self):
101 """Test that the thread list is properly split into pages"""
101 """Test that the thread list is properly split into pages"""
102
102
103 for i in range(settings.MAX_THREAD_COUNT):
103 for i in range(settings.get_int('Messages', 'MaxThreadCount')):
104 self._create_post()
104 self._create_post()
105
105
106 all_threads = Thread.objects.filter(archived=False)
106 all_threads = Thread.objects.filter(archived=False)
107
107
108 paginator = Paginator(Thread.objects.filter(archived=False),
108 paginator = Paginator(Thread.objects.filter(archived=False),
109 settings.THREADS_PER_PAGE)
109 settings.get_int('View', 'ThreadsPerPage'))
110 posts_in_second_page = paginator.page(2).object_list
110 posts_in_second_page = paginator.page(2).object_list
111 first_post = posts_in_second_page[0]
111 first_post = posts_in_second_page[0]
112
112
113 self.assertEqual(all_threads[settings.THREADS_PER_PAGE].id,
113 self.assertEqual(all_threads[settings.get_int('View', 'ThreadsPerPage')].id,
114 first_post.id)
114 first_post.id)
115
115
116 def test_reflinks(self):
116 def test_reflinks(self):
@@ -11,7 +11,7 b' from boards import utils, settings'
11 from boards.abstracts.paginator import get_paginator
11 from boards.abstracts.paginator import get_paginator
12 from boards.abstracts.settingsmanager import get_settings_manager
12 from boards.abstracts.settingsmanager import get_settings_manager
13 from boards.forms import ThreadForm, PlainErrorList
13 from boards.forms import ThreadForm, PlainErrorList
14 from boards.models import Post, Thread, Ban, Tag, PostImage
14 from boards.models import Post, Thread, Ban, Tag, PostImage, Banner
15 from boards.views.banned import BannedView
15 from boards.views.banned import BannedView
16 from boards.views.base import BaseBoardView, CONTEXT_FORM
16 from boards.views.base import BaseBoardView, CONTEXT_FORM
17 from boards.views.posting_mixin import PostMixin
17 from boards.views.posting_mixin import PostMixin
@@ -28,6 +28,7 b" TAG_DELIMITER = ' '"
28 PARAMETER_CURRENT_PAGE = 'current_page'
28 PARAMETER_CURRENT_PAGE = 'current_page'
29 PARAMETER_PAGINATOR = 'paginator'
29 PARAMETER_PAGINATOR = 'paginator'
30 PARAMETER_THREADS = 'threads'
30 PARAMETER_THREADS = 'threads'
31 PARAMETER_BANNERS = 'banners'
31
32
32 PARAMETER_PREV_LINK = 'prev_page_link'
33 PARAMETER_PREV_LINK = 'prev_page_link'
33 PARAMETER_NEXT_LINK = 'next_page_link'
34 PARAMETER_NEXT_LINK = 'next_page_link'
@@ -50,7 +51,7 b' class AllThreadsView(PostMixin, BaseBoar'
50
51
51 self.settings_manager = get_settings_manager(request)
52 self.settings_manager = get_settings_manager(request)
52 paginator = get_paginator(self.get_threads(),
53 paginator = get_paginator(self.get_threads(),
53 settings.THREADS_PER_PAGE)
54 settings.get_int('View', 'ThreadsPerPage'))
54 paginator.current_page = int(page)
55 paginator.current_page = int(page)
55
56
56 try:
57 try:
@@ -60,6 +61,7 b' class AllThreadsView(PostMixin, BaseBoar'
60
61
61 params[PARAMETER_THREADS] = threads
62 params[PARAMETER_THREADS] = threads
62 params[CONTEXT_FORM] = form
63 params[CONTEXT_FORM] = form
64 params[PARAMETER_BANNERS] = Banner.objects.order_by('-id').all()
63
65
64 self.get_page_context(paginator, params, page)
66 self.get_page_context(paginator, params, page)
65
67
@@ -32,7 +32,8 b' class SettingsView(BaseBoardView):'
32 initial={
32 initial={
33 FORM_THEME: selected_theme,
33 FORM_THEME: selected_theme,
34 FORM_IMAGE_VIEWER: settings_manager.get_setting(
34 FORM_IMAGE_VIEWER: settings_manager.get_setting(
35 SETTING_IMAGE_VIEWER, default=settings.DEFAULT_IMAGE_VIEWER),
35 SETTING_IMAGE_VIEWER,
36 default=settings.get('View', 'DefaultImageViewer')),
36 FORM_USERNAME: settings_manager.get_setting(SETTING_USERNAME),
37 FORM_USERNAME: settings_manager.get_setting(SETTING_USERNAME),
37 FORM_TIMEZONE: request.session.get(
38 FORM_TIMEZONE: request.session.get(
38 SESSION_TIMEZONE, timezone.get_current_timezone()),
39 SESSION_TIMEZONE, timezone.get_current_timezone()),
@@ -51,7 +51,7 b' class ThreadView(BaseBoardView, PostMixi'
51 params[CONTEXT_LASTUPDATE] = str(thread_to_show.last_edit_time)
51 params[CONTEXT_LASTUPDATE] = str(thread_to_show.last_edit_time)
52 params[CONTEXT_THREAD] = thread_to_show
52 params[CONTEXT_THREAD] = thread_to_show
53
53
54 if settings.WEBSOCKETS_ENABLED:
54 if settings.get_bool('External', 'WebsocketsEnabled'):
55 token_time = format(timezone.now(), u'U')
55 token_time = format(timezone.now(), u'U')
56
56
57 params[CONTEXT_WS_TIME] = token_time
57 params[CONTEXT_WS_TIME] = token_time
@@ -144,7 +144,7 b' INSTALLED_APPS = ('
144 'django.contrib.admin',
144 'django.contrib.admin',
145 # Uncomment the next line to enable admin documentation:
145 # Uncomment the next line to enable admin documentation:
146 # 'django.contrib.admindocs',
146 # 'django.contrib.admindocs',
147 'django.contrib.humanize',
147 #'django.contrib.humanize',
148
148
149 'debug_toolbar',
149 'debug_toolbar',
150
150
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now