##// END OF EJS Templates
Merged with 1.5 dev branch
neko259 -
r463:8531d7b0 merge 1.5 default
parent child Browse files
Show More
@@ -0,0 +1,92 b''
1 # -*- coding: utf-8 -*-
2 from south.utils import datetime_utils as 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 field 'Post.image_pre_width'
12 db.add_column(u'boards_post', 'image_pre_width',
13 self.gf('django.db.models.fields.IntegerField')(default=0),
14 keep_default=False)
15
16 # Adding field 'Post.image_pre_height'
17 db.add_column(u'boards_post', 'image_pre_height',
18 self.gf('django.db.models.fields.IntegerField')(default=0),
19 keep_default=False)
20
21
22 def backwards(self, orm):
23 # Deleting field 'Post.image_pre_width'
24 db.delete_column(u'boards_post', 'image_pre_width')
25
26 # Deleting field 'Post.image_pre_height'
27 db.delete_column(u'boards_post', 'image_pre_height')
28
29
30 models = {
31 'boards.ban': {
32 'Meta': {'object_name': 'Ban'},
33 'can_read': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
34 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
35 'ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
36 'reason': ('django.db.models.fields.CharField', [], {'default': "'Auto'", 'max_length': '200'})
37 },
38 'boards.post': {
39 'Meta': {'object_name': 'Post'},
40 '_text_rendered': ('django.db.models.fields.TextField', [], {}),
41 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
42 'image': ('boards.thumbs.ImageWithThumbsField', [], {'max_length': '100', 'blank': 'True'}),
43 'image_height': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
44 'image_pre_height': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
45 'image_pre_width': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
46 'image_width': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
47 'last_edit_time': ('django.db.models.fields.DateTimeField', [], {}),
48 'poster_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39'}),
49 'poster_user_agent': ('django.db.models.fields.TextField', [], {}),
50 'pub_time': ('django.db.models.fields.DateTimeField', [], {}),
51 'referenced_posts': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'rfp+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Post']"}),
52 'text': ('markupfield.fields.MarkupField', [], {'rendered_field': 'True'}),
53 'text_markup_type': ('django.db.models.fields.CharField', [], {'default': "'markdown'", 'max_length': '30'}),
54 'thread': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['boards.Post']", 'null': 'True'}),
55 'thread_new': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['boards.Thread']", 'null': 'True'}),
56 'title': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
57 'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['boards.User']", 'null': 'True'})
58 },
59 'boards.setting': {
60 'Meta': {'object_name': 'Setting'},
61 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
62 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
63 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['boards.User']"}),
64 'value': ('django.db.models.fields.CharField', [], {'max_length': '50'})
65 },
66 'boards.tag': {
67 'Meta': {'object_name': 'Tag'},
68 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
69 'linked': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['boards.Tag']", 'null': 'True', 'blank': 'True'}),
70 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
71 'threads': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'tag+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Thread']"})
72 },
73 'boards.thread': {
74 'Meta': {'object_name': 'Thread'},
75 'bump_time': ('django.db.models.fields.DateTimeField', [], {}),
76 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
77 'last_edit_time': ('django.db.models.fields.DateTimeField', [], {}),
78 'replies': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'tre+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Post']"}),
79 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['boards.Tag']", 'symmetrical': 'False'})
80 },
81 'boards.user': {
82 'Meta': {'object_name': 'User'},
83 'fav_tags': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['boards.Tag']", 'null': 'True', 'blank': 'True'}),
84 'fav_threads': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['boards.Post']"}),
85 u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
86 'rank': ('django.db.models.fields.IntegerField', [], {}),
87 'registration_time': ('django.db.models.fields.DateTimeField', [], {}),
88 'user_id': ('django.db.models.fields.CharField', [], {'max_length': '50'})
89 }
90 }
91
92 complete_apps = ['boards'] No newline at end of file
@@ -0,0 +1,66 b''
1 {% extends "boards/base.html" %}
2
3 {% load i18n %}
4 {% load cache %}
5 {% load static from staticfiles %}
6 {% load board %}
7
8 {% block head %}
9 <title>Neboard - {{ thread.get_opening_post.get_title }}</title>
10 {% endblock %}
11
12 {% block content %}
13 {% spaceless %}
14 {% get_current_language as LANGUAGE_CODE %}
15
16 <script src="{% static 'js/thread.js' %}"></script>
17
18 {% cache 600 thread_gallery_view thread.id thread.last_edit_time LANGUAGE_CODE %}
19 <div class="image-mode-tab">
20 <a href="{% url 'thread' thread.get_opening_post.id %}">{% trans 'Normal mode' %}</a>,
21 <a class="current_mode" href="{% url 'thread_mode' thread.get_opening_post.id 'gallery' %}">{% trans 'Gallery mode' %}</a>
22 </div>
23
24 <div id="posts-table">
25 {% for post in thread.get_replies %}
26 {% if post.image %}
27 <div class="gallery_image">
28 <div>
29 <a
30 class="thumb"
31 href="{{ post.image.url }}"><img
32 src="{{ post.image.url_200x150 }}"
33 alt="{{ post.id }}"
34 width="{{ post.image_pre_width }}"
35 height="{{ post.image_pre_height }}"
36 data-width="{{ post.image_width }}"
37 data-height="{{ post.image_height }}"/>
38 </a>
39 </div>
40 <div class="gallery_image_metadata">
41 {{ post.image_width }}x{{ post.image_height }}
42 {% image_actions post.image.url request.get_host %}
43 </div>
44 </div>
45 {% endif %}
46 {% endfor %}
47 </div>
48 {% endcache %}
49
50 {% endspaceless %}
51 {% endblock %}
52
53 {% block metapanel %}
54
55 {% get_current_language as LANGUAGE_CODE %}
56
57 <span class="metapanel" data-last-update="{{ last_update }}">
58 {% cache 600 thread_meta thread.last_edit_time moderator LANGUAGE_CODE %}
59 <span id="reply-count">{{ thread.get_reply_count }}</span> {% trans 'replies' %},
60 <span id="image-count">{{ thread.get_images_count }}</span> {% trans 'images' %}.
61 {% trans 'Last update: ' %}{{ thread.last_edit_time }}
62 [<a href="rss/">RSS</a>]
63 {% endcache %}
64 </span>
65
66 {% endblock %}
@@ -0,0 +1,7 b''
1 # 1.5 Aker #
2 * Saving image previews size. No space will be shown below images in some
3 styles.
4 * Showing notification in page title when new posts are loaded into the open
5 thread.
6 * Thread moderation fixes
7 * Added new gallery with search links and image metadata No newline at end of file
1 NO CONTENT: modified file, binary diff hidden
@@ -7,7 +7,7 b' msgid ""'
7 7 msgstr ""
8 8 "Project-Id-Version: PACKAGE VERSION\n"
9 9 "Report-Msgid-Bugs-To: \n"
10 "POT-Creation-Date: 2013-11-27 12:34+0200\n"
10 "POT-Creation-Date: 2013-12-24 20:39+0200\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"
@@ -34,77 +34,72 b' msgstr "\xd1\x80\xd0\xb0\xd0\xb7\xd1\x80\xd0\xb0\xd0\xb1\xd0\xbe\xd1\x82\xd1\x87\xd0\xb8\xd0\xba javascript"'
34 34 msgid "designer"
35 35 msgstr "дизайнер"
36 36
37 #: forms.py:48 templates/boards/posting_general.html:209
38 #: templates/boards/thread.html:101
37 #: forms.py:72
39 38 msgid "Title"
40 39 msgstr "Заголовок"
41 40
42 #: forms.py:50 templates/boards/posting_general.html:224
43 #: templates/boards/thread.html:116
41 #: forms.py:74
44 42 msgid "Text"
45 43 msgstr "Текст"
46 44
47 #: forms.py:51 templates/boards/posting_general.html:229
48 #: templates/boards/thread.html:121
45 #: forms.py:75
49 46 msgid "Image"
50 47 msgstr "Изображение"
51 48
52 #: forms.py:54 templates/boards/posting_general.html:239
53 #: templates/boards/thread.html:126
49 #: forms.py:78
54 50 msgid "e-mail"
55 51 msgstr ""
56 52
57 #: forms.py:65
53 #: forms.py:89
58 54 #, python-format
59 55 msgid "Title must have less than %s characters"
60 56 msgstr "Заголовок должен иметь меньше %s символов"
61 57
62 #: forms.py:74
58 #: forms.py:98
63 59 #, python-format
64 60 msgid "Text must have less than %s characters"
65 61 msgstr "Текст должен быть короче %s символов"
66 62
67 #: forms.py:85
63 #: forms.py:109
68 64 #, python-format
69 65 msgid "Image must be less than %s bytes"
70 66 msgstr "Изображение должно быть менее %s байт"
71 67
72 #: forms.py:112
68 #: forms.py:136
73 69 msgid "Either text or image must be entered."
74 70 msgstr "Текст или картинка должны быть введены."
75 71
76 #: forms.py:125
72 #: forms.py:149
77 73 #, python-format
78 74 msgid "Wait %s seconds after last posting"
79 75 msgstr "Подождите %s секунд после последнего постинга"
80 76
81 #: forms.py:139 templates/boards/post.html:60
82 #: templates/boards/posting_general.html:234 templates/boards/tags.html:6
77 #: forms.py:163 templates/boards/post.html:61 templates/boards/tags.html:6
83 78 #: templates/boards/rss/post.html:10
84 79 msgid "Tags"
85 80 msgstr "Теги"
86 81
87 #: forms.py:147
82 #: forms.py:171
88 83 msgid "Inappropriate characters in tags."
89 84 msgstr "Недопустимые символы в тегах."
90 85
91 #: forms.py:175 forms.py:196
86 #: forms.py:199 forms.py:220
92 87 msgid "Captcha validation failed"
93 88 msgstr "Проверка капчи провалена"
94 89
95 #: forms.py:202
90 #: forms.py:226
96 91 msgid "Theme"
97 92 msgstr "Тема"
98 93
99 #: forms.py:207
94 #: forms.py:231
100 95 msgid "Enable moderation panel"
101 96 msgstr "Включить панель модерации"
102 97
103 #: forms.py:222
98 #: forms.py:246
104 99 msgid "No such user found"
105 100 msgstr "Данный пользователь не найден"
106 101
107 #: forms.py:236
102 #: forms.py:260
108 103 #, python-format
109 104 msgid "Wait %s minutes after last login"
110 105 msgstr "Подождите %s минут после последнего входа"
@@ -137,15 +132,15 b' msgstr "\xd0\xa0\xd0\xb5\xd0\xbf\xd0\xbe\xd0\xb7\xd0\xb8\xd1\x82\xd0\xbe\xd1\x80\xd0\xb8\xd0\xb9"'
137 132 msgid "Feed"
138 133 msgstr "Лента"
139 134
140 #: templates/boards/base.html:36
135 #: templates/boards/base.html:31
141 136 msgid "All threads"
142 137 msgstr "Все темы"
143 138
144 #: templates/boards/base.html:41
139 #: templates/boards/base.html:36
145 140 msgid "Tag management"
146 141 msgstr "Управление тегами"
147 142
148 #: templates/boards/base.html:43
143 #: templates/boards/base.html:38
149 144 msgid "Settings"
150 145 msgstr "Настройки"
151 146
@@ -171,92 +166,65 b' msgstr "ID \xd0\xbf\xd0\xbe\xd0\xbb\xd1\x8c\xd0\xb7\xd0\xbe\xd0\xb2\xd0\xb0\xd1\x82\xd0\xb5\xd0\xbb\xd1\x8f"'
171 166 msgid "Insert your user id above"
172 167 msgstr "Вставьте свой ID пользователя выше"
173 168
174 #: templates/boards/post.html:34 templates/boards/posting_general.html:100
175 #: templates/boards/thread.html:59
169 #: templates/boards/post.html:35 templates/boards/posting_general.html:103
170 #: templates/boards/thread.html:68
176 171 msgid "Delete"
177 172 msgstr "Удалить"
178 173
179 #: templates/boards/post.html:37 templates/boards/posting_general.html:104
180 #: templates/boards/thread.html:62
174 #: templates/boards/post.html:38 templates/boards/posting_general.html:107
175 #: templates/boards/thread.html:71
181 176 msgid "Ban IP"
182 177 msgstr "Заблокировать IP"
183 178
184 #: templates/boards/post.html:50 templates/boards/posting_general.html:113
185 #: templates/boards/posting_general.html:172 templates/boards/thread.html:71
179 #: templates/boards/post.html:51 templates/boards/posting_general.html:116
180 #: templates/boards/posting_general.html:180 templates/boards/thread.html:80
186 181 msgid "Replies"
187 182 msgstr "Ответы"
188 183
189 #: templates/boards/posting_general.html:63
184 #: templates/boards/posting_general.html:64
190 185 msgid "Previous page"
191 186 msgstr "Предыдущая страница"
192 187
193 #: templates/boards/posting_general.html:94
188 #: templates/boards/posting_general.html:97
194 189 msgid "Reply"
195 190 msgstr "Ответ"
196 191
197 #: templates/boards/posting_general.html:122 templates/boards/thread.html:154
198 msgid "replies"
199 msgstr "ответов"
200
201 #: templates/boards/posting_general.html:123 templates/boards/thread.html:155
192 #: templates/boards/posting_general.html:125 templates/boards/thread.html:130
193 #: templates/boards/thread_gallery.html:52
202 194 msgid "images"
203 195 msgstr "изображений"
204 196
205 #: templates/boards/posting_general.html:138
197 #: templates/boards/posting_general.html:142
206 198 #, python-format
207 199 msgid "Skipped %(count)s replies. Open thread to see all replies."
208 200 msgstr "Пропущено %(count)s ответов. Откройте тред, чтобы увидеть все ответы."
209 201
210 #: templates/boards/posting_general.html:195
202 #: templates/boards/posting_general.html:203
211 203 msgid "Next page"
212 204 msgstr "Следующая страница"
213 205
214 #: templates/boards/posting_general.html:200
206 #: templates/boards/posting_general.html:208
215 207 msgid "No threads exist. Create the first one!"
216 208 msgstr "Нет тем. Создайте первую!"
217 209
218 #: templates/boards/posting_general.html:206
210 #: templates/boards/posting_general.html:214
219 211 msgid "Create new thread"
220 212 msgstr "Создать новую тему"
221 213
222 #: templates/boards/posting_general.html:214 templates/boards/thread.html:106
223 msgid "Formatting"
224 msgstr "Форматирование"
225
226 #: templates/boards/posting_general.html:216 templates/boards/thread.html:108
227 msgid "quote"
228 msgstr "цитата"
229
230 #: templates/boards/posting_general.html:217 templates/boards/thread.html:109
231 msgid "italic"
232 msgstr "курсив"
233
234 #: templates/boards/posting_general.html:218 templates/boards/thread.html:110
235 msgid "bold"
236 msgstr "полужирный"
237
238 #: templates/boards/posting_general.html:219 templates/boards/thread.html:111
239 msgid "spoiler"
240 msgstr "спойлер"
241
242 #: templates/boards/posting_general.html:220 templates/boards/thread.html:112
243 msgid "comment"
244 msgstr "комментарий"
245
246 #: templates/boards/posting_general.html:252 templates/boards/thread.html:140
214 #: templates/boards/posting_general.html:218 templates/boards/thread.html:112
247 215 msgid "Post"
248 216 msgstr "Отправить"
249 217
250 #: templates/boards/posting_general.html:254
218 #: templates/boards/posting_general.html:222
251 219 msgid "Tags must be delimited by spaces. Text or image is required."
252 220 msgstr ""
253 221 "Теги должны быть разделены пробелами. Текст или изображение обязательны."
254 222
255 #: templates/boards/posting_general.html:257 templates/boards/thread.html:142
223 #: templates/boards/posting_general.html:225 templates/boards/thread.html:116
256 224 msgid "Text syntax"
257 225 msgstr "Синтаксис текста"
258 226
259 #: templates/boards/posting_general.html:267
227 #: templates/boards/posting_general.html:235
260 228 msgid "Pages:"
261 229 msgstr "Страницы: "
262 230
@@ -292,15 +260,27 b' msgstr "\xd1\x82\xd0\xb5\xd0\xbc"'
292 260 msgid "No tags found."
293 261 msgstr "Теги не найдены."
294 262
295 #: templates/boards/thread.html:24
263 #: templates/boards/thread.html:22 templates/boards/thread_gallery.html:20
264 msgid "Normal mode"
265 msgstr "Нормальный режим"
266
267 #: templates/boards/thread.html:23 templates/boards/thread_gallery.html:21
268 msgid "Gallery mode"
269 msgstr "Режим галереи"
270
271 #: templates/boards/thread.html:31
296 272 msgid "posts to bumplimit"
297 273 msgstr "сообщений до бамплимита"
298 274
299 #: templates/boards/thread.html:98
275 #: templates/boards/thread.html:106
300 276 msgid "Reply to thread"
301 277 msgstr "Ответить в тему"
302 278
303 #: templates/boards/thread.html:156
279 #: templates/boards/thread.html:129 templates/boards/thread_gallery.html:51
280 msgid "replies"
281 msgstr "ответов"
282
283 #: templates/boards/thread.html:131 templates/boards/thread_gallery.html:53
304 284 msgid "Last update: "
305 285 msgstr "Последнее обновление: "
306 286
@@ -353,6 +333,24 b' msgstr "\xd0\xa1\xd1\x81\xd1\x8b\xd0\xbb\xd0\xba\xd0\xb0 \xd0\xbd\xd0\xb0 \xd1\x81\xd0\xbe\xd0\xbe\xd0\xb1\xd1\x89\xd0\xb5\xd0\xbd\xd0\xb8\xd0\xb5"'
353 333 msgid "Strikethrough text"
354 334 msgstr "Зачеркнутый текст"
355 335
336 #~ msgid "Formatting"
337 #~ msgstr "Форматирование"
338
339 #~ msgid "quote"
340 #~ msgstr "цитата"
341
342 #~ msgid "italic"
343 #~ msgstr "курсив"
344
345 #~ msgid "bold"
346 #~ msgstr "полужирный"
347
348 #~ msgid "spoiler"
349 #~ msgstr "спойлер"
350
351 #~ msgid "comment"
352 #~ msgstr "комментарий"
353
356 354 #~ msgid "Tag: "
357 355 #~ msgstr "Тег: "
358 356
1 NO CONTENT: modified file, binary diff hidden
@@ -8,7 +8,7 b' msgid ""'
8 8 msgstr ""
9 9 "Project-Id-Version: PACKAGE VERSION\n"
10 10 "Report-Msgid-Bugs-To: \n"
11 "POT-Creation-Date: 2013-11-13 17:25+0200\n"
11 "POT-Creation-Date: 2013-12-21 21:45+0200\n"
12 12 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 13 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14 14 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -19,11 +19,11 b' msgstr ""'
19 19 "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
20 20 "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
21 21
22 #: static/js/refpopup.js:60
22 #: static/js/refpopup.js:57
23 23 msgid "Loading..."
24 24 msgstr "Загрузка..."
25 25
26 #: static/js/refpopup.js:86
26 #: static/js/refpopup.js:76
27 27 msgid "Post not found"
28 28 msgstr "Сообщение не найдено"
29 29
@@ -35,5 +35,9 b' msgstr "\xd0\x9d\xd0\xbe\xd1\x80\xd0\xbc\xd0\xb0\xd0\xbb\xd1\x8c\xd0\xbd\xd1\x8b\xd0\xb9"'
35 35 msgid "Gallery"
36 36 msgstr "Галерея"
37 37
38 #: static/js/thread_update.js:177
39 msgid "[new posts]"
40 msgstr "[новые посты]"
41
38 42 #~ msgid "Replies"
39 43 #~ msgstr "Ответы"
@@ -34,7 +34,8 b' class MinifyHTMLMiddleware(object):'
34 34 except AttributeError:
35 35 compress_html = False
36 36
37 if TYPE_HTML in response[RESPONSE_CONTENT_TYPE] and compress_html:
37 if RESPONSE_CONTENT_TYPE in response\
38 and TYPE_HTML in response[RESPONSE_CONTENT_TYPE] and compress_html:
38 39 response.content = strip_spaces_between_tags(
39 40 response.content.strip())
40 41 return response No newline at end of file
@@ -86,12 +86,18 b' class PostManager(models.Manager):'
86 86
87 87 def delete_post(self, post):
88 88 """
89 Delete post and update its thread
89 Delete post and update or delete its thread
90 90 """
91
92 thread = post.thread_new
91 93
92 thread = post.thread_new
93 thread.last_edit_time = timezone.now()
94 thread.save()
94 if thread.get_opening_post() == self:
95 thread.replies.delete()
96
97 thread.delete()
98 else:
99 thread.last_edit_time = timezone.now()
100 thread.save()
95 101
96 102 post.delete()
97 103
@@ -235,10 +241,15 b' class Post(models.Model):'
235 241 image_width = models.IntegerField(default=0)
236 242 image_height = models.IntegerField(default=0)
237 243
244 image_pre_width = models.IntegerField(default=0)
245 image_pre_height = models.IntegerField(default=0)
246
238 247 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
239 248 blank=True, sizes=(IMAGE_THUMB_SIZE,),
240 249 width_field='image_width',
241 height_field='image_height')
250 height_field='image_height',
251 preview_width_field='image_pre_width',
252 preview_height_field='image_pre_height')
242 253
243 254 poster_ip = models.GenericIPAddressField()
244 255 poster_user_agent = models.TextField()
@@ -27,3 +27,7 b''
27 27 z-index: 300;
28 28 position:absolute;
29 29 }
30
31 .gallery_image {
32 display: inline-block;
33 } No newline at end of file
@@ -173,8 +173,6 b' blockquote {'
173 173 min-width: 1px;
174 174 text-align: center;
175 175 display: table-row;
176
177 height: 150px;
178 176 }
179 177
180 178 .post > .metadata {
@@ -356,3 +354,15 b' li {'
356 354 .skipped_replies {
357 355 margin: 5px;
358 356 }
357
358 .current_page, .current_mode {
359 border: solid 1px #afdcec;
360 padding: 2px;
361 }
362
363 .gallery_image {
364 border: solid 1px;
365 padding: 0.5ex;
366 margin: 0.5ex;
367 text-align: center;
368 } No newline at end of file
@@ -171,8 +171,6 b' blockquote {'
171 171 min-width: 1px;
172 172 text-align: center;
173 173 display: table-row;
174
175 height: 150px;
176 174 }
177 175
178 176 .post > .metadata {
@@ -342,3 +340,8 b' input[type="submit"]:hover {'
342 340 .skipped_replies {
343 341 margin: 5px;
344 342 }
343
344 .current_page, .current_mode {
345 border: solid 1px #000;
346 padding: 2px;
347 } No newline at end of file
@@ -330,4 +330,28 b' li {'
330 330
331 331 .mark_btn:last-child {
332 332 border-right: 1px solid #182F6F;
333 }
334
335 .current_page {
336 border-bottom: 1px solid #FFF;
337 padding: 0px 0.5ex;
338 }
339
340 .image-mode-tab a {
341 text-decoration: none;
342 }
343 .image-mode-tab .current_mode::before {
344 content: "✓ ";
345 padding: 0 0 0 .5ex;
346 color: #182F6F;
347 background: #FFF;
348 }
349 .image-mode-tab .current_mode {
350 padding: 0 .5ex 0 0;
351 color: #182F6F;
352 background: #FFF;
353 }
354
355 .gallery_image_metadata {
356 margin-bottom: 1em;
333 357 } No newline at end of file
@@ -23,33 +23,6 b''
23 23 for the JavaScript code in this page.
24 24 */
25 25
26 function addGalleryPanel() {
27 var gallery = $('a[class="thumb"]').clone(true),
28 normal = $('.post').clone(true);
29
30 $('.navigation_panel').filter(':first').after(
31 '<div class="image-mode-tab" role="radiogroup" aria-label="Image mode2">' +
32 '<label><input type="radio" class="image-mode-normal" name="image-mode" value="0" checked="checked"/>'+ gettext('Normal') +'</label>' +
33 '<label><input type="radio" class="image-mode-table" name="image-mode" value="1"/>'+ gettext('Gallery') +'</label>' +
34 '</div>'
35 );
36
37 $('input[name="image-mode"]').change(function() {
38 //gallery mode
39 if($(this).val() === '1') {
40 $('.thread').replaceWith(
41 $('<div id="posts-table"></div>').append(gallery)
42 );
43 }
44 //normal mode
45 else {
46 $('#posts-table').replaceWith(
47 $('<div class="thread"></div>').append(normal)
48 );
49 }
50 });
51 }
52
53 26 function moveCaretToEnd(el) {
54 27 if (typeof el.selectionStart == "number") {
55 28 el.selectionStart = el.selectionEnd = el.value.length;
@@ -72,10 +45,3 b' function addQuickReply(postId) {'
72 45
73 46 $("html, body").animate({ scrollTop: $(textAreaId).offset().top }, "slow");
74 47 }
75
76
77
78 $(document).ready(function(){
79 addGalleryPanel();
80 initAutoupdate();
81 });
@@ -97,6 +97,10 b' function updateThread() {'
97 97
98 98 updateBumplimitProgress(data.added.length);
99 99 updatePostBumpableStatus();
100
101 if (data.added.length + data.updated.length > 0) {
102 showNewPostsTitle();
103 }
100 104 })
101 105 .error(function(data) {
102 106 // TODO Show error message that server is unavailable?
@@ -162,3 +166,27 b' function updatePostBumpableStatus() {'
162 166 $('.thread').find('.post').addClass('dead_post');
163 167 }
164 168 }
169
170 var documentOriginalTitle = '';
171 /**
172 * Show 'new posts' text in the title if the document is not visible to a user
173 */
174 function showNewPostsTitle() {
175 if (document.hidden) {
176 documentOriginalTitle = document.title;
177 document.title = gettext('[new posts]') + ' ' + document.title;
178
179 document.addEventListener('visibilitychange', function() {
180 if (documentOriginalTitle !== '') {
181 document.title = documentOriginalTitle;
182 documentOriginalTitle = '';
183 }
184
185 document.removeEventListener('visibilitychange', null);
186 });
187 }
188 }
189
190 $(document).ready(function(){
191 initAutoupdate();
192 });
@@ -79,6 +79,8 b''
79 79 href="{{ thread.op.image.url }}"><img
80 80 src="{{ thread.op.image.url_200x150 }}"
81 81 alt="{{ thread.op.id }}"
82 width="{{ thread.op.image_pre_width }}"
83 height="{{ thread.op.image_pre_height }}"
82 84 data-width="{{ thread.op.image_width }}"
83 85 data-height="{{ thread.op.image_height }}"/>
84 86 </a>
@@ -99,7 +101,7 b''
99 101 [<a href="
100 102 {% url 'delete' post_id=thread.op.id %}?next={{ request.path }}"
101 103 >{% trans 'Delete' %}</a>]
102 ({{ thread.thread.poster_ip }})
104 ({{ thread.op.poster_ip }})
103 105 [<a href="
104 106 {% url 'ban' post_id=thread.op.id %}?next={{ request.path }}"
105 107 >{% trans 'Ban IP' %}</a>]
@@ -154,6 +156,8 b''
154 156 href="{{ post.image.url }}"><img
155 157 src=" {{ post.image.url_200x150 }}"
156 158 alt="{{ post.id }}"
159 width="{{ post.image_pre_width }}"
160 height="{{ post.image_pre_height }}"
157 161 data-width="{{ post.image_width }}"
158 162 data-height="{{ post.image_height }}"/>
159 163 </a>
@@ -227,17 +231,23 b''
227 231 {% block metapanel %}
228 232
229 233 <span class="metapanel">
230 <b><a href="{% url "authors" %}">Neboard</a> 1.4.1</b>
231 {% trans "Pages:" %}
234 <b><a href="{% url "authors" %}">Neboard</a> 1.5 Aker</b>
235 {% trans "Pages:" %}[
232 236 {% for page in pages %}
233 [<a href="
237 <a
238 {% ifequal page current_page %}
239 class="current_page"
240 {% endifequal %}
241 href="
234 242 {% if tag %}
235 243 {% url "tag" tag_name=tag page=page %}
236 244 {% else %}
237 245 {% url "index" page=page %}
238 246 {% endif %}
239 ">{{ page }}</a>]
247 ">{{ page }}</a>
248 {% if not forloop.last %},{% endif %}
240 249 {% endfor %}
250 ]
241 251 [<a href="rss/">RSS</a>]
242 252 </span>
243 253
@@ -6,7 +6,7 b''
6 6 {% load board %}
7 7
8 8 {% block head %}
9 <title>Neboard - {{ thread.get_replies.0.get_title }}</title>
9 <title>Neboard - {{ thread.get_opening_post.get_title }}</title>
10 10 {% endblock %}
11 11
12 12 {% block content %}
@@ -17,6 +17,12 b''
17 17 <script src="{% static 'js/thread.js' %}"></script>
18 18
19 19 {% cache 600 thread_view thread.id thread.last_edit_time moderator LANGUAGE_CODE %}
20
21 <div class="image-mode-tab">
22 <a class="current_mode" href="{% url 'thread' thread.get_opening_post.id %}">{% trans 'Normal mode' %}</a>,
23 <a href="{% url 'thread_mode' thread.get_opening_post.id 'gallery' %}">{% trans 'Gallery mode' %}</a>
24 </div>
25
20 26 {% if bumpable %}
21 27 <div class="bar-bg">
22 28 <div class="bar-value" style="width:{{ bumplimit_progress }}%" id="bumplimit_progress">
@@ -40,6 +46,8 b''
40 46 href="{{ post.image.url }}"><img
41 47 src="{{ post.image.url_200x150 }}"
42 48 alt="{{ post.id }}"
49 width="{{ post.image_pre_width }}"
50 height="{{ post.image_pre_height }}"
43 51 data-width="{{ post.image_width }}"
44 52 data-height="{{ post.image_height }}"/>
45 53 </a>
@@ -6,6 +6,17 b' from django import template'
6 6
7 7 register = template.Library()
8 8
9 actions = [
10 {
11 'name': 'google',
12 'link': 'http://google.com/searchbyimage?image_url=%s',
13 },
14 {
15 'name': 'iqdb',
16 'link': 'http://iqdb.org/?url=%s',
17 },
18 ]
19
9 20
10 21 @register.simple_tag(name='post_url')
11 22 def post_url(*args, **kwargs):
@@ -21,3 +32,18 b' def post_url(*args, **kwargs):'
21 32 link = reverse(thread, kwargs={'post_id': post_id})
22 33
23 34 return link
35
36
37 @register.simple_tag(name='image_actions')
38 def image_actions(*args, **kwargs):
39 image_link = args[0]
40 if len(args) > 1:
41 image_link = 'http://' + args[1] + image_link # TODO https?
42
43 result = ''
44
45 for action in actions:
46 result += '[<a href="' + action['link'] % image_link + '">' + \
47 action['name'] + '</a>]'
48
49 return result
@@ -3,6 +3,7 b''
3 3 django-thumbs by Antonio Melé
4 4 http://django.es
5 5 """
6 from django.core.files.images import ImageFile
6 7 from django.db.models import ImageField
7 8 from django.db.models.fields.files import ImageFieldFile
8 9 from PIL import Image
@@ -13,13 +14,13 b' import cStringIO'
13 14 def generate_thumb(img, thumb_size, format):
14 15 """
15 16 Generates a thumbnail image and returns a ContentFile object with the thumbnail
16
17
17 18 Parameters:
18 19 ===========
19 20 img File object
20
21
21 22 thumb_size desired thumbnail size, ie: (200,120)
22
23
23 24 format format of the original image ('jpeg','gif','png',...)
24 25 (this format will be used for the generated thumbnail, too)
25 26 """
@@ -41,7 +42,7 b' def generate_thumb(img, thumb_size, form'
41 42 # crop it
42 43 image2 = image.crop(
43 44 (xnewsize, ynewsize, xsize - xnewsize, ysize - ynewsize))
44 # load is necessary after crop
45 # load is necessary after crop
45 46 image2.load()
46 47 # thumbnail of the cropped image (with ANTIALIAS to make it look better)
47 48 image2.thumbnail(thumb_size, Image.ANTIALIAS)
@@ -160,7 +161,9 b' class ImageWithThumbsField(ImageField):'
160 161 """
161 162
162 163 def __init__(self, verbose_name=None, name=None, width_field=None,
163 height_field=None, sizes=None, **kwargs):
164 height_field=None, sizes=None,
165 preview_width_field=None, preview_height_field=None,
166 **kwargs):
164 167 self.verbose_name = verbose_name
165 168 self.name = name
166 169 self.width_field = width_field
@@ -168,6 +171,46 b' class ImageWithThumbsField(ImageField):'
168 171 self.sizes = sizes
169 172 super(ImageField, self).__init__(**kwargs)
170 173
174 if sizes is not None and len(sizes) == 1:
175 self.preview_width_field = preview_width_field
176 self.preview_height_field = preview_height_field
177
178 def update_dimension_fields(self, instance, force=False, *args, **kwargs):
179 """
180 Update original image dimension fields and thumb dimension fields
181 (only if 1 thumb size is defined)
182 """
183
184 super(ImageWithThumbsField, self).update_dimension_fields(instance,
185 force, *args,
186 **kwargs)
187 thumb_width_field = self.preview_width_field
188 thumb_height_field = self.preview_height_field
189
190 if thumb_width_field is None or thumb_height_field is None \
191 or len(self.sizes) != 1:
192 return
193
194 original_width = getattr(instance, self.width_field)
195 original_height = getattr(instance, self.height_field)
196
197 if original_width > 0 and original_height > 0:
198 thumb_width, thumb_height = self.sizes[0]
199
200 w_scale = float(thumb_width) / original_width
201 h_scale = float(thumb_height) / original_height
202 scale_ratio = min(w_scale, h_scale)
203
204 if scale_ratio >= 1:
205 thumb_width_ratio = original_width
206 thumb_height_ratio = original_height
207 else:
208 thumb_width_ratio = int(original_width * scale_ratio)
209 thumb_height_ratio = int(original_height * scale_ratio)
210
211 setattr(instance, thumb_width_field, thumb_width_ratio)
212 setattr(instance, thumb_height_field, thumb_height_ratio)
213
171 214
172 215 from south.modelsinspector import add_introspection_rules
173 216 add_introspection_rules([], ["^boards\.thumbs\.ImageWithThumbsField"])
@@ -31,6 +31,7 b" urlpatterns = patterns('',"
31 31
32 32 # /boards/thread/
33 33 url(r'^thread/(?P<post_id>\w+)/$', views.thread, name='thread'),
34 url(r'^thread/(?P<post_id>\w+)/(?P<mode>\w+)/$', views.thread, name='thread_mode'),
34 35 url(r'^settings/$', views.settings, name='settings'),
35 36 url(r'^tags/$', views.all_tags, name='tags'),
36 37 url(r'^captcha/', include('captcha.urls')),
@@ -7,7 +7,7 b' import re'
7 7
8 8 from django.core import serializers
9 9 from django.core.urlresolvers import reverse
10 from django.http import HttpResponseRedirect
10 from django.http import HttpResponseRedirect, Http404
11 11 from django.http.response import HttpResponse
12 12 from django.template import RequestContext
13 13 from django.shortcuts import render, redirect, get_object_or_404
@@ -30,6 +30,8 b' import neboard'
30 30
31 31
32 32 BAN_REASON_SPAM = 'Autoban: spam bot'
33 MODE_GALLERY = 'gallery'
34 MODE_NORMAL = 'normal'
33 35
34 36
35 37 def index(request, page=0):
@@ -62,6 +64,7 b' def index(request, page=0):'
62 64 # TODO Make this generic for tag and threads list pages
63 65 context['threads'] = None if len(threads) == 0 else threads
64 66 context['form'] = form
67 context['current_page'] = int(page)
65 68
66 69 page_count = Post.objects.get_thread_page_count()
67 70 context['pages'] = range(page_count)
@@ -154,6 +157,7 b' def tag(request, tag_name, page=0):'
154 157 context = _init_default_context(request)
155 158 context['threads'] = None if len(threads) == 0 else threads
156 159 context['tag'] = tag
160 context['current_page'] = int(page)
157 161
158 162 page_count = Post.objects.get_thread_page_count(tag=tag)
159 163 context['pages'] = range(page_count)
@@ -169,7 +173,7 b' def tag(request, tag_name, page=0):'
169 173 context)
170 174
171 175
172 def thread(request, post_id):
176 def thread(request, post_id, mode=MODE_NORMAL):
173 177 """Get all thread posts"""
174 178
175 179 if utils.need_include_captcha(request):
@@ -209,7 +213,14 b' def thread(request, post_id):'
209 213 context["last_update"] = _datetime_to_epoch(thread_to_show.last_edit_time)
210 214 context["thread"] = thread_to_show
211 215
212 return render(request, 'boards/thread.html', context)
216 if MODE_NORMAL == mode:
217 document = 'boards/thread.html'
218 elif MODE_GALLERY == mode:
219 document = 'boards/thread_gallery.html'
220 else:
221 raise Http404
222
223 return render(request, document, context)
213 224
214 225
215 226 def login(request):
@@ -27,7 +27,7 b' Site: http://neboard.me/'
27 27 # INSTALLATION #
28 28
29 29 1. Install all dependencies over pip or system-wide
30 2. Setup a database in neboard/settings.py
30 2. Setup a database in `neboard/settings.py`
31 31 3. Run `./manage.py syncdb` and ensure the database was created
32 32 4. Run `./manage.py migrate boards` to apply all south migrations
33 33
@@ -41,6 +41,15 b' See django-admin command help for detail'
41 41
42 42 Also consider using wsgi or fcgi interfaces on production servers.
43 43
44 # UPGRADE #
45
46 1. Backup your project data.
47 2. Save the settings in `neboard/settings.py` and `boards/settings.py`
48 3. Copy the project contents over the old project directory
49 4. Run migrations by `./manage.py migrate boards`
50
51 You can also just clone the mercurial project and pull it to update
52
44 53 # CONCLUSION #
45 54
46 55 Enjoy our software and thank you!
@@ -12,6 +12,7 b' denied". Use second only for autoban for'
12 12 [DONE] Split up post model into post and thread,
13 13 and move everything that is used only in 1st post to thread model.
14 14 [DONE] Show board speed in the lower panel (posts per day)
15 [DONE] Save image thumbnails size to the separate field
15 16
16 17 [NOT STARTED] Tree view (JS)
17 18 [NOT STARTED] Adding tags to images filename
@@ -22,7 +23,6 b' and move everything that is used only in'
22 23 [NOT STARTED] Javascript disabling engine
23 24 [NOT STARTED] Group tags by first letter in all tags list
24 25 [NOT STARTED] Character counter in the post field
25 [NOT STARTED] Save image thumbnails size to the separate field
26 26 [NOT STARTED] Whitelist functionality. Permin autoban of an address
27 27 [NOT STARTED] Statistics module. Count views (optional, may result in bad
28 28 performance), posts per day/week/month, users (or IPs)
General Comments 0
You need to be logged in to leave comments. Login now