##// END OF EJS Templates
Fixed bug with creating thread from tag view
Pavel Ryapolov -
r278:21d89568 default
parent child Browse files
Show More
@@ -1,448 +1,450 b''
1 import hashlib
1 import hashlib
2 import string
2 import string
3 from django.core import serializers
3 from django.core import serializers
4 from django.core.urlresolvers import reverse
4 from django.core.urlresolvers import reverse
5 from django.http import HttpResponseRedirect
5 from django.http import HttpResponseRedirect
6 from django.http.response import HttpResponse
6 from django.http.response import HttpResponse
7 from django.template import RequestContext
7 from django.template import RequestContext
8 from django.shortcuts import render, redirect, get_object_or_404
8 from django.shortcuts import render, redirect, get_object_or_404
9 from django.utils import timezone
9 from django.utils import timezone
10
10
11 from boards import forms
11 from boards import forms
12 import boards
12 import boards
13 from boards import utils
13 from boards import utils
14 from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
14 from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
15 ThreadCaptchaForm, PostCaptchaForm, LoginForm, ModeratorSettingsForm
15 ThreadCaptchaForm, PostCaptchaForm, LoginForm, ModeratorSettingsForm
16
16
17 from boards.models import Post, Tag, Ban, User, RANK_USER, SETTING_MODERATE
17 from boards.models import Post, Tag, Ban, User, RANK_USER, SETTING_MODERATE
18 from boards import authors
18 from boards import authors
19 from boards.utils import get_client_ip
19 from boards.utils import get_client_ip
20 import neboard
20 import neboard
21
21
22
22
23 def index(request, page=0):
23 def index(request, page=0):
24 context = _init_default_context(request)
24 context = _init_default_context(request)
25
25
26 if utils.need_include_captcha(request):
26 if utils.need_include_captcha(request):
27 threadFormClass = ThreadCaptchaForm
27 threadFormClass = ThreadCaptchaForm
28 kwargs = {'request': request}
28 kwargs = {'request': request}
29 else:
29 else:
30 threadFormClass = ThreadForm
30 threadFormClass = ThreadForm
31 kwargs = {}
31 kwargs = {}
32
32
33 if request.method == 'POST':
33 if request.method == 'POST':
34 form = threadFormClass(request.POST, request.FILES,
34 form = threadFormClass(request.POST, request.FILES,
35 error_class=PlainErrorList, **kwargs)
35 error_class=PlainErrorList, **kwargs)
36 form.session = request.session
36 form.session = request.session
37
37
38 if form.is_valid():
38 if form.is_valid():
39 return _new_post(request, form)
39 return _new_post(request, form)
40 if form.need_to_ban:
40 if form.need_to_ban:
41 # Ban user because he is suspected to be a bot
41 # Ban user because he is suspected to be a bot
42 _ban_current_user(request)
42 _ban_current_user(request)
43 else:
43 else:
44 form = threadFormClass(error_class=PlainErrorList, **kwargs)
44 form = threadFormClass(error_class=PlainErrorList, **kwargs)
45
45
46 threads = []
46 threads = []
47 for thread in Post.objects.get_threads(page=int(page)):
47 for thread in Post.objects.get_threads(page=int(page)):
48 threads.append({'thread': thread,
48 threads.append({'thread': thread,
49 'bumpable': thread.can_bump()})
49 'bumpable': thread.can_bump()})
50
50
51 context['threads'] = None if len(threads) == 0 else threads
51 context['threads'] = None if len(threads) == 0 else threads
52 context['form'] = form
52 context['form'] = form
53 context['pages'] = range(Post.objects.get_thread_page_count())
53 context['pages'] = range(Post.objects.get_thread_page_count())
54
54
55 return render(request, 'boards/posting_general.html',
55 return render(request, 'boards/posting_general.html',
56 context)
56 context)
57
57
58
58
59 def _new_post(request, form, thread_id=boards.models.NO_PARENT):
59 def _new_post(request, form, thread_id=boards.models.NO_PARENT):
60 """Add a new post (in thread or as a reply)."""
60 """Add a new post (in thread or as a reply)."""
61
61
62 ip = get_client_ip(request)
62 ip = get_client_ip(request)
63 is_banned = Ban.objects.filter(ip=ip).exists()
63 is_banned = Ban.objects.filter(ip=ip).exists()
64
64
65 if is_banned:
65 if is_banned:
66 return redirect(you_are_banned)
66 return redirect(you_are_banned)
67
67
68 data = form.cleaned_data
68 data = form.cleaned_data
69
69
70 title = data['title']
70 title = data['title']
71 text = data['text']
71 text = data['text']
72
72
73 if 'image' in data.keys():
73 if 'image' in data.keys():
74 image = data['image']
74 image = data['image']
75 else:
75 else:
76 image = None
76 image = None
77
77
78 tags = []
78 tags = []
79
79
80 new_thread = thread_id == boards.models.NO_PARENT
80 new_thread = thread_id == boards.models.NO_PARENT
81 if new_thread:
81 if new_thread:
82 tag_strings = data['tags']
82 tag_strings = data['tags']
83
83
84 if tag_strings:
84 if tag_strings:
85 tag_strings = tag_strings.split(' ')
85 tag_strings = tag_strings.split(' ')
86 for tag_name in tag_strings:
86 for tag_name in tag_strings:
87 tag_name = string.lower(tag_name.strip())
87 tag_name = string.lower(tag_name.strip())
88 if len(tag_name) > 0:
88 if len(tag_name) > 0:
89 tag, created = Tag.objects.get_or_create(name=tag_name)
89 tag, created = Tag.objects.get_or_create(name=tag_name)
90 tags.append(tag)
90 tags.append(tag)
91
91
92 op = None if thread_id == boards.models.NO_PARENT else \
92 op = None if thread_id == boards.models.NO_PARENT else \
93 get_object_or_404(Post, id=thread_id)
93 get_object_or_404(Post, id=thread_id)
94 post = Post.objects.create_post(title=title, text=text, ip=ip,
94 post = Post.objects.create_post(title=title, text=text, ip=ip,
95 thread=op, image=image,
95 thread=op, image=image,
96 tags=tags, user=_get_user(request))
96 tags=tags, user=_get_user(request))
97
97
98 thread_to_show = (post.id if new_thread else thread_id)
98 thread_to_show = (post.id if new_thread else thread_id)
99
99
100 if new_thread:
100 if new_thread:
101 return redirect(thread, post_id=thread_to_show)
101 return redirect(thread, post_id=thread_to_show)
102 else:
102 else:
103 return redirect(reverse(thread, kwargs={'post_id': thread_to_show}) +
103 return redirect(reverse(thread, kwargs={'post_id': thread_to_show}) +
104 '#' + str(post.id))
104 '#' + str(post.id))
105
105
106
106
107 def tag(request, tag_name, page=0):
107 def tag(request, tag_name, page=0):
108 """
108 """
109 Get all tag threads. Threads are split in pages, so some page is
109 Get all tag threads. Threads are split in pages, so some page is
110 requested. Default page is 0.
110 requested. Default page is 0.
111 """
111 """
112
112
113 tag = get_object_or_404(Tag, name=tag_name)
113 tag = get_object_or_404(Tag, name=tag_name)
114 threads = []
114 threads = []
115 for thread in Post.objects.get_threads(tag=tag, page=int(page)):
115 for thread in Post.objects.get_threads(tag=tag, page=int(page)):
116 threads.append({'thread': thread,
116 threads.append({'thread': thread,
117 'bumpable': thread.can_bump()})
117 'bumpable': thread.can_bump()})
118
118
119 if request.method == 'POST':
119 if request.method == 'POST':
120 form = ThreadForm(request.POST, request.FILES,
120 form = ThreadForm(request.POST, request.FILES,
121 error_class=PlainErrorList)
121 error_class=PlainErrorList)
122 form.session = request.session
123
122 if form.is_valid():
124 if form.is_valid():
123 return _new_post(request, form)
125 return _new_post(request, form)
124 if form.need_to_ban:
126 if form.need_to_ban:
125 # Ban user because he is suspected to be a bot
127 # Ban user because he is suspected to be a bot
126 _ban_current_user(request)
128 _ban_current_user(request)
127 else:
129 else:
128 form = forms.ThreadForm(initial={'tags': tag_name},
130 form = forms.ThreadForm(initial={'tags': tag_name},
129 error_class=PlainErrorList)
131 error_class=PlainErrorList)
130
132
131 context = _init_default_context(request)
133 context = _init_default_context(request)
132 context['threads'] = None if len(threads) == 0 else threads
134 context['threads'] = None if len(threads) == 0 else threads
133 context['tag'] = tag
135 context['tag'] = tag
134 context['pages'] = range(Post.objects.get_thread_page_count(tag=tag))
136 context['pages'] = range(Post.objects.get_thread_page_count(tag=tag))
135
137
136 context['form'] = form
138 context['form'] = form
137
139
138 return render(request, 'boards/posting_general.html',
140 return render(request, 'boards/posting_general.html',
139 context)
141 context)
140
142
141
143
142 def thread(request, post_id):
144 def thread(request, post_id):
143 """Get all thread posts"""
145 """Get all thread posts"""
144
146
145 if utils.need_include_captcha(request):
147 if utils.need_include_captcha(request):
146 postFormClass = PostCaptchaForm
148 postFormClass = PostCaptchaForm
147 kwargs = {'request': request}
149 kwargs = {'request': request}
148 else:
150 else:
149 postFormClass = PostForm
151 postFormClass = PostForm
150 kwargs = {}
152 kwargs = {}
151
153
152 if request.method == 'POST':
154 if request.method == 'POST':
153 form = postFormClass(request.POST, request.FILES,
155 form = postFormClass(request.POST, request.FILES,
154 error_class=PlainErrorList, **kwargs)
156 error_class=PlainErrorList, **kwargs)
155 form.session = request.session
157 form.session = request.session
156
158
157 if form.is_valid():
159 if form.is_valid():
158 return _new_post(request, form, post_id)
160 return _new_post(request, form, post_id)
159 if form.need_to_ban:
161 if form.need_to_ban:
160 # Ban user because he is suspected to be a bot
162 # Ban user because he is suspected to be a bot
161 _ban_current_user(request)
163 _ban_current_user(request)
162 else:
164 else:
163 form = postFormClass(error_class=PlainErrorList, **kwargs)
165 form = postFormClass(error_class=PlainErrorList, **kwargs)
164
166
165 posts = Post.objects.get_thread(post_id)
167 posts = Post.objects.get_thread(post_id)
166
168
167 context = _init_default_context(request)
169 context = _init_default_context(request)
168
170
169 context['posts'] = posts
171 context['posts'] = posts
170 context['form'] = form
172 context['form'] = form
171 context['bumpable'] = posts[0].can_bump()
173 context['bumpable'] = posts[0].can_bump()
172
174
173 return render(request, 'boards/thread.html', context)
175 return render(request, 'boards/thread.html', context)
174
176
175
177
176 def login(request):
178 def login(request):
177 """Log in with user id"""
179 """Log in with user id"""
178
180
179 context = _init_default_context(request)
181 context = _init_default_context(request)
180
182
181 if request.method == 'POST':
183 if request.method == 'POST':
182 form = LoginForm(request.POST, request.FILES,
184 form = LoginForm(request.POST, request.FILES,
183 error_class=PlainErrorList)
185 error_class=PlainErrorList)
184 form.session = request.session
186 form.session = request.session
185
187
186 if form.is_valid():
188 if form.is_valid():
187 user = User.objects.get(user_id=form.cleaned_data['user_id'])
189 user = User.objects.get(user_id=form.cleaned_data['user_id'])
188 request.session['user_id'] = user.id
190 request.session['user_id'] = user.id
189 return redirect(index)
191 return redirect(index)
190
192
191 else:
193 else:
192 form = LoginForm()
194 form = LoginForm()
193
195
194 context['form'] = form
196 context['form'] = form
195
197
196 return render(request, 'boards/login.html', context)
198 return render(request, 'boards/login.html', context)
197
199
198
200
199 def settings(request):
201 def settings(request):
200 """User's settings"""
202 """User's settings"""
201
203
202 context = _init_default_context(request)
204 context = _init_default_context(request)
203 user = _get_user(request)
205 user = _get_user(request)
204 is_moderator = user.is_moderator()
206 is_moderator = user.is_moderator()
205
207
206 if request.method == 'POST':
208 if request.method == 'POST':
207 if is_moderator:
209 if is_moderator:
208 form = ModeratorSettingsForm(request.POST,
210 form = ModeratorSettingsForm(request.POST,
209 error_class=PlainErrorList)
211 error_class=PlainErrorList)
210 else:
212 else:
211 form = SettingsForm(request.POST, error_class=PlainErrorList)
213 form = SettingsForm(request.POST, error_class=PlainErrorList)
212
214
213 if form.is_valid():
215 if form.is_valid():
214 selected_theme = form.cleaned_data['theme']
216 selected_theme = form.cleaned_data['theme']
215
217
216 user.save_setting('theme', selected_theme)
218 user.save_setting('theme', selected_theme)
217
219
218 if is_moderator:
220 if is_moderator:
219 moderate = form.cleaned_data['moderate']
221 moderate = form.cleaned_data['moderate']
220 user.save_setting(SETTING_MODERATE, moderate)
222 user.save_setting(SETTING_MODERATE, moderate)
221
223
222 return redirect(settings)
224 return redirect(settings)
223 else:
225 else:
224 selected_theme = _get_theme(request)
226 selected_theme = _get_theme(request)
225
227
226 if is_moderator:
228 if is_moderator:
227 form = ModeratorSettingsForm(initial={'theme': selected_theme,
229 form = ModeratorSettingsForm(initial={'theme': selected_theme,
228 'moderate': context['moderator']},
230 'moderate': context['moderator']},
229 error_class=PlainErrorList)
231 error_class=PlainErrorList)
230 else:
232 else:
231 form = SettingsForm(initial={'theme': selected_theme},
233 form = SettingsForm(initial={'theme': selected_theme},
232 error_class=PlainErrorList)
234 error_class=PlainErrorList)
233
235
234 context['form'] = form
236 context['form'] = form
235
237
236 return render(request, 'boards/settings.html', context)
238 return render(request, 'boards/settings.html', context)
237
239
238
240
239 def all_tags(request):
241 def all_tags(request):
240 """All tags list"""
242 """All tags list"""
241
243
242 context = _init_default_context(request)
244 context = _init_default_context(request)
243 context['all_tags'] = Tag.objects.get_not_empty_tags()
245 context['all_tags'] = Tag.objects.get_not_empty_tags()
244
246
245 return render(request, 'boards/tags.html', context)
247 return render(request, 'boards/tags.html', context)
246
248
247
249
248 def jump_to_post(request, post_id):
250 def jump_to_post(request, post_id):
249 """Determine thread in which the requested post is and open it's page"""
251 """Determine thread in which the requested post is and open it's page"""
250
252
251 post = get_object_or_404(Post, id=post_id)
253 post = get_object_or_404(Post, id=post_id)
252
254
253 if not post.thread:
255 if not post.thread:
254 return redirect(thread, post_id=post.id)
256 return redirect(thread, post_id=post.id)
255 else:
257 else:
256 return redirect(reverse(thread, kwargs={'post_id': post.thread.id})
258 return redirect(reverse(thread, kwargs={'post_id': post.thread.id})
257 + '#' + str(post.id))
259 + '#' + str(post.id))
258
260
259
261
260 def authors(request):
262 def authors(request):
261 """Show authors list"""
263 """Show authors list"""
262
264
263 context = _init_default_context(request)
265 context = _init_default_context(request)
264 context['authors'] = boards.authors.authors
266 context['authors'] = boards.authors.authors
265
267
266 return render(request, 'boards/authors.html', context)
268 return render(request, 'boards/authors.html', context)
267
269
268
270
269 def delete(request, post_id):
271 def delete(request, post_id):
270 """Delete post"""
272 """Delete post"""
271
273
272 user = _get_user(request)
274 user = _get_user(request)
273 post = get_object_or_404(Post, id=post_id)
275 post = get_object_or_404(Post, id=post_id)
274
276
275 if user.is_moderator():
277 if user.is_moderator():
276 # TODO Show confirmation page before deletion
278 # TODO Show confirmation page before deletion
277 Post.objects.delete_post(post)
279 Post.objects.delete_post(post)
278
280
279 if not post.thread:
281 if not post.thread:
280 return _redirect_to_next(request)
282 return _redirect_to_next(request)
281 else:
283 else:
282 return redirect(thread, post_id=post.thread.id)
284 return redirect(thread, post_id=post.thread.id)
283
285
284
286
285 def ban(request, post_id):
287 def ban(request, post_id):
286 """Ban user"""
288 """Ban user"""
287
289
288 user = _get_user(request)
290 user = _get_user(request)
289 post = get_object_or_404(Post, id=post_id)
291 post = get_object_or_404(Post, id=post_id)
290
292
291 if user.is_moderator():
293 if user.is_moderator():
292 # TODO Show confirmation page before ban
294 # TODO Show confirmation page before ban
293 Ban.objects.get_or_create(ip=post.poster_ip)
295 Ban.objects.get_or_create(ip=post.poster_ip)
294
296
295 return _redirect_to_next(request)
297 return _redirect_to_next(request)
296
298
297
299
298 def you_are_banned(request):
300 def you_are_banned(request):
299 """Show the page that notifies that user is banned"""
301 """Show the page that notifies that user is banned"""
300
302
301 context = _init_default_context(request)
303 context = _init_default_context(request)
302 return render(request, 'boards/staticpages/banned.html', context)
304 return render(request, 'boards/staticpages/banned.html', context)
303
305
304
306
305 def page_404(request):
307 def page_404(request):
306 """Show page 404 (not found error)"""
308 """Show page 404 (not found error)"""
307
309
308 context = _init_default_context(request)
310 context = _init_default_context(request)
309 return render(request, 'boards/404.html', context)
311 return render(request, 'boards/404.html', context)
310
312
311
313
312 def tag_subscribe(request, tag_name):
314 def tag_subscribe(request, tag_name):
313 """Add tag to favorites"""
315 """Add tag to favorites"""
314
316
315 user = _get_user(request)
317 user = _get_user(request)
316 tag = get_object_or_404(Tag, name=tag_name)
318 tag = get_object_or_404(Tag, name=tag_name)
317
319
318 if not tag in user.fav_tags.all():
320 if not tag in user.fav_tags.all():
319 user.fav_tags.add(tag)
321 user.fav_tags.add(tag)
320
322
321 return _redirect_to_next(request)
323 return _redirect_to_next(request)
322
324
323
325
324 def tag_unsubscribe(request, tag_name):
326 def tag_unsubscribe(request, tag_name):
325 """Remove tag from favorites"""
327 """Remove tag from favorites"""
326
328
327 user = _get_user(request)
329 user = _get_user(request)
328 tag = get_object_or_404(Tag, name=tag_name)
330 tag = get_object_or_404(Tag, name=tag_name)
329
331
330 if tag in user.fav_tags.all():
332 if tag in user.fav_tags.all():
331 user.fav_tags.remove(tag)
333 user.fav_tags.remove(tag)
332
334
333 return _redirect_to_next(request)
335 return _redirect_to_next(request)
334
336
335
337
336 def static_page(request, name):
338 def static_page(request, name):
337 """Show a static page that needs only tags list and a CSS"""
339 """Show a static page that needs only tags list and a CSS"""
338
340
339 context = _init_default_context(request)
341 context = _init_default_context(request)
340 return render(request, 'boards/staticpages/' + name + '.html', context)
342 return render(request, 'boards/staticpages/' + name + '.html', context)
341
343
342
344
343 def api_get_post(request, post_id):
345 def api_get_post(request, post_id):
344 """
346 """
345 Get the JSON of a post. This can be
347 Get the JSON of a post. This can be
346 used as and API for external clients.
348 used as and API for external clients.
347 """
349 """
348
350
349 post = get_object_or_404(Post, id=post_id)
351 post = get_object_or_404(Post, id=post_id)
350
352
351 json = serializers.serialize("json", [post], fields=(
353 json = serializers.serialize("json", [post], fields=(
352 "pub_time", "_text_rendered", "title", "text", "image",
354 "pub_time", "_text_rendered", "title", "text", "image",
353 "image_width", "image_height", "replies", "tags"
355 "image_width", "image_height", "replies", "tags"
354 ))
356 ))
355
357
356 return HttpResponse(content=json)
358 return HttpResponse(content=json)
357
359
358
360
359 def get_post(request, post_id):
361 def get_post(request, post_id):
360 """Get the html of a post. Used for popups."""
362 """Get the html of a post. Used for popups."""
361
363
362 post = get_object_or_404(Post, id=post_id)
364 post = get_object_or_404(Post, id=post_id)
363
365
364 context = RequestContext(request)
366 context = RequestContext(request)
365 context["post"] = post
367 context["post"] = post
366
368
367 return render(request, 'boards/post.html', context)
369 return render(request, 'boards/post.html', context)
368
370
369
371
370 def _get_theme(request, user=None):
372 def _get_theme(request, user=None):
371 """Get user's CSS theme"""
373 """Get user's CSS theme"""
372
374
373 if not user:
375 if not user:
374 user = _get_user(request)
376 user = _get_user(request)
375 theme = user.get_setting('theme')
377 theme = user.get_setting('theme')
376 if not theme:
378 if not theme:
377 theme = neboard.settings.DEFAULT_THEME
379 theme = neboard.settings.DEFAULT_THEME
378
380
379 return theme
381 return theme
380
382
381
383
382 def _init_default_context(request):
384 def _init_default_context(request):
383 """Create context with default values that are used in most views"""
385 """Create context with default values that are used in most views"""
384
386
385 context = RequestContext(request)
387 context = RequestContext(request)
386
388
387 user = _get_user(request)
389 user = _get_user(request)
388 context['user'] = user
390 context['user'] = user
389 context['tags'] = user.get_sorted_fav_tags()
391 context['tags'] = user.get_sorted_fav_tags()
390
392
391 theme = _get_theme(request, user)
393 theme = _get_theme(request, user)
392 context['theme'] = theme
394 context['theme'] = theme
393 context['theme_css'] = 'css/' + theme + '/base_page.css'
395 context['theme_css'] = 'css/' + theme + '/base_page.css'
394
396
395 # This shows the moderator panel
397 # This shows the moderator panel
396 moderate = user.get_setting(SETTING_MODERATE)
398 moderate = user.get_setting(SETTING_MODERATE)
397 if moderate == 'True':
399 if moderate == 'True':
398 context['moderator'] = user.is_moderator()
400 context['moderator'] = user.is_moderator()
399 else:
401 else:
400 context['moderator'] = False
402 context['moderator'] = False
401
403
402 return context
404 return context
403
405
404
406
405 def _get_user(request):
407 def _get_user(request):
406 """
408 """
407 Get current user from the session. If the user does not exist, create
409 Get current user from the session. If the user does not exist, create
408 a new one.
410 a new one.
409 """
411 """
410
412
411 session = request.session
413 session = request.session
412 if not 'user_id' in session:
414 if not 'user_id' in session:
413 request.session.save()
415 request.session.save()
414
416
415 md5 = hashlib.md5()
417 md5 = hashlib.md5()
416 md5.update(session.session_key)
418 md5.update(session.session_key)
417 new_id = md5.hexdigest()
419 new_id = md5.hexdigest()
418
420
419 time_now = timezone.now()
421 time_now = timezone.now()
420 user = User.objects.create(user_id=new_id, rank=RANK_USER,
422 user = User.objects.create(user_id=new_id, rank=RANK_USER,
421 registration_time=time_now)
423 registration_time=time_now)
422
424
423 session['user_id'] = user.id
425 session['user_id'] = user.id
424 else:
426 else:
425 user = User.objects.get(id=session['user_id'])
427 user = User.objects.get(id=session['user_id'])
426
428
427 return user
429 return user
428
430
429
431
430 def _redirect_to_next(request):
432 def _redirect_to_next(request):
431 """
433 """
432 If a 'next' parameter was specified, redirect to the next page. This is
434 If a 'next' parameter was specified, redirect to the next page. This is
433 used when the user is required to return to some page after the current
435 used when the user is required to return to some page after the current
434 view has finished its work.
436 view has finished its work.
435 """
437 """
436
438
437 if 'next' in request.GET:
439 if 'next' in request.GET:
438 next_page = request.GET['next']
440 next_page = request.GET['next']
439 return HttpResponseRedirect(next_page)
441 return HttpResponseRedirect(next_page)
440 else:
442 else:
441 return redirect(index)
443 return redirect(index)
442
444
443
445
444 def _ban_current_user(request):
446 def _ban_current_user(request):
445 """Add current user to the IP ban list"""
447 """Add current user to the IP ban list"""
446
448
447 ip = utils.get_client_ip(request)
449 ip = utils.get_client_ip(request)
448 Ban.objects.get_or_create(ip=ip)
450 Ban.objects.get_or_create(ip=ip)
@@ -1,13 +1,13 b''
1 = Features =
1 = Features =
2 [NOT STARTED] Connecting tags to each other
2 [NOT STARTED] Connecting tags to each other
3 [NOT STARTED] Tree view (JS)
3 [NOT STARTED] Tree view (JS)
4 [NOT STARTED] Adding tags to images filename
4 [NOT STARTED] Adding tags to images filename
5 [NOT STARTED] Federative network for s2s communication
5 [NOT STARTED] Federative network for s2s communication
6 [NOT STARTED] XMPP gate
6 [NOT STARTED] XMPP gate
7 [NOT STARTED] Bitmessage gate
7 [NOT STARTED] Bitmessage gate
8 [NOT STARTED] Notification engine
8 [NOT STARTED] Notification engine
9 [NOT STARTED] Javascript disabling engine
9 [NOT STARTED] Javascript disabling engine
10 [NOT STARTED] Thread autoupdate (JS + API)
10 [NOT STARTED] Thread autoupdate (JS + API)
11
11
12 = Bugs =
12 = Bugs =
13 [NOT STARTED] Fix bug with creating threads from tag view
13 [DONE] Fix bug with creating threads from tag view
General Comments 0
You need to be logged in to leave comments. Login now