Show More
@@ -1,31 +1,35 | |||||
|
1 | __author__ = 'neko259' | |||
|
2 | ||||
1 | from django.utils.translation import ugettext_lazy as _ |
|
3 | from django.utils.translation import ugettext_lazy as _ | |
2 |
|
4 | |||
3 | __author__ = 'neko259' |
|
5 | ATTR_ROLES = 'roles' | |
|
6 | ATTR_CONTACTS = 'contacts' | |||
|
7 | ATTR_NAME = 'name' | |||
4 |
|
8 | |||
5 | ROLE_AUTHOR = _('author') |
|
9 | ROLE_AUTHOR = _('author') | |
6 | ROLE_DEVELOPER = _('developer') |
|
10 | ROLE_DEVELOPER = _('developer') | |
7 | ROLE_JS_DEV = _('javascript developer') |
|
11 | ROLE_JS_DEV = _('javascript developer') | |
8 | ROLE_DESIGNER = _('designer') |
|
12 | ROLE_DESIGNER = _('designer') | |
9 |
|
13 | |||
10 | authors = { |
|
14 | authors = { | |
11 | 'neko259': { |
|
15 | 'neko259': { | |
12 |
|
|
16 | ATTR_NAME: 'Pavel Ryapolov', | |
13 |
|
|
17 | ATTR_CONTACTS: ['neko259@gmail.com'], | |
14 |
|
|
18 | ATTR_ROLES: [ROLE_AUTHOR, ROLE_DEVELOPER], | |
15 | }, |
|
19 | }, | |
16 | 'ilyas': { |
|
20 | 'ilyas': { | |
17 |
|
|
21 | ATTR_NAME: 'Ilyas Babayev', | |
18 |
|
|
22 | ATTR_CONTACTS: ['zamesilyasa@gmail.com'], | |
19 |
|
|
23 | ATTR_ROLES: [ROLE_AUTHOR, ROLE_DEVELOPER], | |
20 | }, |
|
24 | }, | |
21 | 'ritsufag': { |
|
25 | 'ritsufag': { | |
22 |
|
|
26 | ATTR_NAME: 'Aiko Kirino', | |
23 |
|
|
27 | ATTR_CONTACTS: ['ritsufag@gmail.com'], | |
24 |
|
|
28 | ATTR_ROLES: [ROLE_JS_DEV, ROLE_DESIGNER], | |
25 | }, |
|
29 | }, | |
26 | 'Tenno Seremel': { |
|
30 | 'Tenno Seremel': { | |
27 |
|
|
31 | ATTR_NAME: 'anonymous', | |
28 |
|
|
32 | ATTR_CONTACTS: ['html@serenareem.net'], | |
29 |
|
|
33 | ATTR_ROLES: [ROLE_JS_DEV, ROLE_DESIGNER], | |
30 | }, |
|
34 | }, | |
31 | } No newline at end of file |
|
35 | } |
@@ -1,30 +1,39 | |||||
|
1 | __author__ = 'neko259' | |||
|
2 | ||||
1 | from boards import utils, settings |
|
3 | from boards import utils, settings | |
2 | from boards.models import Post |
|
4 | from boards.models import Post | |
3 | from boards.models.post import SETTING_MODERATE |
|
5 | from boards.models.post import SETTING_MODERATE | |
4 |
|
6 | |||
5 | __author__ = 'neko259' |
|
7 | CONTEXT_SITE_NAME = 'site_name' | |
|
8 | CONTEXT_VERSION = 'version' | |||
|
9 | CONTEXT_MODERATOR = 'moderator' | |||
|
10 | CONTEXT_THEME_CSS = 'theme_css' | |||
|
11 | CONTEXT_THEME = 'theme' | |||
|
12 | CONTEXT_PPD = 'posts_per_day' | |||
|
13 | CONTEXT_TAGS = 'tags' | |||
|
14 | CONTEXT_USER = 'user' | |||
6 |
|
15 | |||
7 |
|
16 | |||
8 | def user_and_ui_processor(request): |
|
17 | def user_and_ui_processor(request): | |
9 | context = {} |
|
18 | context = {} | |
10 |
|
19 | |||
11 | user = utils.get_user(request) |
|
20 | user = utils.get_user(request) | |
12 |
context[ |
|
21 | context[CONTEXT_USER] = user | |
13 |
context[ |
|
22 | context[CONTEXT_TAGS] = user.fav_tags.all() | |
14 |
context[ |
|
23 | context[CONTEXT_PPD] = float(Post.objects.get_posts_per_day()) | |
15 |
|
24 | |||
16 | theme = utils.get_theme(request, user) |
|
25 | theme = utils.get_theme(request, user) | |
17 |
context[ |
|
26 | context[CONTEXT_THEME] = theme | |
18 |
context[ |
|
27 | context[CONTEXT_THEME_CSS] = 'css/' + theme + '/base_page.css' | |
19 |
|
28 | |||
20 | # This shows the moderator panel |
|
29 | # This shows the moderator panel | |
21 | moderate = user.get_setting(SETTING_MODERATE) |
|
30 | moderate = user.get_setting(SETTING_MODERATE) | |
22 | if moderate == 'True': |
|
31 | if moderate == 'True': | |
23 |
context[ |
|
32 | context[CONTEXT_MODERATOR] = user.is_moderator() | |
24 | else: |
|
33 | else: | |
25 |
context[ |
|
34 | context[CONTEXT_MODERATOR] = False | |
26 |
|
35 | |||
27 |
context[ |
|
36 | context[CONTEXT_VERSION] = settings.VERSION | |
28 |
context[ |
|
37 | context[CONTEXT_SITE_NAME] = settings.SITE_NAME | |
29 |
|
38 | |||
30 | return context No newline at end of file |
|
39 | return context |
@@ -1,355 +1,356 | |||||
1 | import re |
|
1 | import re | |
2 | import time |
|
2 | import time | |
3 | import hashlib |
|
3 | import hashlib | |
4 |
|
4 | |||
5 | from captcha.fields import CaptchaField |
|
5 | from captcha.fields import CaptchaField | |
6 | from django import forms |
|
6 | from django import forms | |
7 | from django.forms.util import ErrorList |
|
7 | from django.forms.util import ErrorList | |
8 | from django.utils.translation import ugettext_lazy as _ |
|
8 | from django.utils.translation import ugettext_lazy as _ | |
9 |
|
9 | |||
10 | from boards.mdx_neboard import formatters |
|
10 | from boards.mdx_neboard import formatters | |
11 | from boards.models.post import TITLE_MAX_LENGTH |
|
11 | from boards.models.post import TITLE_MAX_LENGTH | |
12 | from boards.models import User, PostImage |
|
12 | from boards.models import User, PostImage | |
13 | from neboard import settings |
|
13 | from neboard import settings | |
14 | from boards import utils |
|
14 | from boards import utils | |
15 | import boards.settings as board_settings |
|
15 | import boards.settings as board_settings | |
16 |
|
16 | |||
17 | VETERAN_POSTING_DELAY = 5 |
|
17 | VETERAN_POSTING_DELAY = 5 | |
18 |
|
18 | |||
19 | ATTRIBUTE_PLACEHOLDER = 'placeholder' |
|
19 | ATTRIBUTE_PLACEHOLDER = 'placeholder' | |
20 |
|
20 | |||
21 | LAST_POST_TIME = 'last_post_time' |
|
21 | LAST_POST_TIME = 'last_post_time' | |
22 | LAST_LOGIN_TIME = 'last_login_time' |
|
22 | LAST_LOGIN_TIME = 'last_login_time' | |
23 | TEXT_PLACEHOLDER = _('''Type message here. You can reply to message >>123 like |
|
23 | TEXT_PLACEHOLDER = _('''Type message here. You can reply to message >>123 like | |
24 | this. 2 new lines are required to start new paragraph.''') |
|
24 | this. 2 new lines are required to start new paragraph.''') | |
25 | TAGS_PLACEHOLDER = _('tag1 several_words_tag') |
|
25 | TAGS_PLACEHOLDER = _('tag1 several_words_tag') | |
26 |
|
26 | |||
27 | ERROR_IMAGE_DUPLICATE = _('Such image was already posted') |
|
27 | ERROR_IMAGE_DUPLICATE = _('Such image was already posted') | |
28 |
|
28 | |||
29 | LABEL_TITLE = _('Title') |
|
29 | LABEL_TITLE = _('Title') | |
30 | LABEL_TEXT = _('Text') |
|
30 | LABEL_TEXT = _('Text') | |
31 | LABEL_TAG = _('Tag') |
|
31 | LABEL_TAG = _('Tag') | |
32 | LABEL_SEARCH = _('Search') |
|
32 | LABEL_SEARCH = _('Search') | |
33 |
|
33 | |||
34 | TAG_MAX_LENGTH = 20 |
|
34 | TAG_MAX_LENGTH = 20 | |
35 |
|
35 | |||
36 | REGEX_TAG = ur'^[\w\d]+$' |
|
36 | REGEX_TAG = ur'^[\w\d]+$' | |
37 |
|
37 | |||
38 |
|
38 | |||
39 | class FormatPanel(forms.Textarea): |
|
39 | class FormatPanel(forms.Textarea): | |
40 | def render(self, name, value, attrs=None): |
|
40 | def render(self, name, value, attrs=None): | |
41 | output = '<div id="mark-panel">' |
|
41 | output = '<div id="mark-panel">' | |
42 | for formatter in formatters: |
|
42 | for formatter in formatters: | |
43 | output += u'<span class="mark_btn"' + \ |
|
43 | output += u'<span class="mark_btn"' + \ | |
44 | u' onClick="addMarkToMsg(\'' + formatter.format_left + \ |
|
44 | u' onClick="addMarkToMsg(\'' + formatter.format_left + \ | |
45 | '\', \'' + formatter.format_right + '\')">' + \ |
|
45 | '\', \'' + formatter.format_right + '\')">' + \ | |
46 | formatter.preview_left + formatter.name + \ |
|
46 | formatter.preview_left + formatter.name + \ | |
47 | formatter.preview_right + u'</span>' |
|
47 | formatter.preview_right + u'</span>' | |
48 |
|
48 | |||
49 | output += '</div>' |
|
49 | output += '</div>' | |
50 | output += super(FormatPanel, self).render(name, value, attrs=None) |
|
50 | output += super(FormatPanel, self).render(name, value, attrs=None) | |
51 |
|
51 | |||
52 | return output |
|
52 | return output | |
53 |
|
53 | |||
54 |
|
54 | |||
55 | class PlainErrorList(ErrorList): |
|
55 | class PlainErrorList(ErrorList): | |
56 | def __unicode__(self): |
|
56 | def __unicode__(self): | |
57 | return self.as_text() |
|
57 | return self.as_text() | |
58 |
|
58 | |||
59 | def as_text(self): |
|
59 | def as_text(self): | |
60 | return ''.join([u'(!) %s ' % e for e in self]) |
|
60 | return ''.join([u'(!) %s ' % e for e in self]) | |
61 |
|
61 | |||
62 |
|
62 | |||
63 | class NeboardForm(forms.Form): |
|
63 | class NeboardForm(forms.Form): | |
64 |
|
64 | |||
65 | def as_div(self): |
|
65 | def as_div(self): | |
66 | """ |
|
66 | """ | |
67 | Returns this form rendered as HTML <as_div>s. |
|
67 | Returns this form rendered as HTML <as_div>s. | |
68 | """ |
|
68 | """ | |
69 |
|
69 | |||
70 | return self._html_output( |
|
70 | return self._html_output( | |
71 | # TODO Do not show hidden rows in the list here |
|
71 | # TODO Do not show hidden rows in the list here | |
72 | normal_row='<div class="form-row"><div class="form-label">' |
|
72 | normal_row='<div class="form-row"><div class="form-label">' | |
73 | '%(label)s' |
|
73 | '%(label)s' | |
74 | '</div></div>' |
|
74 | '</div></div>' | |
75 | '<div class="form-row"><div class="form-input">' |
|
75 | '<div class="form-row"><div class="form-input">' | |
76 | '%(field)s' |
|
76 | '%(field)s' | |
77 | '</div></div>' |
|
77 | '</div></div>' | |
78 | '<div class="form-row">' |
|
78 | '<div class="form-row">' | |
79 | '%(help_text)s' |
|
79 | '%(help_text)s' | |
80 | '</div>', |
|
80 | '</div>', | |
81 | error_row='<div class="form-row">' |
|
81 | error_row='<div class="form-row">' | |
82 | '<div class="form-label"></div>' |
|
82 | '<div class="form-label"></div>' | |
83 | '<div class="form-errors">%s</div>' |
|
83 | '<div class="form-errors">%s</div>' | |
84 | '</div>', |
|
84 | '</div>', | |
85 | row_ender='</div>', |
|
85 | row_ender='</div>', | |
86 | help_text_html='%s', |
|
86 | help_text_html='%s', | |
87 | errors_on_separate_row=True) |
|
87 | errors_on_separate_row=True) | |
88 |
|
88 | |||
89 | def as_json_errors(self): |
|
89 | def as_json_errors(self): | |
90 | errors = [] |
|
90 | errors = [] | |
91 |
|
91 | |||
92 | for name, field in self.fields.items(): |
|
92 | for name, field in self.fields.items(): | |
93 | if self[name].errors: |
|
93 | if self[name].errors: | |
94 | errors.append({ |
|
94 | errors.append({ | |
95 | 'field': name, |
|
95 | 'field': name, | |
96 | 'errors': self[name].errors.as_text(), |
|
96 | 'errors': self[name].errors.as_text(), | |
97 | }) |
|
97 | }) | |
98 |
|
98 | |||
99 | return errors |
|
99 | return errors | |
100 |
|
100 | |||
101 |
|
101 | |||
102 | class PostForm(NeboardForm): |
|
102 | class PostForm(NeboardForm): | |
103 |
|
103 | |||
104 | title = forms.CharField(max_length=TITLE_MAX_LENGTH, required=False, |
|
104 | title = forms.CharField(max_length=TITLE_MAX_LENGTH, required=False, | |
105 | label=LABEL_TITLE) |
|
105 | label=LABEL_TITLE) | |
106 | text = forms.CharField( |
|
106 | text = forms.CharField( | |
107 | widget=FormatPanel(attrs={ATTRIBUTE_PLACEHOLDER: TEXT_PLACEHOLDER}), |
|
107 | widget=FormatPanel(attrs={ATTRIBUTE_PLACEHOLDER: TEXT_PLACEHOLDER}), | |
108 | required=False, label=LABEL_TEXT) |
|
108 | required=False, label=LABEL_TEXT) | |
109 | image = forms.ImageField(required=False, label=_('Image'), |
|
109 | image = forms.ImageField(required=False, label=_('Image'), | |
110 |
widget=forms.ClearableFileInput( |
|
110 | widget=forms.ClearableFileInput( | |
|
111 | attrs={'accept': 'image/*'})) | |||
111 |
|
112 | |||
112 | # This field is for spam prevention only |
|
113 | # This field is for spam prevention only | |
113 | email = forms.CharField(max_length=100, required=False, label=_('e-mail'), |
|
114 | email = forms.CharField(max_length=100, required=False, label=_('e-mail'), | |
114 | widget=forms.TextInput(attrs={ |
|
115 | widget=forms.TextInput(attrs={ | |
115 | 'class': 'form-email'})) |
|
116 | 'class': 'form-email'})) | |
116 |
|
117 | |||
117 | session = None |
|
118 | session = None | |
118 | need_to_ban = False |
|
119 | need_to_ban = False | |
119 |
|
120 | |||
120 | def clean_title(self): |
|
121 | def clean_title(self): | |
121 | title = self.cleaned_data['title'] |
|
122 | title = self.cleaned_data['title'] | |
122 | if title: |
|
123 | if title: | |
123 | if len(title) > TITLE_MAX_LENGTH: |
|
124 | if len(title) > TITLE_MAX_LENGTH: | |
124 | raise forms.ValidationError(_('Title must have less than %s ' |
|
125 | raise forms.ValidationError(_('Title must have less than %s ' | |
125 | 'characters') % |
|
126 | 'characters') % | |
126 | str(TITLE_MAX_LENGTH)) |
|
127 | str(TITLE_MAX_LENGTH)) | |
127 | return title |
|
128 | return title | |
128 |
|
129 | |||
129 | def clean_text(self): |
|
130 | def clean_text(self): | |
130 | text = self.cleaned_data['text'].strip() |
|
131 | text = self.cleaned_data['text'].strip() | |
131 | if text: |
|
132 | if text: | |
132 | if len(text) > board_settings.MAX_TEXT_LENGTH: |
|
133 | if len(text) > board_settings.MAX_TEXT_LENGTH: | |
133 | raise forms.ValidationError(_('Text must have less than %s ' |
|
134 | raise forms.ValidationError(_('Text must have less than %s ' | |
134 | 'characters') % |
|
135 | 'characters') % | |
135 | str(board_settings |
|
136 | str(board_settings | |
136 | .MAX_TEXT_LENGTH)) |
|
137 | .MAX_TEXT_LENGTH)) | |
137 | return text |
|
138 | return text | |
138 |
|
139 | |||
139 | def clean_image(self): |
|
140 | def clean_image(self): | |
140 | image = self.cleaned_data['image'] |
|
141 | image = self.cleaned_data['image'] | |
141 | if image: |
|
142 | if image: | |
142 |
if image. |
|
143 | if image.size > board_settings.MAX_IMAGE_SIZE: | |
143 | raise forms.ValidationError( |
|
144 | raise forms.ValidationError( | |
144 | _('Image must be less than %s bytes') |
|
145 | _('Image must be less than %s bytes') | |
145 | % str(board_settings.MAX_IMAGE_SIZE)) |
|
146 | % str(board_settings.MAX_IMAGE_SIZE)) | |
146 |
|
147 | |||
147 | md5 = hashlib.md5() |
|
148 | md5 = hashlib.md5() | |
148 | for chunk in image.chunks(): |
|
149 | for chunk in image.chunks(): | |
149 | md5.update(chunk) |
|
150 | md5.update(chunk) | |
150 | image_hash = md5.hexdigest() |
|
151 | image_hash = md5.hexdigest() | |
151 | if PostImage.objects.filter(hash=image_hash).exists(): |
|
152 | if PostImage.objects.filter(hash=image_hash).exists(): | |
152 | raise forms.ValidationError(ERROR_IMAGE_DUPLICATE) |
|
153 | raise forms.ValidationError(ERROR_IMAGE_DUPLICATE) | |
153 |
|
154 | |||
154 | return image |
|
155 | return image | |
155 |
|
156 | |||
156 | def clean(self): |
|
157 | def clean(self): | |
157 | cleaned_data = super(PostForm, self).clean() |
|
158 | cleaned_data = super(PostForm, self).clean() | |
158 |
|
159 | |||
159 | if not self.session: |
|
160 | if not self.session: | |
160 | raise forms.ValidationError('Humans have sessions') |
|
161 | raise forms.ValidationError('Humans have sessions') | |
161 |
|
162 | |||
162 | if cleaned_data['email']: |
|
163 | if cleaned_data['email']: | |
163 | self.need_to_ban = True |
|
164 | self.need_to_ban = True | |
164 | raise forms.ValidationError('A human cannot enter a hidden field') |
|
165 | raise forms.ValidationError('A human cannot enter a hidden field') | |
165 |
|
166 | |||
166 | if not self.errors: |
|
167 | if not self.errors: | |
167 | self._clean_text_image() |
|
168 | self._clean_text_image() | |
168 |
|
169 | |||
169 | if not self.errors and self.session: |
|
170 | if not self.errors and self.session: | |
170 | self._validate_posting_speed() |
|
171 | self._validate_posting_speed() | |
171 |
|
172 | |||
172 | return cleaned_data |
|
173 | return cleaned_data | |
173 |
|
174 | |||
174 | def _clean_text_image(self): |
|
175 | def _clean_text_image(self): | |
175 | text = self.cleaned_data.get('text') |
|
176 | text = self.cleaned_data.get('text') | |
176 | image = self.cleaned_data.get('image') |
|
177 | image = self.cleaned_data.get('image') | |
177 |
|
178 | |||
178 | if (not text) and (not image): |
|
179 | if (not text) and (not image): | |
179 | error_message = _('Either text or image must be entered.') |
|
180 | error_message = _('Either text or image must be entered.') | |
180 | self._errors['text'] = self.error_class([error_message]) |
|
181 | self._errors['text'] = self.error_class([error_message]) | |
181 |
|
182 | |||
182 | def _validate_posting_speed(self): |
|
183 | def _validate_posting_speed(self): | |
183 | can_post = True |
|
184 | can_post = True | |
184 |
|
185 | |||
185 | # TODO Remove this, it's only for test |
|
186 | # TODO Remove this, it's only for test | |
186 | if not 'user_id' in self.session: |
|
187 | if not 'user_id' in self.session: | |
187 | return |
|
188 | return | |
188 |
|
189 | |||
189 | user = User.objects.get(id=self.session['user_id']) |
|
190 | user = User.objects.get(id=self.session['user_id']) | |
190 | if user.is_veteran(): |
|
191 | if user.is_veteran(): | |
191 | posting_delay = VETERAN_POSTING_DELAY |
|
192 | posting_delay = VETERAN_POSTING_DELAY | |
192 | else: |
|
193 | else: | |
193 | posting_delay = settings.POSTING_DELAY |
|
194 | posting_delay = settings.POSTING_DELAY | |
194 |
|
195 | |||
195 | if LAST_POST_TIME in self.session: |
|
196 | if LAST_POST_TIME in self.session: | |
196 | now = time.time() |
|
197 | now = time.time() | |
197 | last_post_time = self.session[LAST_POST_TIME] |
|
198 | last_post_time = self.session[LAST_POST_TIME] | |
198 |
|
199 | |||
199 | current_delay = int(now - last_post_time) |
|
200 | current_delay = int(now - last_post_time) | |
200 |
|
201 | |||
201 | if current_delay < posting_delay: |
|
202 | if current_delay < posting_delay: | |
202 | error_message = _('Wait %s seconds after last posting') % str( |
|
203 | error_message = _('Wait %s seconds after last posting') % str( | |
203 | posting_delay - current_delay) |
|
204 | posting_delay - current_delay) | |
204 | self._errors['text'] = self.error_class([error_message]) |
|
205 | self._errors['text'] = self.error_class([error_message]) | |
205 |
|
206 | |||
206 | can_post = False |
|
207 | can_post = False | |
207 |
|
208 | |||
208 | if can_post: |
|
209 | if can_post: | |
209 | self.session[LAST_POST_TIME] = time.time() |
|
210 | self.session[LAST_POST_TIME] = time.time() | |
210 |
|
211 | |||
211 |
|
212 | |||
212 | class ThreadForm(PostForm): |
|
213 | class ThreadForm(PostForm): | |
213 |
|
214 | |||
214 | regex_tags = re.compile(ur'^[\w\s\d]+$', re.UNICODE) |
|
215 | regex_tags = re.compile(ur'^[\w\s\d]+$', re.UNICODE) | |
215 |
|
216 | |||
216 | tags = forms.CharField( |
|
217 | tags = forms.CharField( | |
217 | widget=forms.TextInput(attrs={ATTRIBUTE_PLACEHOLDER: TAGS_PLACEHOLDER}), |
|
218 | widget=forms.TextInput(attrs={ATTRIBUTE_PLACEHOLDER: TAGS_PLACEHOLDER}), | |
218 | max_length=100, label=_('Tags'), required=True) |
|
219 | max_length=100, label=_('Tags'), required=True) | |
219 |
|
220 | |||
220 | def clean_tags(self): |
|
221 | def clean_tags(self): | |
221 | tags = self.cleaned_data['tags'].strip() |
|
222 | tags = self.cleaned_data['tags'].strip() | |
222 |
|
223 | |||
223 | if not tags or not self.regex_tags.match(tags): |
|
224 | if not tags or not self.regex_tags.match(tags): | |
224 | raise forms.ValidationError( |
|
225 | raise forms.ValidationError( | |
225 | _('Inappropriate characters in tags.')) |
|
226 | _('Inappropriate characters in tags.')) | |
226 |
|
227 | |||
227 | return tags |
|
228 | return tags | |
228 |
|
229 | |||
229 | def clean(self): |
|
230 | def clean(self): | |
230 | cleaned_data = super(ThreadForm, self).clean() |
|
231 | cleaned_data = super(ThreadForm, self).clean() | |
231 |
|
232 | |||
232 | return cleaned_data |
|
233 | return cleaned_data | |
233 |
|
234 | |||
234 |
|
235 | |||
235 | class PostCaptchaForm(PostForm): |
|
236 | class PostCaptchaForm(PostForm): | |
236 | captcha = CaptchaField() |
|
237 | captcha = CaptchaField() | |
237 |
|
238 | |||
238 | def __init__(self, *args, **kwargs): |
|
239 | def __init__(self, *args, **kwargs): | |
239 | self.request = kwargs['request'] |
|
240 | self.request = kwargs['request'] | |
240 | del kwargs['request'] |
|
241 | del kwargs['request'] | |
241 |
|
242 | |||
242 | super(PostCaptchaForm, self).__init__(*args, **kwargs) |
|
243 | super(PostCaptchaForm, self).__init__(*args, **kwargs) | |
243 |
|
244 | |||
244 | def clean(self): |
|
245 | def clean(self): | |
245 | cleaned_data = super(PostCaptchaForm, self).clean() |
|
246 | cleaned_data = super(PostCaptchaForm, self).clean() | |
246 |
|
247 | |||
247 | success = self.is_valid() |
|
248 | success = self.is_valid() | |
248 | utils.update_captcha_access(self.request, success) |
|
249 | utils.update_captcha_access(self.request, success) | |
249 |
|
250 | |||
250 | if success: |
|
251 | if success: | |
251 | return cleaned_data |
|
252 | return cleaned_data | |
252 | else: |
|
253 | else: | |
253 | raise forms.ValidationError(_("Captcha validation failed")) |
|
254 | raise forms.ValidationError(_("Captcha validation failed")) | |
254 |
|
255 | |||
255 |
|
256 | |||
256 | class ThreadCaptchaForm(ThreadForm): |
|
257 | class ThreadCaptchaForm(ThreadForm): | |
257 | captcha = CaptchaField() |
|
258 | captcha = CaptchaField() | |
258 |
|
259 | |||
259 | def __init__(self, *args, **kwargs): |
|
260 | def __init__(self, *args, **kwargs): | |
260 | self.request = kwargs['request'] |
|
261 | self.request = kwargs['request'] | |
261 | del kwargs['request'] |
|
262 | del kwargs['request'] | |
262 |
|
263 | |||
263 | super(ThreadCaptchaForm, self).__init__(*args, **kwargs) |
|
264 | super(ThreadCaptchaForm, self).__init__(*args, **kwargs) | |
264 |
|
265 | |||
265 | def clean(self): |
|
266 | def clean(self): | |
266 | cleaned_data = super(ThreadCaptchaForm, self).clean() |
|
267 | cleaned_data = super(ThreadCaptchaForm, self).clean() | |
267 |
|
268 | |||
268 | success = self.is_valid() |
|
269 | success = self.is_valid() | |
269 | utils.update_captcha_access(self.request, success) |
|
270 | utils.update_captcha_access(self.request, success) | |
270 |
|
271 | |||
271 | if success: |
|
272 | if success: | |
272 | return cleaned_data |
|
273 | return cleaned_data | |
273 | else: |
|
274 | else: | |
274 | raise forms.ValidationError(_("Captcha validation failed")) |
|
275 | raise forms.ValidationError(_("Captcha validation failed")) | |
275 |
|
276 | |||
276 |
|
277 | |||
277 | class SettingsForm(NeboardForm): |
|
278 | class SettingsForm(NeboardForm): | |
278 |
|
279 | |||
279 | theme = forms.ChoiceField(choices=settings.THEMES, |
|
280 | theme = forms.ChoiceField(choices=settings.THEMES, | |
280 | label=_('Theme')) |
|
281 | label=_('Theme')) | |
281 |
|
282 | |||
282 |
|
283 | |||
283 | class ModeratorSettingsForm(SettingsForm): |
|
284 | class ModeratorSettingsForm(SettingsForm): | |
284 |
|
285 | |||
285 | moderate = forms.BooleanField(required=False, label=_('Enable moderation ' |
|
286 | moderate = forms.BooleanField(required=False, label=_('Enable moderation ' | |
286 | 'panel')) |
|
287 | 'panel')) | |
287 |
|
288 | |||
288 |
|
289 | |||
289 | class LoginForm(NeboardForm): |
|
290 | class LoginForm(NeboardForm): | |
290 |
|
291 | |||
291 | user_id = forms.CharField() |
|
292 | user_id = forms.CharField() | |
292 |
|
293 | |||
293 | session = None |
|
294 | session = None | |
294 |
|
295 | |||
295 | def clean_user_id(self): |
|
296 | def clean_user_id(self): | |
296 | user_id = self.cleaned_data['user_id'] |
|
297 | user_id = self.cleaned_data['user_id'] | |
297 | if user_id: |
|
298 | if user_id: | |
298 | users = User.objects.filter(user_id=user_id) |
|
299 | users = User.objects.filter(user_id=user_id) | |
299 | if len(users) == 0: |
|
300 | if len(users) == 0: | |
300 | raise forms.ValidationError(_('No such user found')) |
|
301 | raise forms.ValidationError(_('No such user found')) | |
301 |
|
302 | |||
302 | return user_id |
|
303 | return user_id | |
303 |
|
304 | |||
304 | def _validate_login_speed(self): |
|
305 | def _validate_login_speed(self): | |
305 | can_post = True |
|
306 | can_post = True | |
306 |
|
307 | |||
307 | if LAST_LOGIN_TIME in self.session: |
|
308 | if LAST_LOGIN_TIME in self.session: | |
308 | now = time.time() |
|
309 | now = time.time() | |
309 | last_login_time = self.session[LAST_LOGIN_TIME] |
|
310 | last_login_time = self.session[LAST_LOGIN_TIME] | |
310 |
|
311 | |||
311 | current_delay = int(now - last_login_time) |
|
312 | current_delay = int(now - last_login_time) | |
312 |
|
313 | |||
313 | if current_delay < board_settings.LOGIN_TIMEOUT: |
|
314 | if current_delay < board_settings.LOGIN_TIMEOUT: | |
314 | error_message = _('Wait %s minutes after last login') % str( |
|
315 | error_message = _('Wait %s minutes after last login') % str( | |
315 | (board_settings.LOGIN_TIMEOUT - current_delay) / 60) |
|
316 | (board_settings.LOGIN_TIMEOUT - current_delay) / 60) | |
316 | self._errors['user_id'] = self.error_class([error_message]) |
|
317 | self._errors['user_id'] = self.error_class([error_message]) | |
317 |
|
318 | |||
318 | can_post = False |
|
319 | can_post = False | |
319 |
|
320 | |||
320 | if can_post: |
|
321 | if can_post: | |
321 | self.session[LAST_LOGIN_TIME] = time.time() |
|
322 | self.session[LAST_LOGIN_TIME] = time.time() | |
322 |
|
323 | |||
323 | def clean(self): |
|
324 | def clean(self): | |
324 | if not self.session: |
|
325 | if not self.session: | |
325 | raise forms.ValidationError('Humans have sessions') |
|
326 | raise forms.ValidationError('Humans have sessions') | |
326 |
|
327 | |||
327 | self._validate_login_speed() |
|
328 | self._validate_login_speed() | |
328 |
|
329 | |||
329 | cleaned_data = super(LoginForm, self).clean() |
|
330 | cleaned_data = super(LoginForm, self).clean() | |
330 |
|
331 | |||
331 | return cleaned_data |
|
332 | return cleaned_data | |
332 |
|
333 | |||
333 |
|
334 | |||
334 | class AddTagForm(NeboardForm): |
|
335 | class AddTagForm(NeboardForm): | |
335 |
|
336 | |||
336 | tag = forms.CharField(max_length=TAG_MAX_LENGTH, label=LABEL_TAG) |
|
337 | tag = forms.CharField(max_length=TAG_MAX_LENGTH, label=LABEL_TAG) | |
337 | method = forms.CharField(widget=forms.HiddenInput(), initial='add_tag') |
|
338 | method = forms.CharField(widget=forms.HiddenInput(), initial='add_tag') | |
338 |
|
339 | |||
339 | def clean_tag(self): |
|
340 | def clean_tag(self): | |
340 | tag = self.cleaned_data['tag'] |
|
341 | tag = self.cleaned_data['tag'] | |
341 |
|
342 | |||
342 | regex_tag = re.compile(REGEX_TAG, re.UNICODE) |
|
343 | regex_tag = re.compile(REGEX_TAG, re.UNICODE) | |
343 | if not regex_tag.match(tag): |
|
344 | if not regex_tag.match(tag): | |
344 | raise forms.ValidationError(_('Inappropriate characters in tags.')) |
|
345 | raise forms.ValidationError(_('Inappropriate characters in tags.')) | |
345 |
|
346 | |||
346 | return tag |
|
347 | return tag | |
347 |
|
348 | |||
348 | def clean(self): |
|
349 | def clean(self): | |
349 | cleaned_data = super(AddTagForm, self).clean() |
|
350 | cleaned_data = super(AddTagForm, self).clean() | |
350 |
|
351 | |||
351 | return cleaned_data |
|
352 | return cleaned_data | |
352 |
|
353 | |||
353 |
|
354 | |||
354 | class SearchForm(NeboardForm): |
|
355 | class SearchForm(NeboardForm): | |
355 | query = forms.CharField(max_length=500, label=LABEL_SEARCH, required=False) No newline at end of file |
|
356 | query = forms.CharField(max_length=500, label=LABEL_SEARCH, required=False) |
@@ -1,202 +1,207 | |||||
1 | # coding=utf-8 |
|
1 | # coding=utf-8 | |
2 |
|
2 | |||
3 | import markdown |
|
3 | import markdown | |
4 |
from markdown.inlinepatterns import Pattern |
|
4 | from markdown.inlinepatterns import Pattern | |
5 | from markdown.util import etree |
|
5 | from markdown.util import etree | |
|
6 | ||||
6 | import boards |
|
7 | import boards | |
7 |
|
8 | |||
|
9 | ||||
8 | __author__ = 'neko259' |
|
10 | __author__ = 'neko259' | |
9 |
|
11 | |||
10 |
|
12 | |||
11 | AUTOLINK_PATTERN = r'(https?://\S+)' |
|
13 | AUTOLINK_PATTERN = r'(https?://\S+)' | |
12 | QUOTE_PATTERN = r'^(?<!>)(>[^>].*)$' |
|
14 | QUOTE_PATTERN = r'^(?<!>)(>[^>].*)$' | |
13 | REFLINK_PATTERN = r'((>>)(\d+))' |
|
15 | REFLINK_PATTERN = r'((>>)(\d+))' | |
14 | SPOILER_PATTERN = r'%%([^(%%)]+)%%' |
|
16 | SPOILER_PATTERN = r'%%([^(%%)]+)%%' | |
15 | COMMENT_PATTERN = r'^(//(.+))' |
|
17 | COMMENT_PATTERN = r'^(//(.+))' | |
16 | STRIKETHROUGH_PATTERN = r'~(.+)~' |
|
18 | STRIKETHROUGH_PATTERN = r'~(.+)~' | |
17 | DASH_PATTERN = r'--' |
|
19 | DASH_PATTERN = r'--' | |
18 |
|
20 | |||
19 |
|
21 | |||
20 | class TextFormatter(): |
|
22 | class TextFormatter(): | |
21 | """ |
|
23 | """ | |
22 | An interface for formatter that can be used in the text format panel |
|
24 | An interface for formatter that can be used in the text format panel | |
23 | """ |
|
25 | """ | |
24 |
|
26 | |||
|
27 | def __init__(self): | |||
|
28 | pass | |||
|
29 | ||||
25 | name = '' |
|
30 | name = '' | |
26 |
|
31 | |||
27 | # Left and right tags for the button preview |
|
32 | # Left and right tags for the button preview | |
28 | preview_left = '' |
|
33 | preview_left = '' | |
29 | preview_right = '' |
|
34 | preview_right = '' | |
30 |
|
35 | |||
31 | # Left and right characters for the textarea input |
|
36 | # Left and right characters for the textarea input | |
32 | format_left = '' |
|
37 | format_left = '' | |
33 | format_right = '' |
|
38 | format_right = '' | |
34 |
|
39 | |||
35 |
|
40 | |||
36 | class AutolinkPattern(Pattern): |
|
41 | class AutolinkPattern(Pattern): | |
37 | def handleMatch(self, m): |
|
42 | def handleMatch(self, m): | |
38 | link_element = etree.Element('a') |
|
43 | link_element = etree.Element('a') | |
39 | href = m.group(2) |
|
44 | href = m.group(2) | |
40 | link_element.set('href', href) |
|
45 | link_element.set('href', href) | |
41 | link_element.text = href |
|
46 | link_element.text = href | |
42 |
|
47 | |||
43 | return link_element |
|
48 | return link_element | |
44 |
|
49 | |||
45 |
|
50 | |||
46 | class QuotePattern(Pattern, TextFormatter): |
|
51 | class QuotePattern(Pattern, TextFormatter): | |
47 | name = '' |
|
52 | name = '' | |
48 | preview_left = '<span class="quote">> ' |
|
53 | preview_left = '<span class="quote">> ' | |
49 | preview_right = '</span>' |
|
54 | preview_right = '</span>' | |
50 |
|
55 | |||
51 | format_left = '>' |
|
56 | format_left = '>' | |
52 |
|
57 | |||
53 | def handleMatch(self, m): |
|
58 | def handleMatch(self, m): | |
54 | quote_element = etree.Element('span') |
|
59 | quote_element = etree.Element('span') | |
55 | quote_element.set('class', 'quote') |
|
60 | quote_element.set('class', 'quote') | |
56 | quote_element.text = m.group(2) |
|
61 | quote_element.text = m.group(2) | |
57 |
|
62 | |||
58 | return quote_element |
|
63 | return quote_element | |
59 |
|
64 | |||
60 |
|
65 | |||
61 | class ReflinkPattern(Pattern): |
|
66 | class ReflinkPattern(Pattern): | |
62 | def handleMatch(self, m): |
|
67 | def handleMatch(self, m): | |
63 | post_id = m.group(4) |
|
68 | post_id = m.group(4) | |
64 |
|
69 | |||
65 | posts = boards.models.Post.objects.filter(id=post_id) |
|
70 | posts = boards.models.Post.objects.filter(id=post_id) | |
66 | if posts.count() > 0: |
|
71 | if posts.count() > 0: | |
67 | ref_element = etree.Element('a') |
|
72 | ref_element = etree.Element('a') | |
68 |
|
73 | |||
69 | post = posts[0] |
|
74 | post = posts[0] | |
70 |
|
75 | |||
71 | ref_element.set('href', post.get_url()) |
|
76 | ref_element.set('href', post.get_url()) | |
72 | ref_element.text = m.group(2) |
|
77 | ref_element.text = m.group(2) | |
73 |
|
78 | |||
74 | return ref_element |
|
79 | return ref_element | |
75 |
|
80 | |||
76 |
|
81 | |||
77 | class SpoilerPattern(Pattern, TextFormatter): |
|
82 | class SpoilerPattern(Pattern, TextFormatter): | |
78 | name = 's' |
|
83 | name = 's' | |
79 | preview_left = '<span class="spoiler">' |
|
84 | preview_left = '<span class="spoiler">' | |
80 | preview_right = '</span>' |
|
85 | preview_right = '</span>' | |
81 |
|
86 | |||
82 | format_left = '%%' |
|
87 | format_left = '%%' | |
83 | format_right = '%%' |
|
88 | format_right = '%%' | |
84 |
|
89 | |||
85 | def handleMatch(self, m): |
|
90 | def handleMatch(self, m): | |
86 | quote_element = etree.Element('span') |
|
91 | quote_element = etree.Element('span') | |
87 | quote_element.set('class', 'spoiler') |
|
92 | quote_element.set('class', 'spoiler') | |
88 | quote_element.text = m.group(2) |
|
93 | quote_element.text = m.group(2) | |
89 |
|
94 | |||
90 | return quote_element |
|
95 | return quote_element | |
91 |
|
96 | |||
92 |
|
97 | |||
93 | class CommentPattern(Pattern, TextFormatter): |
|
98 | class CommentPattern(Pattern, TextFormatter): | |
94 | name = '' |
|
99 | name = '' | |
95 | preview_left = '<span class="comment">// ' |
|
100 | preview_left = '<span class="comment">// ' | |
96 | preview_right = '</span>' |
|
101 | preview_right = '</span>' | |
97 |
|
102 | |||
98 | format_left = '//' |
|
103 | format_left = '//' | |
99 |
|
104 | |||
100 | def handleMatch(self, m): |
|
105 | def handleMatch(self, m): | |
101 | quote_element = etree.Element('span') |
|
106 | quote_element = etree.Element('span') | |
102 | quote_element.set('class', 'comment') |
|
107 | quote_element.set('class', 'comment') | |
103 | quote_element.text = '//' + m.group(3) |
|
108 | quote_element.text = '//' + m.group(3) | |
104 |
|
109 | |||
105 | return quote_element |
|
110 | return quote_element | |
106 |
|
111 | |||
107 |
|
112 | |||
108 | class StrikeThroughPattern(Pattern, TextFormatter): |
|
113 | class StrikeThroughPattern(Pattern, TextFormatter): | |
109 | name = 's' |
|
114 | name = 's' | |
110 | preview_left = '<span class="strikethrough">' |
|
115 | preview_left = '<span class="strikethrough">' | |
111 | preview_right = '</span>' |
|
116 | preview_right = '</span>' | |
112 |
|
117 | |||
113 | format_left = '~' |
|
118 | format_left = '~' | |
114 | format_right = '~' |
|
119 | format_right = '~' | |
115 |
|
120 | |||
116 | def handleMatch(self, m): |
|
121 | def handleMatch(self, m): | |
117 | quote_element = etree.Element('span') |
|
122 | quote_element = etree.Element('span') | |
118 | quote_element.set('class', 'strikethrough') |
|
123 | quote_element.set('class', 'strikethrough') | |
119 | quote_element.text = m.group(2) |
|
124 | quote_element.text = m.group(2) | |
120 |
|
125 | |||
121 | return quote_element |
|
126 | return quote_element | |
122 |
|
127 | |||
123 |
|
128 | |||
124 | class ItalicPattern(TextFormatter): |
|
129 | class ItalicPattern(TextFormatter): | |
125 | name = 'i' |
|
130 | name = 'i' | |
126 | preview_left = '<i>' |
|
131 | preview_left = '<i>' | |
127 | preview_right = '</i>' |
|
132 | preview_right = '</i>' | |
128 |
|
133 | |||
129 | format_left = '_' |
|
134 | format_left = '_' | |
130 | format_right = '_' |
|
135 | format_right = '_' | |
131 |
|
136 | |||
132 |
|
137 | |||
133 | class BoldPattern(TextFormatter): |
|
138 | class BoldPattern(TextFormatter): | |
134 | name = 'b' |
|
139 | name = 'b' | |
135 | preview_left = '<b>' |
|
140 | preview_left = '<b>' | |
136 | preview_right = '</b>' |
|
141 | preview_right = '</b>' | |
137 |
|
142 | |||
138 | format_left = '__' |
|
143 | format_left = '__' | |
139 | format_right = '__' |
|
144 | format_right = '__' | |
140 |
|
145 | |||
141 |
|
146 | |||
142 | class CodePattern(TextFormatter): |
|
147 | class CodePattern(TextFormatter): | |
143 | name = 'code' |
|
148 | name = 'code' | |
144 | preview_left = '<code>' |
|
149 | preview_left = '<code>' | |
145 | preview_right = '</code>' |
|
150 | preview_right = '</code>' | |
146 |
|
151 | |||
147 | format_left = ' ' |
|
152 | format_left = ' ' | |
148 |
|
153 | |||
149 |
|
154 | |||
150 | class DashPattern(Pattern): |
|
155 | class DashPattern(Pattern): | |
151 | def handleMatch(self, m): |
|
156 | def handleMatch(self, m): | |
152 | return u'—' |
|
157 | return u'—' | |
153 |
|
158 | |||
154 |
|
159 | |||
155 | class NeboardMarkdown(markdown.Extension): |
|
160 | class NeboardMarkdown(markdown.Extension): | |
156 | def extendMarkdown(self, md, md_globals): |
|
161 | def extendMarkdown(self, md, md_globals): | |
157 | self._add_neboard_patterns(md) |
|
162 | self._add_neboard_patterns(md) | |
158 | self._delete_patterns(md) |
|
163 | self._delete_patterns(md) | |
159 |
|
164 | |||
160 | def _delete_patterns(self, md): |
|
165 | def _delete_patterns(self, md): | |
161 | del md.parser.blockprocessors['quote'] |
|
166 | del md.parser.blockprocessors['quote'] | |
162 |
|
167 | |||
163 | del md.inlinePatterns['image_link'] |
|
168 | del md.inlinePatterns['image_link'] | |
164 | del md.inlinePatterns['image_reference'] |
|
169 | del md.inlinePatterns['image_reference'] | |
165 |
|
170 | |||
166 | def _add_neboard_patterns(self, md): |
|
171 | def _add_neboard_patterns(self, md): | |
167 | autolink = AutolinkPattern(AUTOLINK_PATTERN, md) |
|
172 | autolink = AutolinkPattern(AUTOLINK_PATTERN, md) | |
168 | quote = QuotePattern(QUOTE_PATTERN, md) |
|
173 | quote = QuotePattern(QUOTE_PATTERN, md) | |
169 | reflink = ReflinkPattern(REFLINK_PATTERN, md) |
|
174 | reflink = ReflinkPattern(REFLINK_PATTERN, md) | |
170 | spoiler = SpoilerPattern(SPOILER_PATTERN, md) |
|
175 | spoiler = SpoilerPattern(SPOILER_PATTERN, md) | |
171 | comment = CommentPattern(COMMENT_PATTERN, md) |
|
176 | comment = CommentPattern(COMMENT_PATTERN, md) | |
172 | strikethrough = StrikeThroughPattern(STRIKETHROUGH_PATTERN, md) |
|
177 | strikethrough = StrikeThroughPattern(STRIKETHROUGH_PATTERN, md) | |
173 | dash = DashPattern(DASH_PATTERN, md) |
|
178 | dash = DashPattern(DASH_PATTERN, md) | |
174 |
|
179 | |||
175 | md.inlinePatterns[u'autolink_ext'] = autolink |
|
180 | md.inlinePatterns[u'autolink_ext'] = autolink | |
176 | md.inlinePatterns[u'spoiler'] = spoiler |
|
181 | md.inlinePatterns[u'spoiler'] = spoiler | |
177 | md.inlinePatterns[u'strikethrough'] = strikethrough |
|
182 | md.inlinePatterns[u'strikethrough'] = strikethrough | |
178 | md.inlinePatterns[u'comment'] = comment |
|
183 | md.inlinePatterns[u'comment'] = comment | |
179 | md.inlinePatterns[u'reflink'] = reflink |
|
184 | md.inlinePatterns[u'reflink'] = reflink | |
180 | md.inlinePatterns[u'quote'] = quote |
|
185 | md.inlinePatterns[u'quote'] = quote | |
181 | md.inlinePatterns[u'dash'] = dash |
|
186 | md.inlinePatterns[u'dash'] = dash | |
182 |
|
187 | |||
183 |
|
188 | |||
184 | def make_extension(configs=None): |
|
189 | def make_extension(configs=None): | |
185 | return NeboardMarkdown(configs=configs) |
|
190 | return NeboardMarkdown(configs=configs) | |
186 |
|
191 | |||
187 | neboard_extension = make_extension() |
|
192 | neboard_extension = make_extension() | |
188 |
|
193 | |||
189 |
|
194 | |||
190 | def markdown_extended(markup): |
|
195 | def markdown_extended(markup): | |
191 | return markdown.markdown(markup, [neboard_extension, 'nl2br'], |
|
196 | return markdown.markdown(markup, [neboard_extension, 'nl2br'], | |
192 | safe_mode='escape') |
|
197 | safe_mode='escape') | |
193 |
|
198 | |||
194 | formatters = [ |
|
199 | formatters = [ | |
195 | QuotePattern, |
|
200 | QuotePattern, | |
196 | SpoilerPattern, |
|
201 | SpoilerPattern, | |
197 | ItalicPattern, |
|
202 | ItalicPattern, | |
198 | BoldPattern, |
|
203 | BoldPattern, | |
199 | CommentPattern, |
|
204 | CommentPattern, | |
200 | StrikeThroughPattern, |
|
205 | StrikeThroughPattern, | |
201 | CodePattern, |
|
206 | CodePattern, | |
202 | ] |
|
207 | ] |
@@ -1,42 +1,46 | |||||
1 | from django.shortcuts import redirect |
|
1 | from django.shortcuts import redirect | |
2 | from boards import utils |
|
2 | from boards import utils | |
3 | from boards.models import Ban |
|
3 | from boards.models import Ban | |
4 | from django.utils.html import strip_spaces_between_tags |
|
4 | from django.utils.html import strip_spaces_between_tags | |
5 | from django.conf import settings |
|
5 | from django.conf import settings | |
6 | from boards.views.banned import BannedView |
|
6 | from boards.views.banned import BannedView | |
7 |
|
7 | |||
8 | RESPONSE_CONTENT_TYPE = 'Content-Type' |
|
8 | RESPONSE_CONTENT_TYPE = 'Content-Type' | |
9 |
|
9 | |||
10 | TYPE_HTML = 'text/html' |
|
10 | TYPE_HTML = 'text/html' | |
11 |
|
11 | |||
12 |
|
12 | |||
13 | class BanMiddleware: |
|
13 | class BanMiddleware: | |
14 | """ |
|
14 | """ | |
15 | This is run before showing the thread. Banned users don't need to see |
|
15 | This is run before showing the thread. Banned users don't need to see | |
16 | anything |
|
16 | anything | |
17 | """ |
|
17 | """ | |
18 |
|
18 | |||
|
19 | def __init__(self): | |||
|
20 | pass | |||
|
21 | ||||
19 | def process_view(self, request, view_func, view_args, view_kwargs): |
|
22 | def process_view(self, request, view_func, view_args, view_kwargs): | |
20 |
|
23 | |||
21 | if view_func != BannedView.as_view: |
|
24 | if view_func != BannedView.as_view: | |
22 | ip = utils.get_client_ip(request) |
|
25 | ip = utils.get_client_ip(request) | |
23 | bans = Ban.objects.filter(ip=ip) |
|
26 | bans = Ban.objects.filter(ip=ip) | |
24 |
|
27 | |||
25 | if bans.exists(): |
|
28 | if bans.exists(): | |
26 | ban = bans[0] |
|
29 | ban = bans[0] | |
27 | if not ban.can_read: |
|
30 | if not ban.can_read: | |
28 | return redirect('banned') |
|
31 | return redirect('banned') | |
29 |
|
32 | |||
30 |
|
33 | |||
31 | class MinifyHTMLMiddleware(object): |
|
34 | class MinifyHTMLMiddleware(object): | |
32 | def process_response(self, request, response): |
|
35 | def process_response(self, request, response): | |
33 | try: |
|
36 | try: | |
34 | compress_html = settings.COMPRESS_HTML |
|
37 | compress_html = settings.COMPRESS_HTML | |
35 | except AttributeError: |
|
38 | except AttributeError: | |
36 | compress_html = False |
|
39 | compress_html = False | |
37 |
|
40 | |||
38 | if RESPONSE_CONTENT_TYPE in response\ |
|
41 | if RESPONSE_CONTENT_TYPE in response\ | |
39 |
and TYPE_HTML in response[RESPONSE_CONTENT_TYPE] |
|
42 | and TYPE_HTML in response[RESPONSE_CONTENT_TYPE]\ | |
|
43 | and compress_html: | |||
40 | response.content = strip_spaces_between_tags( |
|
44 | response.content = strip_spaces_between_tags( | |
41 | response.content.strip()) |
|
45 | response.content.strip()) | |
42 | return response No newline at end of file |
|
46 | return response |
@@ -1,23 +1,26 | |||||
1 | import sys |
|
1 | import sys | |
2 | from cStringIO import StringIO |
|
2 | from cStringIO import StringIO | |
3 | from django.conf import settings |
|
3 | from django.conf import settings | |
4 | import line_profiler |
|
4 | import line_profiler | |
5 |
|
5 | |||
6 |
|
6 | |||
7 |
class ProfilerMiddleware( |
|
7 | class ProfilerMiddleware(): | |
|
8 | def __init__(self): | |||
|
9 | self.profiler = None | |||
|
10 | ||||
8 | def process_view(self, request, callback, callback_args, callback_kwargs): |
|
11 | def process_view(self, request, callback, callback_args, callback_kwargs): | |
9 | if settings.DEBUG and 'prof' in request.GET: |
|
12 | if settings.DEBUG and 'prof' in request.GET: | |
10 | self.profiler = line_profiler.LineProfiler() |
|
13 | self.profiler = line_profiler.LineProfiler() | |
11 | self.profiler.add_function(callback) |
|
14 | self.profiler.add_function(callback) | |
12 | self.profiler.enable() |
|
15 | self.profiler.enable() | |
13 | args = (request,) + callback_args |
|
16 | args = (request,) + callback_args | |
14 | return callback(*args, **callback_kwargs) |
|
17 | return callback(*args, **callback_kwargs) | |
15 |
|
18 | |||
16 | def process_response(self, request, response): |
|
19 | def process_response(self, request, response): | |
17 | if settings.DEBUG and 'prof' in request.GET: |
|
20 | if settings.DEBUG and 'prof' in request.GET: | |
18 | out = StringIO() |
|
21 | out = StringIO() | |
19 | old_stdout, sys.stdout = sys.stdout, out |
|
22 | old_stdout, sys.stdout = sys.stdout, out | |
20 | self.profiler.print_stats() |
|
23 | self.profiler.print_stats() | |
21 | sys.stdout = old_stdout |
|
24 | sys.stdout = old_stdout | |
22 | response.content = '<pre>%s</pre>' % out.getvalue() |
|
25 | response.content = '<pre>%s</pre>' % out.getvalue() | |
23 | return response |
|
26 | return response |
@@ -1,33 +1,41 | |||||
1 | from django.shortcuts import render |
|
1 | from django.shortcuts import render | |
2 | from django.template import RequestContext |
|
2 | from django.template import RequestContext | |
3 | from django.views.generic import View |
|
3 | from django.views.generic import View | |
4 | from haystack.query import SearchQuerySet |
|
4 | from haystack.query import SearchQuerySet | |
5 | from boards.abstracts.paginator import get_paginator |
|
5 | from boards.abstracts.paginator import get_paginator | |
6 | from boards.forms import SearchForm, PlainErrorList |
|
6 | from boards.forms import SearchForm, PlainErrorList | |
7 |
|
7 | |||
|
8 | FORM_QUERY = 'query' | |||
|
9 | ||||
|
10 | CONTEXT_QUERY = 'query' | |||
|
11 | CONTEXT_FORM = 'form' | |||
|
12 | CONTEXT_PAGE = 'page' | |||
|
13 | ||||
|
14 | REQUEST_PAGE = 'page' | |||
|
15 | ||||
8 | __author__ = 'neko259' |
|
16 | __author__ = 'neko259' | |
9 |
|
17 | |||
10 | TEMPLATE = 'search/search.html' |
|
18 | TEMPLATE = 'search/search.html' | |
11 |
|
19 | |||
12 |
|
20 | |||
13 | class BoardSearchView(View): |
|
21 | class BoardSearchView(View): | |
14 | def get(self, request): |
|
22 | def get(self, request): | |
15 | context = RequestContext(request) |
|
23 | context = RequestContext(request) | |
16 | form = SearchForm(request.GET, error_class=PlainErrorList) |
|
24 | form = SearchForm(request.GET, error_class=PlainErrorList) | |
17 |
context[ |
|
25 | context[CONTEXT_FORM] = form | |
18 |
|
26 | |||
19 | if form.is_valid(): |
|
27 | if form.is_valid(): | |
20 |
query = form.cleaned_data[ |
|
28 | query = form.cleaned_data[FORM_QUERY] | |
21 | if len(query) >= 3: |
|
29 | if len(query) >= 3: | |
22 | results = SearchQuerySet().auto_query(query).order_by('-id') \ |
|
30 | results = SearchQuerySet().auto_query(query).order_by('-id') \ | |
23 | .highlight() |
|
31 | .highlight() | |
24 | paginator = get_paginator(results, 10) |
|
32 | paginator = get_paginator(results, 10) | |
25 |
|
33 | |||
26 |
if |
|
34 | if REQUEST_PAGE in request.GET: | |
27 |
page = int(request.GET[ |
|
35 | page = int(request.GET[REQUEST_PAGE]) | |
28 | else: |
|
36 | else: | |
29 | page = 1 |
|
37 | page = 1 | |
30 |
context[ |
|
38 | context[CONTEXT_PAGE] = paginator.page(page) | |
31 |
context[ |
|
39 | context[CONTEXT_QUERY] = query | |
32 |
|
40 | |||
33 | return render(request, TEMPLATE, context) No newline at end of file |
|
41 | return render(request, TEMPLATE, context) |
@@ -1,9 +1,10 | |||||
|
1 | line_profiler | |||
1 | haystack |
|
2 | haystack | |
2 | pillow |
|
3 | pillow | |
3 | django>=1.6 |
|
4 | django>=1.6 | |
4 | django_cleanup |
|
5 | django_cleanup | |
5 | django-markupfield |
|
6 | django-markupfield | |
6 | markdown |
|
7 | markdown | |
7 | python-markdown |
|
8 | python-markdown | |
8 | django-simple-captcha |
|
9 | django-simple-captcha | |
9 | line-profiler |
|
10 | line-profiler |
General Comments 0
You need to be logged in to leave comments.
Login now