##// END OF EJS Templates
Truncate posts in reflink popups
neko259 -
r368:39307834 thread_autoupdate
parent child Browse files
Show More
@@ -1,108 +1,109 b''
1 function $X(path, root) {
1 function $X(path, root) {
2 return document.evaluate(path, root || document, null, 6, null);
2 return document.evaluate(path, root || document, null, 6, null);
3 }
3 }
4 function $x(path, root) {
4 function $x(path, root) {
5 return document.evaluate(path, root || document, null, 8, null).singleNodeValue;
5 return document.evaluate(path, root || document, null, 8, null).singleNodeValue;
6 }
6 }
7
7
8 function $del(el) {
8 function $del(el) {
9 if(el) el.parentNode.removeChild(el);
9 if(el) el.parentNode.removeChild(el);
10 }
10 }
11
11
12 function $each(list, fn) {
12 function $each(list, fn) {
13 if(!list) return;
13 if(!list) return;
14 var i = list.snapshotLength;
14 var i = list.snapshotLength;
15 if(i > 0) while(i--) fn(list.snapshotItem(i), i);
15 if(i > 0) while(i--) fn(list.snapshotItem(i), i);
16 }
16 }
17
17
18 function addRefLinkPreview(node) {
18 function addRefLinkPreview(node) {
19 $each($X('.//a[starts-with(text(),">>")]', node || document), function(link) {
19 $each($X('.//a[starts-with(text(),">>")]', node || document), function(link) {
20 link.addEventListener('mouseover', showPostPreview, false);
20 link.addEventListener('mouseover', showPostPreview, false);
21 link.addEventListener('mouseout', delPostPreview, false);
21 link.addEventListener('mouseout', delPostPreview, false);
22 });
22 });
23 }
23 }
24
24
25 function showPostPreview(e) {
25 function showPostPreview(e) {
26 var doc = document;
26 var doc = document;
27 //ref id
27 //ref id
28 var pNum = $(this).text().match(/\d+/);
28 var pNum = $(this).text().match(/\d+/);
29
29
30 if (pNum.length == 0) {
30 if (pNum.length == 0) {
31 return;
31 return;
32 }
32 }
33
33
34 //position
34 //position
35 //var x = e.clientX + (doc.documentElement.scrollLeft || doc.body.scrollLeft) - doc.documentElement.clientLeft + 1;
35 //var x = e.clientX + (doc.documentElement.scrollLeft || doc.body.scrollLeft) - doc.documentElement.clientLeft + 1;
36 //var y = e.clientY + (doc.documentElement.scrollTop || doc.body.scrollTop) - doc.documentElement.clientTop;
36 //var y = e.clientY + (doc.documentElement.scrollTop || doc.body.scrollTop) - doc.documentElement.clientTop;
37
37
38 var x = e.clientX + (doc.documentElement.scrollLeft || doc.body.scrollLeft) + 2;
38 var x = e.clientX + (doc.documentElement.scrollLeft || doc.body.scrollLeft) + 2;
39 var y = e.clientY + (doc.documentElement.scrollTop || doc.body.scrollTop);
39 var y = e.clientY + (doc.documentElement.scrollTop || doc.body.scrollTop);
40
40
41 var cln = doc.createElement('div');
41 var cln = doc.createElement('div');
42 cln.id = 'pstprev_' + pNum;
42 cln.id = 'pstprev_' + pNum;
43 cln.className = 'post_preview';
43 cln.className = 'post_preview';
44
44
45 cln.style.cssText = 'top:' + y + 'px;' + (x < doc.body.clientWidth/2 ? 'left:' + x + 'px' : 'right:' + parseInt(doc.body.clientWidth - x + 1) + 'px');
45 cln.style.cssText = 'top:' + y + 'px;' + (x < doc.body.clientWidth/2 ? 'left:' + x + 'px' : 'right:' + parseInt(doc.body.clientWidth - x + 1) + 'px');
46
46
47 cln.addEventListener('mouseout', delPostPreview, false);
47 cln.addEventListener('mouseout', delPostPreview, false);
48
48
49
49
50 var mkPreview = function(cln, html) {
50 var mkPreview = function(cln, html) {
51
51
52 cln.innerHTML = html;
52 cln.innerHTML = html;
53
53
54 addRefLinkPreview(cln);
54 addRefLinkPreview(cln);
55
55
56 //if(!$x('.//small', cln)) showRefMap(post, p_num, refMap)
56 //if(!$x('.//small', cln)) showRefMap(post, p_num, refMap)
57 };
57 };
58
58
59
59
60 cln.innerHTML = gettext('Loading...');
60 cln.innerHTML = "<div class=\"post\">" + gettext('Loading...') + "</div>";
61
61
62 //Ссли пост Π½Π°ΠΉΠ΄Π΅Π½ Π² Π΄Π΅Ρ€Π΅Π²Π΅.
62 //Ссли пост Π½Π°ΠΉΠ΄Π΅Π½ Π² Π΄Π΅Ρ€Π΅Π²Π΅.
63 if($('div[id='+pNum+']').length > 0) {
63 if($('div[id='+pNum+']').length > 0) {
64 var postdata = $('div[id='+pNum+']').wrap("<div/>").parent().html();
64 var postdata = $('div[id='+pNum+']').wrap("<div/>").parent().html();
65
65
66 //TODO: Π²Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎ
66 //TODO: Π²Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎ
67 //funcInit(postdata);
67 //funcInit(postdata);
68
68
69 //make preview
69 //make preview
70 mkPreview(cln, postdata);
70 mkPreview(cln, postdata);
71 }
71 }
72 //ajax api
72 //ajax api
73 else {
73 else {
74 $.ajax({
74 $.ajax({
75 url: '/api/post/' + pNum + '/'
75 url: '/api/post/' + pNum + '/?truncated'
76 })
76 })
77 .success(function(data) {
77 .success(function(data) {
78 // TODO get a json, not post itself
78 // TODO get a json, not post itself
79 var postdata = $(data).wrap("<div/>").parent().html();
79 var postdata = $(data).wrap("<div/>").parent().html();
80
80
81 //make preview
81 //make preview
82 mkPreview(cln, postdata);
82 mkPreview(cln, postdata);
83
83
84 })
84 })
85 .error(function() {
85 .error(function() {
86 cln.innerHTML = gettext('Post not found');
86 cln.innerHTML = "<div class=\"post\">"
87 + gettext('Post not found') + "</div>";
87 });
88 });
88 }
89 }
89
90
90 $del(doc.getElementById(cln.id));
91 $del(doc.getElementById(cln.id));
91
92
92 //add preview
93 //add preview
93 $('body').append(cln);
94 $('body').append(cln);
94 }
95 }
95
96
96 function delPostPreview(e) {
97 function delPostPreview(e) {
97 var el = $x('ancestor-or-self::*[starts-with(@id,"pstprev")]', e.relatedTarget);
98 var el = $x('ancestor-or-self::*[starts-with(@id,"pstprev")]', e.relatedTarget);
98 if(!el) $each($X('.//div[starts-with(@id,"pstprev")]'), function(clone) {
99 if(!el) $each($X('.//div[starts-with(@id,"pstprev")]'), function(clone) {
99 $del(clone)
100 $del(clone)
100 });
101 });
101 else while(el.nextSibling) $del(el.nextSibling);
102 else while(el.nextSibling) $del(el.nextSibling);
102 }
103 }
103
104
104 function addPreview() {
105 function addPreview() {
105 $('.post').find('a').each(function() {
106 $('.post').find('a').each(function() {
106 showPostPreview($(this));
107 showPostPreview($(this));
107 });
108 });
108 }
109 }
@@ -1,64 +1,68 b''
1 {% load i18n %}
1 {% load i18n %}
2 {% load board %}
2 {% load board %}
3
3
4 {% if can_bump %}
4 {% if can_bump %}
5 <div class="post" id="{{ post.id }}">
5 <div class="post" id="{{ post.id }}">
6 {% else %}
6 {% else %}
7 <div class="post dead_post" id="{{ post.id }}">
7 <div class="post dead_post" id="{{ post.id }}">
8 {% endif %}
8 {% endif %}
9
9
10 {% if post.image %}
10 {% if post.image %}
11 <div class="image">
11 <div class="image">
12 <a
12 <a
13 class="thumb"
13 class="thumb"
14 href="{{ post.image.url }}"><img
14 href="{{ post.image.url }}"><img
15 src="{{ post.image.url_200x150 }}"
15 src="{{ post.image.url_200x150 }}"
16 alt="{% trans 'Post image' %}"
16 alt="{% trans 'Post image' %}"
17 data-width="{{ post.image_width }}"
17 data-width="{{ post.image_width }}"
18 data-height="{{ post.image_height }}"/>
18 data-height="{{ post.image_height }}"/>
19 </a>
19 </a>
20 </div>
20 </div>
21 {% endif %}
21 {% endif %}
22 <div class="message">
22 <div class="message">
23 <div class="post-info">
23 <div class="post-info">
24 <span class="title">{{ post.title }}</span>
24 <span class="title">{{ post.title }}</span>
25 <a class="post_id" href="#{{ post.id }}">
25 <a class="post_id" href="#{{ post.id }}">
26 ({{ post.id }})</a>
26 ({{ post.id }})</a>
27 [{{ post.pub_time }}]
27 [{{ post.pub_time }}]
28 [<a href="#" onclick="javascript:addQuickReply('{{ post.id }}')
28 [<a href="#" onclick="javascript:addQuickReply('{{ post.id }}')
29 ; return false;">&gt;&gt;</a>]
29 ; return false;">&gt;&gt;</a>]
30
30
31 {% if moderator %}
31 {% if moderator %}
32 <span class="moderator_info">
32 <span class="moderator_info">
33 [<a href="{% url 'delete' post_id=post.id %}"
33 [<a href="{% url 'delete' post_id=post.id %}"
34 >{% trans 'Delete' %}</a>]
34 >{% trans 'Delete' %}</a>]
35 ({{ post.poster_ip }})
35 ({{ post.poster_ip }})
36 [<a href="{% url 'ban' post_id=post.id %}?next={{ request.path }}"
36 [<a href="{% url 'ban' post_id=post.id %}?next={{ request.path }}"
37 >{% trans 'Ban IP' %}</a>]
37 >{% trans 'Ban IP' %}</a>]
38 </span>
38 </span>
39 {% endif %}
39 {% endif %}
40 </div>
40 </div>
41 {% autoescape off %}
41 {% autoescape off %}
42 {{ post.text.rendered }}
42 {% if truncated %}
43 {{ post.text.rendered|truncatewords_html:50 }}
44 {% else %}
45 {{ post.text.rendered }}
46 {% endif %}
43 {% endautoescape %}
47 {% endautoescape %}
44 {% if post.is_referenced %}
48 {% if post.is_referenced %}
45 <div class="refmap">
49 <div class="refmap">
46 {% trans "Replies" %}:
50 {% trans "Replies" %}:
47 {% for ref_post in post.get_sorted_referenced_posts %}
51 {% for ref_post in post.get_sorted_referenced_posts %}
48 <a href="{% post_url ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a
52 <a href="{% post_url ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a
49 >{% if not forloop.last %},{% endif %}
53 >{% if not forloop.last %},{% endif %}
50 {% endfor %}
54 {% endfor %}
51 </div>
55 </div>
52 {% endif %}
56 {% endif %}
53 </div>
57 </div>
54 {% if post.tags.exists %}
58 {% if post.tags.exists %}
55 <div class="metadata">
59 <div class="metadata">
56 <span class="tags">{% trans 'Tags' %}:
60 <span class="tags">{% trans 'Tags' %}:
57 {% for tag in post.tags.all %}
61 {% for tag in post.tags.all %}
58 <a class="tag" href="{% url 'tag' tag.name %}">
62 <a class="tag" href="{% url 'tag' tag.name %}">
59 {{ tag.name }}</a>
63 {{ tag.name }}</a>
60 {% endfor %}
64 {% endfor %}
61 </span>
65 </span>
62 </div>
66 </div>
63 {% endif %}
67 {% endif %}
64 </div> No newline at end of file
68 </div>
@@ -1,554 +1,556 b''
1 import hashlib
1 import hashlib
2 import json
2 import json
3 import string
3 import string
4 import time
4 import time
5
5
6 from datetime import datetime
6 from datetime import datetime
7
7
8 from django.core import serializers
8 from django.core import serializers
9 from django.core.urlresolvers import reverse
9 from django.core.urlresolvers import reverse
10 from django.http import HttpResponseRedirect
10 from django.http import HttpResponseRedirect
11 from django.http.response import HttpResponse
11 from django.http.response import HttpResponse
12 from django.template import RequestContext
12 from django.template import RequestContext
13 from django.shortcuts import render, redirect, get_object_or_404
13 from django.shortcuts import render, redirect, get_object_or_404
14 from django.utils import timezone
14 from django.utils import timezone
15 from django.db import transaction
15 from django.db import transaction
16
16
17 from boards import forms
17 from boards import forms
18 import boards
18 import boards
19 from boards import utils
19 from boards import utils
20 from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
20 from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
21 ThreadCaptchaForm, PostCaptchaForm, LoginForm, ModeratorSettingsForm
21 ThreadCaptchaForm, PostCaptchaForm, LoginForm, ModeratorSettingsForm
22
22
23 from boards.models import Post, Tag, Ban, User, RANK_USER, SETTING_MODERATE, \
23 from boards.models import Post, Tag, Ban, User, RANK_USER, SETTING_MODERATE, \
24 REGEX_REPLY
24 REGEX_REPLY
25 from boards import authors
25 from boards import authors
26 from boards.utils import get_client_ip
26 from boards.utils import get_client_ip
27 import neboard
27 import neboard
28 import re
28 import re
29
29
30 BAN_REASON_SPAM = 'Autoban: spam bot'
30 BAN_REASON_SPAM = 'Autoban: spam bot'
31
31
32
32
33 def index(request, page=0):
33 def index(request, page=0):
34 context = _init_default_context(request)
34 context = _init_default_context(request)
35
35
36 if utils.need_include_captcha(request):
36 if utils.need_include_captcha(request):
37 threadFormClass = ThreadCaptchaForm
37 threadFormClass = ThreadCaptchaForm
38 kwargs = {'request': request}
38 kwargs = {'request': request}
39 else:
39 else:
40 threadFormClass = ThreadForm
40 threadFormClass = ThreadForm
41 kwargs = {}
41 kwargs = {}
42
42
43 if request.method == 'POST':
43 if request.method == 'POST':
44 form = threadFormClass(request.POST, request.FILES,
44 form = threadFormClass(request.POST, request.FILES,
45 error_class=PlainErrorList, **kwargs)
45 error_class=PlainErrorList, **kwargs)
46 form.session = request.session
46 form.session = request.session
47
47
48 if form.is_valid():
48 if form.is_valid():
49 return _new_post(request, form)
49 return _new_post(request, form)
50 if form.need_to_ban:
50 if form.need_to_ban:
51 # Ban user because he is suspected to be a bot
51 # Ban user because he is suspected to be a bot
52 _ban_current_user(request)
52 _ban_current_user(request)
53 else:
53 else:
54 form = threadFormClass(error_class=PlainErrorList, **kwargs)
54 form = threadFormClass(error_class=PlainErrorList, **kwargs)
55
55
56 threads = []
56 threads = []
57 for thread in Post.objects.get_threads(page=int(page)):
57 for thread in Post.objects.get_threads(page=int(page)):
58 threads.append({
58 threads.append({
59 'thread': thread,
59 'thread': thread,
60 'bumpable': thread.can_bump(),
60 'bumpable': thread.can_bump(),
61 'last_replies': thread.get_last_replies(),
61 'last_replies': thread.get_last_replies(),
62 })
62 })
63
63
64 # TODO Make this generic for tag and threads list pages
64 # TODO Make this generic for tag and threads list pages
65 context['threads'] = None if len(threads) == 0 else threads
65 context['threads'] = None if len(threads) == 0 else threads
66 context['form'] = form
66 context['form'] = form
67
67
68 page_count = Post.objects.get_thread_page_count()
68 page_count = Post.objects.get_thread_page_count()
69 context['pages'] = range(page_count)
69 context['pages'] = range(page_count)
70 page = int(page)
70 page = int(page)
71 if page < page_count - 1:
71 if page < page_count - 1:
72 context['next_page'] = str(page + 1)
72 context['next_page'] = str(page + 1)
73 if page > 0:
73 if page > 0:
74 context['prev_page'] = str(page - 1)
74 context['prev_page'] = str(page - 1)
75
75
76 return render(request, 'boards/posting_general.html',
76 return render(request, 'boards/posting_general.html',
77 context)
77 context)
78
78
79
79
80 @transaction.commit_on_success
80 @transaction.commit_on_success
81 def _new_post(request, form, thread_id=boards.models.NO_PARENT):
81 def _new_post(request, form, thread_id=boards.models.NO_PARENT):
82 """Add a new post (in thread or as a reply)."""
82 """Add a new post (in thread or as a reply)."""
83
83
84 ip = get_client_ip(request)
84 ip = get_client_ip(request)
85 is_banned = Ban.objects.filter(ip=ip).exists()
85 is_banned = Ban.objects.filter(ip=ip).exists()
86
86
87 if is_banned:
87 if is_banned:
88 return redirect(you_are_banned)
88 return redirect(you_are_banned)
89
89
90 data = form.cleaned_data
90 data = form.cleaned_data
91
91
92 title = data['title']
92 title = data['title']
93 text = data['text']
93 text = data['text']
94
94
95 text = _remove_invalid_links(text)
95 text = _remove_invalid_links(text)
96
96
97 if 'image' in data.keys():
97 if 'image' in data.keys():
98 image = data['image']
98 image = data['image']
99 else:
99 else:
100 image = None
100 image = None
101
101
102 tags = []
102 tags = []
103
103
104 new_thread = thread_id == boards.models.NO_PARENT
104 new_thread = thread_id == boards.models.NO_PARENT
105 if new_thread:
105 if new_thread:
106 tag_strings = data['tags']
106 tag_strings = data['tags']
107
107
108 if tag_strings:
108 if tag_strings:
109 tag_strings = tag_strings.split(' ')
109 tag_strings = tag_strings.split(' ')
110 for tag_name in tag_strings:
110 for tag_name in tag_strings:
111 tag_name = string.lower(tag_name.strip())
111 tag_name = string.lower(tag_name.strip())
112 if len(tag_name) > 0:
112 if len(tag_name) > 0:
113 tag, created = Tag.objects.get_or_create(name=tag_name)
113 tag, created = Tag.objects.get_or_create(name=tag_name)
114 tags.append(tag)
114 tags.append(tag)
115
115
116 linked_tags = tag.get_linked_tags()
116 linked_tags = tag.get_linked_tags()
117 if len(linked_tags) > 0:
117 if len(linked_tags) > 0:
118 tags.extend(linked_tags)
118 tags.extend(linked_tags)
119
119
120 op = None if thread_id == boards.models.NO_PARENT else \
120 op = None if thread_id == boards.models.NO_PARENT else \
121 get_object_or_404(Post, id=thread_id)
121 get_object_or_404(Post, id=thread_id)
122 post = Post.objects.create_post(title=title, text=text, ip=ip,
122 post = Post.objects.create_post(title=title, text=text, ip=ip,
123 thread=op, image=image,
123 thread=op, image=image,
124 tags=tags, user=_get_user(request))
124 tags=tags, user=_get_user(request))
125
125
126 thread_to_show = (post.id if new_thread else thread_id)
126 thread_to_show = (post.id if new_thread else thread_id)
127
127
128 if new_thread:
128 if new_thread:
129 return redirect(thread, post_id=thread_to_show)
129 return redirect(thread, post_id=thread_to_show)
130 else:
130 else:
131 return redirect(reverse(thread, kwargs={'post_id': thread_to_show}) +
131 return redirect(reverse(thread, kwargs={'post_id': thread_to_show}) +
132 '#' + str(post.id))
132 '#' + str(post.id))
133
133
134
134
135 def tag(request, tag_name, page=0):
135 def tag(request, tag_name, page=0):
136 """
136 """
137 Get all tag threads. Threads are split in pages, so some page is
137 Get all tag threads. Threads are split in pages, so some page is
138 requested. Default page is 0.
138 requested. Default page is 0.
139 """
139 """
140
140
141 tag = get_object_or_404(Tag, name=tag_name)
141 tag = get_object_or_404(Tag, name=tag_name)
142 threads = []
142 threads = []
143 for thread in Post.objects.get_threads(tag=tag, page=int(page)):
143 for thread in Post.objects.get_threads(tag=tag, page=int(page)):
144 threads.append({
144 threads.append({
145 'thread': thread,
145 'thread': thread,
146 'bumpable': thread.can_bump(),
146 'bumpable': thread.can_bump(),
147 'last_replies': thread.get_last_replies(),
147 'last_replies': thread.get_last_replies(),
148 })
148 })
149
149
150 if request.method == 'POST':
150 if request.method == 'POST':
151 form = ThreadForm(request.POST, request.FILES,
151 form = ThreadForm(request.POST, request.FILES,
152 error_class=PlainErrorList)
152 error_class=PlainErrorList)
153 form.session = request.session
153 form.session = request.session
154
154
155 if form.is_valid():
155 if form.is_valid():
156 return _new_post(request, form)
156 return _new_post(request, form)
157 if form.need_to_ban:
157 if form.need_to_ban:
158 # Ban user because he is suspected to be a bot
158 # Ban user because he is suspected to be a bot
159 _ban_current_user(request)
159 _ban_current_user(request)
160 else:
160 else:
161 form = forms.ThreadForm(initial={'tags': tag_name},
161 form = forms.ThreadForm(initial={'tags': tag_name},
162 error_class=PlainErrorList)
162 error_class=PlainErrorList)
163
163
164 context = _init_default_context(request)
164 context = _init_default_context(request)
165 context['threads'] = None if len(threads) == 0 else threads
165 context['threads'] = None if len(threads) == 0 else threads
166 context['tag'] = tag
166 context['tag'] = tag
167
167
168 page_count = Post.objects.get_thread_page_count(tag=tag)
168 page_count = Post.objects.get_thread_page_count(tag=tag)
169 context['pages'] = range(page_count)
169 context['pages'] = range(page_count)
170 page = int(page)
170 page = int(page)
171 if page < page_count - 1:
171 if page < page_count - 1:
172 context['next_page'] = str(page + 1)
172 context['next_page'] = str(page + 1)
173 if page > 0:
173 if page > 0:
174 context['prev_page'] = str(page - 1)
174 context['prev_page'] = str(page - 1)
175
175
176 context['form'] = form
176 context['form'] = form
177
177
178 return render(request, 'boards/posting_general.html',
178 return render(request, 'boards/posting_general.html',
179 context)
179 context)
180
180
181
181
182 def thread(request, post_id):
182 def thread(request, post_id):
183 """Get all thread posts"""
183 """Get all thread posts"""
184
184
185 if utils.need_include_captcha(request):
185 if utils.need_include_captcha(request):
186 postFormClass = PostCaptchaForm
186 postFormClass = PostCaptchaForm
187 kwargs = {'request': request}
187 kwargs = {'request': request}
188 else:
188 else:
189 postFormClass = PostForm
189 postFormClass = PostForm
190 kwargs = {}
190 kwargs = {}
191
191
192 if request.method == 'POST':
192 if request.method == 'POST':
193 form = postFormClass(request.POST, request.FILES,
193 form = postFormClass(request.POST, request.FILES,
194 error_class=PlainErrorList, **kwargs)
194 error_class=PlainErrorList, **kwargs)
195 form.session = request.session
195 form.session = request.session
196
196
197 if form.is_valid():
197 if form.is_valid():
198 return _new_post(request, form, post_id)
198 return _new_post(request, form, post_id)
199 if form.need_to_ban:
199 if form.need_to_ban:
200 # Ban user because he is suspected to be a bot
200 # Ban user because he is suspected to be a bot
201 _ban_current_user(request)
201 _ban_current_user(request)
202 else:
202 else:
203 form = postFormClass(error_class=PlainErrorList, **kwargs)
203 form = postFormClass(error_class=PlainErrorList, **kwargs)
204
204
205 posts = Post.objects.get_thread(post_id)
205 posts = Post.objects.get_thread(post_id)
206
206
207 context = _init_default_context(request)
207 context = _init_default_context(request)
208
208
209 context['posts'] = posts
209 context['posts'] = posts
210 context['form'] = form
210 context['form'] = form
211 context['bumpable'] = posts[0].can_bump()
211 context['bumpable'] = posts[0].can_bump()
212 if context['bumpable']:
212 if context['bumpable']:
213 context['posts_left'] = neboard.settings.MAX_POSTS_PER_THREAD - len(
213 context['posts_left'] = neboard.settings.MAX_POSTS_PER_THREAD - len(
214 posts)
214 posts)
215 context['bumplimit_progress'] = str(
215 context['bumplimit_progress'] = str(
216 float(context['posts_left']) /
216 float(context['posts_left']) /
217 neboard.settings.MAX_POSTS_PER_THREAD * 100)
217 neboard.settings.MAX_POSTS_PER_THREAD * 100)
218 # TODO This last update time may not be accurate cause some posts can be
218 # TODO This last update time may not be accurate cause some posts can be
219 # changed or added after this point. See the diff method.
219 # changed or added after this point. See the diff method.
220 context["last_update"] = int(time.time() * 1000)
220 context["last_update"] = int(time.time() * 1000)
221
221
222 return render(request, 'boards/thread.html', context)
222 return render(request, 'boards/thread.html', context)
223
223
224
224
225 def login(request):
225 def login(request):
226 """Log in with user id"""
226 """Log in with user id"""
227
227
228 context = _init_default_context(request)
228 context = _init_default_context(request)
229
229
230 if request.method == 'POST':
230 if request.method == 'POST':
231 form = LoginForm(request.POST, request.FILES,
231 form = LoginForm(request.POST, request.FILES,
232 error_class=PlainErrorList)
232 error_class=PlainErrorList)
233 form.session = request.session
233 form.session = request.session
234
234
235 if form.is_valid():
235 if form.is_valid():
236 user = User.objects.get(user_id=form.cleaned_data['user_id'])
236 user = User.objects.get(user_id=form.cleaned_data['user_id'])
237 request.session['user_id'] = user.id
237 request.session['user_id'] = user.id
238 return redirect(index)
238 return redirect(index)
239
239
240 else:
240 else:
241 form = LoginForm()
241 form = LoginForm()
242
242
243 context['form'] = form
243 context['form'] = form
244
244
245 return render(request, 'boards/login.html', context)
245 return render(request, 'boards/login.html', context)
246
246
247
247
248 def settings(request):
248 def settings(request):
249 """User's settings"""
249 """User's settings"""
250
250
251 context = _init_default_context(request)
251 context = _init_default_context(request)
252 user = _get_user(request)
252 user = _get_user(request)
253 is_moderator = user.is_moderator()
253 is_moderator = user.is_moderator()
254
254
255 if request.method == 'POST':
255 if request.method == 'POST':
256 with transaction.commit_on_success():
256 with transaction.commit_on_success():
257 if is_moderator:
257 if is_moderator:
258 form = ModeratorSettingsForm(request.POST,
258 form = ModeratorSettingsForm(request.POST,
259 error_class=PlainErrorList)
259 error_class=PlainErrorList)
260 else:
260 else:
261 form = SettingsForm(request.POST, error_class=PlainErrorList)
261 form = SettingsForm(request.POST, error_class=PlainErrorList)
262
262
263 if form.is_valid():
263 if form.is_valid():
264 selected_theme = form.cleaned_data['theme']
264 selected_theme = form.cleaned_data['theme']
265
265
266 user.save_setting('theme', selected_theme)
266 user.save_setting('theme', selected_theme)
267
267
268 if is_moderator:
268 if is_moderator:
269 moderate = form.cleaned_data['moderate']
269 moderate = form.cleaned_data['moderate']
270 user.save_setting(SETTING_MODERATE, moderate)
270 user.save_setting(SETTING_MODERATE, moderate)
271
271
272 return redirect(settings)
272 return redirect(settings)
273 else:
273 else:
274 selected_theme = _get_theme(request)
274 selected_theme = _get_theme(request)
275
275
276 if is_moderator:
276 if is_moderator:
277 form = ModeratorSettingsForm(initial={'theme': selected_theme,
277 form = ModeratorSettingsForm(initial={'theme': selected_theme,
278 'moderate': context['moderator']},
278 'moderate': context['moderator']},
279 error_class=PlainErrorList)
279 error_class=PlainErrorList)
280 else:
280 else:
281 form = SettingsForm(initial={'theme': selected_theme},
281 form = SettingsForm(initial={'theme': selected_theme},
282 error_class=PlainErrorList)
282 error_class=PlainErrorList)
283
283
284 context['form'] = form
284 context['form'] = form
285
285
286 return render(request, 'boards/settings.html', context)
286 return render(request, 'boards/settings.html', context)
287
287
288
288
289 def all_tags(request):
289 def all_tags(request):
290 """All tags list"""
290 """All tags list"""
291
291
292 context = _init_default_context(request)
292 context = _init_default_context(request)
293 context['all_tags'] = Tag.objects.get_not_empty_tags()
293 context['all_tags'] = Tag.objects.get_not_empty_tags()
294
294
295 return render(request, 'boards/tags.html', context)
295 return render(request, 'boards/tags.html', context)
296
296
297
297
298 def jump_to_post(request, post_id):
298 def jump_to_post(request, post_id):
299 """Determine thread in which the requested post is and open it's page"""
299 """Determine thread in which the requested post is and open it's page"""
300
300
301 post = get_object_or_404(Post, id=post_id)
301 post = get_object_or_404(Post, id=post_id)
302
302
303 if not post.thread:
303 if not post.thread:
304 return redirect(thread, post_id=post.id)
304 return redirect(thread, post_id=post.id)
305 else:
305 else:
306 return redirect(reverse(thread, kwargs={'post_id': post.thread.id})
306 return redirect(reverse(thread, kwargs={'post_id': post.thread.id})
307 + '#' + str(post.id))
307 + '#' + str(post.id))
308
308
309
309
310 def authors(request):
310 def authors(request):
311 """Show authors list"""
311 """Show authors list"""
312
312
313 context = _init_default_context(request)
313 context = _init_default_context(request)
314 context['authors'] = boards.authors.authors
314 context['authors'] = boards.authors.authors
315
315
316 return render(request, 'boards/authors.html', context)
316 return render(request, 'boards/authors.html', context)
317
317
318
318
319 @transaction.commit_on_success
319 @transaction.commit_on_success
320 def delete(request, post_id):
320 def delete(request, post_id):
321 """Delete post"""
321 """Delete post"""
322
322
323 user = _get_user(request)
323 user = _get_user(request)
324 post = get_object_or_404(Post, id=post_id)
324 post = get_object_or_404(Post, id=post_id)
325
325
326 if user.is_moderator():
326 if user.is_moderator():
327 # TODO Show confirmation page before deletion
327 # TODO Show confirmation page before deletion
328 Post.objects.delete_post(post)
328 Post.objects.delete_post(post)
329
329
330 if not post.thread:
330 if not post.thread:
331 return _redirect_to_next(request)
331 return _redirect_to_next(request)
332 else:
332 else:
333 return redirect(thread, post_id=post.thread.id)
333 return redirect(thread, post_id=post.thread.id)
334
334
335
335
336 @transaction.commit_on_success
336 @transaction.commit_on_success
337 def ban(request, post_id):
337 def ban(request, post_id):
338 """Ban user"""
338 """Ban user"""
339
339
340 user = _get_user(request)
340 user = _get_user(request)
341 post = get_object_or_404(Post, id=post_id)
341 post = get_object_or_404(Post, id=post_id)
342
342
343 if user.is_moderator():
343 if user.is_moderator():
344 # TODO Show confirmation page before ban
344 # TODO Show confirmation page before ban
345 ban, created = Ban.objects.get_or_create(ip=post.poster_ip)
345 ban, created = Ban.objects.get_or_create(ip=post.poster_ip)
346 if created:
346 if created:
347 ban.reason = 'Banned for post ' + str(post_id)
347 ban.reason = 'Banned for post ' + str(post_id)
348 ban.save()
348 ban.save()
349
349
350 return _redirect_to_next(request)
350 return _redirect_to_next(request)
351
351
352
352
353 def you_are_banned(request):
353 def you_are_banned(request):
354 """Show the page that notifies that user is banned"""
354 """Show the page that notifies that user is banned"""
355
355
356 context = _init_default_context(request)
356 context = _init_default_context(request)
357
357
358 ban = get_object_or_404(Ban, ip=utils.get_client_ip(request))
358 ban = get_object_or_404(Ban, ip=utils.get_client_ip(request))
359 context['ban_reason'] = ban.reason
359 context['ban_reason'] = ban.reason
360 return render(request, 'boards/staticpages/banned.html', context)
360 return render(request, 'boards/staticpages/banned.html', context)
361
361
362
362
363 def page_404(request):
363 def page_404(request):
364 """Show page 404 (not found error)"""
364 """Show page 404 (not found error)"""
365
365
366 context = _init_default_context(request)
366 context = _init_default_context(request)
367 return render(request, 'boards/404.html', context)
367 return render(request, 'boards/404.html', context)
368
368
369
369
370 @transaction.commit_on_success
370 @transaction.commit_on_success
371 def tag_subscribe(request, tag_name):
371 def tag_subscribe(request, tag_name):
372 """Add tag to favorites"""
372 """Add tag to favorites"""
373
373
374 user = _get_user(request)
374 user = _get_user(request)
375 tag = get_object_or_404(Tag, name=tag_name)
375 tag = get_object_or_404(Tag, name=tag_name)
376
376
377 if not tag in user.fav_tags.all():
377 if not tag in user.fav_tags.all():
378 user.add_tag(tag)
378 user.add_tag(tag)
379
379
380 return _redirect_to_next(request)
380 return _redirect_to_next(request)
381
381
382
382
383 @transaction.commit_on_success
383 @transaction.commit_on_success
384 def tag_unsubscribe(request, tag_name):
384 def tag_unsubscribe(request, tag_name):
385 """Remove tag from favorites"""
385 """Remove tag from favorites"""
386
386
387 user = _get_user(request)
387 user = _get_user(request)
388 tag = get_object_or_404(Tag, name=tag_name)
388 tag = get_object_or_404(Tag, name=tag_name)
389
389
390 if tag in user.fav_tags.all():
390 if tag in user.fav_tags.all():
391 user.remove_tag(tag)
391 user.remove_tag(tag)
392
392
393 return _redirect_to_next(request)
393 return _redirect_to_next(request)
394
394
395
395
396 def static_page(request, name):
396 def static_page(request, name):
397 """Show a static page that needs only tags list and a CSS"""
397 """Show a static page that needs only tags list and a CSS"""
398
398
399 context = _init_default_context(request)
399 context = _init_default_context(request)
400 return render(request, 'boards/staticpages/' + name + '.html', context)
400 return render(request, 'boards/staticpages/' + name + '.html', context)
401
401
402
402
403 def api_get_post(request, post_id):
403 def api_get_post(request, post_id):
404 """
404 """
405 Get the JSON of a post. This can be
405 Get the JSON of a post. This can be
406 used as and API for external clients.
406 used as and API for external clients.
407 """
407 """
408
408
409 post = get_object_or_404(Post, id=post_id)
409 post = get_object_or_404(Post, id=post_id)
410
410
411 json = serializers.serialize("json", [post], fields=(
411 json = serializers.serialize("json", [post], fields=(
412 "pub_time", "_text_rendered", "title", "text", "image",
412 "pub_time", "_text_rendered", "title", "text", "image",
413 "image_width", "image_height", "replies", "tags"
413 "image_width", "image_height", "replies", "tags"
414 ))
414 ))
415
415
416 return HttpResponse(content=json)
416 return HttpResponse(content=json)
417
417
418
418
419 def api_get_threaddiff(request, thread_id, last_update_time):
419 def api_get_threaddiff(request, thread_id, last_update_time):
420 thread = get_object_or_404(Post, id=thread_id)
420 thread = get_object_or_404(Post, id=thread_id)
421
421
422 filter_time = datetime.fromtimestamp(float(last_update_time) / 1000)
422 filter_time = datetime.fromtimestamp(float(last_update_time) / 1000)
423
423
424 # TODO Set the last update date more properly, cause new posts can be
424 # TODO Set the last update date more properly, cause new posts can be
425 # changed after this point. Perhaps we need to get it from the thread last
425 # changed after this point. Perhaps we need to get it from the thread last
426 # update time at the point of filtering, or as the latest post update time.
426 # update time at the point of filtering, or as the latest post update time.
427 json_data = {
427 json_data = {
428 'added': [],
428 'added': [],
429 'updated': [],
429 'updated': [],
430 'last_update' : int(time.time() * 1000),
430 'last_update' : int(time.time() * 1000),
431 }
431 }
432 added_posts = Post.objects.filter(thread=thread, pub_time__gt=filter_time)
432 added_posts = Post.objects.filter(thread=thread, pub_time__gt=filter_time)
433 updated_posts = Post.objects.filter(thread=thread,
433 updated_posts = Post.objects.filter(thread=thread,
434 pub_time__lt=filter_time, last_edit_time__gt=filter_time)
434 pub_time__lt=filter_time, last_edit_time__gt=filter_time)
435 for post in added_posts:
435 for post in added_posts:
436 json_data['added'].append(get_post(request, post.id).content.strip())
436 json_data['added'].append(get_post(request, post.id).content.strip())
437 for post in updated_posts:
437 for post in updated_posts:
438 json_data['updated'].append(get_post(request, post.id).content.strip())
438 json_data['updated'].append(get_post(request, post.id).content.strip())
439
439
440 return HttpResponse(content=json.dumps(json_data))
440 return HttpResponse(content=json.dumps(json_data))
441
441
442
442
443 def get_post(request, post_id):
443 def get_post(request, post_id):
444 """Get the html of a post. Used for popups."""
444 """Get the html of a post. Used for popups."""
445
445
446 post = get_object_or_404(Post, id=post_id)
446 post = get_object_or_404(Post, id=post_id)
447 thread = post.thread
447 thread = post.thread
448
448
449 context = RequestContext(request)
449 context = RequestContext(request)
450 context["post"] = post
450 context["post"] = post
451 context["can_bump"] = thread.can_bump()
451 context["can_bump"] = thread.can_bump()
452 if "truncated" in request.GET:
453 context["truncated"] = True
452
454
453 return render(request, 'boards/post.html', context)
455 return render(request, 'boards/post.html', context)
454
456
455
457
456 def _get_theme(request, user=None):
458 def _get_theme(request, user=None):
457 """Get user's CSS theme"""
459 """Get user's CSS theme"""
458
460
459 if not user:
461 if not user:
460 user = _get_user(request)
462 user = _get_user(request)
461 theme = user.get_setting('theme')
463 theme = user.get_setting('theme')
462 if not theme:
464 if not theme:
463 theme = neboard.settings.DEFAULT_THEME
465 theme = neboard.settings.DEFAULT_THEME
464
466
465 return theme
467 return theme
466
468
467
469
468 def _init_default_context(request):
470 def _init_default_context(request):
469 """Create context with default values that are used in most views"""
471 """Create context with default values that are used in most views"""
470
472
471 context = RequestContext(request)
473 context = RequestContext(request)
472
474
473 user = _get_user(request)
475 user = _get_user(request)
474 context['user'] = user
476 context['user'] = user
475 context['tags'] = user.get_sorted_fav_tags()
477 context['tags'] = user.get_sorted_fav_tags()
476
478
477 theme = _get_theme(request, user)
479 theme = _get_theme(request, user)
478 context['theme'] = theme
480 context['theme'] = theme
479 context['theme_css'] = 'css/' + theme + '/base_page.css'
481 context['theme_css'] = 'css/' + theme + '/base_page.css'
480
482
481 # This shows the moderator panel
483 # This shows the moderator panel
482 moderate = user.get_setting(SETTING_MODERATE)
484 moderate = user.get_setting(SETTING_MODERATE)
483 if moderate == 'True':
485 if moderate == 'True':
484 context['moderator'] = user.is_moderator()
486 context['moderator'] = user.is_moderator()
485 else:
487 else:
486 context['moderator'] = False
488 context['moderator'] = False
487
489
488 return context
490 return context
489
491
490
492
491 def _get_user(request):
493 def _get_user(request):
492 """
494 """
493 Get current user from the session. If the user does not exist, create
495 Get current user from the session. If the user does not exist, create
494 a new one.
496 a new one.
495 """
497 """
496
498
497 session = request.session
499 session = request.session
498 if not 'user_id' in session:
500 if not 'user_id' in session:
499 request.session.save()
501 request.session.save()
500
502
501 md5 = hashlib.md5()
503 md5 = hashlib.md5()
502 md5.update(session.session_key)
504 md5.update(session.session_key)
503 new_id = md5.hexdigest()
505 new_id = md5.hexdigest()
504
506
505 time_now = timezone.now()
507 time_now = timezone.now()
506 user = User.objects.create(user_id=new_id, rank=RANK_USER,
508 user = User.objects.create(user_id=new_id, rank=RANK_USER,
507 registration_time=time_now)
509 registration_time=time_now)
508
510
509 session['user_id'] = user.id
511 session['user_id'] = user.id
510 else:
512 else:
511 user = User.objects.get(id=session['user_id'])
513 user = User.objects.get(id=session['user_id'])
512
514
513 return user
515 return user
514
516
515
517
516 def _redirect_to_next(request):
518 def _redirect_to_next(request):
517 """
519 """
518 If a 'next' parameter was specified, redirect to the next page. This is
520 If a 'next' parameter was specified, redirect to the next page. This is
519 used when the user is required to return to some page after the current
521 used when the user is required to return to some page after the current
520 view has finished its work.
522 view has finished its work.
521 """
523 """
522
524
523 if 'next' in request.GET:
525 if 'next' in request.GET:
524 next_page = request.GET['next']
526 next_page = request.GET['next']
525 return HttpResponseRedirect(next_page)
527 return HttpResponseRedirect(next_page)
526 else:
528 else:
527 return redirect(index)
529 return redirect(index)
528
530
529
531
530 @transaction.commit_on_success
532 @transaction.commit_on_success
531 def _ban_current_user(request):
533 def _ban_current_user(request):
532 """Add current user to the IP ban list"""
534 """Add current user to the IP ban list"""
533
535
534 ip = utils.get_client_ip(request)
536 ip = utils.get_client_ip(request)
535 ban, created = Ban.objects.get_or_create(ip=ip)
537 ban, created = Ban.objects.get_or_create(ip=ip)
536 if created:
538 if created:
537 ban.can_read = False
539 ban.can_read = False
538 ban.reason = BAN_REASON_SPAM
540 ban.reason = BAN_REASON_SPAM
539 ban.save()
541 ban.save()
540
542
541
543
542 def _remove_invalid_links(text):
544 def _remove_invalid_links(text):
543 """
545 """
544 Replace invalid links in posts so that they won't be parsed.
546 Replace invalid links in posts so that they won't be parsed.
545 Invalid links are links to non-existent posts
547 Invalid links are links to non-existent posts
546 """
548 """
547
549
548 for reply_number in re.finditer(REGEX_REPLY, text):
550 for reply_number in re.finditer(REGEX_REPLY, text):
549 post_id = reply_number.group(1)
551 post_id = reply_number.group(1)
550 post = Post.objects.filter(id=post_id)
552 post = Post.objects.filter(id=post_id)
551 if not post.exists():
553 if not post.exists():
552 text = string.replace(text, '>>' + id, id)
554 text = string.replace(text, '>>' + id, id)
553
555
554 return text
556 return text
General Comments 0
You need to be logged in to leave comments. Login now