# HG changeset patch # User neko259 # Date 2015-05-09 11:36:27 # Node ID d41f2b1c638cbb43b41367b4582c202a88493760 # Parent a82e4e4dac76f277e5bc5f630b9c2e54b9e86c41 # Parent 1c4910b708b2893e29f33761642e81005f5341b2 Partly merged with default branch diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -28,3 +28,4 @@ 119fafc5381b933bf30d97be0b278349f6135075 d528d76d3242cced614fa11bb63f3d342e4e1d09 2.5.2 1b631781ced34fbdeec032e7674bc4e131724699 2.6.0 0f2ef17dc0de678ada279bf7eedf6c5585f1fd7a 2.6.1 +d53fc814a424d7fd90f23025c87b87baa164450e 2.7.0 diff --git a/boards/admin.py b/boards/admin.py --- a/boards/admin.py +++ b/boards/admin.py @@ -1,5 +1,5 @@ from django.contrib import admin -from boards.models import Post, Tag, Ban, Thread, KeyPair +from boards.models import Post, Tag, Ban, Thread, KeyPair, Banner from django.utils.translation import ugettext_lazy as _ @@ -64,3 +64,8 @@ class BanAdmin(admin.ModelAdmin): list_display = ('ip', 'can_read') list_filter = ('can_read',) search_fields = ('ip',) + + +@admin.register(Banner) +class BannerAdmin(admin.ModelAdmin): + list_display = ('title', 'text') diff --git a/boards/config/default_settings.ini b/boards/config/default_settings.ini new file mode 100644 --- /dev/null +++ b/boards/config/default_settings.ini @@ -0,0 +1,33 @@ +[Version] +Version = 2.7.0 Chani +SiteName = Neboard + +[Cache] +# Timeout for caching, if cache is used +CacheTimeout = 600 + +[Forms] +# Max post length in characters +MaxTextLength = 30000 +MaxImageSize = 8000000 +LimitPostingSpeed = false + +[Messages] +# Thread bumplimit +MaxPostsPerThread = 10 +# Old posts will be archived or deleted if this value is reached +MaxThreadCount = 5 + +[View] +DefaultTheme = md +DefaultImageViewer = simple +LastRepliesCount = 3 +ThreadsPerPage = 3 + +[Storage] +# Enable archiving threads instead of deletion when the thread limit is reached +ArchiveThreads = true + +[External] +# Thread update +WebsocketsEnabled = false diff --git a/boards/context_processors.py b/boards/context_processors.py --- a/boards/context_processors.py +++ b/boards/context_processors.py @@ -51,11 +51,12 @@ def user_and_ui_processor(request): # This shows the moderator panel context[CONTEXT_MODERATOR] = utils.is_moderator(request) - context[CONTEXT_VERSION] = settings.VERSION - context[CONTEXT_SITE_NAME] = settings.SITE_NAME + context[CONTEXT_VERSION] = settings.get('Version', 'Version') + context[CONTEXT_SITE_NAME] = settings.get('Version', 'SiteName') context[CONTEXT_IMAGE_VIEWER] = settings_manager.get_setting( - SETTING_IMAGE_VIEWER, default=settings.DEFAULT_IMAGE_VIEWER) + SETTING_IMAGE_VIEWER, + default=settings.get('View', 'DefaultImageViewer')) get_notifications(context, request) diff --git a/boards/default_settings.py b/boards/default_settings.py deleted file mode 100644 --- a/boards/default_settings.py +++ /dev/null @@ -1,23 +0,0 @@ -VERSION = '2.6.1 Cooper' -SITE_NAME = 'Neboard' - -CACHE_TIMEOUT = 600 # Timeout for caching, if cache is used -LOGIN_TIMEOUT = 3600 # Timeout between login tries -MAX_TEXT_LENGTH = 30000 # Max post length in characters -MAX_IMAGE_SIZE = 8 * 1024 * 1024 # Max image size - -# Thread bumplimit -MAX_POSTS_PER_THREAD = 10 -# Old posts will be archived or deleted if this value is reached -MAX_THREAD_COUNT = 5 -THREADS_PER_PAGE = 3 -DEFAULT_THEME = 'md' -DEFAULT_IMAGE_VIEWER = 'simple' -LAST_REPLIES_COUNT = 3 - -# Enable archiving threads instead of deletion when the thread limit is reached -ARCHIVE_THREADS = True -# Limit posting speed -LIMIT_POSTING_SPEED = False -# Thread update -WEBSOCKETS_ENABLED = True diff --git a/boards/forms.py b/boards/forms.py --- a/boards/forms.py +++ b/boards/forms.py @@ -172,11 +172,10 @@ class PostForm(NeboardForm): def clean_text(self): text = self.cleaned_data['text'].strip() if text: - if len(text) > board_settings.MAX_TEXT_LENGTH: + max_length = board_settings.get_int('Forms', 'MaxTextLength') + if len(text) > max_length: raise forms.ValidationError(_('Text must have less than %s ' - 'characters') % - str(board_settings - .MAX_TEXT_LENGTH)) + 'characters') % str(max_length)) return text def clean_image(self): @@ -256,7 +255,7 @@ class PostForm(NeboardForm): posting_delay = settings.POSTING_DELAY - if board_settings.LIMIT_POSTING_SPEED: + if board_settings.get_bool('Forms', 'LimitPostingSpeed'): now = time.time() current_delay = 0 @@ -283,10 +282,11 @@ class PostForm(NeboardForm): self.session[LAST_POST_TIME] = now def validate_image_size(self, size: int): - if size > board_settings.MAX_IMAGE_SIZE: + max_size = board_settings.get_int('Forms', 'MaxImageSize') + if size > max_size: raise forms.ValidationError( _('Image must be less than %s bytes') - % str(board_settings.MAX_IMAGE_SIZE)) + % str(max_size)) def _get_image_from_url(self, url: str) -> SimpleUploadedFile: """ diff --git a/boards/locale/ru/LC_MESSAGES/django.mo b/boards/locale/ru/LC_MESSAGES/django.mo index 75e3e0914050fb17981a7820228b46f9ff73b732..aa52fdcd6d03d5fd737cc6b5b352926ca2030c9c GIT binary patch literal 7264 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 diff --git a/boards/locale/ru/LC_MESSAGES/django.po b/boards/locale/ru/LC_MESSAGES/django.po --- a/boards/locale/ru/LC_MESSAGES/django.po +++ b/boards/locale/ru/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-04-21 21:10+0300\n" +"POT-Creation-Date: 2015-05-07 13:10+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -68,79 +68,79 @@ msgstr "Поиск" msgid "Please wait %s seconds before sending message" msgstr "Пожалуйста подождите %s секунд перед отправкой сообщения" -#: forms.py:142 +#: forms.py:144 msgid "Image" msgstr "Изображение" -#: forms.py:145 +#: forms.py:147 msgid "Image URL" msgstr "URL изображения" -#: forms.py:151 +#: forms.py:153 msgid "e-mail" msgstr "" -#: forms.py:154 +#: forms.py:156 msgid "Additional threads" msgstr "Дополнительные темы" -#: forms.py:165 +#: forms.py:167 #, python-format msgid "Title must have less than %s characters" msgstr "Заголовок должен иметь меньше %s символов" -#: forms.py:174 +#: forms.py:176 #, python-format msgid "Text must have less than %s characters" msgstr "Текст должен быть короче %s символов" -#: forms.py:196 +#: forms.py:198 msgid "Invalid URL" msgstr "Неверный URL" -#: forms.py:217 +#: forms.py:219 msgid "Invalid additional thread list" msgstr "Неверный список дополнительных тем" -#: forms.py:249 +#: forms.py:251 msgid "Either text or image must be entered." msgstr "Текст или картинка должны быть введены." -#: forms.py:286 +#: forms.py:288 #, python-format msgid "Image must be less than %s bytes" msgstr "Изображение должно быть менее %s байт" -#: forms.py:333 templates/boards/posting_general.html:145 +#: forms.py:335 templates/boards/posting_general.html:129 #: templates/boards/rss/post.html:10 templates/boards/tags.html:7 msgid "Tags" msgstr "Метки" -#: forms.py:340 +#: forms.py:342 msgid "Inappropriate characters in tags." msgstr "Недопустимые символы в метках." -#: forms.py:354 +#: forms.py:356 msgid "Need at least one of the tags: " msgstr "Нужна хотя бы одна из меток: " -#: forms.py:367 +#: forms.py:369 msgid "Theme" msgstr "Тема" -#: forms.py:368 +#: forms.py:370 msgid "Image view mode" msgstr "Режим просмотра изображений" -#: forms.py:369 +#: forms.py:371 msgid "User name" msgstr "Имя пользователя" -#: forms.py:370 +#: forms.py:372 msgid "Time zone" msgstr "Часовой пояс" -#: forms.py:376 +#: forms.py:378 msgid "Inappropriate characters." msgstr "Недопустимые символы." @@ -189,7 +189,7 @@ msgstr "Управление метками" msgid "Notifications" msgstr "Уведомления" -#: templates/boards/base.html:52 templates/boards/settings.html:9 +#: templates/boards/base.html:52 templates/boards/settings.html:8 msgid "Settings" msgstr "Настройки" @@ -207,12 +207,12 @@ msgid "Up" msgstr "Вверх" #: templates/boards/notifications.html:17 -#: templates/boards/posting_general.html:80 templates/search/search.html:26 +#: templates/boards/posting_general.html:70 templates/search/search.html:26 msgid "Previous page" msgstr "Предыдущая страница" #: templates/boards/notifications.html:27 -#: templates/boards/posting_general.html:118 templates/search/search.html:37 +#: templates/boards/posting_general.html:102 templates/search/search.html:37 msgid "Next page" msgstr "Следующая страница" @@ -244,44 +244,49 @@ msgstr "сообщений" msgid "images" msgstr "изображений" -#: templates/boards/posting_general.html:64 +#: templates/boards/posting_general.html:35 +#| msgid "messages" +msgid "Related message" +msgstr "Связанное сообщение" + +#: templates/boards/posting_general.html:60 msgid "Edit tag" msgstr "Изменить метку" -#: templates/boards/posting_general.html:67 +#: templates/boards/posting_general.html:63 #, python-format msgid "This tag has %(thread_count)s threads and %(post_count)s posts." msgstr "С этой меткой есть %(thread_count)s тем и %(post_count)s сообщений." -#: templates/boards/posting_general.html:94 +#: templates/boards/posting_general.html:84 #, python-format msgid "Skipped %(count)s replies. Open thread to see all replies." msgstr "Пропущено %(count)s ответов. Откройте тред, чтобы увидеть все ответы." -#: templates/boards/posting_general.html:123 +#: templates/boards/posting_general.html:107 msgid "No threads exist. Create the first one!" msgstr "Нет тем. Создайте первую!" -#: templates/boards/posting_general.html:129 +#: templates/boards/posting_general.html:113 msgid "Create new thread" msgstr "Создать новую тему" -#: templates/boards/posting_general.html:134 templates/boards/preview.html:16 +#: templates/boards/posting_general.html:118 templates/boards/preview.html:16 #: templates/boards/thread_normal.html:43 msgid "Post" msgstr "Отправить" -#: templates/boards/posting_general.html:139 +#: templates/boards/posting_general.html:123 msgid "Tags must be delimited by spaces. Text or image is required." msgstr "" "Метки должны быть разделены пробелами. Текст или изображение обязательны." -#: templates/boards/posting_general.html:142 +#: templates/boards/posting_general.html:126 #: templates/boards/thread_normal.html:48 msgid "Text syntax" msgstr "Синтаксис текста" -#: templates/boards/posting_general.html:159 +#: templates/boards/posting_general.html:143 msgid "Pages:" msgstr "Страницы: " @@ -293,19 +298,19 @@ msgstr "Предпросмотр" msgid "Post image" msgstr "Изображение сообщения" -#: templates/boards/settings.html:17 +#: templates/boards/settings.html:16 msgid "You are moderator." msgstr "Вы модератор." -#: templates/boards/settings.html:21 +#: templates/boards/settings.html:20 msgid "Hidden tags:" msgstr "Скрытые метки:" -#: templates/boards/settings.html:29 +#: templates/boards/settings.html:28 msgid "No hidden tags." msgstr "Нет скрытых меток." -#: templates/boards/settings.html:38 +#: templates/boards/settings.html:37 msgid "Save" msgstr "Сохранить" @@ -377,7 +382,7 @@ msgstr "Нормальный режим" msgid "Gallery mode" msgstr "Режим галереи" -#: templates/boards/thread_gallery.html:50 +#: templates/boards/thread_gallery.html:41 msgid "No images." msgstr "Нет изображений." @@ -401,8 +406,3 @@ msgstr "Обновить" msgid "Ok" msgstr "Ок" -#~ msgid "Wait %s seconds after last posting" -#~ msgstr "Подождите %s секунд после последнего постинга" - -#~ msgid "tag1 several_words_tag" -#~ msgstr "метка1 метка_из_нескольких_слов" diff --git a/boards/migrations/0018_banner.py b/boards/migrations/0018_banner.py new file mode 100644 --- /dev/null +++ b/boards/migrations/0018_banner.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('boards', '0017_auto_20150503_1847'), + ] + + operations = [ + migrations.CreateModel( + name='Banner', + fields=[ + ('id', models.AutoField(serialize=False, primary_key=True, verbose_name='ID', auto_created=True)), + ('title', models.TextField()), + ('text', models.TextField()), + ('post', models.ForeignKey(to='boards.Post')), + ], + ), + ] diff --git a/boards/models/__init__.py b/boards/models/__init__.py --- a/boards/models/__init__.py +++ b/boards/models/__init__.py @@ -7,3 +7,4 @@ from boards.models.thread import Thread from boards.models.post import Post from boards.models.tag import Tag from boards.models.user import Ban +from boards.models.banner import Banner diff --git a/boards/models/banner.py b/boards/models/banner.py new file mode 100644 --- /dev/null +++ b/boards/models/banner.py @@ -0,0 +1,10 @@ +from django.db import models + + +class Banner(models.Model): + title = models.TextField() + text = models.TextField() + post = models.ForeignKey('Post') + + def __str__(self): + return self.title diff --git a/boards/models/post.py b/boards/models/post/__init__.py rename from boards/models/post.py rename to boards/models/post/__init__.py diff --git a/boards/models/post/export.py b/boards/models/post/export.py new file mode 100644 --- /dev/null +++ b/boards/models/post/export.py @@ -0,0 +1,55 @@ +from boards import utils + + +PARAMETER_TRUNCATED = 'truncated' + +DIFF_TYPE_HTML = 'html' +DIFF_TYPE_JSON = 'json' + + +class Exporter(): + @staticmethod + def export(post, request, include_last_update) -> str: + pass + + +class HtmlExporter(Exporter): + @staticmethod + def export(post, request, include_last_update): + if request is not None and PARAMETER_TRUNCATED in request.GET: + truncated = True + reply_link = False + else: + truncated = False + reply_link = True + + return post.get_view(truncated=truncated, reply_link=reply_link, + moderator=utils.is_moderator(request)) + + +class JsonExporter(Exporter): + @staticmethod + def export(post, request, include_last_update): + post_json = { + 'id': post.id, + 'title': post.title, + 'text': post.get_raw_text(), + } + if post.images.exists(): + post_image = post.get_first_image() + post_json['image'] = post_image.image.url + post_json['image_preview'] = post_image.image.url_200x150 + if include_last_update: + post_json['bump_time'] = utils.datetime_to_epoch( + post.get_thread().bump_time) + return post_json + + +EXPORTERS = { + DIFF_TYPE_HTML: HtmlExporter, + DIFF_TYPE_JSON: JsonExporter, +} + + +def get_exporter(export_type: str) -> Exporter: + return EXPORTERS[export_type]() diff --git a/boards/models/tag.py b/boards/models/tag.py --- a/boards/models/tag.py +++ b/boards/models/tag.py @@ -56,9 +56,13 @@ class Tag(models.Model, Viewable): def get_thread_count(self) -> int: return self.get_threads().count() + # TODO Remove this and use get_absolute_url def get_url(self): return reverse('tag', kwargs={'tag_name': self.name}) + def get_absolute_url(self): + return self.get_url() + def get_threads(self): return self.thread_set.order_by('-bump_time') @@ -67,7 +71,7 @@ class Tag(models.Model, Viewable): def get_view(self): link = '{}'.format( - self.get_url(), self.name) + self.get_absolute_url(), self.name) if self.is_required(): link = '{}'.format(link) return link diff --git a/boards/models/thread.py b/boards/models/thread.py --- a/boards/models/thread.py +++ b/boards/models/thread.py @@ -34,12 +34,13 @@ class ThreadManager(models.Manager): threads = Thread.objects.filter(archived=False).order_by('-bump_time') thread_count = threads.count() - if thread_count > settings.MAX_THREAD_COUNT: - num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT + max_thread_count = settings.get_int('Messages', 'MaxThreadCount') + if thread_count > max_thread_count: + num_threads_to_delete = thread_count - max_thread_count old_threads = threads[thread_count - num_threads_to_delete:] for thread in old_threads: - if settings.ARCHIVE_THREADS: + if settings.get_bool('Storage', 'ArchiveThreads'): self._archive_thread(thread) else: thread.delete() @@ -55,7 +56,7 @@ class ThreadManager(models.Manager): def get_thread_max_posts(): - return settings.MAX_POSTS_PER_THREAD + return settings.get_int('Messages', 'MaxPostsPerThread') class Thread(models.Model): @@ -122,11 +123,13 @@ class Thread(models.Model): Gets several last replies, not including opening post """ - if settings.LAST_REPLIES_COUNT > 0: + last_replies_count = settings.get_int('View', 'LastRepliesCount') + + if last_replies_count > 0: reply_count = self.get_reply_count() if reply_count > 0: - reply_count_to_show = min(settings.LAST_REPLIES_COUNT, + reply_count_to_show = min(last_replies_count, reply_count - 1) replies = self.get_replies() last_replies = replies[reply_count - reply_count_to_show:] @@ -138,7 +141,7 @@ class Thread(models.Model): Gets number of posts between opening post and last replies. """ reply_count = self.get_reply_count() - last_replies_count = min(settings.LAST_REPLIES_COUNT, + last_replies_count = min(settings.get_int('View', 'LastRepliesCount'), reply_count - 1) return reply_count - last_replies_count - 1 @@ -222,7 +225,7 @@ class Thread(models.Model): post.threads.update(last_edit_time=self.last_edit_time) def notify_clients(self): - if not settings.WEBSOCKETS_ENABLED: + if not settings.get_bool('External', 'WebsocketsEnabled'): return client = Client() @@ -232,3 +235,6 @@ class Thread(models.Model): WS_NOTIFICATION_TYPE: WS_NOTIFICATION_TYPE_NEW_POST, }) client.send() + + def get_absolute_url(self): + return self.get_opening_post().get_absolute_url() diff --git a/boards/rss.py b/boards/rss.py --- a/boards/rss.py +++ b/boards/rss.py @@ -10,7 +10,7 @@ from boards import settings # TODO Make tests for all of these class AllThreadsFeed(Feed): - title = settings.SITE_NAME + ' - All threads' + title = settings.get('Version', 'SiteName') + ' - All threads' link = '/' description_template = 'boards/rss/post.html' diff --git a/boards/settings.py b/boards/settings.py --- a/boards/settings.py +++ b/boards/settings.py @@ -1,2 +1,18 @@ -from boards.default_settings import * +import configparser + + +config = configparser.ConfigParser() +config.read('boards/config/default_settings.ini') +config.read('boards/config/settings.ini') + +def get(section, name): + return config[section][name] + + +def get_int(section, name): + return int(get(section, name)) + + +def get_bool(section, name): + return get(section, name) == 'true' diff --git a/boards/static/js/thread_update.js b/boards/static/js/thread_update.js --- a/boards/static/js/thread_update.js +++ b/boards/static/js/thread_update.js @@ -28,6 +28,8 @@ var CLASS_POST = '.post' var POST_ADDED = 0; var POST_UPDATED = 1; +var JS_AUTOUPDATE_PERIOD = 20000; + var wsUser = ''; var unreadPosts = 0; @@ -48,36 +50,39 @@ function connectWebsocket() { var wsHost = metapanel.getAttribute('data-ws-host'); var wsPort = metapanel.getAttribute('data-ws-port'); - if (wsHost.length > 0 && wsPort.length > 0) - var centrifuge = new Centrifuge({ - "url": 'ws://' + wsHost + ':' + wsPort + "/connection/websocket", - "project": metapanel.getAttribute('data-ws-project'), - "user": wsUser, - "timestamp": metapanel.getAttribute('data-ws-token-time'), - "token": metapanel.getAttribute('data-ws-token'), - "debug": false - }); + if (wsHost.length > 0 && wsPort.length > 0) { + var centrifuge = new Centrifuge({ + "url": 'ws://' + wsHost + ':' + wsPort + "/connection/websocket", + "project": metapanel.getAttribute('data-ws-project'), + "user": wsUser, + "timestamp": metapanel.getAttribute('data-ws-token-time'), + "token": metapanel.getAttribute('data-ws-token'), + "debug": false + }); - centrifuge.on('error', function(error_message) { - console.log("Error connecting to websocket server."); - console.log(error_message); - return false; - }); - - centrifuge.on('connect', function() { - var channelName = 'thread:' + threadId; - centrifuge.subscribe(channelName, function(message) { - getThreadDiff(); + centrifuge.on('error', function(error_message) { + console.log("Error connecting to websocket server."); + console.log(error_message); + return false; }); - // For the case we closed the browser and missed some updates - getThreadDiff(); - $('#autoupdate').hide(); - }); + centrifuge.on('connect', function() { + var channelName = 'thread:' + threadId; + centrifuge.subscribe(channelName, function(message) { + getThreadDiff(); + }); - centrifuge.connect(); + // For the case we closed the browser and missed some updates + getThreadDiff(); + $('#autoupdate').hide(); + }); - return true; + centrifuge.connect(); + + return true; + } else { + return false; + } } /** @@ -193,8 +198,21 @@ function isPageBottom() { return scroll == 1 } +function enableJsUpdate() { + setInterval(getThreadDiff, JS_AUTOUPDATE_PERIOD); + return true; +} + function initAutoupdate() { - return connectWebsocket(); + if (location.protocol === 'https:') { + return enableJsUpdate(); + } else { + if (connectWebsocket()) { + return true; + } else { + return enableJsUpdate(); + } + } } function getReplyCount() { diff --git a/boards/templates/boards/posting_general.html b/boards/templates/boards/posting_general.html --- a/boards/templates/boards/posting_general.html +++ b/boards/templates/boards/posting_general.html @@ -28,6 +28,14 @@ {% get_current_language as LANGUAGE_CODE %} {% get_current_timezone as TIME_ZONE %} + {% for banner in banners %} +
+
{{ banner.title }}
+
{{ banner.text }}
+
{% trans 'Related message' %}: >>{{ banner.post.id }}
+
+ {% endfor %} + {% if tag %}

