##// END OF EJS Templates
Merged with BB
Pavel Ryapolov -
r297:da4642af merge default
parent child Browse files
Show More
@@ -0,0 +1,78 b''
1 # -*- coding: utf-8 -*-
2 import datetime
3 from south.db import db
4 from south.v2 import SchemaMigration
5 from django.db import models
6
7
8 class Migration(SchemaMigration):
9
10 def forwards(self, orm):
11 # Adding M2M table for field referenced_posts on 'Post'
12 m2m_table_name = db.shorten_name(u'boards_post_referenced_posts')
13 db.create_table(m2m_table_name, (
14 ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
15 ('from_post', models.ForeignKey(orm[u'boards.post'], null=False)),
16 ('to_post', models.ForeignKey(orm[u'boards.post'], null=False))
17 ))
18 db.create_unique(m2m_table_name, ['from_post_id', 'to_post_id'])
19
20
21 def backwards(self, orm):
22 # Removing M2M table for field referenced_posts on 'Post'
23 db.delete_table(db.shorten_name(u'boards_post_referenced_posts'))
24
25
26 models = {
27 u'boards.ban': {
28 'Meta': {'object_name': 'Ban'},
29 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
30 'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'})
31 },
32 u'boards.post': {
33 'Meta': {'object_name': 'Post'},
34 '_text_rendered': ('django.db.models.fields.TextField', [], {}),
35 'bump_time': ('django.db.models.fields.DateTimeField', [], {}),
36 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
37 'image': ('boards.thumbs.ImageWithThumbsField', [], {'max_length': '100', 'blank': 'True'}),
38 'image_height': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
39 'image_width': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
40 'last_edit_time': ('django.db.models.fields.DateTimeField', [], {}),
41 'poster_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
42 'poster_user_agent': ('django.db.models.fields.TextField', [], {}),
43 'pub_time': ('django.db.models.fields.DateTimeField', [], {}),
44 'referenced_posts': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'rfp+'", 'null': 'True', 'symmetrical': 'False', 'to': u"orm['boards.Post']"}),
45 'replies': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'re+'", 'null': 'True', 'symmetrical': 'False', 'to': u"orm['boards.Post']"}),
46 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['boards.Tag']", 'symmetrical': 'False'}),
47 'text': ('markupfield.fields.MarkupField', [], {'rendered_field': 'True'}),
48 'text_markup_type': ('django.db.models.fields.CharField', [], {'default': "'markdown'", 'max_length': '30'}),
49 'thread': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['boards.Post']", 'null': 'True'}),
50 'title': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
51 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': u"orm['boards.User']", 'null': 'True'})
52 },
53 u'boards.setting': {
54 'Meta': {'object_name': 'Setting'},
55 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
56 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
57 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['boards.User']"}),
58 'value': ('django.db.models.fields.CharField', [], {'max_length': '50'})
59 },
60 u'boards.tag': {
61 'Meta': {'object_name': 'Tag'},
62 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
63 'linked': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['boards.Tag']", 'null': 'True', 'blank': 'True'}),
64 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
65 'threads': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'tag+'", 'null': 'True', 'symmetrical': 'False', 'to': u"orm['boards.Post']"})
66 },
67 u'boards.user': {
68 'Meta': {'object_name': 'User'},
69 'fav_tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['boards.Tag']", 'null': 'True', 'blank': 'True'}),
70 'fav_threads': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'symmetrical': 'False', 'to': u"orm['boards.Post']"}),
71 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
72 'rank': ('django.db.models.fields.IntegerField', [], {}),
73 'registration_time': ('django.db.models.fields.DateTimeField', [], {}),
74 'user_id': ('django.db.models.fields.CharField', [], {'max_length': '50'})
75 }
76 }
77
78 complete_apps = ['boards'] No newline at end of file
@@ -0,0 +1,15 b''
1 """
2 Maintenance script for neboard imageboard. Use this to update data after
3 migrations etc.
4 """
5
6 from boards.models import Post
7 from boards import views
8
9 def update_posts():
10 for post in Post.objects.all():
11 print 'Updating post #' + str(post.id)
12
13 post.save()
14
15 Post.objects.connect_replies(post)
1 NO CONTENT: modified file, binary diff hidden
NO CONTENT: modified file, binary diff hidden
@@ -1,356 +1,360 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-10-21 21:13+0300\n"
10 "POT-Creation-Date: 2013-10-24 16:06+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 #: authors.py:5
21 #: authors.py:5
22 msgid "author"
22 msgid "author"
23 msgstr "автор"
23 msgstr "автор"
24
24
25 #: authors.py:6
25 #: authors.py:6
26 msgid "developer"
26 msgid "developer"
27 msgstr "разработчик"
27 msgstr "разработчик"
28
28
29 #: authors.py:7
29 #: authors.py:7
30 msgid "javascript developer"
30 msgid "javascript developer"
31 msgstr "разработчик javascript"
31 msgstr "разработчик javascript"
32
32
33 #: authors.py:8
33 #: authors.py:8
34 msgid "designer"
34 msgid "designer"
35 msgstr "дизайнер"
35 msgstr "дизайнер"
36
36
37 #: forms.py:51 templates/boards/posting_general.html:135
37 #: forms.py:51 templates/boards/posting_general.html:135
38 #: templates/boards/thread.html:90
38 #: templates/boards/thread.html:100
39 msgid "Title"
39 msgid "Title"
40 msgstr "Заголовок"
40 msgstr "Заголовок"
41
41
42 #: forms.py:53 templates/boards/posting_general.html:150
42 #: forms.py:53 templates/boards/posting_general.html:150
43 #: templates/boards/thread.html:105
43 #: templates/boards/thread.html:115
44 msgid "Text"
44 msgid "Text"
45 msgstr "Текст"
45 msgstr "Текст"
46
46
47 #: forms.py:54 templates/boards/posting_general.html:155
47 #: forms.py:54 templates/boards/posting_general.html:155
48 #: templates/boards/thread.html:110
48 #: templates/boards/thread.html:120
49 msgid "Image"
49 msgid "Image"
50 msgstr "Изображение"
50 msgstr "Изображение"
51
51
52 #: forms.py:57 templates/boards/posting_general.html:165
52 #: forms.py:57 templates/boards/posting_general.html:165
53 #: templates/boards/thread.html:115
53 #: templates/boards/thread.html:125
54 msgid "e-mail"
54 msgid "e-mail"
55 msgstr ""
55 msgstr ""
56
56
57 #: forms.py:68
57 #: forms.py:68
58 #, python-format
58 #, python-format
59 msgid "Title must have less than %s characters"
59 msgid "Title must have less than %s characters"
60 msgstr "Заголовок должен иметь меньше %s символов"
60 msgstr "Заголовок должен иметь меньше %s символов"
61
61
62 #: forms.py:77
62 #: forms.py:77
63 #, python-format
63 #, python-format
64 msgid "Text must have less than %s characters"
64 msgid "Text must have less than %s characters"
65 msgstr "Текст должен быть короче %s символов"
65 msgstr "Текст должен быть короче %s символов"
66
66
67 #: forms.py:86
67 #: forms.py:86
68 #, python-format
68 #, python-format
69 msgid "Image must be less than %s bytes"
69 msgid "Image must be less than %s bytes"
70 msgstr "Изображение должно быть менее %s байт"
70 msgstr "Изображение должно быть менее %s байт"
71
71
72 #: forms.py:113
72 #: forms.py:113
73 msgid "Either text or image must be entered."
73 msgid "Either text or image must be entered."
74 msgstr "Текст или картинка должны быть введены."
74 msgstr "Текст или картинка должны быть введены."
75
75
76 #: forms.py:126
76 #: forms.py:126
77 #, python-format
77 #, python-format
78 msgid "Wait %s seconds after last posting"
78 msgid "Wait %s seconds after last posting"
79 msgstr "Подождите %s секунд после последнего постинга"
79 msgstr "Подождите %s секунд после последнего постинга"
80
80
81 #: forms.py:140 templates/boards/post.html:39
81 #: forms.py:140 templates/boards/post.html:39
82 #: templates/boards/posting_general.html:77
82 #: templates/boards/posting_general.html:77
83 #: templates/boards/posting_general.html:160 templates/boards/tags.html:7
83 #: templates/boards/posting_general.html:160 templates/boards/tags.html:7
84 #: templates/boards/thread.html:70 templates/boards/rss/post.html:10
84 #: templates/boards/thread.html:80 templates/boards/rss/post.html:10
85 msgid "Tags"
85 msgid "Tags"
86 msgstr "Теги"
86 msgstr "Теги"
87
87
88 #: forms.py:148
88 #: forms.py:148
89 msgid "Inappropriate characters in tags."
89 msgid "Inappropriate characters in tags."
90 msgstr "Недопустимые символы в тегах."
90 msgstr "Недопустимые символы в тегах."
91
91
92 #: forms.py:176 forms.py:197
92 #: forms.py:176 forms.py:197
93 msgid "Captcha validation failed"
93 msgid "Captcha validation failed"
94 msgstr "Проверка капчи провалена"
94 msgstr "Проверка капчи провалена"
95
95
96 #: forms.py:203
96 #: forms.py:203
97 msgid "Theme"
97 msgid "Theme"
98 msgstr "Тема"
98 msgstr "Тема"
99
99
100 #: forms.py:208
100 #: forms.py:208
101 msgid "Enable moderation panel"
101 msgid "Enable moderation panel"
102 msgstr "Включить панель модерации"
102 msgstr "Включить панель модерации"
103
103
104 #: forms.py:223
104 #: forms.py:223
105 msgid "No such user found"
105 msgid "No such user found"
106 msgstr "Данный пользователь не найден"
106 msgstr "Данный пользователь не найден"
107
107
108 #: forms.py:237
108 #: forms.py:237
109 #, python-format
109 #, python-format
110 msgid "Wait %s minutes after last login"
110 msgid "Wait %s minutes after last login"
111 msgstr "Подождите %s минут после последнего входа"
111 msgstr "Подождите %s минут после последнего входа"
112
112
113 #: templates/boards/404.html:6
113 #: templates/boards/404.html:6
114 msgid "Not found"
114 msgid "Not found"
115 msgstr "Не найдено"
115 msgstr "Не найдено"
116
116
117 #: templates/boards/404.html:12
117 #: templates/boards/404.html:12
118 msgid "This page does not exist"
118 msgid "This page does not exist"
119 msgstr "Этой страницы не существует"
119 msgstr "Этой страницы не существует"
120
120
121 #: templates/boards/authors.html:6
121 #: templates/boards/authors.html:6 templates/boards/authors.html.py:12
122 msgid "Authors"
122 msgid "Authors"
123 msgstr "Авторы"
123 msgstr "Авторы"
124
124
125 #: templates/boards/authors.html:24
125 #: templates/boards/authors.html:25
126 msgid "Distributed under the"
126 msgid "Distributed under the"
127 msgstr "Распространяется под"
127 msgstr "Распространяется под"
128
128
129 #: templates/boards/authors.html:26
129 #: templates/boards/authors.html:27
130 msgid "license"
130 msgid "license"
131 msgstr "лицензией"
131 msgstr "лицензией"
132
132
133 #: templates/boards/authors.html:28
133 #: templates/boards/authors.html:29
134 msgid "Repository"
134 msgid "Repository"
135 msgstr "Репозиторий"
135 msgstr "Репозиторий"
136
136
137 #: templates/boards/base.html:13
137 #: templates/boards/base.html:13
138 msgid "Feed"
138 msgid "Feed"
139 msgstr "Лента"
139 msgstr "Лента"
140
140
141 #: templates/boards/base.html:35
141 #: templates/boards/base.html:35
142 msgid "All threads"
142 msgid "All threads"
143 msgstr "Все темы"
143 msgstr "Все темы"
144
144
145 #: templates/boards/base.html:40
145 #: templates/boards/base.html:40
146 msgid "Tag management"
146 msgid "Tag management"
147 msgstr "Управление тегами"
147 msgstr "Управление тегами"
148
148
149 #: templates/boards/base.html:42
149 #: templates/boards/base.html:42
150 msgid "Settings"
150 msgid "Settings"
151 msgstr "Настройки"
151 msgstr "Настройки"
152
152
153 #: templates/boards/base.html:49 templates/boards/login.html:6
153 #: templates/boards/base.html:49 templates/boards/login.html:6
154 #: templates/boards/login.html.py:21
154 #: templates/boards/login.html.py:21
155 msgid "Login"
155 msgid "Login"
156 msgstr "Вход"
156 msgstr "Вход"
157
157
158 #: templates/boards/base.html:50
158 #: templates/boards/base.html:50
159 msgid "Up"
159 msgid "Up"
160 msgstr "Вверх"
160 msgstr "Вверх"
161
161
162 #: templates/boards/login.html:15
162 #: templates/boards/login.html:15
163 msgid "User ID"
163 msgid "User ID"
164 msgstr "ID пользователя"
164 msgstr "ID пользователя"
165
165
166 #: templates/boards/login.html:24
166 #: templates/boards/login.html:24
167 msgid "Insert your user id above"
167 msgid "Insert your user id above"
168 msgstr "Вставьте свой ID пользователя выше"
168 msgstr "Вставьте свой ID пользователя выше"
169
169
170 #: templates/boards/post.html:10 templates/boards/rss/post.html:5
170 #: templates/boards/post.html:10 templates/boards/rss/post.html:5
171 msgid "Post image"
171 msgid "Post image"
172 msgstr "Изображение сообщения"
172 msgstr "Изображение сообщения"
173
173
174 #: templates/boards/post.html:26 templates/boards/posting_general.html:62
174 #: templates/boards/post.html:26 templates/boards/posting_general.html:62
175 #: templates/boards/thread.html:57
175 #: templates/boards/thread.html:59
176 msgid "Delete"
176 msgid "Delete"
177 msgstr "Удалить"
177 msgstr "Удалить"
178
178
179 #: templates/boards/post.html:29 templates/boards/posting_general.html:65
179 #: templates/boards/post.html:29 templates/boards/posting_general.html:65
180 #: templates/boards/thread.html:60
180 #: templates/boards/thread.html:62
181 msgid "Ban IP"
181 msgid "Ban IP"
182 msgstr "Заблокировать IP"
182 msgstr "Заблокировать IP"
183
183
184 #: templates/boards/posting_general.html:19
184 #: templates/boards/posting_general.html:19
185 msgid "Tag: "
185 msgid "Tag: "
186 msgstr "Тег: "
186 msgstr "Тег: "
187
187
188 #: templates/boards/posting_general.html:57
188 #: templates/boards/posting_general.html:57
189 msgid "Reply"
189 msgid "Reply"
190 msgstr "Ответ"
190 msgstr "Ответ"
191
191
192 #: templates/boards/posting_general.html:74 templates/boards/thread.html:143
192 #: templates/boards/posting_general.html:74 templates/boards/thread.html:153
193 msgid "replies"
193 msgid "replies"
194 msgstr "ответов"
194 msgstr "ответов"
195
195
196 #: templates/boards/posting_general.html:75 templates/boards/thread.html:144
196 #: templates/boards/posting_general.html:75 templates/boards/thread.html:154
197 msgid "images"
197 msgid "images"
198 msgstr "изображений"
198 msgstr "изображений"
199
199
200 #: templates/boards/posting_general.html:126
200 #: templates/boards/posting_general.html:126
201 msgid "No threads exist. Create the first one!"
201 msgid "No threads exist. Create the first one!"
202 msgstr "Нет тем. Создайте первую!"
202 msgstr "Нет тем. Создайте первую!"
203
203
204 #: templates/boards/posting_general.html:132
204 #: templates/boards/posting_general.html:132
205 msgid "Create new thread"
205 msgid "Create new thread"
206 msgstr "Создать новую тему"
206 msgstr "Создать новую тему"
207
207
208 #: templates/boards/posting_general.html:140 templates/boards/thread.html:95
208 #: templates/boards/posting_general.html:140 templates/boards/thread.html:105
209 msgid "Formatting"
209 msgid "Formatting"
210 msgstr "Форматирование"
210 msgstr "Форматирование"
211
211
212 #: templates/boards/posting_general.html:142 templates/boards/thread.html:97
212 #: templates/boards/posting_general.html:142 templates/boards/thread.html:107
213 msgid "quote"
213 msgid "quote"
214 msgstr "цитата"
214 msgstr "цитата"
215
215
216 #: templates/boards/posting_general.html:143 templates/boards/thread.html:98
216 #: templates/boards/posting_general.html:143 templates/boards/thread.html:108
217 msgid "italic"
217 msgid "italic"
218 msgstr "курсив"
218 msgstr "курсив"
219
219
220 #: templates/boards/posting_general.html:144 templates/boards/thread.html:99
220 #: templates/boards/posting_general.html:144 templates/boards/thread.html:109
221 msgid "bold"
221 msgid "bold"
222 msgstr "полужирный"
222 msgstr "полужирный"
223
223
224 #: templates/boards/posting_general.html:145 templates/boards/thread.html:100
224 #: templates/boards/posting_general.html:145 templates/boards/thread.html:110
225 msgid "spoiler"
225 msgid "spoiler"
226 msgstr "спойлер"
226 msgstr "спойлер"
227
227
228 #: templates/boards/posting_general.html:146 templates/boards/thread.html:101
228 #: templates/boards/posting_general.html:146 templates/boards/thread.html:111
229 msgid "comment"
229 msgid "comment"
230 msgstr "комментарий"
230 msgstr "комментарий"
231
231
232 #: templates/boards/posting_general.html:178 templates/boards/thread.html:129
232 #: templates/boards/posting_general.html:178 templates/boards/thread.html:139
233 msgid "Post"
233 msgid "Post"
234 msgstr "Отправить"
234 msgstr "Отправить"
235
235
236 #: templates/boards/posting_general.html:180
236 #: templates/boards/posting_general.html:180
237 msgid "Tags must be delimited by spaces. Text or image is required."
237 msgid "Tags must be delimited by spaces. Text or image is required."
238 msgstr ""
238 msgstr ""
239 "Теги должны быть разделены пробелами. Текст или изображение обязательны."
239 "Теги должны быть разделены пробелами. Текст или изображение обязательны."
240
240
241 #: templates/boards/posting_general.html:183 templates/boards/thread.html:131
241 #: templates/boards/posting_general.html:183 templates/boards/thread.html:141
242 msgid "Text syntax"
242 msgid "Text syntax"
243 msgstr "Синтаксис текста"
243 msgstr "Синтаксис текста"
244
244
245 #: templates/boards/posting_general.html:193
245 #: templates/boards/posting_general.html:193
246 msgid "Pages:"
246 msgid "Pages:"
247 msgstr "Страницы: "
247 msgstr "Страницы: "
248
248
249 #: templates/boards/settings.html:13
249 #: templates/boards/settings.html:13
250 msgid "User:"
250 msgid "User:"
251 msgstr "Пользователь:"
251 msgstr "Пользователь:"
252
252
253 #: templates/boards/settings.html:15
253 #: templates/boards/settings.html:15
254 msgid "You are moderator."
254 msgid "You are moderator."
255 msgstr "Вы модератор."
255 msgstr "Вы модератор."
256
256
257 #: templates/boards/settings.html:18
257 #: templates/boards/settings.html:18
258 msgid "Posts:"
258 msgid "Posts:"
259 msgstr "Сообщений:"
259 msgstr "Сообщений:"
260
260
261 #: templates/boards/settings.html:19
261 #: templates/boards/settings.html:19
262 msgid "First access:"
262 msgid "First access:"
263 msgstr "Первый доступ:"
263 msgstr "Первый доступ:"
264
264
265 #: templates/boards/settings.html:21
265 #: templates/boards/settings.html:21
266 msgid "Last access:"
266 msgid "Last access:"
267 msgstr "Последний доступ: "
267 msgstr "Последний доступ: "
268
268
269 #: templates/boards/settings.html:30
269 #: templates/boards/settings.html:30
270 msgid "Save"
270 msgid "Save"
271 msgstr "Сохранить"
271 msgstr "Сохранить"
272
272
273 #: templates/boards/tags.html:17
273 #: templates/boards/tags.html:24
274 msgid "threads"
274 msgid "threads"
275 msgstr "тем"
275 msgstr "тем"
276
276
277 #: templates/boards/tags.html:37
277 #: templates/boards/tags.html:37
278 msgid "No tags found."
278 msgid "No tags found."
279 msgstr "Теги не найдены."
279 msgstr "Теги не найдены."
280
280
281 #: templates/boards/thread.html:22
281 #: templates/boards/thread.html:24
282 msgid "posts to bumplimit"
282 msgid "posts to bumplimit"
283 msgstr "сообщений до бамплимита"
283 msgstr "сообщений до бамплимита"
284
284
285 #: templates/boards/thread.html:87
285 #: templates/boards/thread.html:71
286 msgid "Replies"
287 msgstr "Ответы"
288
289 #: templates/boards/thread.html:97
286 msgid "Reply to thread"
290 msgid "Reply to thread"
287 msgstr "Ответить в тему"
291 msgstr "Ответить в тему"
288
292
289 #: templates/boards/thread.html:145
293 #: templates/boards/thread.html:155
290 msgid "Last update: "
294 msgid "Last update: "
291 msgstr "Последнее обновление: "
295 msgstr "Последнее обновление: "
292
296
293 #: templates/boards/staticpages/banned.html:6
297 #: templates/boards/staticpages/banned.html:6
294 msgid "Banned"
298 msgid "Banned"
295 msgstr "Заблокирован"
299 msgstr "Заблокирован"
296
300
297 #: templates/boards/staticpages/banned.html:11
301 #: templates/boards/staticpages/banned.html:11
298 msgid "Your IP address has been banned. Contact the administrator"
302 msgid "Your IP address has been banned. Contact the administrator"
299 msgstr "Ваш IP адрес был заблокирован. Свяжитесь с администратором"
303 msgstr "Ваш IP адрес был заблокирован. Свяжитесь с администратором"
300
304
301 #: templates/boards/staticpages/help.html:6
305 #: templates/boards/staticpages/help.html:6
302 #: templates/boards/staticpages/help.html:10
306 #: templates/boards/staticpages/help.html:10
303 msgid "Syntax"
307 msgid "Syntax"
304 msgstr "Синтаксис"
308 msgstr "Синтаксис"
305
309
306 #: templates/boards/staticpages/help.html:11
310 #: templates/boards/staticpages/help.html:11
307 msgid "2 line breaks for a new line."
311 msgid "2 line breaks for a new line."
308 msgstr "2 перевода строки создают новый абзац."
312 msgstr "2 перевода строки создают новый абзац."
309
313
310 #: templates/boards/staticpages/help.html:12
314 #: templates/boards/staticpages/help.html:12
311 msgid "Italic text"
315 msgid "Italic text"
312 msgstr "Курсивный текст"
316 msgstr "Курсивный текст"
313
317
314 #: templates/boards/staticpages/help.html:13
318 #: templates/boards/staticpages/help.html:13
315 msgid "Bold text"
319 msgid "Bold text"
316 msgstr "Полужирный текст"
320 msgstr "Полужирный текст"
317
321
318 #: templates/boards/staticpages/help.html:14
322 #: templates/boards/staticpages/help.html:14
319 msgid "Spoiler"
323 msgid "Spoiler"
320 msgstr "Спойлер"
324 msgstr "Спойлер"
321
325
322 #: templates/boards/staticpages/help.html:15
326 #: templates/boards/staticpages/help.html:15
323 msgid "Comment"
327 msgid "Comment"
324 msgstr "Комментарий"
328 msgstr "Комментарий"
325
329
326 #: templates/boards/staticpages/help.html:16
330 #: templates/boards/staticpages/help.html:16
327 msgid "Quote"
331 msgid "Quote"
328 msgstr "Цитата"
332 msgstr "Цитата"
329
333
330 #: templates/boards/staticpages/help.html:17
334 #: templates/boards/staticpages/help.html:17
331 msgid "Link to a post"
335 msgid "Link to a post"
332 msgstr "Ссылка на сообщение"
336 msgstr "Ссылка на сообщение"
333
337
334 #~ msgid "Remove"
338 #~ msgid "Remove"
335 #~ msgstr "Удалить"
339 #~ msgstr "Удалить"
336
340
337 #~ msgid "Add"
341 #~ msgid "Add"
338 #~ msgstr "Добавить"
342 #~ msgstr "Добавить"
339
343
340 #~ msgid "Basic markdown syntax."
344 #~ msgid "Basic markdown syntax."
341 #~ msgstr "Базовый синтаксис markdown."
345 #~ msgstr "Базовый синтаксис markdown."
342
346
343 #~ msgid "Example: "
347 #~ msgid "Example: "
344 #~ msgstr "Пример: "
348 #~ msgstr "Пример: "
345
349
346 #~ msgid "tags"
350 #~ msgid "tags"
347 #~ msgstr "тегов"
351 #~ msgstr "тегов"
348
352
349 #~ msgid "Get!"
353 #~ msgid "Get!"
350 #~ msgstr "Гет!"
354 #~ msgstr "Гет!"
351
355
352 #~ msgid "View"
356 #~ msgid "View"
353 #~ msgstr "Просмотр"
357 #~ msgstr "Просмотр"
354
358
355 #~ msgid "gets"
359 #~ msgid "gets"
356 #~ msgstr "гетов"
360 #~ msgstr "гетов"
@@ -1,358 +1,375 b''
1 import os
1 import os
2 from random import random
2 from random import random
3 import time
3 import time
4 import math
4 import math
5
5
6 from django.db import models
6 from django.db import models
7 from django.db.models import Count
7 from django.db.models import Count
8 from django.http import Http404
8 from django.http import Http404
9 from django.utils import timezone
9 from django.utils import timezone
10 from markupfield.fields import MarkupField
10 from markupfield.fields import MarkupField
11
11
12 from neboard import settings
12 from neboard import settings
13 import thumbs
13 import thumbs
14
14
15 import re
16
15 IMAGE_THUMB_SIZE = (200, 150)
17 IMAGE_THUMB_SIZE = (200, 150)
16
18
17 TITLE_MAX_LENGTH = 50
19 TITLE_MAX_LENGTH = 50
18
20
19 DEFAULT_MARKUP_TYPE = 'markdown'
21 DEFAULT_MARKUP_TYPE = 'markdown'
20
22
21 NO_PARENT = -1
23 NO_PARENT = -1
22 NO_IP = '0.0.0.0'
24 NO_IP = '0.0.0.0'
23 UNKNOWN_UA = ''
25 UNKNOWN_UA = ''
24 ALL_PAGES = -1
26 ALL_PAGES = -1
25 OPENING_POST_POPULARITY_WEIGHT = 2
27 OPENING_POST_POPULARITY_WEIGHT = 2
26 IMAGES_DIRECTORY = 'images/'
28 IMAGES_DIRECTORY = 'images/'
27 FILE_EXTENSION_DELIMITER = '.'
29 FILE_EXTENSION_DELIMITER = '.'
28
30
29 RANK_ADMIN = 0
31 RANK_ADMIN = 0
30 RANK_MODERATOR = 10
32 RANK_MODERATOR = 10
31 RANK_USER = 100 \
33 RANK_USER = 100
32
34
33 SETTING_MODERATE = "moderate"
35 SETTING_MODERATE = "moderate"
34
36
37 REGEX_REPLY = re.compile('>>(\d+)')
38
35
39
36 class PostManager(models.Manager):
40 class PostManager(models.Manager):
37
41
38 def create_post(self, title, text, image=None, thread=None,
42 def create_post(self, title, text, image=None, thread=None,
39 ip=NO_IP, tags=None, user=None):
43 ip=NO_IP, tags=None, user=None):
40 post = self.create(title=title,
44 post = self.create(title=title,
41 text=text,
45 text=text,
42 pub_time=timezone.now(),
46 pub_time=timezone.now(),
43 thread=thread,
47 thread=thread,
44 image=image,
48 image=image,
45 poster_ip=ip,
49 poster_ip=ip,
46 poster_user_agent=UNKNOWN_UA,
50 poster_user_agent=UNKNOWN_UA,
47 last_edit_time=timezone.now(),
51 last_edit_time=timezone.now(),
48 bump_time=timezone.now(),
52 bump_time=timezone.now(),
49 user=user)
53 user=user)
50
54
51 if tags:
55 if tags:
52 map(post.tags.add, tags)
56 map(post.tags.add, tags)
53 for tag in tags:
57 for tag in tags:
54 tag.threads.add(post)
58 tag.threads.add(post)
55
59
56 if thread:
60 if thread:
57 thread.replies.add(post)
61 thread.replies.add(post)
58 thread.bump()
62 thread.bump()
59 thread.last_edit_time = timezone.now()
63 thread.last_edit_time = timezone.now()
60 thread.save()
64 thread.save()
61 else:
65 else:
62 self._delete_old_threads()
66 self._delete_old_threads()
63
67
68 self.connect_replies(post)
69
64 return post
70 return post
65
71
66 def delete_post(self, post):
72 def delete_post(self, post):
67 if post.replies.count() > 0:
73 if post.replies.count() > 0:
68 map(self.delete_post, post.replies.all())
74 map(self.delete_post, post.replies.all())
69
75
70 # Update thread's last edit time (used as cache key)
76 # Update thread's last edit time (used as cache key)
71 thread = post.thread
77 thread = post.thread
72 if thread:
78 if thread:
73 thread.last_edit_time = timezone.now()
79 thread.last_edit_time = timezone.now()
74 thread.save()
80 thread.save()
75
81
76 post.delete()
82 post.delete()
77
83
78 def delete_posts_by_ip(self, ip):
84 def delete_posts_by_ip(self, ip):
79 posts = self.filter(poster_ip=ip)
85 posts = self.filter(poster_ip=ip)
80 map(self.delete_post, posts)
86 map(self.delete_post, posts)
81
87
82 def get_threads(self, tag=None, page=ALL_PAGES,
88 def get_threads(self, tag=None, page=ALL_PAGES,
83 order_by='-bump_time'):
89 order_by='-bump_time'):
84 if tag:
90 if tag:
85 threads = tag.threads
91 threads = tag.threads
86
92
87 if threads.count() == 0:
93 if threads.count() == 0:
88 raise Http404
94 raise Http404
89 else:
95 else:
90 threads = self.filter(thread=None)
96 threads = self.filter(thread=None)
91
97
92 threads = threads.order_by(order_by)
98 threads = threads.order_by(order_by)
93
99
94 if page != ALL_PAGES:
100 if page != ALL_PAGES:
95 thread_count = threads.count()
101 thread_count = threads.count()
96
102
97 if page < self.get_thread_page_count(tag=tag):
103 if page < self.get_thread_page_count(tag=tag):
98 start_thread = page * settings.THREADS_PER_PAGE
104 start_thread = page * settings.THREADS_PER_PAGE
99 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
105 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
100 thread_count)
106 thread_count)
101 threads = threads[start_thread:end_thread]
107 threads = threads[start_thread:end_thread]
102
108
103 return threads
109 return threads
104
110
105 def get_thread(self, opening_post_id):
111 def get_thread(self, opening_post_id):
106 try:
112 try:
107 opening_post = self.get(id=opening_post_id, thread=None)
113 opening_post = self.get(id=opening_post_id, thread=None)
108 except Post.DoesNotExist:
114 except Post.DoesNotExist:
109 raise Http404
115 raise Http404
110
116
111 if opening_post.replies:
117 if opening_post.replies:
112 thread = [opening_post]
118 thread = [opening_post]
113 thread.extend(opening_post.replies.all().order_by('pub_time'))
119 thread.extend(opening_post.replies.all().order_by('pub_time'))
114
120
115 return thread
121 return thread
116
122
117 def exists(self, post_id):
123 def exists(self, post_id):
118 posts = self.filter(id=post_id)
124 posts = self.filter(id=post_id)
119
125
120 return posts.count() > 0
126 return posts.count() > 0
121
127
122 def get_thread_page_count(self, tag=None):
128 def get_thread_page_count(self, tag=None):
123 if tag:
129 if tag:
124 threads = self.filter(thread=None, tags=tag)
130 threads = self.filter(thread=None, tags=tag)
125 else:
131 else:
126 threads = self.filter(thread=None)
132 threads = self.filter(thread=None)
127
133
128 return int(math.ceil(threads.count() / float(
134 return int(math.ceil(threads.count() / float(
129 settings.THREADS_PER_PAGE)))
135 settings.THREADS_PER_PAGE)))
130
136
131 def _delete_old_threads(self):
137 def _delete_old_threads(self):
132 """
138 """
133 Preserves maximum thread count. If there are too many threads,
139 Preserves maximum thread count. If there are too many threads,
134 delete the old ones.
140 delete the old ones.
135 """
141 """
136
142
137 # TODO Move old threads to the archive instead of deleting them.
143 # TODO Move old threads to the archive instead of deleting them.
138 # Maybe make some 'old' field in the model to indicate the thread
144 # Maybe make some 'old' field in the model to indicate the thread
139 # must not be shown and be able for replying.
145 # must not be shown and be able for replying.
140
146
141 threads = self.get_threads()
147 threads = self.get_threads()
142 thread_count = threads.count()
148 thread_count = threads.count()
143
149
144 if thread_count > settings.MAX_THREAD_COUNT:
150 if thread_count > settings.MAX_THREAD_COUNT:
145 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
151 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
146 old_threads = threads[thread_count - num_threads_to_delete:]
152 old_threads = threads[thread_count - num_threads_to_delete:]
147
153
148 map(self.delete_post, old_threads)
154 map(self.delete_post, old_threads)
149
155
156 def connect_replies(self, post):
157 """Connect replies to a post to show them as a refmap"""
158
159 for reply_number in re.finditer(REGEX_REPLY, post.text.raw):
160 id = reply_number.group(1)
161 ref_post = self.filter(id=id)
162 if ref_post.count() > 0:
163 ref_post[0].referenced_posts.add(post)
164
150
165
151 class TagManager(models.Manager):
166 class TagManager(models.Manager):
152
167
153 def get_not_empty_tags(self):
168 def get_not_empty_tags(self):
154 tags = self.annotate(Count('threads')) \
169 tags = self.annotate(Count('threads')) \
155 .filter(threads__count__gt=0).order_by('name')
170 .filter(threads__count__gt=0).order_by('name')
156
171
157 return tags
172 return tags
158
173
159
174
160 class Tag(models.Model):
175 class Tag(models.Model):
161 """
176 """
162 A tag is a text node assigned to the post. The tag serves as a board
177 A tag is a text node assigned to the post. The tag serves as a board
163 section. There can be multiple tags for each message
178 section. There can be multiple tags for each message
164 """
179 """
165
180
166 objects = TagManager()
181 objects = TagManager()
167
182
168 name = models.CharField(max_length=100)
183 name = models.CharField(max_length=100)
169 threads = models.ManyToManyField('Post', null=True,
184 threads = models.ManyToManyField('Post', null=True,
170 blank=True, related_name='tag+')
185 blank=True, related_name='tag+')
171 linked = models.ForeignKey('Tag', null=True, blank=True)
186 linked = models.ForeignKey('Tag', null=True, blank=True)
172
187
173 def __unicode__(self):
188 def __unicode__(self):
174 return self.name
189 return self.name
175
190
176 def is_empty(self):
191 def is_empty(self):
177 return self.get_post_count() == 0
192 return self.get_post_count() == 0
178
193
179 def get_post_count(self):
194 def get_post_count(self):
180 return self.threads.count()
195 return self.threads.count()
181
196
182 def get_popularity(self):
197 def get_popularity(self):
183 posts_with_tag = Post.objects.get_threads(tag=self)
198 posts_with_tag = Post.objects.get_threads(tag=self)
184 reply_count = 0
199 reply_count = 0
185 for post in posts_with_tag:
200 for post in posts_with_tag:
186 reply_count += post.get_reply_count()
201 reply_count += post.get_reply_count()
187 reply_count += OPENING_POST_POPULARITY_WEIGHT
202 reply_count += OPENING_POST_POPULARITY_WEIGHT
188
203
189 return reply_count
204 return reply_count
190
205
191 def get_linked_tags(self):
206 def get_linked_tags(self):
192 tag_list = []
207 tag_list = []
193 self.get_linked_tags_list(tag_list)
208 self.get_linked_tags_list(tag_list)
194
209
195 return tag_list
210 return tag_list
196
211
197 def get_linked_tags_list(self, tag_list=[]):
212 def get_linked_tags_list(self, tag_list=[]):
198 """
213 """
199 Returns the list of tags linked to current. The list can be got
214 Returns the list of tags linked to current. The list can be got
200 through returned value or tag_list parameter
215 through returned value or tag_list parameter
201 """
216 """
202
217
203 linked_tag = self.linked
218 linked_tag = self.linked
204
219
205 if linked_tag and not (linked_tag in tag_list):
220 if linked_tag and not (linked_tag in tag_list):
206 tag_list.append(linked_tag)
221 tag_list.append(linked_tag)
207
222
208 linked_tag.get_linked_tags_list(tag_list)
223 linked_tag.get_linked_tags_list(tag_list)
209
224
210
225
211 class Post(models.Model):
226 class Post(models.Model):
212 """A post is a message."""
227 """A post is a message."""
213
228
214 objects = PostManager()
229 objects = PostManager()
215
230
216 def _update_image_filename(self, filename):
231 def _update_image_filename(self, filename):
217 """Get unique image filename"""
232 """Get unique image filename"""
218
233
219 path = IMAGES_DIRECTORY
234 path = IMAGES_DIRECTORY
220 new_name = str(int(time.mktime(time.gmtime())))
235 new_name = str(int(time.mktime(time.gmtime())))
221 new_name += str(int(random() * 1000))
236 new_name += str(int(random() * 1000))
222 new_name += FILE_EXTENSION_DELIMITER
237 new_name += FILE_EXTENSION_DELIMITER
223 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
238 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
224
239
225 return os.path.join(path, new_name)
240 return os.path.join(path, new_name)
226
241
227 title = models.CharField(max_length=TITLE_MAX_LENGTH)
242 title = models.CharField(max_length=TITLE_MAX_LENGTH)
228 pub_time = models.DateTimeField()
243 pub_time = models.DateTimeField()
229 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
244 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
230 escape_html=False)
245 escape_html=False)
231
246
232 image_width = models.IntegerField(default=0)
247 image_width = models.IntegerField(default=0)
233 image_height = models.IntegerField(default=0)
248 image_height = models.IntegerField(default=0)
234
249
235 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
250 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
236 blank=True, sizes=(IMAGE_THUMB_SIZE,),
251 blank=True, sizes=(IMAGE_THUMB_SIZE,),
237 width_field='image_width',
252 width_field='image_width',
238 height_field='image_height')
253 height_field='image_height')
239
254
240 poster_ip = models.GenericIPAddressField()
255 poster_ip = models.GenericIPAddressField()
241 poster_user_agent = models.TextField()
256 poster_user_agent = models.TextField()
242
257
243 thread = models.ForeignKey('Post', null=True, default=None)
258 thread = models.ForeignKey('Post', null=True, default=None)
244 tags = models.ManyToManyField(Tag)
259 tags = models.ManyToManyField(Tag)
245 last_edit_time = models.DateTimeField()
260 last_edit_time = models.DateTimeField()
246 bump_time = models.DateTimeField()
261 bump_time = models.DateTimeField()
247 user = models.ForeignKey('User', null=True, default=None)
262 user = models.ForeignKey('User', null=True, default=None)
248
263
249 replies = models.ManyToManyField('Post', symmetrical=False, null=True,
264 replies = models.ManyToManyField('Post', symmetrical=False, null=True,
250 blank=True, related_name='re+')
265 blank=True, related_name='re+')
266 referenced_posts = models.ManyToManyField('Post', symmetrical=False, null=True,
267 blank=True, related_name='rfp+')
251
268
252 def __unicode__(self):
269 def __unicode__(self):
253 return '#' + str(self.id) + ' ' + self.title + ' (' + \
270 return '#' + str(self.id) + ' ' + self.title + ' (' + \
254 self.text.raw[:50] + ')'
271 self.text.raw[:50] + ')'
255
272
256 def get_title(self):
273 def get_title(self):
257 title = self.title
274 title = self.title
258 if len(title) == 0:
275 if len(title) == 0:
259 title = self.text.raw[:20]
276 title = self.text.raw[:20]
260
277
261 return title
278 return title
262
279
263 def get_reply_count(self):
280 def get_reply_count(self):
264 return self.replies.count()
281 return self.replies.count()
265
282
266 def get_images_count(self):
283 def get_images_count(self):
267 images_count = 1 if self.image else 0
284 images_count = 1 if self.image else 0
268 images_count += self.replies.filter(image_width__gt=0).count()
285 images_count += self.replies.filter(image_width__gt=0).count()
269
286
270 return images_count
287 return images_count
271
288
272 def can_bump(self):
289 def can_bump(self):
273 """Check if the thread can be bumped by replying"""
290 """Check if the thread can be bumped by replying"""
274
291
275 post_count = self.get_reply_count() + 1
292 post_count = self.get_reply_count() + 1
276
293
277 return post_count <= settings.MAX_POSTS_PER_THREAD
294 return post_count <= settings.MAX_POSTS_PER_THREAD
278
295
279 def bump(self):
296 def bump(self):
280 """Bump (move to up) thread"""
297 """Bump (move to up) thread"""
281
298
282 if self.can_bump():
299 if self.can_bump():
283 self.bump_time = timezone.now()
300 self.bump_time = timezone.now()
284
301
285 def get_last_replies(self):
302 def get_last_replies(self):
286 if settings.LAST_REPLIES_COUNT > 0:
303 if settings.LAST_REPLIES_COUNT > 0:
287 reply_count = self.get_reply_count()
304 reply_count = self.get_reply_count()
288
305
289 if reply_count > 0:
306 if reply_count > 0:
290 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
307 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
291 reply_count)
308 reply_count)
292 last_replies = self.replies.all().order_by('pub_time')[reply_count -
309 last_replies = self.replies.all().order_by('pub_time')[reply_count -
293 reply_count_to_show:]
310 reply_count_to_show:]
294
311
295 return last_replies
312 return last_replies
296
313
297
314
298 class User(models.Model):
315 class User(models.Model):
299
316
300 user_id = models.CharField(max_length=50)
317 user_id = models.CharField(max_length=50)
301 rank = models.IntegerField()
318 rank = models.IntegerField()
302
319
303 registration_time = models.DateTimeField()
320 registration_time = models.DateTimeField()
304
321
305 fav_tags = models.ManyToManyField(Tag, null=True, blank=True)
322 fav_tags = models.ManyToManyField(Tag, null=True, blank=True)
306 fav_threads = models.ManyToManyField(Post, related_name='+', null=True,
323 fav_threads = models.ManyToManyField(Post, related_name='+', null=True,
307 blank=True)
324 blank=True)
308
325
309 def save_setting(self, name, value):
326 def save_setting(self, name, value):
310 setting, created = Setting.objects.get_or_create(name=name, user=self)
327 setting, created = Setting.objects.get_or_create(name=name, user=self)
311 setting.value = str(value)
328 setting.value = str(value)
312 setting.save()
329 setting.save()
313
330
314 return setting
331 return setting
315
332
316 def get_setting(self, name):
333 def get_setting(self, name):
317 if Setting.objects.filter(name=name, user=self).exists():
334 if Setting.objects.filter(name=name, user=self).exists():
318 setting = Setting.objects.get(name=name, user=self)
335 setting = Setting.objects.get(name=name, user=self)
319 setting_value = setting.value
336 setting_value = setting.value
320 else:
337 else:
321 setting_value = None
338 setting_value = None
322
339
323 return setting_value
340 return setting_value
324
341
325 def is_moderator(self):
342 def is_moderator(self):
326 return RANK_MODERATOR >= self.rank
343 return RANK_MODERATOR >= self.rank
327
344
328 def get_sorted_fav_tags(self):
345 def get_sorted_fav_tags(self):
329 tags = self.fav_tags.annotate(Count('threads'))\
346 tags = self.fav_tags.annotate(Count('threads'))\
330 .filter(threads__count__gt=0).order_by('name')
347 .filter(threads__count__gt=0).order_by('name')
331
348
332 return tags
349 return tags
333
350
334 def get_post_count(self):
351 def get_post_count(self):
335 return Post.objects.filter(user=self).count()
352 return Post.objects.filter(user=self).count()
336
353
337 def __unicode__(self):
354 def __unicode__(self):
338 return self.user_id + '(' + str(self.rank) + ')'
355 return self.user_id + '(' + str(self.rank) + ')'
339
356
340 def get_last_access_time(self):
357 def get_last_access_time(self):
341 posts = Post.objects.filter(user=self)
358 posts = Post.objects.filter(user=self)
342 if posts.count() > 0:
359 if posts.count() > 0:
343 return posts.latest('pub_time').pub_time
360 return posts.latest('pub_time').pub_time
344
361
345
362
346 class Setting(models.Model):
363 class Setting(models.Model):
347
364
348 name = models.CharField(max_length=50)
365 name = models.CharField(max_length=50)
349 value = models.CharField(max_length=50)
366 value = models.CharField(max_length=50)
350 user = models.ForeignKey(User)
367 user = models.ForeignKey(User)
351
368
352
369
353 class Ban(models.Model):
370 class Ban(models.Model):
354
371
355 ip = models.GenericIPAddressField()
372 ip = models.GenericIPAddressField()
356
373
357 def __unicode__(self):
374 def __unicode__(self):
358 return self.ip
375 return self.ip
@@ -1,13 +1,13 b''
1 $( document ).ready(function() {
1 $( document ).ready(function() {
2 $("a[href='#top']").click(function() {
2 $("a[href='#top']").click(function() {
3 $("html, body").animate({ scrollTop: 0 }, "slow");
3 $("html, body").animate({ scrollTop: 0 }, "slow");
4 return false;
4 return false;
5 });
5 });
6
6
7 addImgPreview();
7 addImgPreview();
8 addRefLinkMap();
9
8
10 // TODO Rewrite popups module and reenable it
9 // TODO Rewrite popups module and reenable it
11 //addPopups();
10 //addPopups();
11
12 addMarkPanel();
12 addMarkPanel();
13 });
13 });
@@ -1,56 +1,55 b''
1 function addGalleryPanel() {
1 function addGalleryPanel() {
2 var gallery = $('a[class="thumb"]').clone(true),
2 var gallery = $('a[class="thumb"]').clone(true),
3 normal = $('.post').clone(true);
3 normal = $('.post').clone(true);
4
4
5 $('.navigation_panel').filter(':first').after(
5 $('.navigation_panel').filter(':first').after(
6 '<div class="image-mode-tab" role="radiogroup" aria-label="Image mode2">' +
6 '<div class="image-mode-tab" role="radiogroup" aria-label="Image mode2">' +
7 '<label><input type="radio" class="image-mode-normal" name="image-mode" value="0" checked="checked"/>'+ gettext('Normal') +'</label>' +
7 '<label><input type="radio" class="image-mode-normal" name="image-mode" value="0" checked="checked"/>'+ gettext('Normal') +'</label>' +
8 '<label><input type="radio" class="image-mode-table" name="image-mode" value="1"/>'+ gettext('Gallery') +'</label>' +
8 '<label><input type="radio" class="image-mode-table" name="image-mode" value="1"/>'+ gettext('Gallery') +'</label>' +
9 '</div>'
9 '</div>'
10 );
10 );
11
11
12 $('input[name="image-mode"]').change(function() {
12 $('input[name="image-mode"]').change(function() {
13 //gallery mode
13 //gallery mode
14 if($(this).val() === '1') {
14 if($(this).val() === '1') {
15 $('.thread').replaceWith(
15 $('.thread').replaceWith(
16 $('<div id="posts-table"></div>').append(gallery)
16 $('<div id="posts-table"></div>').append(gallery)
17 );
17 );
18 }
18 }
19 //normal mode
19 //normal mode
20 else {
20 else {
21 $('#posts-table').replaceWith(
21 $('#posts-table').replaceWith(
22 $('<div class="thread"></div>').append(normal)
22 $('<div class="thread"></div>').append(normal)
23 );
23 );
24 }
24 }
25 });
25 });
26 }
26 }
27
27
28 function moveCaretToEnd(el) {
28 function moveCaretToEnd(el) {
29 if (typeof el.selectionStart == "number") {
29 if (typeof el.selectionStart == "number") {
30 el.selectionStart = el.selectionEnd = el.value.length;
30 el.selectionStart = el.selectionEnd = el.value.length;
31 } else if (typeof el.createTextRange != "undefined") {
31 } else if (typeof el.createTextRange != "undefined") {
32 el.focus();
32 el.focus();
33 var range = el.createTextRange();
33 var range = el.createTextRange();
34 range.collapse(false);
34 range.collapse(false);
35 range.select();
35 range.select();
36 }
36 }
37 }
37 }
38
38
39 function addQuickReply(postId) {
39 function addQuickReply(postId) {
40 var textToAdd = '>>' + postId + '\n\n';
40 var textToAdd = '>>' + postId + '\n\n';
41 var textAreaId = '#id_text';
41 var textAreaId = '#id_text';
42 $(textAreaId).val($(textAreaId).val()+ textToAdd);
42 $(textAreaId).val($(textAreaId).val()+ textToAdd);
43
43
44 var textarea = document.getElementById('id_text');
44 var textarea = document.getElementById('id_text');
45 $(textAreaId).focus();
45 $(textAreaId).focus();
46 moveCaretToEnd(textarea);
46 moveCaretToEnd(textarea);
47
47
48 $("html, body").animate({ scrollTop: $(textAreaId).offset().top }, "slow");
48 $("html, body").animate({ scrollTop: $(textAreaId).offset().top }, "slow");
49 }
49 }
50
50
51
51
52
52
53 $(document).ready(function(){
53 $(document).ready(function(){
54 addGalleryPanel();
54 addGalleryPanel();
55 addRefLinkMap();
56 });
55 });
@@ -1,58 +1,57 b''
1 {% load staticfiles %}
1 {% load staticfiles %}
2 {% load i18n %}
2 {% load i18n %}
3 {% load static from staticfiles %}
3 {% load static from staticfiles %}
4
4
5 <!DOCTYPE html>
5 <!DOCTYPE html>
6 <html>
6 <html>
7 <head>
7 <head>
8 <link rel="stylesheet" type="text/css"
8 <link rel="stylesheet" type="text/css"
9 href="{% static 'css/base.css' %}" media="all"/>
9 href="{% static 'css/base.css' %}" media="all"/>
10 <link rel="stylesheet" type="text/css"
10 <link rel="stylesheet" type="text/css"
11 href="{% static theme_css %}" media="all"/>
11 href="{% static theme_css %}" media="all"/>
12 <link rel="alternate" type="application/rss+xml" href="rss/" title=
12 <link rel="alternate" type="application/rss+xml" href="rss/" title=
13 "{% trans 'Feed' %}"/>
13 "{% trans 'Feed' %}"/>
14
14
15 <link rel="icon" type="image/png"
15 <link rel="icon" type="image/png"
16 href="{% static 'favicon.png' %}">
16 href="{% static 'favicon.png' %}">
17
17
18 <meta name="viewport" content="width=device-width, initial-scale=1"/>
18 <meta name="viewport" content="width=device-width, initial-scale=1"/>
19 <meta charset="utf-8"/>
19 <meta charset="utf-8"/>
20
20
21 {% block head %}{% endblock %}
21 {% block head %}{% endblock %}
22 </head>
22 </head>
23 <body>
23 <body>
24 <script src="{% static 'js/jquery-2.0.1.min.js' %}"></script>
24 <script src="{% static 'js/jquery-2.0.1.min.js' %}"></script>
25 <script src="{% static 'js/jquery-ui-1.10.3.custom.min.js' %}"></script>
25 <script src="{% static 'js/jquery-ui-1.10.3.custom.min.js' %}"></script>
26 <script src="{% static 'js/jquery.mousewheel.js' %}"></script>
26 <script src="{% static 'js/jquery.mousewheel.js' %}"></script>
27 <script src="{% url 'django.views.i18n.javascript_catalog' %}"></script>
27 <script src="{% url 'django.views.i18n.javascript_catalog' %}"></script>
28 <script src="{% static 'js/refmaps.js' %}"></script>
29 <script src="{% static 'js/panel.js' %}"></script>
28 <script src="{% static 'js/panel.js' %}"></script>
30 <script src="{% static 'js/popup.js' %}"></script>
29 <script src="{% static 'js/popup.js' %}"></script>
31 <script src="{% static 'js/image.js' %}"></script>
30 <script src="{% static 'js/image.js' %}"></script>
32 <script src="{% static 'js/main.js' %}"></script>
31 <script src="{% static 'js/main.js' %}"></script>
33
32
34 <div class="navigation_panel">
33 <div class="navigation_panel">
35 <a class="link" href="{% url 'index' %}">{% trans "All threads" %}</a>
34 <a class="link" href="{% url 'index' %}">{% trans "All threads" %}</a>
36 {% for tag in tags %}
35 {% for tag in tags %}
37 <a class="tag" href="{% url 'tag' tag_name=tag.name %}"
36 <a class="tag" href="{% url 'tag' tag_name=tag.name %}"
38 >{{ tag.name }}</a>
37 >{{ tag.name }}</a>
39 {% endfor %}
38 {% endfor %}
40 <a class="tag" href="{% url 'tags' %}" title="{% trans 'Tag management' %}"
39 <a class="tag" href="{% url 'tags' %}" title="{% trans 'Tag management' %}"
41 >[...]</a>
40 >[...]</a>
42 <a class="link" href="{% url 'settings' %}">{% trans 'Settings' %}</a>
41 <a class="link" href="{% url 'settings' %}">{% trans 'Settings' %}</a>
43 </div>
42 </div>
44
43
45 {% block content %}{% endblock %}
44 {% block content %}{% endblock %}
46
45
47 <div class="navigation_panel">
46 <div class="navigation_panel">
48 {% block metapanel %}{% endblock %}
47 {% block metapanel %}{% endblock %}
49 [<a href="{% url "login" %}">{% trans 'Login' %}</a>]
48 [<a href="{% url "login" %}">{% trans 'Login' %}</a>]
50 <a class="link" href="#top">{% trans 'Up' %}</a>
49 <a class="link" href="#top">{% trans 'Up' %}</a>
51 </div>
50 </div>
52
51
53 <div class="footer">
52 <div class="footer">
54 <!-- Put your banners here -->
53 <!-- Put your banners here -->
55 </div>
54 </div>
56
55
57 </body>
56 </body>
58 </html>
57 </html>
@@ -1,206 +1,222 b''
1 {% extends "boards/base.html" %}
1 {% extends "boards/base.html" %}
2
2
3 {% load i18n %}
3 {% load i18n %}
4 {% load markup %}
4 {% load markup %}
5 {% load cache %}
5 {% load cache %}
6
6
7 {% block head %}
7 {% block head %}
8 {% if tag %}
8 {% if tag %}
9 <title>Neboard - {{ tag.name }}</title>
9 <title>Neboard - {{ tag.name }}</title>
10 {% else %}
10 {% else %}
11 <title>Neboard</title>
11 <title>Neboard</title>
12 {% endif %}
12 {% endif %}
13 {% endblock %}
13 {% endblock %}
14
14
15 {% block content %}
15 {% block content %}
16
16
17 {% if tag %}
17 {% if tag %}
18 <div class="tag_info">
18 <div class="tag_info">
19 <h2>{% trans 'Tag: ' %}{{ tag.name }}
19 <h2>{% trans 'Tag: ' %}{{ tag.name }}
20 {% if tag in user.fav_tags.all %}
20 {% if tag in user.fav_tags.all %}
21 <a href="{% url 'tag_unsubscribe' tag.name %}?next={{ request.path }}"
21 <a href="{% url 'tag_unsubscribe' tag.name %}?next={{ request.path }}"
22 class="fav"></a>
22 class="fav"></a>
23 {% else %}
23 {% else %}
24 <a href="{% url 'tag_subscribe' tag.name %}?next={{ request.path }}"
24 <a href="{% url 'tag_subscribe' tag.name %}?next={{ request.path }}"
25 class="not_fav"></a>
25 class="not_fav"></a>
26 {% endif %}
26 {% endif %}
27 </h2>
27 </h2>
28 </div>
28 </div>
29 {% endif %}
29 {% endif %}
30
30
31 {% if threads %}
31 {% if threads %}
32 {% for thread in threads %}
32 {% for thread in threads %}
33 <div class="thread">
33 <div class="thread">
34 {% if thread.bumpable %}
34 {% if thread.bumpable %}
35 <div class="post" id="{{ thread.thread.id }}">
35 <div class="post" id="{{ thread.thread.id }}">
36 {% else %}
36 {% else %}
37 <div class="post dead_post" id="{{ thread.thread.id }}">
37 <div class="post dead_post" id="{{ thread.thread.id }}">
38 {% endif %}
38 {% endif %}
39 {% if thread.thread.image %}
39 {% if thread.thread.image %}
40 <div class="image">
40 <div class="image">
41 <a class="thumb"
41 <a class="thumb"
42 href="{{ thread.thread.image.url }}"><img
42 href="{{ thread.thread.image.url }}"><img
43 src="{{ thread.thread.image.url_200x150 }}"
43 src="{{ thread.thread.image.url_200x150 }}"
44 alt="{{ thread.thread.id }}"
44 alt="{{ thread.thread.id }}"
45 data-width="{{ thread.thread.image_width }}"
45 data-width="{{ thread.thread.image_width }}"
46 data-height="{{ thread.thread.image_height }}" />
46 data-height="{{ thread.thread.image_height }}" />
47 </a>
47 </a>
48 </div>
48 </div>
49 {% endif %}
49 {% endif %}
50 <div class="message">
50 <div class="message">
51 <div class="post-info">
51 <div class="post-info">
52 <span class="title">{{ thread.thread.title }}</span>
52 <span class="title">{{ thread.thread.title }}</span>
53 <a class="post_id" href="{% url 'thread' thread.thread.id %}"
53 <a class="post_id" href="{% url 'thread' thread.thread.id %}"
54 >(#{{ thread.thread.id }})</a>
54 >(#{{ thread.thread.id }})</a>
55 [{{ thread.thread.pub_time }}]
55 [{{ thread.thread.pub_time }}]
56 [<a class="link" href="{% url 'thread' thread.thread.id %}#form"
56 [<a class="link" href="{% url 'thread' thread.thread.id %}#form"
57 >{% trans "Reply" %}</a>]
57 >{% trans "Reply" %}</a>]
58
58
59 {% if moderator %}
59 {% if moderator %}
60 <span class="moderator_info">
60 <span class="moderator_info">
61 [<a href="{% url 'delete' post_id=thread.thread.id %}?next={{ request.path }}"
61 [<a href="{% url 'delete' post_id=thread.thread.id %}?next={{ request.path }}"
62 >{% trans 'Delete' %}</a>]
62 >{% trans 'Delete' %}</a>]
63 ({{ thread.thread.poster_ip }})
63 ({{ thread.thread.poster_ip }})
64 [<a href="{% url 'ban' post_id=thread.thread.id %}?next={{ request.path }}"
64 [<a href="{% url 'ban' post_id=thread.thread.id %}?next={{ request.path }}"
65 >{% trans 'Ban IP' %}</a>]
65 >{% trans 'Ban IP' %}</a>]
66 </span>
66 </span>
67 {% endif %}
67 {% endif %}
68 </div>
68 </div>
69 {% autoescape off %}
69 {% autoescape off %}
70 {{ thread.thread.text.rendered|truncatewords_html:50 }}
70 {{ thread.thread.text.rendered|truncatewords_html:50 }}
71 {% endautoescape %}
71 {% endautoescape %}
72 </div>
72 </div>
73 <div class="metadata">
73 <div class="metadata">
74 {{ thread.thread.get_reply_count }} {% trans 'replies' %},
74 {{ thread.thread.get_reply_count }} {% trans 'replies' %},
75 {{ thread.thread.get_images_count }} {% trans 'images' %}.
75 {{ thread.thread.get_images_count }} {% trans 'images' %}.
76 {% if thread.thread.tags %}
76 {% if thread.thread.tags %}
77 <span class="tags">{% trans 'Tags' %}:
77 <span class="tags">{% trans 'Tags' %}:
78 {% for tag in thread.thread.tags.all %}
78 {% for tag in thread.thread.tags.all %}
79 <a class="tag" href="
79 <a class="tag" href="
80 {% url 'tag' tag_name=tag.name %}">
80 {% url 'tag' tag_name=tag.name %}">
81 {{ tag.name }}</a>
81 {{ tag.name }}</a>
82 {% endfor %}
82 {% endfor %}
83 </span>
83 </span>
84 {% endif %}
84 {% endif %}
85 </div>
85 </div>
86 {% if thread.thread.referenced_posts.all %}
87 <div class="refmap">
88 {% trans "Replies" %}:
89 {% for ref_post in thread.thread.referenced_posts.all %}
90 <a href="{% url 'jumper' ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a>
91 {% endfor %}
92 </div>
93 {% endif %}
86 </div>
94 </div>
87 {% if thread.thread.get_last_replies.exists %}
95 {% if thread.thread.get_last_replies.exists %}
88 <div class="last-replies">
96 <div class="last-replies">
89 {% for post in thread.thread.get_last_replies %}
97 {% for post in thread.thread.get_last_replies %}
90 {% if thread.bumpable %}
98 {% if thread.bumpable %}
91 <div class="post" id="{{ post.id }}">
99 <div class="post" id="{{ post.id }}">
92 {% else %}
100 {% else %}
93 <div class="post dead_post" id="{{ post.id }}">
101 <div class="post dead_post" id="{{ post.id }}">
94 {% endif %}
102 {% endif %}
95 {% if post.image %}
103 {% if post.image %}
96 <div class="image">
104 <div class="image">
97 <a class="thumb"
105 <a class="thumb"
98 href="{{ post.image.url }}"><img
106 href="{{ post.image.url }}"><img
99 src=" {{ post.image.url_200x150 }}"
107 src=" {{ post.image.url_200x150 }}"
100 alt="{{ post.id }}"
108 alt="{{ post.id }}"
101 data-width="{{ post.image_width }}"
109 data-width="{{ post.image_width }}"
102 data-height="{{ post.image_height }}"/>
110 data-height="{{ post.image_height }}"/>
103 </a>
111 </a>
104 </div>
112 </div>
105 {% endif %}
113 {% endif %}
106 <div class="message">
114 <div class="message">
107 <div class="post-info">
115 <div class="post-info">
108 <span class="title">{{ post.title }}</span>
116 <span class="title">{{ post.title }}</span>
109 <a class="post_id" href="
117 <a class="post_id" href="
110 {% url 'thread' thread.thread.id %}#{{ post.id }}">
118 {% url 'thread' thread.thread.id %}#{{ post.id }}">
111 (#{{ post.id }})</a>
119 (#{{ post.id }})</a>
112 [{{ post.pub_time }}]
120 [{{ post.pub_time }}]
113 </div>
121 </div>
114 {% autoescape off %}
122 {% autoescape off %}
115 {{ post.text.rendered|truncatewords_html:50 }}
123 {{ post.text.rendered|truncatewords_html:50 }}
116 {% endautoescape %}
124 {% endautoescape %}
117 </div>
125 </div>
126 {% if post.referenced_posts.all %}
127 <div class="refmap">
128 {% trans "Replies" %}:
129 {% for ref_post in post.referenced_posts.all %}
130 <a href="{% url 'jumper' ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a>
131 {% endfor %}
132 </div>
133 {% endif %}
118 </div>
134 </div>
119 {% endfor %}
135 {% endfor %}
120 </div>
136 </div>
121 {% endif %}
137 {% endif %}
122 </div>
138 </div>
123 {% endfor %}
139 {% endfor %}
124 {% else %}
140 {% else %}
125 <div class="post">
141 <div class="post">
126 {% trans 'No threads exist. Create the first one!' %}</div>
142 {% trans 'No threads exist. Create the first one!' %}</div>
127 {% endif %}
143 {% endif %}
128
144
129 <form enctype="multipart/form-data" method="post">{% csrf_token %}
145 <form enctype="multipart/form-data" method="post">{% csrf_token %}
130 <div class="post-form-w">
146 <div class="post-form-w">
131
147
132 <div class="form-title">{% trans "Create new thread" %}</div>
148 <div class="form-title">{% trans "Create new thread" %}</div>
133 <div class="post-form">
149 <div class="post-form">
134 <div class="form-row">
150 <div class="form-row">
135 <div class="form-label">{% trans 'Title' %}</div>
151 <div class="form-label">{% trans 'Title' %}</div>
136 <div class="form-input">{{ form.title }}</div>
152 <div class="form-input">{{ form.title }}</div>
137 <div class="form-errors">{{ form.title.errors }}</div>
153 <div class="form-errors">{{ form.title.errors }}</div>
138 </div>
154 </div>
139 <div class="form-row">
155 <div class="form-row">
140 <div class="form-label">{% trans 'Formatting' %}</div>
156 <div class="form-label">{% trans 'Formatting' %}</div>
141 <div class="form-input" id="mark_panel">
157 <div class="form-input" id="mark_panel">
142 <span class="mark_btn" id="quote"><span class="quote">&gt;{% trans 'quote' %}</span></span>
158 <span class="mark_btn" id="quote"><span class="quote">&gt;{% trans 'quote' %}</span></span>
143 <span class="mark_btn" id="italic"><i>{% trans 'italic' %}</i></span>
159 <span class="mark_btn" id="italic"><i>{% trans 'italic' %}</i></span>
144 <span class="mark_btn" id="bold"><b>{% trans 'bold' %}</b></span>
160 <span class="mark_btn" id="bold"><b>{% trans 'bold' %}</b></span>
145 <span class="mark_btn" id="spoiler"><span class="spoiler">{% trans 'spoiler' %}</span></span>
161 <span class="mark_btn" id="spoiler"><span class="spoiler">{% trans 'spoiler' %}</span></span>
146 <span class="mark_btn" id="comment"><span class="comment">// {% trans 'comment' %}</span></span>
162 <span class="mark_btn" id="comment"><span class="comment">// {% trans 'comment' %}</span></span>
147 </div>
163 </div>
148 </div>
164 </div>
149 <div class="form-row">
165 <div class="form-row">
150 <div class="form-label">{% trans 'Text' %}</div>
166 <div class="form-label">{% trans 'Text' %}</div>
151 <div class="form-input">{{ form.text }}</div>
167 <div class="form-input">{{ form.text }}</div>
152 <div class="form-errors">{{ form.text.errors }}</div>
168 <div class="form-errors">{{ form.text.errors }}</div>
153 </div>
169 </div>
154 <div class="form-row">
170 <div class="form-row">
155 <div class="form-label">{% trans 'Image' %}</div>
171 <div class="form-label">{% trans 'Image' %}</div>
156 <div class="form-input">{{ form.image }}</div>
172 <div class="form-input">{{ form.image }}</div>
157 <div class="form-errors">{{ form.image.errors }}</div>
173 <div class="form-errors">{{ form.image.errors }}</div>
158 </div>
174 </div>
159 <div class="form-row">
175 <div class="form-row">
160 <div class="form-label">{% trans 'Tags' %}</div>
176 <div class="form-label">{% trans 'Tags' %}</div>
161 <div class="form-input">{{ form.tags }}</div>
177 <div class="form-input">{{ form.tags }}</div>
162 <div class="form-errors">{{ form.tags.errors }}</div>
178 <div class="form-errors">{{ form.tags.errors }}</div>
163 </div>
179 </div>
164 <div class="form-row form-email">
180 <div class="form-row form-email">
165 <div class="form-label">{% trans 'e-mail' %}</div>
181 <div class="form-label">{% trans 'e-mail' %}</div>
166 <div class="form-input">{{ form.email }}</div>
182 <div class="form-input">{{ form.email }}</div>
167 <div class="form-errors">{{ form.email.errors }}</div>
183 <div class="form-errors">{{ form.email.errors }}</div>
168 </div>
184 </div>
169 <div class="form-row">
185 <div class="form-row">
170 {{ form.captcha }}
186 {{ form.captcha }}
171 <div class="form-errors">{{ form.captcha.errors }}</div>
187 <div class="form-errors">{{ form.captcha.errors }}</div>
172 </div>
188 </div>
173 <div class="form-row">
189 <div class="form-row">
174 <div class="form-errors">{{ form.other.errors }}</div>
190 <div class="form-errors">{{ form.other.errors }}</div>
175 </div>
191 </div>
176 </div>
192 </div>
177 <div class="form-submit">
193 <div class="form-submit">
178 <input type="submit" value="{% trans "Post" %}"/></div>
194 <input type="submit" value="{% trans "Post" %}"/></div>
179 <div>
195 <div>
180 {% trans 'Tags must be delimited by spaces. Text or image is required.' %}
196 {% trans 'Tags must be delimited by spaces. Text or image is required.' %}
181 </div>
197 </div>
182 <div><a href="{% url "staticpage" name="help" %}">
198 <div><a href="{% url "staticpage" name="help" %}">
183 {% trans 'Text syntax' %}</a></div>
199 {% trans 'Text syntax' %}</a></div>
184 </div>
200 </div>
185 </form>
201 </form>
186
202
187 {% endblock %}
203 {% endblock %}
188
204
189 {% block metapanel %}
205 {% block metapanel %}
190
206
191 <span class="metapanel">
207 <span class="metapanel">
192 <b><a href="{% url "authors" %}">Neboard</a> 1.3</b>
208 <b><a href="{% url "authors" %}">Neboard</a> 1.3</b>
193 {% trans "Pages:" %}
209 {% trans "Pages:" %}
194 {% for page in pages %}
210 {% for page in pages %}
195 [<a href="
211 [<a href="
196 {% if tag %}
212 {% if tag %}
197 {% url "tag" tag_name=tag page=page %}
213 {% url "tag" tag_name=tag page=page %}
198 {% else %}
214 {% else %}
199 {% url "index" page=page %}
215 {% url "index" page=page %}
200 {% endif %}
216 {% endif %}
201 ">{{ page }}</a>]
217 ">{{ page }}</a>]
202 {% endfor %}
218 {% endfor %}
203 [<a href="rss/">RSS</a>]
219 [<a href="rss/">RSS</a>]
204 </span>
220 </span>
205
221
206 {% endblock %}
222 {% endblock %}
@@ -1,152 +1,160 b''
1 {% extends "boards/base.html" %}
1 {% extends "boards/base.html" %}
2
2
3 {% load i18n %}
3 {% load i18n %}
4 {% load markup %}
4 {% load markup %}
5 {% load cache %}
5 {% load cache %}
6 {% load static from staticfiles %}
6 {% load static from staticfiles %}
7
7
8 {% block head %}
8 {% block head %}
9 <title>Neboard - {{ posts.0.get_title }}</title>
9 <title>Neboard - {{ posts.0.get_title }}</title>
10 {% endblock %}
10 {% endblock %}
11
11
12 {% block content %}
12 {% block content %}
13 {% get_current_language as LANGUAGE_CODE %}
13 {% get_current_language as LANGUAGE_CODE %}
14
14
15 <script src="{% static 'js/thread.js' %}"></script>
15 <script src="{% static 'js/thread.js' %}"></script>
16
16
17 {% if posts %}
17 {% if posts %}
18 {% cache 600 thread_view posts.0.last_edit_time moderator LANGUAGE_CODE %}
18 {% cache 600 thread_view posts.0.last_edit_time moderator LANGUAGE_CODE %}
19 {% if bumpable %}
19 {% if bumpable %}
20 <div class="bar-bg">
20 <div class="bar-bg">
21 <div class="bar-value" style="width:{{ bumplimit_progress }}%">
21 <div class="bar-value" style="width:{{ bumplimit_progress }}%">
22 </div>
22 </div>
23 <div class="bar-text">
23 <div class="bar-text">
24 {{ posts_left }} {% trans 'posts to bumplimit' %}
24 {{ posts_left }} {% trans 'posts to bumplimit' %}
25 </div>
25 </div>
26 </div>
26 </div>
27 {% endif %}
27 {% endif %}
28 <div class="thread">
28 <div class="thread">
29 {% for post in posts %}
29 {% for post in posts %}
30 {% if bumpable %}
30 {% if bumpable %}
31 <div class="post" id="{{ post.id }}">
31 <div class="post" id="{{ post.id }}">
32 {% else %}
32 {% else %}
33 <div class="post dead_post" id="{{ post.id }}">
33 <div class="post dead_post" id="{{ post.id }}">
34 {% endif %}
34 {% endif %}
35 {% if post.image %}
35 {% if post.image %}
36 <div class="image">
36 <div class="image">
37 <a
37 <a
38 class="thumb"
38 class="thumb"
39 href="{{ post.image.url }}"><img
39 href="{{ post.image.url }}"><img
40 src="{{ post.image.url_200x150 }}"
40 src="{{ post.image.url_200x150 }}"
41 alt="{{ post.id }}"
41 alt="{{ post.id }}"
42 data-width="{{ post.image_width }}"
42 data-width="{{ post.image_width }}"
43 data-height="{{ post.image_height }}"/>
43 data-height="{{ post.image_height }}"/>
44 </a>
44 </a>
45 </div>
45 </div>
46 {% endif %}
46 {% endif %}
47 <div class="message">
47 <div class="message">
48 <div class="post-info">
48 <div class="post-info">
49 <span class="title">{{ post.title }}</span>
49 <span class="title">{{ post.title }}</span>
50 <a class="post_id" href="#{{ post.id }}">
50 <a class="post_id" href="#{{ post.id }}">
51 (#{{ post.id }})</a>
51 (#{{ post.id }})</a>
52 [{{ post.pub_time }}]
52 [{{ post.pub_time }}]
53 [<a href="#" onclick="javascript:addQuickReply('{{ post.id }}')
53 [<a href="#" onclick="javascript:addQuickReply('{{ post.id }}')
54 ; return false;">&gt;&gt;</a>]
54 ; return false;">&gt;&gt;</a>]
55
55
56 {% if moderator %}
56 {% if moderator %}
57 <span class="moderator_info">
57 <span class="moderator_info">
58 [<a href="{% url 'delete' post_id=post.id %}"
58 [<a href="{% url 'delete' post_id=post.id %}"
59 >{% trans 'Delete' %}</a>]
59 >{% trans 'Delete' %}</a>]
60 ({{ post.poster_ip }})
60 ({{ post.poster_ip }})
61 [<a href="{% url 'ban' post_id=post.id %}?next={{ request.path }}"
61 [<a href="{% url 'ban' post_id=post.id %}?next={{ request.path }}"
62 >{% trans 'Ban IP' %}</a>]
62 >{% trans 'Ban IP' %}</a>]
63 </span>
63 </span>
64 {% endif %}
64 {% endif %}
65 </div>
65 </div>
66 {% autoescape off %}
66 {% autoescape off %}
67 {{ post.text.rendered }}
67 {{ post.text.rendered }}
68 {% endautoescape %}
68 {% endautoescape %}
69 {% if post.referenced_posts.all %}
70 <div class="refmap">
71 {% trans "Replies" %}:
72 {% for ref_post in post.referenced_posts.all %}
73 <a href="{% url 'jumper' ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a>
74 {% endfor %}
75 </div>
76 {% endif %}
69 </div>
77 </div>
70 {% if post.id == posts.0.id %}
78 {% if post.id == posts.0.id %}
71 <div class="metadata">
79 <div class="metadata">
72 <span class="tags">{% trans 'Tags' %}:
80 <span class="tags">{% trans 'Tags' %}:
73 {% for tag in post.tags.all %}
81 {% for tag in post.tags.all %}
74 <a class="tag" href="{% url 'tag' tag.name %}">
82 <a class="tag" href="{% url 'tag' tag.name %}">
75 {{ tag.name }}</a>
83 {{ tag.name }}</a>
76 {% endfor %}
84 {% endfor %}
77 </span>
85 </span>
78 </div>
86 </div>
79 {% endif %}
87 {% endif %}
80 </div>
88 </div>
81 {% endfor %}
89 {% endfor %}
82 </div>
90 </div>
83 {% endcache %}
91 {% endcache %}
84 {% endif %}
92 {% endif %}
85
93
86 <form id="form" enctype="multipart/form-data" method="post"
94 <form id="form" enctype="multipart/form-data" method="post"
87 >{% csrf_token %}
95 >{% csrf_token %}
88 <div class="post-form-w">
96 <div class="post-form-w">
89 <div class="form-title">{% trans "Reply to thread" %} #{{ posts.0.id }}</div>
97 <div class="form-title">{% trans "Reply to thread" %} #{{ posts.0.id }}</div>
90 <div class="post-form">
98 <div class="post-form">
91 <div class="form-row">
99 <div class="form-row">
92 <div class="form-label">{% trans 'Title' %}</div>
100 <div class="form-label">{% trans 'Title' %}</div>
93 <div class="form-input">{{ form.title }}</div>
101 <div class="form-input">{{ form.title }}</div>
94 <div class="form-errors">{{ form.title.errors }}</div>
102 <div class="form-errors">{{ form.title.errors }}</div>
95 </div>
103 </div>
96 <div class="form-row">
104 <div class="form-row">
97 <div class="form-label">{% trans 'Formatting' %}</div>
105 <div class="form-label">{% trans 'Formatting' %}</div>
98 <div class="form-input" id="mark_panel">
106 <div class="form-input" id="mark_panel">
99 <span class="mark_btn" id="quote"><span class="quote">&gt;{% trans 'quote' %}</span></span>
107 <span class="mark_btn" id="quote"><span class="quote">&gt;{% trans 'quote' %}</span></span>
100 <span class="mark_btn" id="italic"><i>{% trans 'italic' %}</i></span>
108 <span class="mark_btn" id="italic"><i>{% trans 'italic' %}</i></span>
101 <span class="mark_btn" id="bold"><b>{% trans 'bold' %}</b></span>
109 <span class="mark_btn" id="bold"><b>{% trans 'bold' %}</b></span>
102 <span class="mark_btn" id="spoiler"><span class="spoiler">{% trans 'spoiler' %}</span></span>
110 <span class="mark_btn" id="spoiler"><span class="spoiler">{% trans 'spoiler' %}</span></span>
103 <span class="mark_btn" id="comment"><span class="comment">// {% trans 'comment' %}</span></span>
111 <span class="mark_btn" id="comment"><span class="comment">// {% trans 'comment' %}</span></span>
104 </div>
112 </div>
105 </div>
113 </div>
106 <div class="form-row">
114 <div class="form-row">
107 <div class="form-label">{% trans 'Text' %}</div>
115 <div class="form-label">{% trans 'Text' %}</div>
108 <div class="form-input">{{ form.text }}</div>
116 <div class="form-input">{{ form.text }}</div>
109 <div class="form-errors">{{ form.text.errors }}</div>
117 <div class="form-errors">{{ form.text.errors }}</div>
110 </div>
118 </div>
111 <div class="form-row">
119 <div class="form-row">
112 <div class="form-label">{% trans 'Image' %}</div>
120 <div class="form-label">{% trans 'Image' %}</div>
113 <div class="form-input">{{ form.image }}</div>
121 <div class="form-input">{{ form.image }}</div>
114 <div class="form-errors">{{ form.image.errors }}</div>
122 <div class="form-errors">{{ form.image.errors }}</div>
115 </div>
123 </div>
116 <div class="form-row form-email">
124 <div class="form-row form-email">
117 <div class="form-label">{% trans 'e-mail' %}</div>
125 <div class="form-label">{% trans 'e-mail' %}</div>
118 <div class="form-input">{{ form.email }}</div>
126 <div class="form-input">{{ form.email }}</div>
119 <div class="form-errors">{{ form.email.errors }}</div>
127 <div class="form-errors">{{ form.email.errors }}</div>
120 </div>
128 </div>
121 <div class="form-row">
129 <div class="form-row">
122 {{ form.captcha }}
130 {{ form.captcha }}
123 <div class="form-errors">{{ form.captcha.errors }}</div>
131 <div class="form-errors">{{ form.captcha.errors }}</div>
124 </div>
132 </div>
125 <div class="form-row">
133 <div class="form-row">
126 <div class="form-errors">{{ form.other.errors }}</div>
134 <div class="form-errors">{{ form.other.errors }}</div>
127 </div>
135 </div>
128 </div>
136 </div>
129
137
130 <div class="form-submit"><input type="submit"
138 <div class="form-submit"><input type="submit"
131 value="{% trans "Post" %}"/></div>
139 value="{% trans "Post" %}"/></div>
132 <div><a href="{% url "staticpage" name="help" %}">
140 <div><a href="{% url "staticpage" name="help" %}">
133 {% trans 'Text syntax' %}</a></div>
141 {% trans 'Text syntax' %}</a></div>
134 </div>
142 </div>
135 </form>
143 </form>
136
144
137 {% endblock %}
145 {% endblock %}
138
146
139 {% block metapanel %}
147 {% block metapanel %}
140
148
141 {% get_current_language as LANGUAGE_CODE %}
149 {% get_current_language as LANGUAGE_CODE %}
142
150
143 <span class="metapanel">
151 <span class="metapanel">
144 {% cache 600 thread_meta posts.0.last_edit_time moderator LANGUAGE_CODE %}
152 {% cache 600 thread_meta posts.0.last_edit_time moderator LANGUAGE_CODE %}
145 {{ posts.0.get_reply_count }} {% trans 'replies' %},
153 {{ posts.0.get_reply_count }} {% trans 'replies' %},
146 {{ posts.0.get_images_count }} {% trans 'images' %}.
154 {{ posts.0.get_images_count }} {% trans 'images' %}.
147 {% trans 'Last update: ' %}{{ posts.0.last_edit_time }}
155 {% trans 'Last update: ' %}{{ posts.0.last_edit_time }}
148 [<a href="rss/">RSS</a>]
156 [<a href="rss/">RSS</a>]
149 {% endcache %}
157 {% endcache %}
150 </span>
158 </span>
151
159
152 {% endblock %}
160 {% endblock %}
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