##// END OF EJS Templates
Merged with default
neko259 -
r834:cfa74d10 merge decentral
parent child Browse files
Show More
@@ -0,0 +1,30 b''
1 {% extends "boards/base.html" %}
2
3 {% load i18n %}
4
5 {% block head %}
6 <title>{% trans 'Preview' %} - {{ site_name }}</title>
7 {% endblock %}
8
9 {% block content %}
10
11 <div class="post-form-w">
12 <div class="post-form">
13 <form enctype="multipart/form-data" method="post">
14 <textarea name="query">{{ query }}</textarea>
15 <div class="form-submit">
16 <input type="submit" value="{% trans "Post" %}"/>
17 </div>
18 </form>
19 </div>
20 </div>
21
22 {% if result %}
23 <div class="post">
24 {% autoescape off %}
25 {{ result }}
26 {% endautoescape %}
27 </div>
28 {% endif %}
29
30 {% endblock %}
@@ -0,0 +1,35 b''
1 from django.shortcuts import render
2 from django.template import RequestContext
3 from django.views.generic import View
4
5 from boards.mdx_neboard import bbcode_extended
6
7 FORM_QUERY = 'query'
8
9 CONTEXT_RESULT = 'result'
10 CONTEXT_QUERY = 'query'
11
12 __author__ = 'neko259'
13
14 TEMPLATE = 'boards/preview.html'
15
16
17 class PostPreviewView(View):
18 def get(self, request):
19 context = RequestContext(request)
20
21 return render(request, TEMPLATE, context)
22
23 def post(self, request):
24 context = RequestContext(request)
25
26 if FORM_QUERY in request.POST:
27 raw_text = request.POST[FORM_QUERY]
28
29 if len(raw_text) >= 0:
30 rendered_text = bbcode_extended(raw_text)
31
32 context[CONTEXT_RESULT] = rendered_text
33 context[CONTEXT_QUERY] = raw_text
34
35 return render(request, TEMPLATE, context)
@@ -1,298 +1,294 b''
1 1 import re
2 2 import time
3 3 import hashlib
4 4
5 5 from django import forms
6 6 from django.forms.util import ErrorList
7 7 from django.utils.translation import ugettext_lazy as _
8 8
9 9 from boards.mdx_neboard import formatters
10 10 from boards.models.post import TITLE_MAX_LENGTH
11 11 from boards.models import PostImage
12 12 from neboard import settings
13 13 from boards import utils
14 14 import boards.settings as board_settings
15 15
16 16 VETERAN_POSTING_DELAY = 5
17 17
18 18 ATTRIBUTE_PLACEHOLDER = 'placeholder'
19 19
20 20 LAST_POST_TIME = 'last_post_time'
21 21 LAST_LOGIN_TIME = 'last_login_time'
22 22 TEXT_PLACEHOLDER = _('''Type message here. Use formatting panel for more advanced usage.''')
23 23 TAGS_PLACEHOLDER = _('tag1 several_words_tag')
24 24
25 25 ERROR_IMAGE_DUPLICATE = _('Such image was already posted')
26 26
27 27 LABEL_TITLE = _('Title')
28 28 LABEL_TEXT = _('Text')
29 29 LABEL_TAG = _('Tag')
30 30 LABEL_SEARCH = _('Search')
31 31
32 32 TAG_MAX_LENGTH = 20
33 33
34 34 REGEX_TAG = r'^[\w\d]+$'
35 35
36 36
37 37 class FormatPanel(forms.Textarea):
38 38 def render(self, name, value, attrs=None):
39 39 output = '<div id="mark-panel">'
40 40 for formatter in formatters:
41 41 output += '<span class="mark_btn"' + \
42 42 ' onClick="addMarkToMsg(\'' + formatter.format_left + \
43 43 '\', \'' + formatter.format_right + '\')">' + \
44 44 formatter.preview_left + formatter.name + \
45 45 formatter.preview_right + '</span>'
46 46
47 47 output += '</div>'
48 48 output += super(FormatPanel, self).render(name, value, attrs=None)
49 49
50 50 return output
51 51
52 52
53 53 class PlainErrorList(ErrorList):
54 54 def __unicode__(self):
55 55 return self.as_text()
56 56
57 57 def as_text(self):
58 58 return ''.join(['(!) %s ' % e for e in self])
59 59
60 60
61 61 class NeboardForm(forms.Form):
62 62
63 63 def as_div(self):
64 64 """
65 65 Returns this form rendered as HTML <as_div>s.
66 66 """
67 67
68 68 return self._html_output(
69 69 # TODO Do not show hidden rows in the list here
70 70 normal_row='<div class="form-row"><div class="form-label">'
71 71 '%(label)s'
72 72 '</div></div>'
73 73 '<div class="form-row"><div class="form-input">'
74 74 '%(field)s'
75 75 '</div></div>'
76 76 '<div class="form-row">'
77 77 '%(help_text)s'
78 78 '</div>',
79 79 error_row='<div class="form-row">'
80 80 '<div class="form-label"></div>'
81 81 '<div class="form-errors">%s</div>'
82 82 '</div>',
83 83 row_ender='</div>',
84 84 help_text_html='%s',
85 85 errors_on_separate_row=True)
86 86
87 87 def as_json_errors(self):
88 88 errors = []
89 89
90 90 for name, field in list(self.fields.items()):
91 91 if self[name].errors:
92 92 errors.append({
93 93 'field': name,
94 94 'errors': self[name].errors.as_text(),
95 95 })
96 96
97 97 return errors
98 98
99 99
100 100 class PostForm(NeboardForm):
101 101
102 102 title = forms.CharField(max_length=TITLE_MAX_LENGTH, required=False,
103 103 label=LABEL_TITLE)
104 104 text = forms.CharField(
105 105 widget=FormatPanel(attrs={ATTRIBUTE_PLACEHOLDER: TEXT_PLACEHOLDER}),
106 106 required=False, label=LABEL_TEXT)
107 107 image = forms.ImageField(required=False, label=_('Image'),
108 108 widget=forms.ClearableFileInput(
109 109 attrs={'accept': 'image/*'}))
110 110
111 111 # This field is for spam prevention only
112 112 email = forms.CharField(max_length=100, required=False, label=_('e-mail'),
113 113 widget=forms.TextInput(attrs={
114 114 'class': 'form-email'}))
115 115
116 116 session = None
117 117 need_to_ban = False
118 118
119 119 def clean_title(self):
120 120 title = self.cleaned_data['title']
121 121 if title:
122 122 if len(title) > TITLE_MAX_LENGTH:
123 123 raise forms.ValidationError(_('Title must have less than %s '
124 124 'characters') %
125 125 str(TITLE_MAX_LENGTH))
126 126 return title
127 127
128 128 def clean_text(self):
129 129 text = self.cleaned_data['text'].strip()
130 130 if text:
131 131 if len(text) > board_settings.MAX_TEXT_LENGTH:
132 132 raise forms.ValidationError(_('Text must have less than %s '
133 133 'characters') %
134 134 str(board_settings
135 135 .MAX_TEXT_LENGTH))
136 136 return text
137 137
138 138 def clean_image(self):
139 139 image = self.cleaned_data['image']
140 140 if image:
141 141 if image.size > board_settings.MAX_IMAGE_SIZE:
142 142 raise forms.ValidationError(
143 143 _('Image must be less than %s bytes')
144 144 % str(board_settings.MAX_IMAGE_SIZE))
145 145
146 146 md5 = hashlib.md5()
147 147 for chunk in image.chunks():
148 148 md5.update(chunk)
149 149 image_hash = md5.hexdigest()
150 150 if PostImage.objects.filter(hash=image_hash).exists():
151 151 raise forms.ValidationError(ERROR_IMAGE_DUPLICATE)
152 152
153 153 return image
154 154
155 155 def clean(self):
156 156 cleaned_data = super(PostForm, self).clean()
157 157
158 158 if not self.session:
159 159 raise forms.ValidationError('Humans have sessions')
160 160
161 161 if cleaned_data['email']:
162 162 self.need_to_ban = True
163 163 raise forms.ValidationError('A human cannot enter a hidden field')
164 164
165 165 if not self.errors:
166 166 self._clean_text_image()
167 167
168 168 if not self.errors and self.session:
169 169 self._validate_posting_speed()
170 170
171 171 return cleaned_data
172 172
173 173 def _clean_text_image(self):
174 174 text = self.cleaned_data.get('text')
175 175 image = self.cleaned_data.get('image')
176 176
177 177 if (not text) and (not image):
178 178 error_message = _('Either text or image must be entered.')
179 179 self._errors['text'] = self.error_class([error_message])
180 180
181 181 def _validate_posting_speed(self):
182 182 can_post = True
183 183
184 # TODO Remove this, it's only for test
185 if not 'user_id' in self.session:
186 return
187
188 184 posting_delay = settings.POSTING_DELAY
189 185
190 186 if board_settings.LIMIT_POSTING_SPEED and LAST_POST_TIME in \
191 187 self.session:
192 188 now = time.time()
193 189 last_post_time = self.session[LAST_POST_TIME]
194 190
195 191 current_delay = int(now - last_post_time)
196 192
197 193 if current_delay < posting_delay:
198 194 error_message = _('Wait %s seconds after last posting') % str(
199 195 posting_delay - current_delay)
200 196 self._errors['text'] = self.error_class([error_message])
201 197
202 198 can_post = False
203 199
204 200 if can_post:
205 201 self.session[LAST_POST_TIME] = time.time()
206 202
207 203
208 204 class ThreadForm(PostForm):
209 205
210 206 regex_tags = re.compile(r'^[\w\s\d]+$', re.UNICODE)
211 207
212 208 tags = forms.CharField(
213 209 widget=forms.TextInput(attrs={ATTRIBUTE_PLACEHOLDER: TAGS_PLACEHOLDER}),
214 210 max_length=100, label=_('Tags'), required=True)
215 211
216 212 def clean_tags(self):
217 213 tags = self.cleaned_data['tags'].strip()
218 214
219 215 if not tags or not self.regex_tags.match(tags):
220 216 raise forms.ValidationError(
221 217 _('Inappropriate characters in tags.'))
222 218
223 219 return tags
224 220
225 221 def clean(self):
226 222 cleaned_data = super(ThreadForm, self).clean()
227 223
228 224 return cleaned_data
229 225
230 226
231 227 class SettingsForm(NeboardForm):
232 228
233 229 theme = forms.ChoiceField(choices=settings.THEMES,
234 230 label=_('Theme'))
235 231
236 232
237 233 class AddTagForm(NeboardForm):
238 234
239 235 tag = forms.CharField(max_length=TAG_MAX_LENGTH, label=LABEL_TAG)
240 236 method = forms.CharField(widget=forms.HiddenInput(), initial='add_tag')
241 237
242 238 def clean_tag(self):
243 239 tag = self.cleaned_data['tag']
244 240
245 241 regex_tag = re.compile(REGEX_TAG, re.UNICODE)
246 242 if not regex_tag.match(tag):
247 243 raise forms.ValidationError(_('Inappropriate characters in tags.'))
248 244
249 245 return tag
250 246
251 247 def clean(self):
252 248 cleaned_data = super(AddTagForm, self).clean()
253 249
254 250 return cleaned_data
255 251
256 252
257 253 class SearchForm(NeboardForm):
258 254 query = forms.CharField(max_length=500, label=LABEL_SEARCH, required=False)
259 255
260 256
261 257 class LoginForm(NeboardForm):
262 258
263 259 password = forms.CharField()
264 260
265 261 session = None
266 262
267 263 def clean_password(self):
268 264 password = self.cleaned_data['password']
269 265 if board_settings.MASTER_PASSWORD != password:
270 266 raise forms.ValidationError(_('Invalid master password'))
271 267
272 268 return password
273 269
274 270 def _validate_login_speed(self):
275 271 can_post = True
276 272
277 273 if LAST_LOGIN_TIME in self.session:
278 274 now = time.time()
279 275 last_login_time = self.session[LAST_LOGIN_TIME]
280 276
281 277 current_delay = int(now - last_login_time)
282 278
283 279 if current_delay < board_settings.LOGIN_TIMEOUT:
284 280 error_message = _('Wait %s minutes after last login') % str(
285 281 (board_settings.LOGIN_TIMEOUT - current_delay) / 60)
286 282 self._errors['password'] = self.error_class([error_message])
287 283
288 284 can_post = False
289 285
290 286 if can_post:
291 287 self.session[LAST_LOGIN_TIME] = time.time()
292 288
293 289 def clean(self):
294 290 self._validate_login_speed()
295 291
296 292 cleaned_data = super(LoginForm, self).clean()
297 293
298 294 return cleaned_data
1 NO CONTENT: modified file, binary diff hidden
@@ -1,361 +1,365 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: 2014-07-20 20:11+0300\n"
10 "POT-Creation-Date: 2014-08-19 15:51+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:9
22 22 msgid "author"
23 23 msgstr "Π°Π²Ρ‚ΠΎΡ€"
24 24
25 25 #: authors.py:10
26 26 msgid "developer"
27 27 msgstr "Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ"
28 28
29 29 #: authors.py:11
30 30 msgid "javascript developer"
31 31 msgstr "Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ javascript"
32 32
33 33 #: authors.py:12
34 34 msgid "designer"
35 35 msgstr "Π΄ΠΈΠ·Π°ΠΉΠ½Π΅Ρ€"
36 36
37 #: forms.py:23
37 #: forms.py:22
38 38 msgid "Type message here. Use formatting panel for more advanced usage."
39 39 msgstr ""
40 40 "Π’Π²ΠΎΠ΄ΠΈΡ‚Π΅ сообщСниС сюда. Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅ панСль для Π±ΠΎΠ»Π΅Π΅ слоТного форматирования."
41 41
42 #: forms.py:24
42 #: forms.py:23
43 43 msgid "tag1 several_words_tag"
44 44 msgstr "Ρ‚Π΅Π³1 Ρ‚Π΅Π³_ΠΈΠ·_Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ…_слов"
45 45
46 #: forms.py:26
46 #: forms.py:25
47 47 msgid "Such image was already posted"
48 48 msgstr "Π’Π°ΠΊΠΎΠ΅ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ ΡƒΠΆΠ΅ Π±Ρ‹Π»ΠΎ Π·Π°Π³Ρ€ΡƒΠΆΠ΅Π½ΠΎ"
49 49
50 #: forms.py:28
50 #: forms.py:27
51 51 msgid "Title"
52 52 msgstr "Π—Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ"
53 53
54 #: forms.py:29
54 #: forms.py:28
55 55 msgid "Text"
56 56 msgstr "ВСкст"
57 57
58 #: forms.py:30
58 #: forms.py:29
59 59 msgid "Tag"
60 60 msgstr "Π’Π΅Π³"
61 61
62 #: forms.py:31 templates/boards/base.html:54 templates/search/search.html:9
62 #: forms.py:30 templates/boards/base.html:36 templates/search/search.html:9
63 63 #: templates/search/search.html.py:13
64 64 msgid "Search"
65 65 msgstr "Поиск"
66 66
67 #: forms.py:108
67 #: forms.py:107
68 68 msgid "Image"
69 69 msgstr "Π˜Π·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅"
70 70
71 #: forms.py:113
71 #: forms.py:112
72 72 msgid "e-mail"
73 73 msgstr ""
74 74
75 #: forms.py:124
75 #: forms.py:123
76 76 #, python-format
77 77 msgid "Title must have less than %s characters"
78 78 msgstr "Π—Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΠΈΠΌΠ΅Ρ‚ΡŒ мСньшС %s символов"
79 79
80 #: forms.py:133
80 #: forms.py:132
81 81 #, python-format
82 82 msgid "Text must have less than %s characters"
83 83 msgstr "ВСкст Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ ΠΊΠΎΡ€ΠΎΡ‡Π΅ %s символов"
84 84
85 #: forms.py:144
85 #: forms.py:143
86 86 #, python-format
87 87 msgid "Image must be less than %s bytes"
88 88 msgstr "Π˜Π·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±Ρ‹Ρ‚ΡŒ ΠΌΠ΅Π½Π΅Π΅ %s Π±Π°ΠΉΡ‚"
89 89
90 #: forms.py:179
90 #: forms.py:178
91 91 msgid "Either text or image must be entered."
92 92 msgstr "ВСкст ΠΈΠ»ΠΈ ΠΊΠ°Ρ€Ρ‚ΠΈΠ½ΠΊΠ° Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±Ρ‹Ρ‚ΡŒ Π²Π²Π΅Π΄Π΅Π½Ρ‹."
93 93
94 #: forms.py:199
94 #: forms.py:198
95 95 #, python-format
96 96 msgid "Wait %s seconds after last posting"
97 97 msgstr "ΠŸΠΎΠ΄ΠΎΠΆΠ΄ΠΈΡ‚Π΅ %s сСкунд послС послСднСго постинга"
98 98
99 #: forms.py:215 templates/boards/tags.html:7 templates/boards/rss/post.html:10
99 #: forms.py:214 templates/boards/tags.html:7 templates/boards/rss/post.html:10
100 100 msgid "Tags"
101 101 msgstr "Π’Π΅Π³ΠΈ"
102 102
103 #: forms.py:222 forms.py:290
103 #: forms.py:221 forms.py:247
104 104 msgid "Inappropriate characters in tags."
105 105 msgstr "НСдопустимыС символы Π² Ρ‚Π΅Π³Π°Ρ…."
106 106
107 #: forms.py:250 forms.py:271
108 msgid "Captcha validation failed"
109 msgstr "ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° ΠΊΠ°ΠΏΡ‡ΠΈ ΠΏΡ€ΠΎΠ²Π°Π»Π΅Π½Π°"
110
111 #: forms.py:277
107 #: forms.py:234
112 108 msgid "Theme"
113 109 msgstr "Π’Π΅ΠΌΠ°"
114 110
115 #: forms.py:313
111 #: forms.py:270
116 112 msgid "Invalid master password"
117 113 msgstr "НСвСрный мастСр-ΠΏΠ°Ρ€ΠΎΠ»ΡŒ"
118 114
119 #: forms.py:327
115 #: forms.py:284
120 116 #, python-format
121 117 msgid "Wait %s minutes after last login"
122 118 msgstr "ΠŸΠΎΠ΄ΠΎΠΆΠ΄ΠΈΡ‚Π΅ %s ΠΌΠΈΠ½ΡƒΡ‚ послС послСднСго Π²Ρ…ΠΎΠ΄Π°"
123 119
124 120 #: templates/boards/404.html:6
125 121 msgid "Not found"
126 122 msgstr "НС найдСно"
127 123
128 124 #: templates/boards/404.html:12
129 125 msgid "This page does not exist"
130 126 msgstr "Π­Ρ‚ΠΎΠΉ страницы Π½Π΅ сущСствуСт"
131 127
132 128 #: templates/boards/authors.html:6 templates/boards/authors.html.py:12
133 129 msgid "Authors"
134 130 msgstr "Авторы"
135 131
136 132 #: templates/boards/authors.html:26
137 133 msgid "Distributed under the"
138 134 msgstr "РаспространяСтся ΠΏΠΎΠ΄"
139 135
140 136 #: templates/boards/authors.html:28
141 137 msgid "license"
142 138 msgstr "Π»ΠΈΡ†Π΅Π½Π·ΠΈΠ΅ΠΉ"
143 139
144 140 #: templates/boards/authors.html:30
145 141 msgid "Repository"
146 142 msgstr "Π Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ"
147 143
148 144 #: templates/boards/base.html:12
149 145 msgid "Feed"
150 146 msgstr "Π›Π΅Π½Ρ‚Π°"
151 147
152 148 #: templates/boards/base.html:29
153 149 msgid "All threads"
154 150 msgstr "ВсС Ρ‚Π΅ΠΌΡ‹"
155 151
156 152 #: templates/boards/base.html:34
157 153 msgid "Tag management"
158 154 msgstr "Π£ΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ Ρ‚Π΅Π³Π°ΠΌΠΈ"
159 155
160 #: templates/boards/base.html:36 templates/boards/settings.html:7
156 #: templates/boards/base.html:37 templates/boards/settings.html:7
161 157 msgid "Settings"
162 158 msgstr "Настройки"
163 159
164 160 #: templates/boards/base.html:50
165 msgid "Logout"
166 msgstr "Π’Ρ‹Ρ…ΠΎΠ΄"
161 msgid "Admin"
162 msgstr ""
167 163
168 #: templates/boards/base.html:52 templates/boards/login.html:6
169 #: templates/boards/login.html.py:16
170 msgid "Login"
171 msgstr "Π’Ρ…ΠΎΠ΄"
172
173 #: templates/boards/base.html:56
164 #: templates/boards/base.html:52
174 165 #, python-format
175 166 msgid "Speed: %(ppd)s posts per day"
176 167 msgstr "Π‘ΠΊΠΎΡ€ΠΎΡΡ‚ΡŒ: %(ppd)s сообщСний Π² дСнь"
177 168
178 #: templates/boards/base.html:58
169 #: templates/boards/base.html:54
179 170 msgid "Up"
180 171 msgstr "Π’Π²Π΅Ρ€Ρ…"
181 172
173 #: templates/boards/login.html:6 templates/boards/login.html.py:16
174 msgid "Login"
175 msgstr "Π’Ρ…ΠΎΠ΄"
176
182 177 #: templates/boards/login.html:19
183 178 msgid "Insert your user id above"
184 179 msgstr "Π’ΡΡ‚Π°Π²ΡŒΡ‚Π΅ свой ID ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ Π²Ρ‹ΡˆΠ΅"
185 180
186 181 #: templates/boards/post.html:21 templates/boards/staticpages/help.html:17
187 182 msgid "Quote"
188 183 msgstr "Π¦ΠΈΡ‚Π°Ρ‚Π°"
189 184
190 185 #: templates/boards/post.html:31
191 186 msgid "Open"
192 187 msgstr "ΠžΡ‚ΠΊΡ€Ρ‹Ρ‚ΡŒ"
193 188
194 189 #: templates/boards/post.html:33
195 190 msgid "Reply"
196 191 msgstr "ΠžΡ‚Π²Π΅Ρ‚"
197 192
198 193 #: templates/boards/post.html:40
199 194 msgid "Edit"
200 195 msgstr "Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ"
201 196
202 197 #: templates/boards/post.html:42
203 198 msgid "Delete"
204 199 msgstr "Π£Π΄Π°Π»ΠΈΡ‚ΡŒ"
205 200
206 201 #: templates/boards/post.html:45
207 202 msgid "Ban IP"
208 203 msgstr "Π—Π°Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ IP"
209 204
210 205 #: templates/boards/post.html:76
211 206 msgid "Replies"
212 207 msgstr "ΠžΡ‚Π²Π΅Ρ‚Ρ‹"
213 208
214 209 #: templates/boards/post.html:86 templates/boards/thread.html:88
215 #: templates/boards/thread_gallery.html:61
210 #: templates/boards/thread_gallery.html:59
216 211 msgid "messages"
217 212 msgstr "сообщСний"
218 213
219 214 #: templates/boards/post.html:87 templates/boards/thread.html:89
220 #: templates/boards/thread_gallery.html:62
215 #: templates/boards/thread_gallery.html:60
221 216 msgid "images"
222 217 msgstr "ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ"
223 218
224 219 #: templates/boards/post_admin.html:19
225 220 msgid "Tags:"
226 221 msgstr "Π’Π΅Π³ΠΈ:"
227 222
228 223 #: templates/boards/post_admin.html:30
229 224 msgid "Add tag"
230 225 msgstr "Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Ρ‚Π΅Π³"
231 226
232 227 #: templates/boards/posting_general.html:56
233 228 msgid "Show tag"
234 229 msgstr "ΠŸΠΎΠΊΠ°Π·Ρ‹Π²Π°Ρ‚ΡŒ Ρ‚Π΅Π³"
235 230
236 231 #: templates/boards/posting_general.html:60
237 232 msgid "Hide tag"
238 233 msgstr "Π‘ΠΊΡ€Ρ‹Π²Π°Ρ‚ΡŒ Ρ‚Π΅Π³"
239 234
240 235 #: templates/boards/posting_general.html:79 templates/search/search.html:22
241 236 msgid "Previous page"
242 237 msgstr "ΠŸΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π°Ρ страница"
243 238
244 239 #: templates/boards/posting_general.html:94
245 240 #, python-format
246 241 msgid "Skipped %(count)s replies. Open thread to see all replies."
247 242 msgstr "ΠŸΡ€ΠΎΠΏΡƒΡ‰Π΅Π½ΠΎ %(count)s ΠΎΡ‚Π²Π΅Ρ‚ΠΎΠ². ΠžΡ‚ΠΊΡ€ΠΎΠΉΡ‚Π΅ Ρ‚Ρ€Π΅Π΄, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡƒΠ²ΠΈΠ΄Π΅Ρ‚ΡŒ всС ΠΎΡ‚Π²Π΅Ρ‚Ρ‹."
248 243
249 244 #: templates/boards/posting_general.html:121 templates/search/search.html:33
250 245 msgid "Next page"
251 246 msgstr "Π‘Π»Π΅Π΄ΡƒΡŽΡ‰Π°Ρ страница"
252 247
253 248 #: templates/boards/posting_general.html:126
254 249 msgid "No threads exist. Create the first one!"
255 250 msgstr "НСт Ρ‚Π΅ΠΌ. Π‘ΠΎΠ·Π΄Π°ΠΉΡ‚Π΅ ΠΏΠ΅Ρ€Π²ΡƒΡŽ!"
256 251
257 252 #: templates/boards/posting_general.html:132
258 253 msgid "Create new thread"
259 254 msgstr "Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ Π½ΠΎΠ²ΡƒΡŽ Ρ‚Π΅ΠΌΡƒ"
260 255
261 #: templates/boards/posting_general.html:137 templates/boards/thread.html:58
256 #: templates/boards/posting_general.html:137 templates/boards/preview.html:16
257 #: templates/boards/thread.html:58
262 258 msgid "Post"
263 259 msgstr "ΠžΡ‚ΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ"
264 260
265 261 #: templates/boards/posting_general.html:142
266 262 msgid "Tags must be delimited by spaces. Text or image is required."
267 263 msgstr ""
268 264 "Π’Π΅Π³ΠΈ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±Ρ‹Ρ‚ΡŒ Ρ€Π°Π·Π΄Π΅Π»Π΅Π½Ρ‹ ΠΏΡ€ΠΎΠ±Π΅Π»Π°ΠΌΠΈ. ВСкст ΠΈΠ»ΠΈ ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹."
269 265
270 266 #: templates/boards/posting_general.html:145 templates/boards/thread.html:66
271 267 msgid "Text syntax"
272 268 msgstr "Бинтаксис тСкста"
273 269
274 270 #: templates/boards/posting_general.html:157
275 271 msgid "Pages:"
276 272 msgstr "Π‘Ρ‚Ρ€Π°Π½ΠΈΡ†Ρ‹: "
277 273
274 #: templates/boards/preview.html:6 templates/boards/staticpages/help.html:19
275 msgid "Preview"
276 msgstr "ΠŸΡ€Π΅Π΄ΠΏΡ€ΠΎΡΠΌΠΎΡ‚Ρ€"
277
278 278 #: templates/boards/settings.html:15
279 279 msgid "You are moderator."
280 280 msgstr "Π’Ρ‹ ΠΌΠΎΠ΄Π΅Ρ€Π°Ρ‚ΠΎΡ€."
281 281
282 282 #: templates/boards/settings.html:19
283 283 msgid "Hidden tags:"
284 284 msgstr "Π‘ΠΊΡ€Ρ‹Ρ‚Ρ‹Π΅ Ρ‚Π΅Π³ΠΈ:"
285 285
286 286 #: templates/boards/settings.html:26
287 287 msgid "No hidden tags."
288 288 msgstr "НСт скрытых Ρ‚Π΅Π³ΠΎΠ²."
289 289
290 290 #: templates/boards/settings.html:35
291 291 msgid "Save"
292 292 msgstr "Π‘ΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ"
293 293
294 294 #: templates/boards/tags.html:22
295 295 msgid "No tags found."
296 296 msgstr "Π’Π΅Π³ΠΈ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½Ρ‹."
297 297
298 #: templates/boards/thread.html:20 templates/boards/thread_gallery.html:21
298 #: templates/boards/thread.html:20 templates/boards/thread_gallery.html:19
299 299 msgid "Normal mode"
300 300 msgstr "ΠΠΎΡ€ΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΉ Ρ€Π΅ΠΆΠΈΠΌ"
301 301
302 #: templates/boards/thread.html:21 templates/boards/thread_gallery.html:22
302 #: templates/boards/thread.html:21 templates/boards/thread_gallery.html:20
303 303 msgid "Gallery mode"
304 304 msgstr "Π Π΅ΠΆΠΈΠΌ Π³Π°Π»Π΅Ρ€Π΅ΠΈ"
305 305
306 306 #: templates/boards/thread.html:29
307 307 msgid "posts to bumplimit"
308 308 msgstr "сообщСний Π΄ΠΎ Π±Π°ΠΌΠΏΠ»ΠΈΠΌΠΈΡ‚Π°"
309 309
310 310 #: templates/boards/thread.html:50
311 311 msgid "Reply to thread"
312 312 msgstr "ΠžΡ‚Π²Π΅Ρ‚ΠΈΡ‚ΡŒ Π² Ρ‚Π΅ΠΌΡƒ"
313 313
314 314 #: templates/boards/thread.html:63
315 315 msgid "Switch mode"
316 316 msgstr "ΠŸΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ Ρ€Π΅ΠΆΠΈΠΌ"
317 317
318 #: templates/boards/thread.html:90 templates/boards/thread_gallery.html:63
318 #: templates/boards/thread.html:90 templates/boards/thread_gallery.html:61
319 319 msgid "Last update: "
320 320 msgstr "ПослСднСС обновлСниС: "
321 321
322 322 #: templates/boards/rss/post.html:5
323 323 msgid "Post image"
324 324 msgstr "Π˜Π·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ сообщСния"
325 325
326 326 #: templates/boards/staticpages/banned.html:6
327 327 msgid "Banned"
328 328 msgstr "Π—Π°Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²Π°Π½"
329 329
330 330 #: templates/boards/staticpages/banned.html:11
331 331 msgid "Your IP address has been banned. Contact the administrator"
332 332 msgstr "Π’Π°Ρˆ IP адрСс Π±Ρ‹Π» Π·Π°Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²Π°Π½. Π‘Π²ΡΠΆΠΈΡ‚Π΅ΡΡŒ с администратором"
333 333
334 334 #: templates/boards/staticpages/help.html:6
335 335 #: templates/boards/staticpages/help.html:10
336 336 msgid "Syntax"
337 337 msgstr "Бинтаксис"
338 338
339 339 #: templates/boards/staticpages/help.html:11
340 340 msgid "Italic text"
341 341 msgstr "ΠšΡƒΡ€ΡΠΈΠ²Π½Ρ‹ΠΉ тСкст"
342 342
343 343 #: templates/boards/staticpages/help.html:12
344 344 msgid "Bold text"
345 345 msgstr "ΠŸΠΎΠ»ΡƒΠΆΠΈΡ€Π½Ρ‹ΠΉ тСкст"
346 346
347 347 #: templates/boards/staticpages/help.html:13
348 348 msgid "Spoiler"
349 349 msgstr "Π‘ΠΏΠΎΠΉΠ»Π΅Ρ€"
350 350
351 351 #: templates/boards/staticpages/help.html:14
352 352 msgid "Link to a post"
353 353 msgstr "Бсылка Π½Π° сообщСниС"
354 354
355 355 #: templates/boards/staticpages/help.html:15
356 356 msgid "Strikethrough text"
357 357 msgstr "Π—Π°Ρ‡Π΅Ρ€ΠΊΠ½ΡƒΡ‚Ρ‹ΠΉ тСкст"
358 358
359 359 #: templates/boards/staticpages/help.html:16
360 360 msgid "Comment"
361 361 msgstr "ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ"
362
363 #: templates/boards/staticpages/help.html:19
364 msgid "You can try pasting the text and previewing the result here:"
365 msgstr "Π’Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΏΠΎΠΏΡ€ΠΎΠ±ΠΎΠ²Π°Ρ‚ΡŒ Π²ΡΡ‚Π°Π²ΠΈΡ‚ΡŒ тСкст ΠΈ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ здСсь:"
@@ -1,180 +1,182 b''
1 1 # coding=utf-8
2 2
3 3 import re
4 4 import bbcode
5 5
6 6 import boards
7 7
8 8
9 9 __author__ = 'neko259'
10 10
11 11
12 12 REFLINK_PATTERN = re.compile(r'^\d+$')
13 13 MULTI_NEWLINES_PATTERN = re.compile(r'(\r?\n){2,}')
14 14 ONE_NEWLINE = '\n'
15 15
16 16
17 17 class TextFormatter():
18 18 """
19 19 An interface for formatter that can be used in the text format panel
20 20 """
21 21
22 22 def __init__(self):
23 23 pass
24 24
25 25 name = ''
26 26
27 27 # Left and right tags for the button preview
28 28 preview_left = ''
29 29 preview_right = ''
30 30
31 31 # Left and right characters for the textarea input
32 32 format_left = ''
33 33 format_right = ''
34 34
35 35
36 36 class AutolinkPattern():
37 37 def handleMatch(self, m):
38 38 link_element = etree.Element('a')
39 39 href = m.group(2)
40 40 link_element.set('href', href)
41 41 link_element.text = href
42 42
43 43 return link_element
44 44
45 45
46 46 class QuotePattern(TextFormatter):
47 47 name = 'q'
48 48 preview_left = '<span class="multiquote">'
49 49 preview_right = '</span>'
50 50
51 51 format_left = '[quote]'
52 52 format_right = '[/quote]'
53 53
54 54
55 55 class SpoilerPattern(TextFormatter):
56 56 name = 'spoiler'
57 57 preview_left = '<span class="spoiler">'
58 58 preview_right = '</span>'
59 59
60 60 format_left = '[spoiler]'
61 61 format_right = '[/spoiler]'
62 62
63 63 def handleMatch(self, m):
64 64 quote_element = etree.Element('span')
65 65 quote_element.set('class', 'spoiler')
66 66 quote_element.text = m.group(2)
67 67
68 68 return quote_element
69 69
70 70
71 71 class CommentPattern(TextFormatter):
72 72 name = ''
73 73 preview_left = '<span class="comment">// '
74 74 preview_right = '</span>'
75 75
76 76 format_left = '[comment]'
77 77 format_right = '[/comment]'
78 78
79 79
80 80 # TODO Use <s> tag here
81 81 class StrikeThroughPattern(TextFormatter):
82 82 name = 's'
83 83 preview_left = '<span class="strikethrough">'
84 84 preview_right = '</span>'
85 85
86 86 format_left = '[s]'
87 87 format_right = '[/s]'
88 88
89 89
90 90 class ItalicPattern(TextFormatter):
91 91 name = 'i'
92 92 preview_left = '<i>'
93 93 preview_right = '</i>'
94 94
95 95 format_left = '[i]'
96 96 format_right = '[/i]'
97 97
98 98
99 99 class BoldPattern(TextFormatter):
100 100 name = 'b'
101 101 preview_left = '<b>'
102 102 preview_right = '</b>'
103 103
104 104 format_left = '[b]'
105 105 format_right = '[/b]'
106 106
107 107
108 108 class CodePattern(TextFormatter):
109 109 name = 'code'
110 110 preview_left = '<code>'
111 111 preview_right = '</code>'
112 112
113 113 format_left = '[code]'
114 114 format_right = '[/code]'
115 115
116 116
117 117 def render_reflink(tag_name, value, options, parent, context):
118 118 if not REFLINK_PATTERN.match(value):
119 return u'>>%s' % value
119 return '>>%s' % value
120 120
121 121 post_id = int(value)
122 122
123 123 posts = boards.models.Post.objects.filter(id=post_id)
124 124 if posts.exists():
125 125 post = posts[0]
126 126
127 return u'<a href="%s">&gt;&gt;%s</a>' % (post.get_url(), post_id)
127 return '<a href="%s">&gt;&gt;%s</a>' % (post.get_url(), post_id)
128 128 else:
129 return u'>>%s' % value
129 return '>>%s' % value
130 130
131 131
132 132 def render_quote(tag_name, value, options, parent, context):
133 source = u''
133 source = ''
134 134 if 'source' in options:
135 135 source = options['source']
136 136
137 result = u''
137 result = ''
138 138 if source:
139 result = u'<div class="multiquote"><div class="quote-header">%s</div><div class="quote-text">%s</div></div>' % (source, value)
139 result = '<div class="multiquote"><div class="quote-header">%s</div><div class="quote-text">%s</div></div>' % (source, value)
140 140 else:
141 result = u'<div class="multiquote"><div class="quote-text">%s</div></div>' % value
141 result = '<div class="multiquote"><div class="quote-text">%s</div></div>' % value
142 142
143 143 return result
144 144
145 145
146 146 def preparse_text(text):
147 147 """
148 148 Performs manual parsing before the bbcode parser is used.
149 149 """
150 150
151 151 return MULTI_NEWLINES_PATTERN.sub(ONE_NEWLINE, text)
152 152
153 153
154 154 def bbcode_extended(markup):
155 parser = bbcode.Parser()
155 # The newline hack is added because br's margin does not work in all
156 # browsers except firefox, when the div's does.
157 parser = bbcode.Parser(newline='<div class="br"></div>')
156 158 parser.add_formatter('post', render_reflink, strip=True)
157 159 parser.add_formatter('quote', render_quote, strip=True)
158 160 parser.add_simple_formatter('comment',
159 161 u'<span class="comment">//%(value)s</span>')
160 162 parser.add_simple_formatter('spoiler',
161 163 u'<span class="spoiler">%(value)s</span>')
162 164 # TODO Use <s> here
163 165 parser.add_simple_formatter('s',
164 166 u'<span class="strikethrough">%(value)s</span>')
165 167 # TODO Why not use built-in tag?
166 168 parser.add_simple_formatter('code',
167 169 u'<pre><code>%(value)s</pre></code>', render_embedded=False)
168 170
169 171 text = preparse_text(markup)
170 172 return parser.format(text)
171 173
172 174 formatters = [
173 175 QuotePattern,
174 176 SpoilerPattern,
175 177 ItalicPattern,
176 178 BoldPattern,
177 179 CommentPattern,
178 180 StrikeThroughPattern,
179 181 CodePattern,
180 182 ]
@@ -1,23 +1,20 b''
1 1 VERSION = '2.1 Aya'
2 2 SITE_NAME = 'n3b0a2d'
3 3
4 4 CACHE_TIMEOUT = 600 # Timeout for caching, if cache is used
5 5 LOGIN_TIMEOUT = 3600 # Timeout between login tries
6 6 MAX_TEXT_LENGTH = 30000 # Max post length in characters
7 7 MAX_IMAGE_SIZE = 8 * 1024 * 1024 # Max image size
8 8
9 9 # Thread bumplimit
10 10 MAX_POSTS_PER_THREAD = 10
11 11 # Old posts will be archived or deleted if this value is reached
12 12 MAX_THREAD_COUNT = 5
13 13 THREADS_PER_PAGE = 3
14 14 DEFAULT_THEME = 'md'
15 15 LAST_REPLIES_COUNT = 3
16 16
17 17 # Enable archiving threads instead of deletion when the thread limit is reached
18 18 ARCHIVE_THREADS = True
19 19 # Limit posting speed
20 20 LIMIT_POSTING_SPEED = False
21
22 # This password is used to add admin permissions to the user
23 MASTER_PASSWORD = u'password'
@@ -1,472 +1,473 b''
1 1 html {
2 2 background: #555;
3 3 color: #ffffff;
4 4 }
5 5
6 6 body {
7 7 margin: 0;
8 8 }
9 9
10 10 #admin_panel {
11 11 background: #FF0000;
12 12 color: #00FF00
13 13 }
14 14
15 15 .input_field_error {
16 16 color: #FF0000;
17 17 }
18 18
19 19 .title {
20 20 font-weight: bold;
21 21 color: #ffcc00;
22 22 }
23 23
24 24 .link, a {
25 25 color: #afdcec;
26 26 }
27 27
28 28 .block {
29 29 display: inline-block;
30 30 vertical-align: top;
31 31 }
32 32
33 33 .tag {
34 34 color: #FFD37D;
35 35 }
36 36
37 37 .post_id {
38 38 color: #fff380;
39 39 }
40 40
41 41 .post, .dead_post, .archive_post, #posts-table {
42 42 background: #333;
43 43 padding: 10px;
44 44 clear: left;
45 45 word-wrap: break-word;
46 46 border-top: 1px solid #777;
47 47 border-bottom: 1px solid #777;
48 48 }
49 49
50 50 .post + .post {
51 51 border-top: none;
52 52 }
53 53
54 54 .dead_post + .dead_post {
55 55 border-top: none;
56 56 }
57 57
58 58 .archive_post + .archive_post {
59 59 border-top: none;
60 60 }
61 61
62 62 .metadata {
63 63 padding-top: 5px;
64 64 margin-top: 10px;
65 65 border-top: solid 1px #666;
66 66 color: #ddd;
67 67 }
68 68
69 69 .navigation_panel, .tag_info {
70 70 background: #444;
71 71 margin-bottom: 5px;
72 72 margin-top: 5px;
73 73 padding: 10px;
74 74 border-bottom: solid 1px #888;
75 75 border-top: solid 1px #888;
76 76 color: #eee;
77 77 }
78 78
79 79 .navigation_panel .link {
80 80 border-right: 1px solid #fff;
81 81 font-weight: bold;
82 82 margin-right: 1ex;
83 83 padding-right: 1ex;
84 84 }
85 85 .navigation_panel .link:last-child {
86 86 border-left: 1px solid #fff;
87 87 border-right: none;
88 88 float: right;
89 89 margin-left: 1ex;
90 90 margin-right: 0;
91 91 padding-left: 1ex;
92 92 padding-right: 0;
93 93 }
94 94
95 95 .navigation_panel::after, .post::after {
96 96 clear: both;
97 97 content: ".";
98 98 display: block;
99 99 height: 0;
100 100 line-height: 0;
101 101 visibility: hidden;
102 102 }
103 103
104 p {
104 p, .br {
105 105 margin-top: .5em;
106 106 margin-bottom: .5em;
107 107 }
108 108
109 br {
110 margin-bottom: .5em;
111 }
112
113 109 .post-form-w {
114 110 background: #333344;
115 111 border-top: solid 1px #888;
116 112 border-bottom: solid 1px #888;
117 113 color: #fff;
118 114 padding: 10px;
119 115 margin-bottom: 5px;
120 116 margin-top: 5px;
121 117 }
122 118
123 119 .form-row {
124 120 width: 100%;
125 121 }
126 122
127 123 .form-label {
128 124 padding: .25em 1ex .25em 0;
129 125 vertical-align: top;
130 126 }
131 127
132 128 .form-input {
133 129 padding: .25em 0;
134 130 }
135 131
136 132 .form-errors {
137 133 font-weight: bolder;
138 134 vertical-align: middle;
139 135 }
140 136
141 137 .post-form input:not([name="image"]), .post-form textarea {
142 138 background: #333;
143 139 color: #fff;
144 140 border: solid 1px;
145 141 padding: 0;
146 142 font: medium sans-serif;
147 143 width: 100%;
148 144 }
149 145
150 146 .form-submit {
151 147 display: table;
152 148 margin-bottom: 1ex;
153 149 margin-top: 1ex;
154 150 }
155 151
156 152 .form-title {
157 153 font-weight: bold;
158 154 font-size: 2ex;
159 155 margin-bottom: 0.5ex;
160 156 }
161 157
162 158 .post-form input[type="submit"], input[type="submit"] {
163 159 background: #222;
164 160 border: solid 2px #fff;
165 161 color: #fff;
166 162 padding: 0.5ex;
167 163 }
168 164
169 165 input[type="submit"]:hover {
170 166 background: #060;
171 167 }
172 168
173 169 blockquote {
174 170 border-left: solid 2px;
175 171 padding-left: 5px;
176 172 color: #B1FB17;
177 173 margin: 0;
178 174 }
179 175
180 176 .post > .image {
181 177 float: left;
182 178 margin: 0 1ex .5ex 0;
183 179 min-width: 1px;
184 180 text-align: center;
185 181 display: table-row;
186 182 }
187 183
188 184 .post > .metadata {
189 185 clear: left;
190 186 }
191 187
192 188 .get {
193 189 font-weight: bold;
194 190 color: #d55;
195 191 }
196 192
197 193 * {
198 194 text-decoration: none;
199 195 }
200 196
201 197 .dead_post {
202 198 background-color: #442222;
203 199 }
204 200
205 201 .archive_post {
206 202 background-color: #000;
207 203 }
208 204
209 205 .mark_btn {
210 206 border: 1px solid;
211 207 min-width: 2ex;
212 208 padding: 2px 2ex;
213 209 }
214 210
215 211 .mark_btn:hover {
216 212 background: #555;
217 213 }
218 214
219 215 .quote {
220 216 color: #92cf38;
221 217 font-style: italic;
222 218 }
223 219
224 220 .multiquote {
225 221 padding: 3px;
226 222 display: inline-block;
227 223 background: #222;
228 224 border-style: solid;
229 225 border-width: 1px 1px 1px 4px;
230 226 font-size: 0.9em;
231 227 }
232 228
233 229 .spoiler {
234 230 background: white;
235 231 color: white;
236 232 }
237 233
238 234 .spoiler:hover {
239 235 color: black;
240 236 }
241 237
242 238 .comment {
243 239 color: #eb2;
244 240 }
245 241
246 242 a:hover {
247 243 text-decoration: underline;
248 244 }
249 245
250 246 .last-replies {
251 247 margin-left: 3ex;
252 248 margin-right: 3ex;
253 249 border-left: solid 1px #777;
254 250 border-right: solid 1px #777;
255 251 }
256 252
253 .last-replies > .post:first-child {
254 border-top: none;
255 }
256
257 257 .thread {
258 258 margin-bottom: 3ex;
259 259 margin-top: 1ex;
260 260 }
261 261
262 262 .post:target {
263 263 border: solid 2px white;
264 264 }
265 265
266 266 pre{
267 267 white-space:pre-wrap
268 268 }
269 269
270 270 li {
271 271 list-style-position: inside;
272 272 }
273 273
274 274 .fancybox-skin {
275 275 position: relative;
276 276 background-color: #fff;
277 277 color: #ddd;
278 278 text-shadow: none;
279 279 }
280 280
281 281 .fancybox-image {
282 282 border: 1px solid black;
283 283 }
284 284
285 285 .image-mode-tab {
286 286 background: #444;
287 287 color: #eee;
288 288 margin-top: 5px;
289 289 padding: 5px;
290 290 border-top: 1px solid #888;
291 291 border-bottom: 1px solid #888;
292 292 }
293 293
294 294 .image-mode-tab > label {
295 295 margin: 0 1ex;
296 296 }
297 297
298 298 .image-mode-tab > label > input {
299 299 margin-right: .5ex;
300 300 }
301 301
302 302 #posts-table {
303 303 margin-top: 5px;
304 304 margin-bottom: 5px;
305 305 }
306 306
307 307 .tag_info > h2 {
308 308 margin: 0;
309 309 }
310 310
311 311 .post-info {
312 312 color: #ddd;
313 313 margin-bottom: 1ex;
314 314 }
315 315
316 316 .moderator_info {
317 317 color: #e99d41;
318 318 float: right;
319 319 font-weight: bold;
320 320 }
321 321
322 322 .refmap {
323 323 font-size: 0.9em;
324 324 color: #ccc;
325 325 margin-top: 1em;
326 326 }
327 327
328 328 .fav {
329 329 color: yellow;
330 330 }
331 331
332 332 .not_fav {
333 333 color: #ccc;
334 334 }
335 335
336 336 .role {
337 337 text-decoration: underline;
338 338 }
339 339
340 340 .form-email {
341 341 display: none;
342 342 }
343 343
344 344 .footer {
345 345 margin: 5px;
346 346 }
347 347
348 348 .bar-value {
349 349 background: rgba(50, 55, 164, 0.45);
350 350 font-size: 0.9em;
351 351 height: 1.5em;
352 352 }
353 353
354 354 .bar-bg {
355 355 position: relative;
356 356 border-top: solid 1px #888;
357 357 border-bottom: solid 1px #888;
358 358 margin-top: 5px;
359 359 overflow: hidden;
360 360 }
361 361
362 362 .bar-text {
363 363 padding: 2px;
364 364 position: absolute;
365 365 left: 0;
366 366 top: 0;
367 367 }
368 368
369 369 .page_link {
370 370 background: #444;
371 371 border-top: solid 1px #888;
372 372 border-bottom: solid 1px #888;
373 373 padding: 5px;
374 374 color: #eee;
375 375 font-size: 2ex;
376 376 }
377 377
378 378 .skipped_replies {
379 379 padding: 5px;
380 380 margin-left: 3ex;
381 381 margin-right: 3ex;
382 382 border-left: solid 1px #888;
383 383 border-right: solid 1px #888;
384 border-bottom: solid 1px #888;
384 385 background: #000;
385 386 }
386 387
387 388 .current_page {
388 389 border: solid 1px #afdcec;
389 390 padding: 2px;
390 391 }
391 392
392 393 .current_mode {
393 394 font-weight: bold;
394 395 }
395 396
396 397 .gallery_image {
397 398 border: solid 1px;
398 399 padding: 0.5ex;
399 400 margin: 0.5ex;
400 401 text-align: center;
401 402 }
402 403
403 404 code {
404 405 border: dashed 1px #ccc;
405 406 background: #111;
406 407 padding: 2px;
407 408 font-size: 1.2em;
408 409 display: inline-block;
409 410 }
410 411
411 412 pre {
412 413 overflow: auto;
413 414 }
414 415
415 416 .img-full {
416 417 background: #222;
417 418 border: solid 1px white;
418 419 }
419 420
420 421 .tag_item {
421 422 display: inline-block;
422 423 border: 1px dashed #666;
423 424 margin: 0.2ex;
424 425 padding: 0.1ex;
425 426 }
426 427
427 428 #id_models li {
428 429 list-style: none;
429 430 }
430 431
431 432 #id_q {
432 433 margin-left: 1ex;
433 434 }
434 435
435 436 ul {
436 437 padding-left: 0px;
437 438 }
438 439
439 440 .quote-header {
440 441 border-bottom: 2px solid #ddd;
441 442 margin-bottom: 1ex;
442 443 padding-bottom: .5ex;
443 444 color: #ddd;
444 445 font-size: 1.2em;
445 446 }
446 447
447 448 .global-id {
448 449 font-weight: bolder;
449 450 opacity: .5;
450 451 }
451 452
452 453 /* Post */
453 454 .post > .message, .post > .image {
454 455 padding-left: 1em;
455 456 }
456 457
457 458 /* Reflink preview */
458 459 .post_preview {
459 460 border-left: 1px solid #777;
460 461 border-right: 1px solid #777;
461 462 }
462 463
463 464 /* Code highlighter */
464 465 .hljs {
465 466 color: #fff;
466 467 background: #000;
467 468 display: inline-block;
468 469 }
469 470
470 471 .hljs, .hljs-subst, .hljs-tag .hljs-title, .lisp .hljs-title, .clojure .hljs-built_in, .nginx .hljs-title {
471 472 color: #fff;
472 473 }
@@ -1,354 +1,364 b''
1 1 html {
2 2 background: rgb(238, 238, 238);
3 3 color: rgb(51, 51, 51);
4 4 }
5 5
6 6 #admin_panel {
7 7 background: #FF0000;
8 8 color: #00FF00
9 9 }
10 10
11 11 .input_field {
12 12
13 13 }
14 14
15 15 .input_field_name {
16 16
17 17 }
18 18
19 19 .input_field_error {
20 20 color: #FF0000;
21 21 }
22 22
23 23
24 24 .title {
25 25 font-weight: bold;
26 26 color: #333;
27 27 font-size: 2ex;
28 28 }
29 29
30 30 .link, a {
31 31 color: rgb(255, 102, 0);
32 32 }
33 33
34 34 .block {
35 35 display: inline-block;
36 36 vertical-align: top;
37 37 }
38 38
39 39 .tag {
40 40 color: #222;
41 41 }
42 42
43 43 .post_id:hover {
44 44 color: #11f;
45 45 }
46 46
47 47 .post_id {
48 48 color: #444;
49 49 }
50 50
51 51 .post, .dead_post, #posts-table {
52 52 margin: 5px;
53 53 padding: 10px;
54 54 background: rgb(221, 221, 221);
55 55 border: 1px solid rgb(204, 204, 204);
56 56 border-radius: 5px 5px 5px 5px;
57 57 clear: left;
58 58 word-wrap: break-word;
59 59 display: table;
60 60 }
61 61
62 62 .metadata {
63 63 padding: 5px;
64 64 margin-top: 10px;
65 65 border: solid 1px #666;
66 66 font-size: 0.9em;
67 67 display: table;
68 68 }
69 69
70 70 .navigation_panel, .tag_info, .page_link {
71 71 margin: 5px;
72 72 padding: 10px;
73 73 border: 1px solid rgb(204, 204, 204);
74 74 border-radius: 5px 5px 5px 5px;
75 75 }
76 76
77 77 .navigation_panel .link {
78 78 border-right: 1px solid #000;
79 79 font-weight: bold;
80 80 margin-right: 1ex;
81 81 padding-right: 1ex;
82 82 }
83 83 .navigation_panel .link:last-child {
84 84 border-left: 1px solid #000;
85 85 border-right: none;
86 86 float: right;
87 87 margin-left: 1ex;
88 88 margin-right: 0;
89 89 padding-left: 1ex;
90 90 padding-right: 0;
91 91 }
92 92
93 93 .navigation_panel::after, .post::after {
94 94 clear: both;
95 95 content: ".";
96 96 display: block;
97 97 height: 0;
98 98 line-height: 0;
99 99 visibility: hidden;
100 100 }
101 101
102 102 p {
103 103 margin-top: .5em;
104 104 margin-bottom: .5em;
105 105 }
106 106
107 107 .post-form-w {
108 108 display: table;
109 109 padding: 10px;
110 110 margin: 5px
111 111 }
112 112
113 113 .form-row {
114 114 display: table-row;
115 115 }
116 116
117 117 .form-label, .form-input, .form-errors {
118 118 display: table-cell;
119 119 }
120 120
121 121 .form-label {
122 122 padding: .25em 1ex .25em 0;
123 123 vertical-align: top;
124 124 }
125 125
126 126 .form-input {
127 127 padding: .25em 0;
128 128 }
129 129
130 130 .form-errors {
131 131 padding-left: 1ex;
132 132 font-weight: bold;
133 133 vertical-align: middle;
134 134 }
135 135
136 .post-form input, .post-form textarea {
136 .post-form input:not([name="image"]), .post-form textarea {
137 137 background: #fff;
138 138 color: #000;
139 139 border: solid 1px;
140 140 padding: 0;
141 141 width: 100%;
142 142 font: medium sans;
143 143 }
144 144
145 145 .form-submit {
146 146 border-bottom: 2px solid #ddd;
147 147 margin-bottom: .5em;
148 148 padding-bottom: .5em;
149 149 }
150 150
151 151 .form-title {
152 152 font-weight: bold;
153 153 }
154 154
155 155 input[type="submit"] {
156 156 background: #fff;
157 157 border: solid 1px #000;
158 158 color: #000;
159 159 }
160 160
161 161 blockquote {
162 162 border-left: solid 2px;
163 163 padding-left: 5px;
164 164 color: #B1FB17;
165 165 margin: 0;
166 166 }
167 167
168 168 .post > .image {
169 169 float: left;
170 170 margin: 0 1ex .5ex 0;
171 171 min-width: 1px;
172 172 text-align: center;
173 173 display: table-row;
174 174 }
175 175
176 176 .post > .metadata {
177 177 clear: left;
178 178 }
179 179
180 180 .get {
181 181 font-weight: bold;
182 182 color: #d55;
183 183 }
184 184
185 185 * {
186 186 text-decoration: none;
187 187 }
188 188
189 189 .dead_post {
190 190 background-color: #ecc;
191 191 }
192 192
193 193 .quote {
194 194 color: #080;
195 195 font-style: italic;
196 196 }
197 197
198 198 .spoiler {
199 199 background: white;
200 200 color: white;
201 201 }
202 202
203 203 .spoiler:hover {
204 204 color: black;
205 205 }
206 206
207 207 .comment {
208 208 color: #8B6914;
209 209 font-style: italic;
210 210 }
211 211
212 212 a:hover {
213 213 text-decoration: underline;
214 214 }
215 215
216 216 .last-replies {
217 217 margin-left: 3ex;
218 218 }
219 219
220 220 .thread {
221 221 margin-bottom: 3ex;
222 222 }
223 223
224 224 .post:target {
225 225 border: solid 2px black;
226 226 }
227 227
228 228 pre{
229 229 white-space:pre-wrap
230 230 }
231 231
232 232 li {
233 233 list-style-position: inside;
234 234 }
235 235
236 236 .fancybox-skin {
237 237 position: relative;
238 238 background-color: #fff;
239 239 color: #ddd;
240 240 text-shadow: none;
241 241 }
242 242
243 243 .fancybox-image {
244 244 border: 1px solid black;
245 245 }
246 246
247 247 .image-mode-tab {
248 248 display: table;
249 249 margin: 5px;
250 250 padding: 5px;
251 251 background: rgb(221, 221, 221);
252 252 border: 1px solid rgb(204, 204, 204);
253 253 border-radius: 5px 5px 5px 5px;
254 254 }
255 255
256 256 .image-mode-tab > label {
257 257 margin: 0 1ex;
258 258 }
259 259
260 260 .image-mode-tab > label > input {
261 261 margin-right: .5ex;
262 262 }
263 263
264 264 #posts-table {
265 265 margin: 5px;
266 266 }
267 267
268 268 .tag_info, .page_link {
269 269 display: table;
270 270 }
271 271
272 272 .tag_info > h2 {
273 273 margin: 0;
274 274 }
275 275
276 276 .moderator_info {
277 277 color: #e99d41;
278 278 border: dashed 1px;
279 279 padding: 3px;
280 280 }
281 281
282 282 .refmap {
283 283 font-size: 0.9em;
284 284 color: #444;
285 285 margin-top: 1em;
286 286 }
287 287
288 288 input[type="submit"]:hover {
289 289 background: #ccc;
290 290 }
291 291
292 292
293 293 .fav {
294 294 color: rgb(255, 102, 0);
295 295 }
296 296
297 297 .not_fav {
298 298 color: #555;
299 299 }
300 300
301 301 .role {
302 302 text-decoration: underline;
303 303 }
304 304
305 305 .form-email {
306 306 display: none;
307 307 }
308 308
309 309 .mark_btn {
310 310 padding: 2px 2ex;
311 311 border: 1px solid;
312 312 }
313 313
314 314 .mark_btn:hover {
315 315 background: #ccc;
316 316 }
317 317
318 318 .bar-value {
319 319 background: rgba(251, 199, 16, 0.61);
320 320 padding: 2px;
321 321 font-size: 0.9em;
322 322 height: 1.5em;
323 323 }
324 324
325 325 .bar-bg {
326 326 position: relative;
327 327 border: 1px solid rgb(204, 204, 204);
328 328 border-radius: 5px 5px 5px 5px;
329 329 margin: 5px;
330 330 overflow: hidden;
331 331 }
332 332
333 333 .bar-text {
334 334 padding: 2px;
335 335 position: absolute;
336 336 left: 0;
337 337 top: 0;
338 338 }
339 339
340 340 .skipped_replies {
341 341 margin: 5px;
342 342 }
343 343
344 344 .current_page, .current_mode {
345 345 border: solid 1px #000;
346 346 padding: 2px;
347 347 }
348 348
349 349 .tag_item {
350 350 display: inline-block;
351 351 border: 1px solid #ccc;
352 352 margin: 0.3ex;
353 353 padding: 0.2ex;
354 354 }
355
356 .multiquote {
357 padding: 3px;
358 display: inline-block;
359 background: #ddd;
360 border-style: solid;
361 border-width: 1px 1px 1px 4px;
362 border-color: #222;
363 font-size: 0.9em;
364 }
@@ -1,62 +1,62 b''
1 1 {% load staticfiles %}
2 2 {% load i18n %}
3 3 {% load l10n %}
4 4 {% load static from staticfiles %}
5 5
6 6 <!DOCTYPE html>
7 7 <html>
8 8 <head>
9 9 <link rel="stylesheet" type="text/css" href="{% static 'css/base.css' %}" media="all"/>
10 10 <link rel="stylesheet" type="text/css" href="{% static 'css/3party/highlight.css' %}" media="all"/>
11 11 <link rel="stylesheet" type="text/css" href="{% static theme_css %}" media="all"/>
12 12 <link rel="alternate" type="application/rss+xml" href="rss/" title="{% trans 'Feed' %}"/>
13 13
14 14 <link rel="icon" type="image/png"
15 15 href="{% static 'favicon.png' %}">
16 16
17 17 <meta name="viewport" content="width=device-width, initial-scale=1"/>
18 18 <meta charset="utf-8"/>
19 19
20 20 {% block head %}{% endblock %}
21 21 </head>
22 22 <body>
23 23 <script src="{% static 'js/jquery-2.0.1.min.js' %}"></script>
24 24 <script src="{% static 'js/jquery-ui-1.10.3.custom.min.js' %}"></script>
25 25 <script src="{% static 'js/jquery.mousewheel.js' %}"></script>
26 26 <script src="{% url 'js_info_dict' %}"></script>
27 27
28 28 <div class="navigation_panel">
29 29 <a class="link" href="{% url 'index' %}">{% trans "All threads" %}</a>
30 30 {% for tag in tags %}
31 31 <a class="tag" href="{% url 'tag' tag_name=tag.name %}"
32 32 >#{{ tag.name }}</a>,
33 33 {% endfor %}
34 34 <a href="{% url 'tags' %}" title="{% trans 'Tag management' %}"
35 >[...]</a>
35 >[...]</a>,
36 <a href="{% url 'search' %}" title="{% trans 'Search' %}">[S]</a>
36 37 <a class="link" href="{% url 'settings' %}">{% trans 'Settings' %}</a>
37 38 </div>
38 39
39 40 {% block content %}{% endblock %}
40 41
41 42 <script src="{% static 'js/popup.js' %}"></script>
42 43 <script src="{% static 'js/image.js' %}"></script>
43 44 <script src="{% static 'js/3party/highlight.min.js' %}"></script>
44 45 <script src="{% static 'js/refpopup.js' %}"></script>
45 46 <script src="{% static 'js/main.js' %}"></script>
46 47
47 48 <div class="navigation_panel">
48 49 {% block metapanel %}{% endblock %}
49 50 [<a href="{% url 'admin:index' %}">{% trans 'Admin' %}</a>]
50 [<a href="{% url 'search' %}">{% trans 'Search' %}</a>]
51 51 {% with ppd=posts_per_day|floatformat:2 %}
52 52 {% blocktrans %}Speed: {{ ppd }} posts per day{% endblocktrans %}
53 53 {% endwith %}
54 54 <a class="link" href="#top">{% trans 'Up' %}</a>
55 55 </div>
56 56
57 57 <div class="footer">
58 58 <!-- Put your banners here -->
59 59 </div>
60 60
61 61 </body>
62 62 </html>
@@ -1,18 +1,20 b''
1 1 {% extends "boards/static_base.html" %}
2 2
3 3 {% load i18n %}
4 4
5 5 {% block head %}
6 6 <title>{% trans "Syntax" %}</title>
7 7 {% endblock %}
8 8
9 9 {% block staticcontent %}
10 10 <h2>{% trans 'Syntax' %}</h2>
11 11 <p>[i]<i>{% trans 'Italic text' %}</i>[/i]</p>
12 12 <p>[b]<b>{% trans 'Bold text' %}</b>[/b]</p>
13 13 <p>[spoiler]<span class="spoiler">{% trans 'Spoiler' %}</span>[/spoiler]</p>
14 14 <p>[post]123[/post] -- {% trans 'Link to a post' %}</p>
15 15 <p>[s]<span class="strikethrough">{% trans 'Strikethrough text' %}</span>[/s]</p>
16 16 <p>[comment]<span class="comment">{% trans 'Comment' %}</span>[/comment]</p>
17 17 <p>[quote]<span class="multiquote">{% trans 'Quote' %}</span>[/quote]</p>
18 <br/>
19 <p>{% trans 'You can try pasting the text and previewing the result here:' %} <a href="{% url 'preview' %}">{% trans 'Preview' %}</a></p>
18 20 {% endblock %}
@@ -1,79 +1,83 b''
1 1 from django.conf.urls import patterns, url, include
2 2 from django.contrib import admin
3 3 from boards import views
4 4 from boards.rss import AllThreadsFeed, TagThreadsFeed, ThreadPostsFeed
5 5 from boards.views import api, tag_threads, all_threads, \
6 6 settings, all_tags
7 7 from boards.views.authors import AuthorsView
8 8 from boards.views.delete_post import DeletePostView
9 9 from boards.views.ban import BanUserView
10 10 from boards.views.search import BoardSearchView
11 11 from boards.views.static import StaticPageView
12 12 from boards.views.post_admin import PostAdminView
13 from boards.views.preview import PostPreviewView
13 14
14 15 js_info_dict = {
15 16 'packages': ('boards',),
16 17 }
17 18
18 19 urlpatterns = patterns('',
19 20 # /boards/
20 21 url(r'^$', all_threads.AllThreadsView.as_view(), name='index'),
21 22 # /boards/page/
22 23 url(r'^page/(?P<page>\w+)/$', all_threads.AllThreadsView.as_view(),
23 24 name='index'),
24 25
25 26 # /boards/tag/tag_name/
26 27 url(r'^tag/(?P<tag_name>\w+)/$', tag_threads.TagView.as_view(),
27 28 name='tag'),
28 29 # /boards/tag/tag_id/page/
29 30 url(r'^tag/(?P<tag_name>\w+)/page/(?P<page>\w+)/$',
30 31 tag_threads.TagView.as_view(), name='tag'),
31 32
32 33 # /boards/thread/
33 34 url(r'^thread/(?P<post_id>\w+)/$', views.thread.ThreadView.as_view(),
34 35 name='thread'),
35 36 url(r'^thread/(?P<post_id>\w+)/mode/(?P<mode>\w+)/$', views.thread.ThreadView
36 37 .as_view(), name='thread_mode'),
37 38
38 39 # /boards/post_admin/
39 40 url(r'^post_admin/(?P<post_id>\w+)/$', PostAdminView.as_view(),
40 41 name='post_admin'),
41 42
42 43 url(r'^settings/$', settings.SettingsView.as_view(), name='settings'),
43 44 url(r'^tags/$', all_tags.AllTagsView.as_view(), name='tags'),
44 45 url(r'^authors/$', AuthorsView.as_view(), name='authors'),
45 46 url(r'^delete/(?P<post_id>\w+)/$', DeletePostView.as_view(),
46 47 name='delete'),
47 48 url(r'^ban/(?P<post_id>\w+)/$', BanUserView.as_view(), name='ban'),
48 49
49 50 url(r'^banned/$', views.banned.BannedView.as_view(), name='banned'),
50 51 url(r'^staticpage/(?P<name>\w+)/$', StaticPageView.as_view(),
51 52 name='staticpage'),
52 53
53 54 # RSS feeds
54 55 url(r'^rss/$', AllThreadsFeed()),
55 56 url(r'^page/(?P<page>\w+)/rss/$', AllThreadsFeed()),
56 57 url(r'^tag/(?P<tag_name>\w+)/rss/$', TagThreadsFeed()),
57 58 url(r'^tag/(?P<tag_name>\w+)/page/(?P<page>\w+)/rss/$', TagThreadsFeed()),
58 59 url(r'^thread/(?P<post_id>\w+)/rss/$', ThreadPostsFeed()),
59 60
60 61 # i18n
61 62 url(r'^jsi18n/$', 'boards.views.cached_js_catalog', js_info_dict,
62 63 name='js_info_dict'),
63 64
64 65 # API
65 66 url(r'^api/post/(?P<post_id>\w+)/$', api.get_post, name="get_post"),
66 67 url(r'^api/diff_thread/(?P<thread_id>\w+)/(?P<last_update_time>\w+)/$',
67 68 api.api_get_threaddiff, name="get_thread_diff"),
68 69 url(r'^api/threads/(?P<count>\w+)/$', api.api_get_threads,
69 70 name='get_threads'),
70 71 url(r'^api/tags/$', api.api_get_tags, name='get_tags'),
71 72 url(r'^api/thread/(?P<opening_post_id>\w+)/$', api.api_get_thread_posts,
72 73 name='get_thread'),
73 74 url(r'^api/add_post/(?P<opening_post_id>\w+)/$', api.api_add_post,
74 75 name='add_post'),
75 76
76 77 # Search
77 78 url(r'^search/$', BoardSearchView.as_view(), name='search'),
78 79
80 # Post preview
81 url(r'^preview/$', PostPreviewView.as_view(), name='preview')
82
79 83 )
General Comments 0
You need to be logged in to leave comments. Login now