##// END OF EJS Templates
Moved some settings to boards.settings
neko259 -
r333:c4e2cbcd default
parent child Browse files
Show More
@@ -1,23 +1,25 b''
1 from django.contrib import admin
1 from django.contrib import admin
2 from boards.models import Post, Tag, User, Ban
2 from boards.models import Post, Tag, User, Ban
3
3
4
4 class PostAdmin(admin.ModelAdmin):
5 class PostAdmin(admin.ModelAdmin):
5
6
6 list_display = ('id', 'title', 'text')
7 list_display = ('id', 'title', 'text')
7 list_filter = ('pub_time', 'tags')
8 list_filter = ('pub_time', 'tags')
8 search_fields = ('id', 'title', 'text')
9 search_fields = ('id', 'title', 'text')
9
10
11
10 class TagAdmin(admin.ModelAdmin):
12 class TagAdmin(admin.ModelAdmin):
11
13
12 list_display = ('name', 'linked')
14 list_display = ('name', 'linked')
13 list_filter = ('linked',)
15 list_filter = ('linked',)
14
16
15
17
16 class UserAdmin(admin.ModelAdmin):
18 class UserAdmin(admin.ModelAdmin):
17
19
18 list_display = ('user_id', 'rank')
20 list_display = ('user_id', 'rank')
19
21
20 admin.site.register(Post, PostAdmin)
22 admin.site.register(Post, PostAdmin)
21 admin.site.register(Tag, TagAdmin)
23 admin.site.register(Tag, TagAdmin)
22 admin.site.register(User, UserAdmin)
24 admin.site.register(User, UserAdmin)
23 admin.site.register(Ban)
25 admin.site.register(Ban)
@@ -1,254 +1,252 b''
1 import re
1 import re
2 from captcha.fields import CaptchaField
2 from captcha.fields import CaptchaField
3 from django import forms
3 from django import forms
4 from django.forms.util import ErrorList
4 from django.forms.util import ErrorList
5 from django.utils.translation import ugettext_lazy as _
5 from django.utils.translation import ugettext_lazy as _
6 import time
6 import time
7 from boards.models import TITLE_MAX_LENGTH, User
7 from boards.models import TITLE_MAX_LENGTH, User
8 from neboard import settings
8 from neboard import settings
9 from boards import utils
9 from boards import utils
10 import boards.settings as board_settings
10
11
11 LAST_POST_TIME = "last_post_time"
12 LAST_POST_TIME = "last_post_time"
12 LAST_LOGIN_TIME = "last_login_time"
13 LAST_LOGIN_TIME = "last_login_time"
13
14
14 LOGIN_DELAY = 60 * 60
15
16 MAX_TEXT_LENGTH = 30000
17 MAX_IMAGE_SIZE = 8 * 1024 * 1024
18
19
15
20 class PlainErrorList(ErrorList):
16 class PlainErrorList(ErrorList):
21 def __unicode__(self):
17 def __unicode__(self):
22 return self.as_text()
18 return self.as_text()
23
19
24 def as_text(self):
20 def as_text(self):
25 return ''.join([u'(!) %s ' % e for e in self])
21 return ''.join([u'(!) %s ' % e for e in self])
26
22
27
23
28 class NeboardForm(forms.Form):
24 class NeboardForm(forms.Form):
29
25
30 def as_p(self):
26 def as_p(self):
31 "Returns this form rendered as HTML <p>s."
27 "Returns this form rendered as HTML <p>s."
32 return self._html_output(
28 return self._html_output(
33 normal_row='<div class="form-row">'
29 normal_row='<div class="form-row">'
34 '<div class="form-label">'
30 '<div class="form-label">'
35 '%(label)s'
31 '%(label)s'
36 '</div>'
32 '</div>'
37 '<div class="form-input">'
33 '<div class="form-input">'
38 '%(field)s'
34 '%(field)s'
39 '</div>'
35 '</div>'
40 '%(help_text)s'
36 '%(help_text)s'
41 '</div>',
37 '</div>',
42 error_row='<div class="form-errors">%s</div>',
38 error_row='<div class="form-errors">%s</div>',
43 row_ender='</p>',
39 row_ender='</p>',
44 help_text_html=' <span class="helptext">%s</span>',
40 help_text_html=' <span class="helptext">%s</span>',
45 errors_on_separate_row=True)
41 errors_on_separate_row=True)
46
42
47
43
48 class PostForm(NeboardForm):
44 class PostForm(NeboardForm):
49
45
50 title = forms.CharField(max_length=TITLE_MAX_LENGTH, required=False,
46 title = forms.CharField(max_length=TITLE_MAX_LENGTH, required=False,
51 label=_('Title'))
47 label=_('Title'))
52 text = forms.CharField(widget=forms.Textarea, required=False,
48 text = forms.CharField(widget=forms.Textarea, required=False,
53 label=_('Text'))
49 label=_('Text'))
54 image = forms.ImageField(required=False, label=_('Image'))
50 image = forms.ImageField(required=False, label=_('Image'))
55
51
56 # This field is for spam prevention only
52 # This field is for spam prevention only
57 email = forms.CharField(max_length=100, required=False, label=_('e-mail'),
53 email = forms.CharField(max_length=100, required=False, label=_('e-mail'),
58 widget=forms.TextInput(attrs={
54 widget=forms.TextInput(attrs={
59 'class': 'form-email'}))
55 'class': 'form-email'}))
60
56
61 session = None
57 session = None
62 need_to_ban = False
58 need_to_ban = False
63
59
64 def clean_title(self):
60 def clean_title(self):
65 title = self.cleaned_data['title']
61 title = self.cleaned_data['title']
66 if title:
62 if title:
67 if len(title) > TITLE_MAX_LENGTH:
63 if len(title) > TITLE_MAX_LENGTH:
68 raise forms.ValidationError(_('Title must have less than %s '
64 raise forms.ValidationError(_('Title must have less than %s '
69 'characters') %
65 'characters') %
70 str(TITLE_MAX_LENGTH))
66 str(TITLE_MAX_LENGTH))
71 return title
67 return title
72
68
73 def clean_text(self):
69 def clean_text(self):
74 text = self.cleaned_data['text']
70 text = self.cleaned_data['text']
75 if text:
71 if text:
76 if len(text) > MAX_TEXT_LENGTH:
72 if len(text) > board_settings.MAX_TEXT_LENGTH:
77 raise forms.ValidationError(_('Text must have less than %s '
73 raise forms.ValidationError(_('Text must have less than %s '
78 'characters') %
74 'characters') %
79 str(MAX_TEXT_LENGTH))
75 str(board_settings
76 .MAX_TEXT_LENGTH))
80 return text
77 return text
81
78
82 def clean_image(self):
79 def clean_image(self):
83 image = self.cleaned_data['image']
80 image = self.cleaned_data['image']
84 if image:
81 if image:
85 if image._size > MAX_IMAGE_SIZE:
82 if image._size > board_settings.MAX_IMAGE_SIZE:
86 raise forms.ValidationError(_('Image must be less than %s '
83 raise forms.ValidationError(
87 'bytes') % str(MAX_IMAGE_SIZE))
84 _('Image must be less than %s bytes')
85 % str(board_settings.MAX_IMAGE_SIZE))
88 return image
86 return image
89
87
90 def clean(self):
88 def clean(self):
91 cleaned_data = super(PostForm, self).clean()
89 cleaned_data = super(PostForm, self).clean()
92
90
93 if not self.session:
91 if not self.session:
94 raise forms.ValidationError('Humans have sessions')
92 raise forms.ValidationError('Humans have sessions')
95
93
96 if cleaned_data['email']:
94 if cleaned_data['email']:
97 self.need_to_ban = True
95 self.need_to_ban = True
98 raise forms.ValidationError('A human cannot enter a hidden field')
96 raise forms.ValidationError('A human cannot enter a hidden field')
99
97
100 if not self.errors:
98 if not self.errors:
101 self._clean_text_image()
99 self._clean_text_image()
102
100
103 if not self.errors and self.session:
101 if not self.errors and self.session:
104 self._validate_posting_speed()
102 self._validate_posting_speed()
105
103
106 return cleaned_data
104 return cleaned_data
107
105
108 def _clean_text_image(self):
106 def _clean_text_image(self):
109 text = self.cleaned_data.get('text')
107 text = self.cleaned_data.get('text')
110 image = self.cleaned_data.get('image')
108 image = self.cleaned_data.get('image')
111
109
112 if (not text) and (not image):
110 if (not text) and (not image):
113 error_message = _('Either text or image must be entered.')
111 error_message = _('Either text or image must be entered.')
114 self._errors['text'] = self.error_class([error_message])
112 self._errors['text'] = self.error_class([error_message])
115
113
116 def _validate_posting_speed(self):
114 def _validate_posting_speed(self):
117 can_post = True
115 can_post = True
118
116
119 if LAST_POST_TIME in self.session:
117 if LAST_POST_TIME in self.session:
120 now = time.time()
118 now = time.time()
121 last_post_time = self.session[LAST_POST_TIME]
119 last_post_time = self.session[LAST_POST_TIME]
122
120
123 current_delay = int(now - last_post_time)
121 current_delay = int(now - last_post_time)
124
122
125 if current_delay < settings.POSTING_DELAY:
123 if current_delay < settings.POSTING_DELAY:
126 error_message = _('Wait %s seconds after last posting') % str(
124 error_message = _('Wait %s seconds after last posting') % str(
127 settings.POSTING_DELAY - current_delay)
125 settings.POSTING_DELAY - current_delay)
128 self._errors['text'] = self.error_class([error_message])
126 self._errors['text'] = self.error_class([error_message])
129
127
130 can_post = False
128 can_post = False
131
129
132 if can_post:
130 if can_post:
133 self.session[LAST_POST_TIME] = time.time()
131 self.session[LAST_POST_TIME] = time.time()
134
132
135
133
136 class ThreadForm(PostForm):
134 class ThreadForm(PostForm):
137
135
138 regex_tags = re.compile(ur'^[\w\s\d]+$', re.UNICODE)
136 regex_tags = re.compile(ur'^[\w\s\d]+$', re.UNICODE)
139
137
140 tags = forms.CharField(max_length=100, label=_('Tags'))
138 tags = forms.CharField(max_length=100, label=_('Tags'))
141
139
142 def clean_tags(self):
140 def clean_tags(self):
143 tags = self.cleaned_data['tags']
141 tags = self.cleaned_data['tags']
144
142
145 if tags:
143 if tags:
146 if not self.regex_tags.match(tags):
144 if not self.regex_tags.match(tags):
147 raise forms.ValidationError(
145 raise forms.ValidationError(
148 _('Inappropriate characters in tags.'))
146 _('Inappropriate characters in tags.'))
149
147
150 return tags
148 return tags
151
149
152 def clean(self):
150 def clean(self):
153 cleaned_data = super(ThreadForm, self).clean()
151 cleaned_data = super(ThreadForm, self).clean()
154
152
155 return cleaned_data
153 return cleaned_data
156
154
157
155
158 class PostCaptchaForm(PostForm):
156 class PostCaptchaForm(PostForm):
159 captcha = CaptchaField()
157 captcha = CaptchaField()
160
158
161 def __init__(self, *args, **kwargs):
159 def __init__(self, *args, **kwargs):
162 self.request = kwargs['request']
160 self.request = kwargs['request']
163 del kwargs['request']
161 del kwargs['request']
164
162
165 super(PostCaptchaForm, self).__init__(*args, **kwargs)
163 super(PostCaptchaForm, self).__init__(*args, **kwargs)
166
164
167 def clean(self):
165 def clean(self):
168 cleaned_data = super(PostCaptchaForm, self).clean()
166 cleaned_data = super(PostCaptchaForm, self).clean()
169
167
170 success = self.is_valid()
168 success = self.is_valid()
171 utils.update_captcha_access(self.request, success)
169 utils.update_captcha_access(self.request, success)
172
170
173 if success:
171 if success:
174 return cleaned_data
172 return cleaned_data
175 else:
173 else:
176 raise forms.ValidationError(_("Captcha validation failed"))
174 raise forms.ValidationError(_("Captcha validation failed"))
177
175
178
176
179 class ThreadCaptchaForm(ThreadForm):
177 class ThreadCaptchaForm(ThreadForm):
180 captcha = CaptchaField()
178 captcha = CaptchaField()
181
179
182 def __init__(self, *args, **kwargs):
180 def __init__(self, *args, **kwargs):
183 self.request = kwargs['request']
181 self.request = kwargs['request']
184 del kwargs['request']
182 del kwargs['request']
185
183
186 super(ThreadCaptchaForm, self).__init__(*args, **kwargs)
184 super(ThreadCaptchaForm, self).__init__(*args, **kwargs)
187
185
188 def clean(self):
186 def clean(self):
189 cleaned_data = super(ThreadCaptchaForm, self).clean()
187 cleaned_data = super(ThreadCaptchaForm, self).clean()
190
188
191 success = self.is_valid()
189 success = self.is_valid()
192 utils.update_captcha_access(self.request, success)
190 utils.update_captcha_access(self.request, success)
193
191
194 if success:
192 if success:
195 return cleaned_data
193 return cleaned_data
196 else:
194 else:
197 raise forms.ValidationError(_("Captcha validation failed"))
195 raise forms.ValidationError(_("Captcha validation failed"))
198
196
199
197
200 class SettingsForm(NeboardForm):
198 class SettingsForm(NeboardForm):
201
199
202 theme = forms.ChoiceField(choices=settings.THEMES,
200 theme = forms.ChoiceField(choices=settings.THEMES,
203 label=_('Theme'))
201 label=_('Theme'))
204
202
205
203
206 class ModeratorSettingsForm(SettingsForm):
204 class ModeratorSettingsForm(SettingsForm):
207
205
208 moderate = forms.BooleanField(required=False, label=_('Enable moderation '
206 moderate = forms.BooleanField(required=False, label=_('Enable moderation '
209 'panel'))
207 'panel'))
210
208
211
209
212 class LoginForm(NeboardForm):
210 class LoginForm(NeboardForm):
213
211
214 user_id = forms.CharField()
212 user_id = forms.CharField()
215
213
216 session = None
214 session = None
217
215
218 def clean_user_id(self):
216 def clean_user_id(self):
219 user_id = self.cleaned_data['user_id']
217 user_id = self.cleaned_data['user_id']
220 if user_id:
218 if user_id:
221 users = User.objects.filter(user_id=user_id)
219 users = User.objects.filter(user_id=user_id)
222 if len(users) == 0:
220 if len(users) == 0:
223 raise forms.ValidationError(_('No such user found'))
221 raise forms.ValidationError(_('No such user found'))
224
222
225 return user_id
223 return user_id
226
224
227 def _validate_login_speed(self):
225 def _validate_login_speed(self):
228 can_post = True
226 can_post = True
229
227
230 if LAST_LOGIN_TIME in self.session:
228 if LAST_LOGIN_TIME in self.session:
231 now = time.time()
229 now = time.time()
232 last_login_time = self.session[LAST_LOGIN_TIME]
230 last_login_time = self.session[LAST_LOGIN_TIME]
233
231
234 current_delay = int(now - last_login_time)
232 current_delay = int(now - last_login_time)
235
233
236 if current_delay < LOGIN_DELAY:
234 if current_delay < board_settings.LOGIN_TIMEOUT:
237 error_message = _('Wait %s minutes after last login') % str(
235 error_message = _('Wait %s minutes after last login') % str(
238 (LOGIN_DELAY - current_delay) / 60)
236 (board_settings.LOGIN_TIMEOUT - current_delay) / 60)
239 self._errors['user_id'] = self.error_class([error_message])
237 self._errors['user_id'] = self.error_class([error_message])
240
238
241 can_post = False
239 can_post = False
242
240
243 if can_post:
241 if can_post:
244 self.session[LAST_LOGIN_TIME] = time.time()
242 self.session[LAST_LOGIN_TIME] = time.time()
245
243
246 def clean(self):
244 def clean(self):
247 if not self.session:
245 if not self.session:
248 raise forms.ValidationError('Humans have sessions')
246 raise forms.ValidationError('Humans have sessions')
249
247
250 self._validate_login_speed()
248 self._validate_login_speed()
251
249
252 cleaned_data = super(LoginForm, self).clean()
250 cleaned_data = super(LoginForm, self).clean()
253
251
254 return cleaned_data
252 return cleaned_data
@@ -1,23 +1,23 b''
1 import sys
1 import sys
2 import cProfile
3 from cStringIO import StringIO
2 from cStringIO import StringIO
4 from django.conf import settings
3 from django.conf import settings
5 import line_profiler
4 import line_profiler
5
6
6
7 class ProfilerMiddleware(object):
7 class ProfilerMiddleware(object):
8 def process_view(self, request, callback, callback_args, callback_kwargs):
8 def process_view(self, request, callback, callback_args, callback_kwargs):
9 if settings.DEBUG and 'prof' in request.GET:
9 if settings.DEBUG and 'prof' in request.GET:
10 self.profiler = line_profiler.LineProfiler()
10 self.profiler = line_profiler.LineProfiler()
11 self.profiler.add_function(callback)
11 self.profiler.add_function(callback)
12 self.profiler.enable()
12 self.profiler.enable()
13 args = (request,) + callback_args
13 args = (request,) + callback_args
14 return callback(*args, **callback_kwargs)
14 return callback(*args, **callback_kwargs)
15
15
16 def process_response(self, request, response):
16 def process_response(self, request, response):
17 if settings.DEBUG and 'prof' in request.GET:
17 if settings.DEBUG and 'prof' in request.GET:
18 out = StringIO()
18 out = StringIO()
19 old_stdout, sys.stdout = sys.stdout, out
19 old_stdout, sys.stdout = sys.stdout, out
20 self.profiler.print_stats()
20 self.profiler.print_stats()
21 sys.stdout = old_stdout
21 sys.stdout = old_stdout
22 response.content = '<pre>%s</pre>' % out.getvalue()
22 response.content = '<pre>%s</pre>' % out.getvalue()
23 return response
23 return response
@@ -1,78 +1,79 b''
1 from django.contrib.syndication.views import Feed
1 from django.contrib.syndication.views import Feed
2 from django.core.urlresolvers import reverse
2 from django.core.urlresolvers import reverse
3 from django.shortcuts import get_object_or_404
3 from django.shortcuts import get_object_or_404
4 from boards.models import Post, Tag
4 from boards.models import Post, Tag
5 from neboard import settings
5 from neboard import settings
6
6
7 __author__ = 'neko259'
7 __author__ = 'neko259'
8
8
9
9 # TODO Make tests for all of these
10 # TODO Make tests for all of these
10 class AllThreadsFeed(Feed):
11 class AllThreadsFeed(Feed):
11
12
12 title = settings.SITE_NAME + ' - All threads'
13 title = settings.SITE_NAME + ' - All threads'
13 link = '/'
14 link = '/'
14 description_template = 'boards/rss/post.html'
15 description_template = 'boards/rss/post.html'
15
16
16 def items(self):
17 def items(self):
17 return Post.objects.get_threads(order_by='-pub_time')
18 return Post.objects.get_threads(order_by='-pub_time')
18
19
19 def item_title(self, item):
20 def item_title(self, item):
20 return item.title
21 return item.title
21
22
22 def item_link(self, item):
23 def item_link(self, item):
23 return reverse('thread', args={item.id})
24 return reverse('thread', args={item.id})
24
25
25 def item_pubdate(self, item):
26 def item_pubdate(self, item):
26 return item.pub_time
27 return item.pub_time
27
28
28
29
29 class TagThreadsFeed(Feed):
30 class TagThreadsFeed(Feed):
30
31
31 link = '/'
32 link = '/'
32 description_template = 'boards/rss/post.html'
33 description_template = 'boards/rss/post.html'
33
34
34 def items(self, obj):
35 def items(self, obj):
35 return Post.objects.get_threads(tag=obj,
36 return Post.objects.get_threads(tag=obj,
36 order_by='-pub_time')
37 order_by='-pub_time')
37
38
38 def get_object(self, request, tag_name):
39 def get_object(self, request, tag_name):
39 return get_object_or_404(Tag, name=tag_name)
40 return get_object_or_404(Tag, name=tag_name)
40
41
41 def item_title(self, item):
42 def item_title(self, item):
42 return item.title
43 return item.title
43
44
44 def item_link(self, item):
45 def item_link(self, item):
45 return reverse('thread', args={item.id})
46 return reverse('thread', args={item.id})
46
47
47 def item_pubdate(self, item):
48 def item_pubdate(self, item):
48 return item.pub_time
49 return item.pub_time
49
50
50 def title(self, obj):
51 def title(self, obj):
51 return obj.name
52 return obj.name
52
53
53
54
54 class ThreadPostsFeed(Feed):
55 class ThreadPostsFeed(Feed):
55
56
56 link = '/'
57 link = '/'
57 description_template = 'boards/rss/post.html'
58 description_template = 'boards/rss/post.html'
58
59
59 def items(self, obj):
60 def items(self, obj):
60 return Post.objects.get_thread(opening_post_id=obj)
61 return Post.objects.get_thread(opening_post_id=obj)
61
62
62 def get_object(self, request, post_id):
63 def get_object(self, request, post_id):
63 return post_id
64 return post_id
64
65
65 def item_title(self, item):
66 def item_title(self, item):
66 return item.title
67 return item.title
67
68
68 def item_link(self, item):
69 def item_link(self, item):
69 if item.thread:
70 if item.thread:
70 return reverse('thread', args={item.thread.id}) + "#" + str(item.id)
71 return reverse('thread', args={item.thread.id}) + "#" + str(item.id)
71 else:
72 else:
72 return reverse('thread', args={item.id})
73 return reverse('thread', args={item.id})
73
74
74 def item_pubdate(self, item):
75 def item_pubdate(self, item):
75 return item.pub_time
76 return item.pub_time
76
77
77 def title(self, obj):
78 def title(self, obj):
78 return get_object_or_404(Post, id=obj).title
79 return get_object_or_404(Post, id=obj).title
@@ -1,1 +1,4 b''
1 CACHE_TIMEOUT = 600 # Timeout for caching, if cache is used No newline at end of file
1 CACHE_TIMEOUT = 600 # Timeout for caching, if cache is used
2 LOGIN_TIMEOUT = 3600 # Timeout between login tries
3 MAX_TEXT_LENGTH = 30000 # Max post length in characters
4 MAX_IMAGE_SIZE = 8 * 1024 * 1024 # Max image size
General Comments 0
You need to be logged in to leave comments. Login now