diff --git a/boards/templates/boards/settings.html b/boards/templates/boards/settings.html --- a/boards/templates/boards/settings.html +++ b/boards/templates/boards/settings.html @@ -1,7 +1,6 @@ {% extends "boards/base.html" %} {% load i18n %} -{% load humanize %} {% load tz %} {% block head %} diff --git a/boards/tests/test_post.py b/boards/tests/test_post.py --- a/boards/tests/test_post.py +++ b/boards/tests/test_post.py @@ -91,26 +91,26 @@ class PostTests(TestCase): def test_thread_max_count(self): """Test deletion of old posts when the max thread count is reached""" - for i in range(settings.MAX_THREAD_COUNT + 1): + for i in range(settings.get_int('Messages', 'MaxThreadCount') + 1): self._create_post() - self.assertEqual(settings.MAX_THREAD_COUNT, + self.assertEqual(settings.get_int('Messages', 'MaxThreadCount'), len(Thread.objects.filter(archived=False))) def test_pages(self): """Test that the thread list is properly split into pages""" - for i in range(settings.MAX_THREAD_COUNT): + for i in range(settings.get_int('Messages', 'MaxThreadCount')): self._create_post() all_threads = Thread.objects.filter(archived=False) paginator = Paginator(Thread.objects.filter(archived=False), - settings.THREADS_PER_PAGE) + settings.get_int('View', 'ThreadsPerPage')) posts_in_second_page = paginator.page(2).object_list first_post = posts_in_second_page[0] - self.assertEqual(all_threads[settings.THREADS_PER_PAGE].id, + self.assertEqual(all_threads[settings.get_int('View', 'ThreadsPerPage')].id, first_post.id) def test_reflinks(self): diff --git a/boards/views/all_threads.py b/boards/views/all_threads.py --- a/boards/views/all_threads.py +++ b/boards/views/all_threads.py @@ -11,7 +11,7 @@ from boards import utils, settings from boards.abstracts.paginator import get_paginator from boards.abstracts.settingsmanager import get_settings_manager from boards.forms import ThreadForm, PlainErrorList -from boards.models import Post, Thread, Ban, Tag, PostImage +from boards.models import Post, Thread, Ban, Tag, PostImage, Banner from boards.views.banned import BannedView from boards.views.base import BaseBoardView, CONTEXT_FORM from boards.views.posting_mixin import PostMixin @@ -28,6 +28,7 @@ TAG_DELIMITER = ' ' PARAMETER_CURRENT_PAGE = 'current_page' PARAMETER_PAGINATOR = 'paginator' PARAMETER_THREADS = 'threads' +PARAMETER_BANNERS = 'banners' PARAMETER_PREV_LINK = 'prev_page_link' PARAMETER_NEXT_LINK = 'next_page_link' @@ -50,7 +51,7 @@ class AllThreadsView(PostMixin, BaseBoar self.settings_manager = get_settings_manager(request) paginator = get_paginator(self.get_threads(), - settings.THREADS_PER_PAGE) + settings.get_int('View', 'ThreadsPerPage')) paginator.current_page = int(page) try: @@ -60,6 +61,7 @@ class AllThreadsView(PostMixin, BaseBoar params[PARAMETER_THREADS] = threads params[CONTEXT_FORM] = form + params[PARAMETER_BANNERS] = Banner.objects.order_by('-id').all() self.get_page_context(paginator, params, page) diff --git a/boards/views/settings.py b/boards/views/settings.py --- a/boards/views/settings.py +++ b/boards/views/settings.py @@ -32,7 +32,8 @@ class SettingsView(BaseBoardView): initial={ FORM_THEME: selected_theme, FORM_IMAGE_VIEWER: settings_manager.get_setting( - SETTING_IMAGE_VIEWER, default=settings.DEFAULT_IMAGE_VIEWER), + SETTING_IMAGE_VIEWER, + default=settings.get('View', 'DefaultImageViewer')), FORM_USERNAME: settings_manager.get_setting(SETTING_USERNAME), FORM_TIMEZONE: request.session.get( SESSION_TIMEZONE, timezone.get_current_timezone()), diff --git a/boards/views/thread/thread.py b/boards/views/thread/thread.py --- a/boards/views/thread/thread.py +++ b/boards/views/thread/thread.py @@ -51,7 +51,7 @@ class ThreadView(BaseBoardView, PostMixi params[CONTEXT_LASTUPDATE] = str(thread_to_show.last_edit_time) params[CONTEXT_THREAD] = thread_to_show - if settings.WEBSOCKETS_ENABLED: + if settings.get_bool('External', 'WebsocketsEnabled'): token_time = format(timezone.now(), u'U') params[CONTEXT_WS_TIME] = token_time diff --git a/neboard/settings.py b/neboard/settings.py --- a/neboard/settings.py +++ b/neboard/settings.py @@ -144,7 +144,7 @@ INSTALLED_APPS = ( 'django.contrib.admin', # Uncomment the next line to enable admin documentation: # 'django.contrib.admindocs', - 'django.contrib.humanize', + #'django.contrib.humanize', 'debug_toolbar',