##// END OF EJS Templates
Moved get_post to an API views module
neko259 -
r491:3cc935fe 1.6-dev
parent child Browse files
Show More
@@ -1,63 +1,63 b''
1 from django.conf.urls import patterns, url, include
1 from django.conf.urls import patterns, url, include
2 from boards import views
2 from boards import views
3 from boards.rss import AllThreadsFeed, TagThreadsFeed, ThreadPostsFeed
3 from boards.rss import AllThreadsFeed, TagThreadsFeed, ThreadPostsFeed
4 from boards.views.api import api_get_threaddiff
4 from boards.views.api import api_get_threaddiff, get_post
5
5
6 js_info_dict = {
6 js_info_dict = {
7 'packages': ('boards',),
7 'packages': ('boards',),
8 }
8 }
9
9
10 urlpatterns = patterns('',
10 urlpatterns = patterns('',
11
11
12 # /boards/
12 # /boards/
13 url(r'^$', views.index, name='index'),
13 url(r'^$', views.index, name='index'),
14 # /boards/page/
14 # /boards/page/
15 url(r'^page/(?P<page>\w+)/$', views.index, name='index'),
15 url(r'^page/(?P<page>\w+)/$', views.index, name='index'),
16
16
17 url(r'^archive/$', views.archive, name='archive'),
17 url(r'^archive/$', views.archive, name='archive'),
18 url(r'^archive/page/(?P<page>\w+)/$', views.archive, name='archive'),
18 url(r'^archive/page/(?P<page>\w+)/$', views.archive, name='archive'),
19
19
20 # login page
20 # login page
21 url(r'^login/$', views.login, name='login'),
21 url(r'^login/$', views.login, name='login'),
22
22
23 # /boards/tag/tag_name/
23 # /boards/tag/tag_name/
24 url(r'^tag/(?P<tag_name>\w+)/$', views.tag, name='tag'),
24 url(r'^tag/(?P<tag_name>\w+)/$', views.tag, name='tag'),
25 # /boards/tag/tag_id/page/
25 # /boards/tag/tag_id/page/
26 url(r'^tag/(?P<tag_name>\w+)/page/(?P<page>\w+)/$', views.tag, name='tag'),
26 url(r'^tag/(?P<tag_name>\w+)/page/(?P<page>\w+)/$', views.tag, name='tag'),
27
27
28 # /boards/tag/tag_name/unsubscribe/
28 # /boards/tag/tag_name/unsubscribe/
29 url(r'^tag/(?P<tag_name>\w+)/subscribe/$', views.tag_subscribe,
29 url(r'^tag/(?P<tag_name>\w+)/subscribe/$', views.tag_subscribe,
30 name='tag_subscribe'),
30 name='tag_subscribe'),
31 # /boards/tag/tag_name/unsubscribe/
31 # /boards/tag/tag_name/unsubscribe/
32 url(r'^tag/(?P<tag_name>\w+)/unsubscribe/$', views.tag_unsubscribe,
32 url(r'^tag/(?P<tag_name>\w+)/unsubscribe/$', views.tag_unsubscribe,
33 name='tag_unsubscribe'),
33 name='tag_unsubscribe'),
34
34
35 # /boards/thread/
35 # /boards/thread/
36 url(r'^thread/(?P<post_id>\w+)/$', views.thread, name='thread'),
36 url(r'^thread/(?P<post_id>\w+)/$', views.thread, name='thread'),
37 url(r'^thread/(?P<post_id>\w+)/(?P<mode>\w+)/$', views.thread, name='thread_mode'),
37 url(r'^thread/(?P<post_id>\w+)/(?P<mode>\w+)/$', views.thread, name='thread_mode'),
38 url(r'^settings/$', views.settings, name='settings'),
38 url(r'^settings/$', views.settings, name='settings'),
39 url(r'^tags/$', views.all_tags, name='tags'),
39 url(r'^tags/$', views.all_tags, name='tags'),
40 url(r'^captcha/', include('captcha.urls')),
40 url(r'^captcha/', include('captcha.urls')),
41 url(r'^jump/(?P<post_id>\w+)/$', views.jump_to_post, name='jumper'),
41 url(r'^jump/(?P<post_id>\w+)/$', views.jump_to_post, name='jumper'),
42 url(r'^authors/$', views.authors, name='authors'),
42 url(r'^authors/$', views.authors, name='authors'),
43 url(r'^delete/(?P<post_id>\w+)/$', views.delete, name='delete'),
43 url(r'^delete/(?P<post_id>\w+)/$', views.delete, name='delete'),
44 url(r'^ban/(?P<post_id>\w+)/$', views.ban, name='ban'),
44 url(r'^ban/(?P<post_id>\w+)/$', views.ban, name='ban'),
45
45
46 url(r'^banned/$', views.you_are_banned, name='banned'),
46 url(r'^banned/$', views.you_are_banned, name='banned'),
47 url(r'^staticpage/(?P<name>\w+)/$', views.static_page, name='staticpage'),
47 url(r'^staticpage/(?P<name>\w+)/$', views.static_page, name='staticpage'),
48
48
49 # RSS feeds
49 # RSS feeds
50 url(r'^rss/$', AllThreadsFeed()),
50 url(r'^rss/$', AllThreadsFeed()),
51 url(r'^page/(?P<page>\w+)/rss/$', AllThreadsFeed()),
51 url(r'^page/(?P<page>\w+)/rss/$', AllThreadsFeed()),
52 url(r'^tag/(?P<tag_name>\w+)/rss/$', TagThreadsFeed()),
52 url(r'^tag/(?P<tag_name>\w+)/rss/$', TagThreadsFeed()),
53 url(r'^tag/(?P<tag_name>\w+)/page/(?P<page>\w+)/rss/$', TagThreadsFeed()),
53 url(r'^tag/(?P<tag_name>\w+)/page/(?P<page>\w+)/rss/$', TagThreadsFeed()),
54 url(r'^thread/(?P<post_id>\w+)/rss/$', ThreadPostsFeed()),
54 url(r'^thread/(?P<post_id>\w+)/rss/$', ThreadPostsFeed()),
55
55
56 # i18n
56 # i18n
57 url(r'^jsi18n/$', 'boards.views.cached_js_catalog', js_info_dict, name='js_info_dict'),
57 url(r'^jsi18n/$', 'boards.views.cached_js_catalog', js_info_dict, name='js_info_dict'),
58
58
59 # API
59 # API
60 url(r'^api/post/(?P<post_id>\w+)/$', views.get_post, name="get_post"),
60 url(r'^api/post/(?P<post_id>\w+)/$', get_post, name="get_post"),
61 url(r'^api/diff_thread/(?P<thread_id>\w+)/(?P<last_update_time>\w+)/$',
61 url(r'^api/diff_thread/(?P<thread_id>\w+)/(?P<last_update_time>\w+)/$',
62 api_get_threaddiff, name="get_thread_diff"),
62 api_get_threaddiff, name="get_thread_diff"),
63 )
63 )
@@ -1,616 +1,604 b''
1 from datetime import datetime, timedelta
1 from datetime import datetime, timedelta
2
2 from django.db.models import Count
3 from django.db.models import Count
3
4
5
4 OLD_USER_AGE_DAYS = 90
6 OLD_USER_AGE_DAYS = 90
5
7
6 __author__ = 'neko259'
8 __author__ = 'neko259'
7
9
8 import hashlib
10 import hashlib
9 import string
11 import string
10 import time
12 import time
11 import re
13 import re
12
14
13 from django.core import serializers
15 from django.core import serializers
14 from django.core.urlresolvers import reverse
16 from django.core.urlresolvers import reverse
15 from django.http import HttpResponseRedirect, Http404
17 from django.http import HttpResponseRedirect, Http404
16 from django.http.response import HttpResponse
18 from django.http.response import HttpResponse
17 from django.template import RequestContext
19 from django.template import RequestContext
18 from django.shortcuts import render, redirect, get_object_or_404
20 from django.shortcuts import render, redirect, get_object_or_404
19 from django.utils import timezone
21 from django.utils import timezone
20 from django.db import transaction
22 from django.db import transaction
21 from django.views.decorators.cache import cache_page
23 from django.views.decorators.cache import cache_page
22 from django.views.i18n import javascript_catalog
24 from django.views.i18n import javascript_catalog
23
25
24 from boards import forms
26 from boards import forms
25 import boards
27 import boards
26 from boards import utils
28 from boards import utils
27 from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
29 from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
28 ThreadCaptchaForm, PostCaptchaForm, LoginForm, ModeratorSettingsForm
30 ThreadCaptchaForm, PostCaptchaForm, LoginForm, ModeratorSettingsForm
29 from boards.models import Post, Tag, Ban, User
31 from boards.models import Post, Tag, Ban, User
30 from boards.models.post import SETTING_MODERATE, REGEX_REPLY
32 from boards.models.post import SETTING_MODERATE, REGEX_REPLY
31 from boards.models.user import RANK_USER
33 from boards.models.user import RANK_USER
32 from boards import authors
34 from boards import authors
33 from boards.utils import get_client_ip
35 from boards.utils import get_client_ip
34 import neboard
36 import neboard
35
37
36
38
37 BAN_REASON_SPAM = 'Autoban: spam bot'
39 BAN_REASON_SPAM = 'Autoban: spam bot'
38 MODE_GALLERY = 'gallery'
40 MODE_GALLERY = 'gallery'
39 MODE_NORMAL = 'normal'
41 MODE_NORMAL = 'normal'
40
42
41
43
42 def index(request, page=0):
44 def index(request, page=0):
43 context = _init_default_context(request)
45 context = _init_default_context(request)
44
46
45 if utils.need_include_captcha(request):
47 if utils.need_include_captcha(request):
46 threadFormClass = ThreadCaptchaForm
48 threadFormClass = ThreadCaptchaForm
47 kwargs = {'request': request}
49 kwargs = {'request': request}
48 else:
50 else:
49 threadFormClass = ThreadForm
51 threadFormClass = ThreadForm
50 kwargs = {}
52 kwargs = {}
51
53
52 if request.method == 'POST':
54 if request.method == 'POST':
53 form = threadFormClass(request.POST, request.FILES,
55 form = threadFormClass(request.POST, request.FILES,
54 error_class=PlainErrorList, **kwargs)
56 error_class=PlainErrorList, **kwargs)
55 form.session = request.session
57 form.session = request.session
56
58
57 if form.is_valid():
59 if form.is_valid():
58 return _new_post(request, form)
60 return _new_post(request, form)
59 if form.need_to_ban:
61 if form.need_to_ban:
60 # Ban user because he is suspected to be a bot
62 # Ban user because he is suspected to be a bot
61 _ban_current_user(request)
63 _ban_current_user(request)
62 else:
64 else:
63 form = threadFormClass(error_class=PlainErrorList, **kwargs)
65 form = threadFormClass(error_class=PlainErrorList, **kwargs)
64
66
65 threads = []
67 threads = []
66 for thread_to_show in Post.objects.get_threads(page=int(page)):
68 for thread_to_show in Post.objects.get_threads(page=int(page)):
67 threads.append(_get_template_thread(thread_to_show))
69 threads.append(_get_template_thread(thread_to_show))
68
70
69 # TODO Make this generic for tag and threads list pages
71 # TODO Make this generic for tag and threads list pages
70 context['threads'] = None if len(threads) == 0 else threads
72 context['threads'] = None if len(threads) == 0 else threads
71 context['form'] = form
73 context['form'] = form
72 context['current_page'] = int(page)
74 context['current_page'] = int(page)
73
75
74 page_count = Post.objects.get_thread_page_count()
76 page_count = Post.objects.get_thread_page_count()
75 context['pages'] = range(page_count) if page_count > 0 else [0]
77 context['pages'] = range(page_count) if page_count > 0 else [0]
76 page = int(page)
78 page = int(page)
77 if page < page_count - 1:
79 if page < page_count - 1:
78 context['next_page'] = str(page + 1)
80 context['next_page'] = str(page + 1)
79 if page > 0:
81 if page > 0:
80 context['prev_page'] = str(page - 1)
82 context['prev_page'] = str(page - 1)
81
83
82 return render(request, 'boards/posting_general.html',
84 return render(request, 'boards/posting_general.html',
83 context)
85 context)
84
86
85
87
86 def archive(request, page=0):
88 def archive(request, page=0):
87 context = _init_default_context(request)
89 context = _init_default_context(request)
88
90
89 threads = []
91 threads = []
90 for thread_to_show in Post.objects.get_threads(page=int(page),
92 for thread_to_show in Post.objects.get_threads(page=int(page),
91 archived=True):
93 archived=True):
92 threads.append(_get_template_thread(thread_to_show))
94 threads.append(_get_template_thread(thread_to_show))
93
95
94 context['threads'] = threads
96 context['threads'] = threads
95 context['current_page'] = int(page)
97 context['current_page'] = int(page)
96
98
97 page_count = Post.objects.get_thread_page_count(archived=True)
99 page_count = Post.objects.get_thread_page_count(archived=True)
98 context['pages'] = range(page_count) if page_count > 0 else [0]
100 context['pages'] = range(page_count) if page_count > 0 else [0]
99 page = int(page)
101 page = int(page)
100 if page < page_count - 1:
102 if page < page_count - 1:
101 context['next_page'] = str(page + 1)
103 context['next_page'] = str(page + 1)
102 if page > 0:
104 if page > 0:
103 context['prev_page'] = str(page - 1)
105 context['prev_page'] = str(page - 1)
104
106
105 return render(request, 'boards/archive.html', context)
107 return render(request, 'boards/archive.html', context)
106
108
107
109
108 @transaction.atomic
110 @transaction.atomic
109 def _new_post(request, form, opening_post=None):
111 def _new_post(request, form, opening_post=None):
110 """Add a new post (in thread or as a reply)."""
112 """Add a new post (in thread or as a reply)."""
111
113
112 ip = get_client_ip(request)
114 ip = get_client_ip(request)
113 is_banned = Ban.objects.filter(ip=ip).exists()
115 is_banned = Ban.objects.filter(ip=ip).exists()
114
116
115 if is_banned:
117 if is_banned:
116 return redirect(you_are_banned)
118 return redirect(you_are_banned)
117
119
118 data = form.cleaned_data
120 data = form.cleaned_data
119
121
120 title = data['title']
122 title = data['title']
121 text = data['text']
123 text = data['text']
122
124
123 text = _remove_invalid_links(text)
125 text = _remove_invalid_links(text)
124
126
125 if 'image' in data.keys():
127 if 'image' in data.keys():
126 image = data['image']
128 image = data['image']
127 else:
129 else:
128 image = None
130 image = None
129
131
130 tags = []
132 tags = []
131
133
132 if not opening_post:
134 if not opening_post:
133 tag_strings = data['tags']
135 tag_strings = data['tags']
134
136
135 if tag_strings:
137 if tag_strings:
136 tag_strings = tag_strings.split(' ')
138 tag_strings = tag_strings.split(' ')
137 for tag_name in tag_strings:
139 for tag_name in tag_strings:
138 tag_name = string.lower(tag_name.strip())
140 tag_name = string.lower(tag_name.strip())
139 if len(tag_name) > 0:
141 if len(tag_name) > 0:
140 tag, created = Tag.objects.get_or_create(name=tag_name)
142 tag, created = Tag.objects.get_or_create(name=tag_name)
141 tags.append(tag)
143 tags.append(tag)
142 post_thread = None
144 post_thread = None
143 else:
145 else:
144 post_thread = opening_post.thread_new
146 post_thread = opening_post.thread_new
145
147
146 post = Post.objects.create_post(title=title, text=text, ip=ip,
148 post = Post.objects.create_post(title=title, text=text, ip=ip,
147 thread=post_thread, image=image,
149 thread=post_thread, image=image,
148 tags=tags, user=_get_user(request))
150 tags=tags, user=_get_user(request))
149
151
150 thread_to_show = (opening_post.id if opening_post else post.id)
152 thread_to_show = (opening_post.id if opening_post else post.id)
151
153
152 if opening_post:
154 if opening_post:
153 return redirect(reverse(thread, kwargs={'post_id': thread_to_show}) +
155 return redirect(reverse(thread, kwargs={'post_id': thread_to_show}) +
154 '#' + str(post.id))
156 '#' + str(post.id))
155 else:
157 else:
156 return redirect(thread, post_id=thread_to_show)
158 return redirect(thread, post_id=thread_to_show)
157
159
158
160
159 def tag(request, tag_name, page=0):
161 def tag(request, tag_name, page=0):
160 """
162 """
161 Get all tag threads. Threads are split in pages, so some page is
163 Get all tag threads. Threads are split in pages, so some page is
162 requested. Default page is 0.
164 requested. Default page is 0.
163 """
165 """
164
166
165 tag = get_object_or_404(Tag, name=tag_name)
167 tag = get_object_or_404(Tag, name=tag_name)
166 threads = []
168 threads = []
167 for thread_to_show in Post.objects.get_threads(page=int(page), tag=tag):
169 for thread_to_show in Post.objects.get_threads(page=int(page), tag=tag):
168 threads.append(_get_template_thread(thread_to_show))
170 threads.append(_get_template_thread(thread_to_show))
169
171
170 if request.method == 'POST':
172 if request.method == 'POST':
171 form = ThreadForm(request.POST, request.FILES,
173 form = ThreadForm(request.POST, request.FILES,
172 error_class=PlainErrorList)
174 error_class=PlainErrorList)
173 form.session = request.session
175 form.session = request.session
174
176
175 if form.is_valid():
177 if form.is_valid():
176 return _new_post(request, form)
178 return _new_post(request, form)
177 if form.need_to_ban:
179 if form.need_to_ban:
178 # Ban user because he is suspected to be a bot
180 # Ban user because he is suspected to be a bot
179 _ban_current_user(request)
181 _ban_current_user(request)
180 else:
182 else:
181 form = forms.ThreadForm(initial={'tags': tag_name},
183 form = forms.ThreadForm(initial={'tags': tag_name},
182 error_class=PlainErrorList)
184 error_class=PlainErrorList)
183
185
184 context = _init_default_context(request)
186 context = _init_default_context(request)
185 context['threads'] = None if len(threads) == 0 else threads
187 context['threads'] = None if len(threads) == 0 else threads
186 context['tag'] = tag
188 context['tag'] = tag
187 context['current_page'] = int(page)
189 context['current_page'] = int(page)
188
190
189 page_count = Post.objects.get_thread_page_count(tag=tag)
191 page_count = Post.objects.get_thread_page_count(tag=tag)
190 context['pages'] = range(page_count)
192 context['pages'] = range(page_count)
191 page = int(page)
193 page = int(page)
192 if page < page_count - 1:
194 if page < page_count - 1:
193 context['next_page'] = str(page + 1)
195 context['next_page'] = str(page + 1)
194 if page > 0:
196 if page > 0:
195 context['prev_page'] = str(page - 1)
197 context['prev_page'] = str(page - 1)
196
198
197 context['form'] = form
199 context['form'] = form
198
200
199 return render(request, 'boards/posting_general.html',
201 return render(request, 'boards/posting_general.html',
200 context)
202 context)
201
203
202
204
203 def thread(request, post_id, mode=MODE_NORMAL):
205 def thread(request, post_id, mode=MODE_NORMAL):
204 """Get all thread posts"""
206 """Get all thread posts"""
205
207
206 if utils.need_include_captcha(request):
208 if utils.need_include_captcha(request):
207 postFormClass = PostCaptchaForm
209 postFormClass = PostCaptchaForm
208 kwargs = {'request': request}
210 kwargs = {'request': request}
209 else:
211 else:
210 postFormClass = PostForm
212 postFormClass = PostForm
211 kwargs = {}
213 kwargs = {}
212
214
213 opening_post = get_object_or_404(Post, id=post_id)
215 opening_post = get_object_or_404(Post, id=post_id)
214
216
215 # If this is not OP, don't show it as it is
217 # If this is not OP, don't show it as it is
216 if not opening_post.is_opening():
218 if not opening_post.is_opening():
217 raise Http404
219 raise Http404
218
220
219 if request.method == 'POST' and not opening_post.thread_new.archived:
221 if request.method == 'POST' and not opening_post.thread_new.archived:
220 form = postFormClass(request.POST, request.FILES,
222 form = postFormClass(request.POST, request.FILES,
221 error_class=PlainErrorList, **kwargs)
223 error_class=PlainErrorList, **kwargs)
222 form.session = request.session
224 form.session = request.session
223
225
224 if form.is_valid():
226 if form.is_valid():
225 return _new_post(request, form, opening_post)
227 return _new_post(request, form, opening_post)
226 if form.need_to_ban:
228 if form.need_to_ban:
227 # Ban user because he is suspected to be a bot
229 # Ban user because he is suspected to be a bot
228 _ban_current_user(request)
230 _ban_current_user(request)
229 else:
231 else:
230 form = postFormClass(error_class=PlainErrorList, **kwargs)
232 form = postFormClass(error_class=PlainErrorList, **kwargs)
231
233
232 thread_to_show = opening_post.thread_new
234 thread_to_show = opening_post.thread_new
233
235
234 context = _init_default_context(request)
236 context = _init_default_context(request)
235
237
236 posts = thread_to_show.get_replies()
238 posts = thread_to_show.get_replies()
237 context['form'] = form
239 context['form'] = form
238 context["last_update"] = _datetime_to_epoch(thread_to_show.last_edit_time)
240 context["last_update"] = _datetime_to_epoch(thread_to_show.last_edit_time)
239 context["thread"] = thread_to_show
241 context["thread"] = thread_to_show
240
242
241 if MODE_NORMAL == mode:
243 if MODE_NORMAL == mode:
242 context['bumpable'] = thread_to_show.can_bump()
244 context['bumpable'] = thread_to_show.can_bump()
243 if context['bumpable']:
245 if context['bumpable']:
244 context['posts_left'] = neboard.settings.MAX_POSTS_PER_THREAD - posts \
246 context['posts_left'] = neboard.settings.MAX_POSTS_PER_THREAD - posts \
245 .count()
247 .count()
246 context['bumplimit_progress'] = str(
248 context['bumplimit_progress'] = str(
247 float(context['posts_left']) /
249 float(context['posts_left']) /
248 neboard.settings.MAX_POSTS_PER_THREAD * 100)
250 neboard.settings.MAX_POSTS_PER_THREAD * 100)
249
251
250 context['posts'] = posts
252 context['posts'] = posts
251
253
252 document = 'boards/thread.html'
254 document = 'boards/thread.html'
253 elif MODE_GALLERY == mode:
255 elif MODE_GALLERY == mode:
254 context['posts'] = posts.filter(image_width__gt=0)
256 context['posts'] = posts.filter(image_width__gt=0)
255
257
256 document = 'boards/thread_gallery.html'
258 document = 'boards/thread_gallery.html'
257 else:
259 else:
258 raise Http404
260 raise Http404
259
261
260 return render(request, document, context)
262 return render(request, document, context)
261
263
262
264
263 def login(request):
265 def login(request):
264 """Log in with user id"""
266 """Log in with user id"""
265
267
266 context = _init_default_context(request)
268 context = _init_default_context(request)
267
269
268 if request.method == 'POST':
270 if request.method == 'POST':
269 form = LoginForm(request.POST, request.FILES,
271 form = LoginForm(request.POST, request.FILES,
270 error_class=PlainErrorList)
272 error_class=PlainErrorList)
271 form.session = request.session
273 form.session = request.session
272
274
273 if form.is_valid():
275 if form.is_valid():
274 user = User.objects.get(user_id=form.cleaned_data['user_id'])
276 user = User.objects.get(user_id=form.cleaned_data['user_id'])
275 request.session['user_id'] = user.id
277 request.session['user_id'] = user.id
276 return redirect(index)
278 return redirect(index)
277
279
278 else:
280 else:
279 form = LoginForm()
281 form = LoginForm()
280
282
281 context['form'] = form
283 context['form'] = form
282
284
283 return render(request, 'boards/login.html', context)
285 return render(request, 'boards/login.html', context)
284
286
285
287
286 def settings(request):
288 def settings(request):
287 """User's settings"""
289 """User's settings"""
288
290
289 context = _init_default_context(request)
291 context = _init_default_context(request)
290 user = _get_user(request)
292 user = _get_user(request)
291 is_moderator = user.is_moderator()
293 is_moderator = user.is_moderator()
292
294
293 if request.method == 'POST':
295 if request.method == 'POST':
294 with transaction.atomic():
296 with transaction.atomic():
295 if is_moderator:
297 if is_moderator:
296 form = ModeratorSettingsForm(request.POST,
298 form = ModeratorSettingsForm(request.POST,
297 error_class=PlainErrorList)
299 error_class=PlainErrorList)
298 else:
300 else:
299 form = SettingsForm(request.POST, error_class=PlainErrorList)
301 form = SettingsForm(request.POST, error_class=PlainErrorList)
300
302
301 if form.is_valid():
303 if form.is_valid():
302 selected_theme = form.cleaned_data['theme']
304 selected_theme = form.cleaned_data['theme']
303
305
304 user.save_setting('theme', selected_theme)
306 user.save_setting('theme', selected_theme)
305
307
306 if is_moderator:
308 if is_moderator:
307 moderate = form.cleaned_data['moderate']
309 moderate = form.cleaned_data['moderate']
308 user.save_setting(SETTING_MODERATE, moderate)
310 user.save_setting(SETTING_MODERATE, moderate)
309
311
310 return redirect(settings)
312 return redirect(settings)
311 else:
313 else:
312 selected_theme = _get_theme(request)
314 selected_theme = _get_theme(request)
313
315
314 if is_moderator:
316 if is_moderator:
315 form = ModeratorSettingsForm(initial={'theme': selected_theme,
317 form = ModeratorSettingsForm(initial={'theme': selected_theme,
316 'moderate': context['moderator']},
318 'moderate': context['moderator']},
317 error_class=PlainErrorList)
319 error_class=PlainErrorList)
318 else:
320 else:
319 form = SettingsForm(initial={'theme': selected_theme},
321 form = SettingsForm(initial={'theme': selected_theme},
320 error_class=PlainErrorList)
322 error_class=PlainErrorList)
321
323
322 context['form'] = form
324 context['form'] = form
323
325
324 return render(request, 'boards/settings.html', context)
326 return render(request, 'boards/settings.html', context)
325
327
326
328
327 def all_tags(request):
329 def all_tags(request):
328 """All tags list"""
330 """All tags list"""
329
331
330 context = _init_default_context(request)
332 context = _init_default_context(request)
331 context['all_tags'] = Tag.objects.get_not_empty_tags()
333 context['all_tags'] = Tag.objects.get_not_empty_tags()
332
334
333 return render(request, 'boards/tags.html', context)
335 return render(request, 'boards/tags.html', context)
334
336
335
337
336 def jump_to_post(request, post_id):
338 def jump_to_post(request, post_id):
337 """Determine thread in which the requested post is and open it's page"""
339 """Determine thread in which the requested post is and open it's page"""
338
340
339 post = get_object_or_404(Post, id=post_id)
341 post = get_object_or_404(Post, id=post_id)
340
342
341 if not post.thread:
343 if not post.thread:
342 return redirect(thread, post_id=post.id)
344 return redirect(thread, post_id=post.id)
343 else:
345 else:
344 return redirect(reverse(thread, kwargs={'post_id': post.thread.id})
346 return redirect(reverse(thread, kwargs={'post_id': post.thread.id})
345 + '#' + str(post.id))
347 + '#' + str(post.id))
346
348
347
349
348 def authors(request):
350 def authors(request):
349 """Show authors list"""
351 """Show authors list"""
350
352
351 context = _init_default_context(request)
353 context = _init_default_context(request)
352 context['authors'] = boards.authors.authors
354 context['authors'] = boards.authors.authors
353
355
354 return render(request, 'boards/authors.html', context)
356 return render(request, 'boards/authors.html', context)
355
357
356
358
357 @transaction.atomic
359 @transaction.atomic
358 def delete(request, post_id):
360 def delete(request, post_id):
359 """Delete post"""
361 """Delete post"""
360
362
361 user = _get_user(request)
363 user = _get_user(request)
362 post = get_object_or_404(Post, id=post_id)
364 post = get_object_or_404(Post, id=post_id)
363
365
364 if user.is_moderator():
366 if user.is_moderator():
365 # TODO Show confirmation page before deletion
367 # TODO Show confirmation page before deletion
366 Post.objects.delete_post(post)
368 Post.objects.delete_post(post)
367
369
368 if not post.thread:
370 if not post.thread:
369 return _redirect_to_next(request)
371 return _redirect_to_next(request)
370 else:
372 else:
371 return redirect(thread, post_id=post.thread.id)
373 return redirect(thread, post_id=post.thread.id)
372
374
373
375
374 @transaction.atomic
376 @transaction.atomic
375 def ban(request, post_id):
377 def ban(request, post_id):
376 """Ban user"""
378 """Ban user"""
377
379
378 user = _get_user(request)
380 user = _get_user(request)
379 post = get_object_or_404(Post, id=post_id)
381 post = get_object_or_404(Post, id=post_id)
380
382
381 if user.is_moderator():
383 if user.is_moderator():
382 # TODO Show confirmation page before ban
384 # TODO Show confirmation page before ban
383 ban, created = Ban.objects.get_or_create(ip=post.poster_ip)
385 ban, created = Ban.objects.get_or_create(ip=post.poster_ip)
384 if created:
386 if created:
385 ban.reason = 'Banned for post ' + str(post_id)
387 ban.reason = 'Banned for post ' + str(post_id)
386 ban.save()
388 ban.save()
387
389
388 return _redirect_to_next(request)
390 return _redirect_to_next(request)
389
391
390
392
391 def you_are_banned(request):
393 def you_are_banned(request):
392 """Show the page that notifies that user is banned"""
394 """Show the page that notifies that user is banned"""
393
395
394 context = _init_default_context(request)
396 context = _init_default_context(request)
395
397
396 ban = get_object_or_404(Ban, ip=utils.get_client_ip(request))
398 ban = get_object_or_404(Ban, ip=utils.get_client_ip(request))
397 context['ban_reason'] = ban.reason
399 context['ban_reason'] = ban.reason
398 return render(request, 'boards/staticpages/banned.html', context)
400 return render(request, 'boards/staticpages/banned.html', context)
399
401
400
402
401 def page_404(request):
403 def page_404(request):
402 """Show page 404 (not found error)"""
404 """Show page 404 (not found error)"""
403
405
404 context = _init_default_context(request)
406 context = _init_default_context(request)
405 return render(request, 'boards/404.html', context)
407 return render(request, 'boards/404.html', context)
406
408
407
409
408 @transaction.atomic
410 @transaction.atomic
409 def tag_subscribe(request, tag_name):
411 def tag_subscribe(request, tag_name):
410 """Add tag to favorites"""
412 """Add tag to favorites"""
411
413
412 user = _get_user(request)
414 user = _get_user(request)
413 tag = get_object_or_404(Tag, name=tag_name)
415 tag = get_object_or_404(Tag, name=tag_name)
414
416
415 if not tag in user.fav_tags.all():
417 if not tag in user.fav_tags.all():
416 user.add_tag(tag)
418 user.add_tag(tag)
417
419
418 return _redirect_to_next(request)
420 return _redirect_to_next(request)
419
421
420
422
421 @transaction.atomic
423 @transaction.atomic
422 def tag_unsubscribe(request, tag_name):
424 def tag_unsubscribe(request, tag_name):
423 """Remove tag from favorites"""
425 """Remove tag from favorites"""
424
426
425 user = _get_user(request)
427 user = _get_user(request)
426 tag = get_object_or_404(Tag, name=tag_name)
428 tag = get_object_or_404(Tag, name=tag_name)
427
429
428 if tag in user.fav_tags.all():
430 if tag in user.fav_tags.all():
429 user.remove_tag(tag)
431 user.remove_tag(tag)
430
432
431 return _redirect_to_next(request)
433 return _redirect_to_next(request)
432
434
433
435
434 def static_page(request, name):
436 def static_page(request, name):
435 """Show a static page that needs only tags list and a CSS"""
437 """Show a static page that needs only tags list and a CSS"""
436
438
437 context = _init_default_context(request)
439 context = _init_default_context(request)
438 return render(request, 'boards/staticpages/' + name + '.html', context)
440 return render(request, 'boards/staticpages/' + name + '.html', context)
439
441
440
442
441 def api_get_post(request, post_id):
443 def api_get_post(request, post_id):
442 """
444 """
443 Get the JSON of a post. This can be
445 Get the JSON of a post. This can be
444 used as and API for external clients.
446 used as and API for external clients.
445 """
447 """
446
448
447 post = get_object_or_404(Post, id=post_id)
449 post = get_object_or_404(Post, id=post_id)
448
450
449 json = serializers.serialize("json", [post], fields=(
451 json = serializers.serialize("json", [post], fields=(
450 "pub_time", "_text_rendered", "title", "text", "image",
452 "pub_time", "_text_rendered", "title", "text", "image",
451 "image_width", "image_height", "replies", "tags"
453 "image_width", "image_height", "replies", "tags"
452 ))
454 ))
453
455
454 return HttpResponse(content=json)
456 return HttpResponse(content=json)
455
457
456
458
457 def get_post(request, post_id):
458 """Get the html of a post. Used for popups."""
459
460 post = get_object_or_404(Post, id=post_id)
461 thread = post.thread_new
462
463 context = RequestContext(request)
464 context["post"] = post
465 context["can_bump"] = thread.can_bump()
466 if "truncated" in request.GET:
467 context["truncated"] = True
468
469 return render(request, 'boards/post.html', context)
470
471 @cache_page(86400)
459 @cache_page(86400)
472 def cached_js_catalog(request, domain='djangojs', packages=None):
460 def cached_js_catalog(request, domain='djangojs', packages=None):
473 return javascript_catalog(request, domain, packages)
461 return javascript_catalog(request, domain, packages)
474
462
475
463
476 def _get_theme(request, user=None):
464 def _get_theme(request, user=None):
477 """Get user's CSS theme"""
465 """Get user's CSS theme"""
478
466
479 if not user:
467 if not user:
480 user = _get_user(request)
468 user = _get_user(request)
481 theme = user.get_setting('theme')
469 theme = user.get_setting('theme')
482 if not theme:
470 if not theme:
483 theme = neboard.settings.DEFAULT_THEME
471 theme = neboard.settings.DEFAULT_THEME
484
472
485 return theme
473 return theme
486
474
487
475
488 def _init_default_context(request):
476 def _init_default_context(request):
489 """Create context with default values that are used in most views"""
477 """Create context with default values that are used in most views"""
490
478
491 context = RequestContext(request)
479 context = RequestContext(request)
492
480
493 user = _get_user(request)
481 user = _get_user(request)
494 context['user'] = user
482 context['user'] = user
495 context['tags'] = user.get_sorted_fav_tags()
483 context['tags'] = user.get_sorted_fav_tags()
496 context['posts_per_day'] = float(Post.objects.get_posts_per_day())
484 context['posts_per_day'] = float(Post.objects.get_posts_per_day())
497
485
498 theme = _get_theme(request, user)
486 theme = _get_theme(request, user)
499 context['theme'] = theme
487 context['theme'] = theme
500 context['theme_css'] = 'css/' + theme + '/base_page.css'
488 context['theme_css'] = 'css/' + theme + '/base_page.css'
501
489
502 # This shows the moderator panel
490 # This shows the moderator panel
503 moderate = user.get_setting(SETTING_MODERATE)
491 moderate = user.get_setting(SETTING_MODERATE)
504 if moderate == 'True':
492 if moderate == 'True':
505 context['moderator'] = user.is_moderator()
493 context['moderator'] = user.is_moderator()
506 else:
494 else:
507 context['moderator'] = False
495 context['moderator'] = False
508
496
509 return context
497 return context
510
498
511
499
512 def _get_user(request):
500 def _get_user(request):
513 """
501 """
514 Get current user from the session. If the user does not exist, create
502 Get current user from the session. If the user does not exist, create
515 a new one.
503 a new one.
516 """
504 """
517
505
518 session = request.session
506 session = request.session
519 if not 'user_id' in session:
507 if not 'user_id' in session:
520 request.session.save()
508 request.session.save()
521
509
522 md5 = hashlib.md5()
510 md5 = hashlib.md5()
523 md5.update(session.session_key)
511 md5.update(session.session_key)
524 new_id = md5.hexdigest()
512 new_id = md5.hexdigest()
525
513
526 while User.objects.filter(user_id=new_id).exists():
514 while User.objects.filter(user_id=new_id).exists():
527 md5.update(str(timezone.now()))
515 md5.update(str(timezone.now()))
528 new_id = md5.hexdigest()
516 new_id = md5.hexdigest()
529
517
530 time_now = timezone.now()
518 time_now = timezone.now()
531 user = User.objects.create(user_id=new_id, rank=RANK_USER,
519 user = User.objects.create(user_id=new_id, rank=RANK_USER,
532 registration_time=time_now)
520 registration_time=time_now)
533
521
534 _delete_old_users()
522 _delete_old_users()
535
523
536 session['user_id'] = user.id
524 session['user_id'] = user.id
537 else:
525 else:
538 user = User.objects.get(id=session['user_id'])
526 user = User.objects.get(id=session['user_id'])
539
527
540 return user
528 return user
541
529
542
530
543 def _redirect_to_next(request):
531 def _redirect_to_next(request):
544 """
532 """
545 If a 'next' parameter was specified, redirect to the next page. This is
533 If a 'next' parameter was specified, redirect to the next page. This is
546 used when the user is required to return to some page after the current
534 used when the user is required to return to some page after the current
547 view has finished its work.
535 view has finished its work.
548 """
536 """
549
537
550 if 'next' in request.GET:
538 if 'next' in request.GET:
551 next_page = request.GET['next']
539 next_page = request.GET['next']
552 return HttpResponseRedirect(next_page)
540 return HttpResponseRedirect(next_page)
553 else:
541 else:
554 return redirect(index)
542 return redirect(index)
555
543
556
544
557 @transaction.atomic
545 @transaction.atomic
558 def _ban_current_user(request):
546 def _ban_current_user(request):
559 """Add current user to the IP ban list"""
547 """Add current user to the IP ban list"""
560
548
561 ip = utils.get_client_ip(request)
549 ip = utils.get_client_ip(request)
562 ban, created = Ban.objects.get_or_create(ip=ip)
550 ban, created = Ban.objects.get_or_create(ip=ip)
563 if created:
551 if created:
564 ban.can_read = False
552 ban.can_read = False
565 ban.reason = BAN_REASON_SPAM
553 ban.reason = BAN_REASON_SPAM
566 ban.save()
554 ban.save()
567
555
568
556
569 def _remove_invalid_links(text):
557 def _remove_invalid_links(text):
570 """
558 """
571 Replace invalid links in posts so that they won't be parsed.
559 Replace invalid links in posts so that they won't be parsed.
572 Invalid links are links to non-existent posts
560 Invalid links are links to non-existent posts
573 """
561 """
574
562
575 for reply_number in re.finditer(REGEX_REPLY, text):
563 for reply_number in re.finditer(REGEX_REPLY, text):
576 post_id = reply_number.group(1)
564 post_id = reply_number.group(1)
577 post = Post.objects.filter(id=post_id)
565 post = Post.objects.filter(id=post_id)
578 if not post.exists():
566 if not post.exists():
579 text = string.replace(text, '>>' + post_id, post_id)
567 text = string.replace(text, '>>' + post_id, post_id)
580
568
581 return text
569 return text
582
570
583
571
584 def _datetime_to_epoch(datetime):
572 def _datetime_to_epoch(datetime):
585 return int(time.mktime(timezone.localtime(
573 return int(time.mktime(timezone.localtime(
586 datetime,timezone.get_current_timezone()).timetuple())
574 datetime,timezone.get_current_timezone()).timetuple())
587 * 1000000 + datetime.microsecond)
575 * 1000000 + datetime.microsecond)
588
576
589
577
590 def _get_template_thread(thread_to_show):
578 def _get_template_thread(thread_to_show):
591 """Get template values for thread"""
579 """Get template values for thread"""
592
580
593 last_replies = thread_to_show.get_last_replies()
581 last_replies = thread_to_show.get_last_replies()
594 skipped_replies_count = thread_to_show.get_replies().count() \
582 skipped_replies_count = thread_to_show.get_replies().count() \
595 - len(last_replies) - 1
583 - len(last_replies) - 1
596 return {
584 return {
597 'thread': thread_to_show,
585 'thread': thread_to_show,
598 'op': thread_to_show.get_replies()[0],
586 'op': thread_to_show.get_replies()[0],
599 'bumpable': thread_to_show.can_bump(),
587 'bumpable': thread_to_show.can_bump(),
600 'last_replies': last_replies,
588 'last_replies': last_replies,
601 'skipped_replies': skipped_replies_count,
589 'skipped_replies': skipped_replies_count,
602 }
590 }
603
591
604
592
605 def _delete_old_users():
593 def _delete_old_users():
606 """
594 """
607 Delete users with no favorite tags and posted messages. These can be spam
595 Delete users with no favorite tags and posted messages. These can be spam
608 bots or just old user accounts
596 bots or just old user accounts
609 """
597 """
610
598
611 old_registration_date = datetime.now().date() - timedelta(OLD_USER_AGE_DAYS)
599 old_registration_date = datetime.now().date() - timedelta(OLD_USER_AGE_DAYS)
612
600
613 for user in User.objects.annotate(tags_count=Count('fav_tags')).filter(
601 for user in User.objects.annotate(tags_count=Count('fav_tags')).filter(
614 tags_count=0).filter(registration_time__lt=old_registration_date):
602 tags_count=0).filter(registration_time__lt=old_registration_date):
615 if not Post.objects.filter(user=user).exists():
603 if not Post.objects.filter(user=user).exists():
616 user.delete()
604 user.delete()
@@ -1,75 +1,89 b''
1 from datetime import datetime
1 from datetime import datetime
2 import json
2 import json
3 from django.db import transaction
3 from django.db import transaction
4 from django.http import HttpResponse
4 from django.http import HttpResponse
5 from django.shortcuts import get_object_or_404
5 from django.shortcuts import get_object_or_404, render
6 from django.template import RequestContext
6 from django.utils import timezone
7 from django.utils import timezone
7 from boards.forms import ThreadForm, PlainErrorList
8 from boards.forms import ThreadForm, PlainErrorList
8 from boards.models import Post
9 from boards.models import Post
9 from boards.views import get_post, _datetime_to_epoch, _new_post, \
10 from boards.views import _datetime_to_epoch, _new_post, \
10 _ban_current_user
11 _ban_current_user
11
12
12 __author__ = 'neko259'
13 __author__ = 'neko259'
13
14
14
15
15 @transaction.atomic
16 @transaction.atomic
16 def api_get_threaddiff(request, thread_id, last_update_time):
17 def api_get_threaddiff(request, thread_id, last_update_time):
17 """Get posts that were changed or added since time"""
18 """Get posts that were changed or added since time"""
18
19
19 thread = get_object_or_404(Post, id=thread_id).thread_new
20 thread = get_object_or_404(Post, id=thread_id).thread_new
20
21
21 filter_time = datetime.fromtimestamp(float(last_update_time) / 1000000,
22 filter_time = datetime.fromtimestamp(float(last_update_time) / 1000000,
22 timezone.get_current_timezone())
23 timezone.get_current_timezone())
23
24
24 json_data = {
25 json_data = {
25 'added': [],
26 'added': [],
26 'updated': [],
27 'updated': [],
27 'last_update': None,
28 'last_update': None,
28 }
29 }
29 added_posts = Post.objects.filter(thread_new=thread,
30 added_posts = Post.objects.filter(thread_new=thread,
30 pub_time__gt=filter_time) \
31 pub_time__gt=filter_time) \
31 .order_by('pub_time')
32 .order_by('pub_time')
32 updated_posts = Post.objects.filter(thread_new=thread,
33 updated_posts = Post.objects.filter(thread_new=thread,
33 pub_time__lte=filter_time,
34 pub_time__lte=filter_time,
34 last_edit_time__gt=filter_time)
35 last_edit_time__gt=filter_time)
35 for post in added_posts:
36 for post in added_posts:
36 json_data['added'].append(get_post(request, post.id).content.strip())
37 json_data['added'].append(get_post(request, post.id).content.strip())
37 for post in updated_posts:
38 for post in updated_posts:
38 json_data['updated'].append(get_post(request, post.id).content.strip())
39 json_data['updated'].append(get_post(request, post.id).content.strip())
39 json_data['last_update'] = _datetime_to_epoch(thread.last_edit_time)
40 json_data['last_update'] = _datetime_to_epoch(thread.last_edit_time)
40
41
41 return HttpResponse(content=json.dumps(json_data))
42 return HttpResponse(content=json.dumps(json_data))
42
43
43
44
44 # TODO This method needs to be implemented properly
45 # TODO This method needs to be implemented properly
45 # def api_add_post(request, form):
46 # def api_add_post(request, form):
46 # """
47 # """
47 # Add a post and return the JSON response for it
48 # Add a post and return the JSON response for it
48 # """
49 # """
49 #
50 #
50 # status = 'ok'
51 # status = 'ok'
51 # errors = []
52 # errors = []
52 #
53 #
53 # if request.method == 'POST':
54 # if request.method == 'POST':
54 # form = ThreadForm(request.POST, request.FILES,
55 # form = ThreadForm(request.POST, request.FILES,
55 # error_class=PlainErrorList)
56 # error_class=PlainErrorList)
56 # form.session = request.session
57 # form.session = request.session
57 #
58 #
58 # if form.is_valid():
59 # if form.is_valid():
59 # # TODO Don't form a response here cause we'll not need it
60 # # TODO Don't form a response here cause we'll not need it
60 # _new_post(request, form)
61 # _new_post(request, form)
61 # if form.need_to_ban:
62 # if form.need_to_ban:
62 # # Ban user because he is suspected to be a bot
63 # # Ban user because he is suspected to be a bot
63 # _ban_current_user(request)
64 # _ban_current_user(request)
64 # else:
65 # else:
65 # status = 'error'
66 # status = 'error'
66 # for field in form.fields:
67 # for field in form.fields:
67 # if field.errors:
68 # if field.errors:
68 # errors.append(field.errors)
69 # errors.append(field.errors)
69 #
70 #
70 # response = {
71 # response = {
71 # 'status': status,
72 # 'status': status,
72 # 'errors': errors,
73 # 'errors': errors,
73 # }
74 # }
74 #
75 #
75 # return HttpResponse(content=json.dumps(response))
76 # return HttpResponse(content=json.dumps(response))
77 def get_post(request, post_id):
78 """Get the html of a post. Used for popups."""
79
80 post = get_object_or_404(Post, id=post_id)
81 thread = post.thread_new
82
83 context = RequestContext(request)
84 context["post"] = post
85 context["can_bump"] = thread.can_bump()
86 if "truncated" in request.GET:
87 context["truncated"] = True
88
89 return render(request, 'boards/post.html', context) No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now