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