##// END OF EJS Templates
Basic support for the generic forms. Still need to fix some things in the form...
neko259 -
r425:abb0b1c9 default
parent child Browse files
Show More
@@ -1,253 +1,253 b''
1 import re
1 import re
2 from captcha.fields import CaptchaField
2 from captcha.fields import CaptchaField
3 from django import forms
3 from django import forms
4 from django.forms.util import ErrorList
4 from django.forms.util import ErrorList
5 from django.utils.translation import ugettext_lazy as _
5 from django.utils.translation import ugettext_lazy as _
6 import time
6 import time
7 from boards.models.post import TITLE_MAX_LENGTH
7 from boards.models.post import TITLE_MAX_LENGTH
8 from boards.models import User
8 from boards.models import User
9 from neboard import settings
9 from neboard import settings
10 from boards import utils
10 from boards import utils
11 import boards.settings as board_settings
11 import boards.settings as board_settings
12
12
13 LAST_POST_TIME = "last_post_time"
13 LAST_POST_TIME = "last_post_time"
14 LAST_LOGIN_TIME = "last_login_time"
14 LAST_LOGIN_TIME = "last_login_time"
15
15
16
16
17 class PlainErrorList(ErrorList):
17 class PlainErrorList(ErrorList):
18 def __unicode__(self):
18 def __unicode__(self):
19 return self.as_text()
19 return self.as_text()
20
20
21 def as_text(self):
21 def as_text(self):
22 return ''.join([u'(!) %s ' % e for e in self])
22 return ''.join([u'(!) %s ' % e for e in self])
23
23
24
24
25 class NeboardForm(forms.Form):
25 class NeboardForm(forms.Form):
26
26
27 def as_p(self):
27 def as_p(self):
28 "Returns this form rendered as HTML <p>s."
28 "Returns this form rendered as HTML <p>s."
29 return self._html_output(
29 return self._html_output(
30 # TODO Do not show hidden rows in the list here
30 normal_row='<div class="form-row">'
31 normal_row='<div class="form-row">'
31 '<div class="form-label">'
32 '<div class="form-label">'
32 '%(label)s'
33 '%(label)s'
33 '</div>'
34 '</div>'
34 '<div class="form-input">'
35 '<div class="form-input">'
35 '%(field)s'
36 '%(field)s'
36 '</div>'
37 '</div>'
37 '%(help_text)s'
38 '%(help_text)s'
38 '</div>',
39 '</div>',
39 error_row='<div class="form-errors">%s</div>',
40 error_row='<div class="form-row"><div class="form-label"></div><div class="form-errors">%s</div></div>',
40 row_ender='</p>',
41 row_ender='</p>',
41 help_text_html=' <span class="helptext">%s</span>',
42 help_text_html=' <span class="helptext">%s</span>',
42 errors_on_separate_row=True)
43 errors_on_separate_row=True)
43
44
44
45 class PostForm(NeboardForm):
45 class PostForm(NeboardForm):
46
46
47 title = forms.CharField(max_length=TITLE_MAX_LENGTH, required=False,
47 title = forms.CharField(max_length=TITLE_MAX_LENGTH, required=False,
48 label=_('Title'))
48 label=_('Title'))
49 text = forms.CharField(widget=forms.Textarea, required=False,
49 text = forms.CharField(widget=forms.Textarea, required=False,
50 label=_('Text'))
50 label=_('Text'))
51 image = forms.ImageField(required=False, label=_('Image'))
51 image = forms.ImageField(required=False, label=_('Image'))
52
52
53 # This field is for spam prevention only
53 # This field is for spam prevention only
54 email = forms.CharField(max_length=100, required=False, label=_('e-mail'),
54 email = forms.CharField(max_length=100, required=False, label=_('e-mail'),
55 widget=forms.TextInput(attrs={
55 widget=forms.TextInput(attrs={
56 'class': 'form-email'}))
56 'class': 'form-email'}))
57
57
58 session = None
58 session = None
59 need_to_ban = False
59 need_to_ban = False
60
60
61 def clean_title(self):
61 def clean_title(self):
62 title = self.cleaned_data['title']
62 title = self.cleaned_data['title']
63 if title:
63 if title:
64 if len(title) > TITLE_MAX_LENGTH:
64 if len(title) > TITLE_MAX_LENGTH:
65 raise forms.ValidationError(_('Title must have less than %s '
65 raise forms.ValidationError(_('Title must have less than %s '
66 'characters') %
66 'characters') %
67 str(TITLE_MAX_LENGTH))
67 str(TITLE_MAX_LENGTH))
68 return title
68 return title
69
69
70 def clean_text(self):
70 def clean_text(self):
71 text = self.cleaned_data['text']
71 text = self.cleaned_data['text']
72 if text:
72 if text:
73 if len(text) > board_settings.MAX_TEXT_LENGTH:
73 if len(text) > board_settings.MAX_TEXT_LENGTH:
74 raise forms.ValidationError(_('Text must have less than %s '
74 raise forms.ValidationError(_('Text must have less than %s '
75 'characters') %
75 'characters') %
76 str(board_settings
76 str(board_settings
77 .MAX_TEXT_LENGTH))
77 .MAX_TEXT_LENGTH))
78 return text
78 return text
79
79
80 def clean_image(self):
80 def clean_image(self):
81 image = self.cleaned_data['image']
81 image = self.cleaned_data['image']
82 if image:
82 if image:
83 if image._size > board_settings.MAX_IMAGE_SIZE:
83 if image._size > board_settings.MAX_IMAGE_SIZE:
84 raise forms.ValidationError(
84 raise forms.ValidationError(
85 _('Image must be less than %s bytes')
85 _('Image must be less than %s bytes')
86 % str(board_settings.MAX_IMAGE_SIZE))
86 % str(board_settings.MAX_IMAGE_SIZE))
87 return image
87 return image
88
88
89 def clean(self):
89 def clean(self):
90 cleaned_data = super(PostForm, self).clean()
90 cleaned_data = super(PostForm, self).clean()
91
91
92 if not self.session:
92 if not self.session:
93 raise forms.ValidationError('Humans have sessions')
93 raise forms.ValidationError('Humans have sessions')
94
94
95 if cleaned_data['email']:
95 if cleaned_data['email']:
96 self.need_to_ban = True
96 self.need_to_ban = True
97 raise forms.ValidationError('A human cannot enter a hidden field')
97 raise forms.ValidationError('A human cannot enter a hidden field')
98
98
99 if not self.errors:
99 if not self.errors:
100 self._clean_text_image()
100 self._clean_text_image()
101
101
102 if not self.errors and self.session:
102 if not self.errors and self.session:
103 self._validate_posting_speed()
103 self._validate_posting_speed()
104
104
105 return cleaned_data
105 return cleaned_data
106
106
107 def _clean_text_image(self):
107 def _clean_text_image(self):
108 text = self.cleaned_data.get('text')
108 text = self.cleaned_data.get('text')
109 image = self.cleaned_data.get('image')
109 image = self.cleaned_data.get('image')
110
110
111 if (not text) and (not image):
111 if (not text) and (not image):
112 error_message = _('Either text or image must be entered.')
112 error_message = _('Either text or image must be entered.')
113 self._errors['text'] = self.error_class([error_message])
113 self._errors['text'] = self.error_class([error_message])
114
114
115 def _validate_posting_speed(self):
115 def _validate_posting_speed(self):
116 can_post = True
116 can_post = True
117
117
118 if LAST_POST_TIME in self.session:
118 if LAST_POST_TIME in self.session:
119 now = time.time()
119 now = time.time()
120 last_post_time = self.session[LAST_POST_TIME]
120 last_post_time = self.session[LAST_POST_TIME]
121
121
122 current_delay = int(now - last_post_time)
122 current_delay = int(now - last_post_time)
123
123
124 if current_delay < settings.POSTING_DELAY:
124 if current_delay < settings.POSTING_DELAY:
125 error_message = _('Wait %s seconds after last posting') % str(
125 error_message = _('Wait %s seconds after last posting') % str(
126 settings.POSTING_DELAY - current_delay)
126 settings.POSTING_DELAY - current_delay)
127 self._errors['text'] = self.error_class([error_message])
127 self._errors['text'] = self.error_class([error_message])
128
128
129 can_post = False
129 can_post = False
130
130
131 if can_post:
131 if can_post:
132 self.session[LAST_POST_TIME] = time.time()
132 self.session[LAST_POST_TIME] = time.time()
133
133
134
134
135 class ThreadForm(PostForm):
135 class ThreadForm(PostForm):
136
136
137 regex_tags = re.compile(ur'^[\w\s\d]+$', re.UNICODE)
137 regex_tags = re.compile(ur'^[\w\s\d]+$', re.UNICODE)
138
138
139 tags = forms.CharField(max_length=100, label=_('Tags'))
139 tags = forms.CharField(max_length=100, label=_('Tags'))
140
140
141 def clean_tags(self):
141 def clean_tags(self):
142 tags = self.cleaned_data['tags']
142 tags = self.cleaned_data['tags']
143
143
144 if tags:
144 if tags:
145 if not self.regex_tags.match(tags):
145 if not self.regex_tags.match(tags):
146 raise forms.ValidationError(
146 raise forms.ValidationError(
147 _('Inappropriate characters in tags.'))
147 _('Inappropriate characters in tags.'))
148
148
149 return tags
149 return tags
150
150
151 def clean(self):
151 def clean(self):
152 cleaned_data = super(ThreadForm, self).clean()
152 cleaned_data = super(ThreadForm, self).clean()
153
153
154 return cleaned_data
154 return cleaned_data
155
155
156
156
157 class PostCaptchaForm(PostForm):
157 class PostCaptchaForm(PostForm):
158 captcha = CaptchaField()
158 captcha = CaptchaField()
159
159
160 def __init__(self, *args, **kwargs):
160 def __init__(self, *args, **kwargs):
161 self.request = kwargs['request']
161 self.request = kwargs['request']
162 del kwargs['request']
162 del kwargs['request']
163
163
164 super(PostCaptchaForm, self).__init__(*args, **kwargs)
164 super(PostCaptchaForm, self).__init__(*args, **kwargs)
165
165
166 def clean(self):
166 def clean(self):
167 cleaned_data = super(PostCaptchaForm, self).clean()
167 cleaned_data = super(PostCaptchaForm, self).clean()
168
168
169 success = self.is_valid()
169 success = self.is_valid()
170 utils.update_captcha_access(self.request, success)
170 utils.update_captcha_access(self.request, success)
171
171
172 if success:
172 if success:
173 return cleaned_data
173 return cleaned_data
174 else:
174 else:
175 raise forms.ValidationError(_("Captcha validation failed"))
175 raise forms.ValidationError(_("Captcha validation failed"))
176
176
177
177
178 class ThreadCaptchaForm(ThreadForm):
178 class ThreadCaptchaForm(ThreadForm):
179 captcha = CaptchaField()
179 captcha = CaptchaField()
180
180
181 def __init__(self, *args, **kwargs):
181 def __init__(self, *args, **kwargs):
182 self.request = kwargs['request']
182 self.request = kwargs['request']
183 del kwargs['request']
183 del kwargs['request']
184
184
185 super(ThreadCaptchaForm, self).__init__(*args, **kwargs)
185 super(ThreadCaptchaForm, self).__init__(*args, **kwargs)
186
186
187 def clean(self):
187 def clean(self):
188 cleaned_data = super(ThreadCaptchaForm, self).clean()
188 cleaned_data = super(ThreadCaptchaForm, self).clean()
189
189
190 success = self.is_valid()
190 success = self.is_valid()
191 utils.update_captcha_access(self.request, success)
191 utils.update_captcha_access(self.request, success)
192
192
193 if success:
193 if success:
194 return cleaned_data
194 return cleaned_data
195 else:
195 else:
196 raise forms.ValidationError(_("Captcha validation failed"))
196 raise forms.ValidationError(_("Captcha validation failed"))
197
197
198
198
199 class SettingsForm(NeboardForm):
199 class SettingsForm(NeboardForm):
200
200
201 theme = forms.ChoiceField(choices=settings.THEMES,
201 theme = forms.ChoiceField(choices=settings.THEMES,
202 label=_('Theme'))
202 label=_('Theme'))
203
203
204
204
205 class ModeratorSettingsForm(SettingsForm):
205 class ModeratorSettingsForm(SettingsForm):
206
206
207 moderate = forms.BooleanField(required=False, label=_('Enable moderation '
207 moderate = forms.BooleanField(required=False, label=_('Enable moderation '
208 'panel'))
208 'panel'))
209
209
210
210
211 class LoginForm(NeboardForm):
211 class LoginForm(NeboardForm):
212
212
213 user_id = forms.CharField()
213 user_id = forms.CharField()
214
214
215 session = None
215 session = None
216
216
217 def clean_user_id(self):
217 def clean_user_id(self):
218 user_id = self.cleaned_data['user_id']
218 user_id = self.cleaned_data['user_id']
219 if user_id:
219 if user_id:
220 users = User.objects.filter(user_id=user_id)
220 users = User.objects.filter(user_id=user_id)
221 if len(users) == 0:
221 if len(users) == 0:
222 raise forms.ValidationError(_('No such user found'))
222 raise forms.ValidationError(_('No such user found'))
223
223
224 return user_id
224 return user_id
225
225
226 def _validate_login_speed(self):
226 def _validate_login_speed(self):
227 can_post = True
227 can_post = True
228
228
229 if LAST_LOGIN_TIME in self.session:
229 if LAST_LOGIN_TIME in self.session:
230 now = time.time()
230 now = time.time()
231 last_login_time = self.session[LAST_LOGIN_TIME]
231 last_login_time = self.session[LAST_LOGIN_TIME]
232
232
233 current_delay = int(now - last_login_time)
233 current_delay = int(now - last_login_time)
234
234
235 if current_delay < board_settings.LOGIN_TIMEOUT:
235 if current_delay < board_settings.LOGIN_TIMEOUT:
236 error_message = _('Wait %s minutes after last login') % str(
236 error_message = _('Wait %s minutes after last login') % str(
237 (board_settings.LOGIN_TIMEOUT - current_delay) / 60)
237 (board_settings.LOGIN_TIMEOUT - current_delay) / 60)
238 self._errors['user_id'] = self.error_class([error_message])
238 self._errors['user_id'] = self.error_class([error_message])
239
239
240 can_post = False
240 can_post = False
241
241
242 if can_post:
242 if can_post:
243 self.session[LAST_LOGIN_TIME] = time.time()
243 self.session[LAST_LOGIN_TIME] = time.time()
244
244
245 def clean(self):
245 def clean(self):
246 if not self.session:
246 if not self.session:
247 raise forms.ValidationError('Humans have sessions')
247 raise forms.ValidationError('Humans have sessions')
248
248
249 self._validate_login_speed()
249 self._validate_login_speed()
250
250
251 cleaned_data = super(LoginForm, self).clean()
251 cleaned_data = super(LoginForm, self).clean()
252
252
253 return cleaned_data
253 return cleaned_data
@@ -1,283 +1,241 b''
1 {% extends "boards/base.html" %}
1 {% extends "boards/base.html" %}
2
2
3 {% load i18n %}
3 {% load i18n %}
4 {% load cache %}
4 {% load cache %}
5 {% load board %}
5 {% load board %}
6
6
7 {% block head %}
7 {% block head %}
8 {% if tag %}
8 {% if tag %}
9 <title>Neboard - {{ tag.name }}</title>
9 <title>Neboard - {{ tag.name }}</title>
10 {% else %}
10 {% else %}
11 <title>Neboard</title>
11 <title>Neboard</title>
12 {% endif %}
12 {% endif %}
13
13
14 {% if prev_page %}
14 {% if prev_page %}
15 <link rel="next" href="
15 <link rel="next" href="
16 {% if tag %}
16 {% if tag %}
17 {% url "tag" tag_name=tag page=prev_page %}
17 {% url "tag" tag_name=tag page=prev_page %}
18 {% else %}
18 {% else %}
19 {% url "index" page=prev_page %}
19 {% url "index" page=prev_page %}
20 {% endif %}
20 {% endif %}
21 " />
21 " />
22 {% endif %}
22 {% endif %}
23 {% if next_page %}
23 {% if next_page %}
24 <link rel="next" href="
24 <link rel="next" href="
25 {% if tag %}
25 {% if tag %}
26 {% url "tag" tag_name=tag page=next_page %}
26 {% url "tag" tag_name=tag page=next_page %}
27 {% else %}
27 {% else %}
28 {% url "index" page=next_page %}
28 {% url "index" page=next_page %}
29 {% endif %}
29 {% endif %}
30 " />
30 " />
31 {% endif %}
31 {% endif %}
32
32
33 {% endblock %}
33 {% endblock %}
34
34
35 {% block content %}
35 {% block content %}
36
36
37 {% get_current_language as LANGUAGE_CODE %}
37 {% get_current_language as LANGUAGE_CODE %}
38
38
39 {% if tag %}
39 {% if tag %}
40 <div class="tag_info">
40 <div class="tag_info">
41 <h2>
41 <h2>
42 {% if tag in user.fav_tags.all %}
42 {% if tag in user.fav_tags.all %}
43 <a href="{% url 'tag_unsubscribe' tag.name %}?next={{ request.path }}"
43 <a href="{% url 'tag_unsubscribe' tag.name %}?next={{ request.path }}"
44 class="fav">β˜…</a>
44 class="fav">β˜…</a>
45 {% else %}
45 {% else %}
46 <a href="{% url 'tag_subscribe' tag.name %}?next={{ request.path }}"
46 <a href="{% url 'tag_subscribe' tag.name %}?next={{ request.path }}"
47 class="not_fav">β˜…</a>
47 class="not_fav">β˜…</a>
48 {% endif %}
48 {% endif %}
49 #{{ tag.name }}
49 #{{ tag.name }}
50 </h2>
50 </h2>
51 </div>
51 </div>
52 {% endif %}
52 {% endif %}
53
53
54 {% if threads %}
54 {% if threads %}
55 {% if prev_page %}
55 {% if prev_page %}
56 <div class="page_link">
56 <div class="page_link">
57 <a href="
57 <a href="
58 {% if tag %}
58 {% if tag %}
59 {% url "tag" tag_name=tag page=prev_page %}
59 {% url "tag" tag_name=tag page=prev_page %}
60 {% else %}
60 {% else %}
61 {% url "index" page=prev_page %}
61 {% url "index" page=prev_page %}
62 {% endif %}
62 {% endif %}
63 ">{% trans "Previous page" %}</a>
63 ">{% trans "Previous page" %}</a>
64 </div>
64 </div>
65 {% endif %}
65 {% endif %}
66
66
67 {% for thread in threads %}
67 {% for thread in threads %}
68 {% cache 600 thread_short thread.id thread.thread.last_edit_time moderator LANGUAGE_CODE %}
68 {% cache 600 thread_short thread.id thread.thread.last_edit_time moderator LANGUAGE_CODE %}
69 <div class="thread">
69 <div class="thread">
70 {% if thread.bumpable %}
70 {% if thread.bumpable %}
71 <div class="post" id="{{ thread.op.id }}">
71 <div class="post" id="{{ thread.op.id }}">
72 {% else %}
72 {% else %}
73 <div class="post dead_post" id="{{ thread.op.id }}">
73 <div class="post dead_post" id="{{ thread.op.id }}">
74 {% endif %}
74 {% endif %}
75 {% if thread.op.image %}
75 {% if thread.op.image %}
76 <div class="image">
76 <div class="image">
77 <a class="thumb"
77 <a class="thumb"
78 href="{{ thread.op.image.url }}"><img
78 href="{{ thread.op.image.url }}"><img
79 src="{{ thread.op.image.url_200x150 }}"
79 src="{{ thread.op.image.url_200x150 }}"
80 alt="{{ thread.op.id }}"
80 alt="{{ thread.op.id }}"
81 data-width="{{ thread.op.image_width }}"
81 data-width="{{ thread.op.image_width }}"
82 data-height="{{ thread.op.image_height }}"/>
82 data-height="{{ thread.op.image_height }}"/>
83 </a>
83 </a>
84 </div>
84 </div>
85 {% endif %}
85 {% endif %}
86 <div class="message">
86 <div class="message">
87 <div class="post-info">
87 <div class="post-info">
88 <span class="title">{{ thread.op.title }}</span>
88 <span class="title">{{ thread.op.title }}</span>
89 <a class="post_id" href="{% url 'thread' thread.op.id %}"
89 <a class="post_id" href="{% url 'thread' thread.op.id %}"
90 >({{ thread.op.id }})</a>
90 >({{ thread.op.id }})</a>
91 [{{ thread.op.pub_time }}]
91 [{{ thread.op.pub_time }}]
92 [<a class="link" href="
92 [<a class="link" href="
93 {% url 'thread' thread.op.id %}#form"
93 {% url 'thread' thread.op.id %}#form"
94 >{% trans "Reply" %}</a>]
94 >{% trans "Reply" %}</a>]
95
95
96 {% if moderator %}
96 {% if moderator %}
97 <span class="moderator_info">
97 <span class="moderator_info">
98 [<a href="
98 [<a href="
99 {% url 'delete' post_id=thread.op.id %}?next={{ request.path }}"
99 {% url 'delete' post_id=thread.op.id %}?next={{ request.path }}"
100 >{% trans 'Delete' %}</a>]
100 >{% trans 'Delete' %}</a>]
101 ({{ thread.thread.poster_ip }})
101 ({{ thread.thread.poster_ip }})
102 [<a href="
102 [<a href="
103 {% url 'ban' post_id=thread.op.id %}?next={{ request.path }}"
103 {% url 'ban' post_id=thread.op.id %}?next={{ request.path }}"
104 >{% trans 'Ban IP' %}</a>]
104 >{% trans 'Ban IP' %}</a>]
105 </span>
105 </span>
106 {% endif %}
106 {% endif %}
107 </div>
107 </div>
108 {% autoescape off %}
108 {% autoescape off %}
109 {{ thread.op.text.rendered|truncatewords_html:50 }}
109 {{ thread.op.text.rendered|truncatewords_html:50 }}
110 {% endautoescape %}
110 {% endautoescape %}
111 {% if thread.op.is_referenced %}
111 {% if thread.op.is_referenced %}
112 <div class="refmap">
112 <div class="refmap">
113 {% trans "Replies" %}:
113 {% trans "Replies" %}:
114 {% for ref_post in thread.op.get_sorted_referenced_posts %}
114 {% for ref_post in thread.op.get_sorted_referenced_posts %}
115 <a href="{% post_url ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a
115 <a href="{% post_url ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a
116 >{% if not forloop.last %},{% endif %}
116 >{% if not forloop.last %},{% endif %}
117 {% endfor %}
117 {% endfor %}
118 </div>
118 </div>
119 {% endif %}
119 {% endif %}
120 </div>
120 </div>
121 <div class="metadata">
121 <div class="metadata">
122 {{ thread.thread.get_images_count }} {% trans 'images' %}.
122 {{ thread.thread.get_images_count }} {% trans 'images' %}.
123 {% if thread.thread.tags %}
123 {% if thread.thread.tags %}
124 <span class="tags">
124 <span class="tags">
125 {% for tag in thread.thread.get_tags %}
125 {% for tag in thread.thread.get_tags %}
126 <a class="tag" href="
126 <a class="tag" href="
127 {% url 'tag' tag_name=tag.name %}">
127 {% url 'tag' tag_name=tag.name %}">
128 #{{ tag.name }}</a
128 #{{ tag.name }}</a
129 >{% if not forloop.last %},{% endif %}
129 >{% if not forloop.last %},{% endif %}
130 {% endfor %}
130 {% endfor %}
131 </span>
131 </span>
132 {% endif %}
132 {% endif %}
133 </div>
133 </div>
134 </div>
134 </div>
135 {% if thread.last_replies.exists %}
135 {% if thread.last_replies.exists %}
136 {% if thread.skipped_replies %}
136 {% if thread.skipped_replies %}
137 <div class="skipped_replies">
137 <div class="skipped_replies">
138 <a href="{% url 'thread' thread.op.id %}">
138 <a href="{% url 'thread' thread.op.id %}">
139 {% blocktrans with count=thread.skipped_replies %}Skipped {{ count }} replies. Open thread to see all replies.{% endblocktrans %}
139 {% blocktrans with count=thread.skipped_replies %}Skipped {{ count }} replies. Open thread to see all replies.{% endblocktrans %}
140 </a>
140 </a>
141 </div>
141 </div>
142 {% endif %}
142 {% endif %}
143 <div class="last-replies">
143 <div class="last-replies">
144 {% for post in thread.last_replies %}
144 {% for post in thread.last_replies %}
145 {% if thread.bumpable %}
145 {% if thread.bumpable %}
146 <div class="post" id="{{ post.id }}">
146 <div class="post" id="{{ post.id }}">
147 {% else %}
147 {% else %}
148 <div class="post dead_post" id="{{ post.id }}">
148 <div class="post dead_post" id="{{ post.id }}">
149 {% endif %}
149 {% endif %}
150 {% if post.image %}
150 {% if post.image %}
151 <div class="image">
151 <div class="image">
152 <a class="thumb"
152 <a class="thumb"
153 href="{{ post.image.url }}"><img
153 href="{{ post.image.url }}"><img
154 src=" {{ post.image.url_200x150 }}"
154 src=" {{ post.image.url_200x150 }}"
155 alt="{{ post.id }}"
155 alt="{{ post.id }}"
156 data-width="{{ post.image_width }}"
156 data-width="{{ post.image_width }}"
157 data-height="{{ post.image_height }}"/>
157 data-height="{{ post.image_height }}"/>
158 </a>
158 </a>
159 </div>
159 </div>
160 {% endif %}
160 {% endif %}
161 <div class="message">
161 <div class="message">
162 <div class="post-info">
162 <div class="post-info">
163 <span class="title">{{ post.title }}</span>
163 <span class="title">{{ post.title }}</span>
164 <a class="post_id" href="
164 <a class="post_id" href="
165 {% url 'thread' thread.op.id %}#{{ post.id }}">
165 {% url 'thread' thread.op.id %}#{{ post.id }}">
166 ({{ post.id }})</a>
166 ({{ post.id }})</a>
167 [{{ post.pub_time }}]
167 [{{ post.pub_time }}]
168 </div>
168 </div>
169 {% autoescape off %}
169 {% autoescape off %}
170 {{ post.text.rendered|truncatewords_html:50 }}
170 {{ post.text.rendered|truncatewords_html:50 }}
171 {% endautoescape %}
171 {% endautoescape %}
172 </div>
172 </div>
173 {% if post.is_referenced %}
173 {% if post.is_referenced %}
174 <div class="refmap">
174 <div class="refmap">
175 {% trans "Replies" %}:
175 {% trans "Replies" %}:
176 {% for ref_post in post.get_sorted_referenced_posts %}
176 {% for ref_post in post.get_sorted_referenced_posts %}
177 <a href="{% post_url ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a
177 <a href="{% post_url ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a
178 >{% if not forloop.last %},{% endif %}
178 >{% if not forloop.last %},{% endif %}
179 {% endfor %}
179 {% endfor %}
180 </div>
180 </div>
181 {% endif %}
181 {% endif %}
182 </div>
182 </div>
183 {% endfor %}
183 {% endfor %}
184 </div>
184 </div>
185 {% endif %}
185 {% endif %}
186 </div>
186 </div>
187 {% endcache %}
187 {% endcache %}
188 {% endfor %}
188 {% endfor %}
189
189
190 {% if next_page %}
190 {% if next_page %}
191 <div class="page_link">
191 <div class="page_link">
192 <a href="
192 <a href="
193 {% if tag %}
193 {% if tag %}
194 {% url "tag" tag_name=tag page=next_page %}
194 {% url "tag" tag_name=tag page=next_page %}
195 {% else %}
195 {% else %}
196 {% url "index" page=next_page %}
196 {% url "index" page=next_page %}
197 {% endif %}
197 {% endif %}
198 ">{% trans "Next page" %}</a>
198 ">{% trans "Next page" %}</a>
199 </div>
199 </div>
200 {% endif %}
200 {% endif %}
201 {% else %}
201 {% else %}
202 <div class="post">
202 <div class="post">
203 {% trans 'No threads exist. Create the first one!' %}</div>
203 {% trans 'No threads exist. Create the first one!' %}</div>
204 {% endif %}
204 {% endif %}
205
205
206 <form enctype="multipart/form-data" method="post">{% csrf_token %}
207 <div class="post-form-w">
206 <div class="post-form-w">
208
209 <div class="form-title">{% trans "Create new thread" %}</div>
210 <div class="post-form">
207 <div class="post-form">
211 <div class="form-row">
208 <div class="form-title">{% trans "Create new thread" %}</div>
212 <div class="form-label">{% trans 'Title' %}</div>
209 <form enctype="multipart/form-data" method="post">{% csrf_token %}
213 <div class="form-input">{{ form.title }}</div>
210 {{ form.as_p }}
214 <div class="form-errors">{{ form.title.errors }}</div>
211 <div class="form-submit">
215 </div>
212 <input type="submit" value="{% trans "Post" %}"/></div>
216 <div class="form-row">
213 <div>
217 <div class="form-label">{% trans 'Formatting' %}</div>
214 </form>
218 <div class="form-input" id="mark_panel">
215 {% trans 'Tags must be delimited by spaces. Text or image is required.' %}
219 <span class="mark_btn" id="quote"><span class="quote">&gt;{% trans 'quote' %}</span></span>
220 <span class="mark_btn" id="italic"><i>{% trans 'italic' %}</i></span>
221 <span class="mark_btn" id="bold"><b>{% trans 'bold' %}</b></span>
222 <span class="mark_btn" id="spoiler"><span class="spoiler">{% trans 'spoiler' %}</span></span>
223 <span class="mark_btn" id="comment"><span class="comment">// {% trans 'comment' %}</span></span>
224 </div>
225 </div>
226 <div class="form-row">
227 <div class="form-label">{% trans 'Text' %}</div>
228 <div class="form-input">{{ form.text }}</div>
229 <div class="form-errors">{{ form.text.errors }}</div>
230 </div>
216 </div>
231 <div class="form-row">
217 <div><a href="{% url "staticpage" name="help" %}">
232 <div class="form-label">{% trans 'Image' %}</div>
218 {% trans 'Text syntax' %}</a></div>
233 <div class="form-input">{{ form.image }}</div>
234 <div class="form-errors">{{ form.image.errors }}</div>
235 </div>
236 <div class="form-row">
237 <div class="form-label">{% trans 'Tags' %}</div>
238 <div class="form-input">{{ form.tags }}</div>
239 <div class="form-errors">{{ form.tags.errors }}</div>
240 </div>
241 <div class="form-row form-email">
242 <div class="form-label">{% trans 'e-mail' %}</div>
243 <div class="form-input">{{ form.email }}</div>
244 <div class="form-errors">{{ form.email.errors }}</div>
245 </div>
246 <div class="form-row">
247 {{ form.captcha }}
248 <div class="form-errors">{{ form.captcha.errors }}</div>
249 </div>
250 <div class="form-row">
251 <div class="form-errors">{{ form.other.errors }}</div>
252 </div>
253 </div>
219 </div>
254 <div class="form-submit">
255 <input type="submit" value="{% trans "Post" %}"/></div>
256 <div>
257 {% trans 'Tags must be delimited by spaces. Text or image is required.' %}
258 </div>
259 <div><a href="{% url "staticpage" name="help" %}">
260 {% trans 'Text syntax' %}</a></div>
261 </div>
220 </div>
262 </form>
263
221
264 {% endblock %}
222 {% endblock %}
265
223
266 {% block metapanel %}
224 {% block metapanel %}
267
225
268 <span class="metapanel">
226 <span class="metapanel">
269 <b><a href="{% url "authors" %}">Neboard</a> 1.4</b>
227 <b><a href="{% url "authors" %}">Neboard</a> 1.4</b>
270 {% trans "Pages:" %}
228 {% trans "Pages:" %}
271 {% for page in pages %}
229 {% for page in pages %}
272 [<a href="
230 [<a href="
273 {% if tag %}
231 {% if tag %}
274 {% url "tag" tag_name=tag page=page %}
232 {% url "tag" tag_name=tag page=page %}
275 {% else %}
233 {% else %}
276 {% url "index" page=page %}
234 {% url "index" page=page %}
277 {% endif %}
235 {% endif %}
278 ">{{ page }}</a>]
236 ">{{ page }}</a>]
279 {% endfor %}
237 {% endfor %}
280 [<a href="rss/">RSS</a>]
238 [<a href="rss/">RSS</a>]
281 </span>
239 </span>
282
240
283 {% endblock %}
241 {% endblock %}
@@ -1,163 +1,127 b''
1 {% extends "boards/base.html" %}
1 {% extends "boards/base.html" %}
2
2
3 {% load i18n %}
3 {% load i18n %}
4 {% load cache %}
4 {% load cache %}
5 {% load static from staticfiles %}
5 {% load static from staticfiles %}
6 {% load board %}
6 {% load board %}
7
7
8 {% block head %}
8 {% block head %}
9 <title>Neboard - {{ thread.get_replies.0.get_title }}</title>
9 <title>Neboard - {{ thread.get_replies.0.get_title }}</title>
10 {% endblock %}
10 {% endblock %}
11
11
12 {% block content %}
12 {% block content %}
13 {% spaceless %}
13 {% spaceless %}
14 {% get_current_language as LANGUAGE_CODE %}
14 {% get_current_language as LANGUAGE_CODE %}
15
15
16 <script src="{% static 'js/thread_update.js' %}"></script>
16 <script src="{% static 'js/thread_update.js' %}"></script>
17 <script src="{% static 'js/thread.js' %}"></script>
17 <script src="{% static 'js/thread.js' %}"></script>
18
18
19 {% cache 600 thread_view thread.id thread.last_edit_time moderator LANGUAGE_CODE %}
19 {% cache 600 thread_view thread.id thread.last_edit_time moderator LANGUAGE_CODE %}
20 {% if bumpable %}
20 {% if bumpable %}
21 <div class="bar-bg">
21 <div class="bar-bg">
22 <div class="bar-value" style="width:{{ bumplimit_progress }}%" id="bumplimit_progress">
22 <div class="bar-value" style="width:{{ bumplimit_progress }}%" id="bumplimit_progress">
23 </div>
23 </div>
24 <div class="bar-text">
24 <div class="bar-text">
25 <span id="left_to_limit">{{ posts_left }}</span> {% trans 'posts to bumplimit' %}
25 <span id="left_to_limit">{{ posts_left }}</span> {% trans 'posts to bumplimit' %}
26 </div>
26 </div>
27 </div>
27 </div>
28 {% endif %}
28 {% endif %}
29 <div class="thread">
29 <div class="thread">
30 {% for post in thread.get_replies %}
30 {% for post in thread.get_replies %}
31 {% if bumpable %}
31 {% if bumpable %}
32 <div class="post" id="{{ post.id }}">
32 <div class="post" id="{{ post.id }}">
33 {% else %}
33 {% else %}
34 <div class="post dead_post" id="{{ post.id }}">
34 <div class="post dead_post" id="{{ post.id }}">
35 {% endif %}
35 {% endif %}
36 {% if post.image %}
36 {% if post.image %}
37 <div class="image">
37 <div class="image">
38 <a
38 <a
39 class="thumb"
39 class="thumb"
40 href="{{ post.image.url }}"><img
40 href="{{ post.image.url }}"><img
41 src="{{ post.image.url_200x150 }}"
41 src="{{ post.image.url_200x150 }}"
42 alt="{{ post.id }}"
42 alt="{{ post.id }}"
43 data-width="{{ post.image_width }}"
43 data-width="{{ post.image_width }}"
44 data-height="{{ post.image_height }}"/>
44 data-height="{{ post.image_height }}"/>
45 </a>
45 </a>
46 </div>
46 </div>
47 {% endif %}
47 {% endif %}
48 <div class="message">
48 <div class="message">
49 <div class="post-info">
49 <div class="post-info">
50 <span class="title">{{ post.title }}</span>
50 <span class="title">{{ post.title }}</span>
51 <a class="post_id" href="#{{ post.id }}">
51 <a class="post_id" href="#{{ post.id }}">
52 ({{ post.id }})</a>
52 ({{ post.id }})</a>
53 [{{ post.pub_time }}]
53 [{{ post.pub_time }}]
54 [<a href="#" onclick="javascript:addQuickReply('{{ post.id }}')
54 [<a href="#" onclick="javascript:addQuickReply('{{ post.id }}')
55 ; return false;">&gt;&gt;</a>]
55 ; return false;">&gt;&gt;</a>]
56
56
57 {% if moderator %}
57 {% if moderator %}
58 <span class="moderator_info">
58 <span class="moderator_info">
59 [<a href="{% url 'delete' post_id=post.id %}"
59 [<a href="{% url 'delete' post_id=post.id %}"
60 >{% trans 'Delete' %}</a>]
60 >{% trans 'Delete' %}</a>]
61 ({{ post.poster_ip }})
61 ({{ post.poster_ip }})
62 [<a href="{% url 'ban' post_id=post.id %}?next={{ request.path }}"
62 [<a href="{% url 'ban' post_id=post.id %}?next={{ request.path }}"
63 >{% trans 'Ban IP' %}</a>]
63 >{% trans 'Ban IP' %}</a>]
64 </span>
64 </span>
65 {% endif %}
65 {% endif %}
66 </div>
66 </div>
67 {% autoescape off %}
67 {% autoescape off %}
68 {{ post.text.rendered }}
68 {{ post.text.rendered }}
69 {% endautoescape %}
69 {% endautoescape %}
70 {% if post.is_referenced %}
70 {% if post.is_referenced %}
71 <div class="refmap">
71 <div class="refmap">
72 {% trans "Replies" %}:
72 {% trans "Replies" %}:
73 {% for ref_post in post.get_sorted_referenced_posts %}
73 {% for ref_post in post.get_sorted_referenced_posts %}
74 <a href="{% post_url ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a
74 <a href="{% post_url ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a
75 >{% if not forloop.last %},{% endif %}
75 >{% if not forloop.last %},{% endif %}
76 {% endfor %}
76 {% endfor %}
77 </div>
77 </div>
78 {% endif %}
78 {% endif %}
79 </div>
79 </div>
80 {% if forloop.first %}
80 {% if forloop.first %}
81 <div class="metadata">
81 <div class="metadata">
82 <span class="tags">
82 <span class="tags">
83 {% for tag in thread.get_tags %}
83 {% for tag in thread.get_tags %}
84 <a class="tag" href="{% url 'tag' tag.name %}">
84 <a class="tag" href="{% url 'tag' tag.name %}">
85 #{{ tag.name }}</a
85 #{{ tag.name }}</a
86 >{% if not forloop.last %},{% endif %}
86 >{% if not forloop.last %},{% endif %}
87 {% endfor %}
87 {% endfor %}
88 </span>
88 </span>
89 </div>
89 </div>
90 {% endif %}
90 {% endif %}
91 </div>
91 </div>
92 {% endfor %}
92 {% endfor %}
93 </div>
93 </div>
94 {% endcache %}
94 {% endcache %}
95
95
96 <form id="form" enctype="multipart/form-data" method="post"
96 <div class="post-form-w">
97 >{% csrf_token %}
97 <div class="form-title">{% trans "Reply to thread" %} #{{ thread.get_opening_post.id }}</div>
98 <div class="post-form-w">
98 <div class="post-form">
99 <div class="form-title">{% trans "Reply to thread" %} #{{ thread.get_opening_post.id }}</div>
99 <form id="form" enctype="multipart/form-data" method="post"
100 <div class="post-form">
100 >{% csrf_token %}
101 <div class="form-row">
101 {{ form.as_p }}
102 <div class="form-label">{% trans 'Title' %}</div>
102 <div class="form-submit">
103 <div class="form-input">{{ form.title }}</div>
103 <input type="submit" value="{% trans "Post" %}"/>
104 <div class="form-errors">{{ form.title.errors }}</div>
105 </div>
106 <div class="form-row">
107 <div class="form-label">{% trans 'Formatting' %}</div>
108 <div class="form-input" id="mark_panel">
109 <span class="mark_btn" id="quote"><span class="quote">&gt;{% trans 'quote' %}</span></span>
110 <span class="mark_btn" id="italic"><i>{% trans 'italic' %}</i></span>
111 <span class="mark_btn" id="bold"><b>{% trans 'bold' %}</b></span>
112 <span class="mark_btn" id="spoiler"><span class="spoiler">{% trans 'spoiler' %}</span></span>
113 <span class="mark_btn" id="comment"><span class="comment">// {% trans 'comment' %}</span></span>
114 </div>
115 </div>
116 <div class="form-row">
117 <div class="form-label">{% trans 'Text' %}</div>
118 <div class="form-input">{{ form.text }}</div>
119 <div class="form-errors">{{ form.text.errors }}</div>
120 </div>
121 <div class="form-row">
122 <div class="form-label">{% trans 'Image' %}</div>
123 <div class="form-input">{{ form.image }}</div>
124 <div class="form-errors">{{ form.image.errors }}</div>
125 </div>
126 <div class="form-row form-email">
127 <div class="form-label">{% trans 'e-mail' %}</div>
128 <div class="form-input">{{ form.email }}</div>
129 <div class="form-errors">{{ form.email.errors }}</div>
130 </div>
131 <div class="form-row">
132 {{ form.captcha }}
133 <div class="form-errors">{{ form.captcha.errors }}</div>
134 </div>
135 <div class="form-row">
136 <div class="form-errors">{{ form.other.errors }}</div>
137 </div>
138 </div>
104 </div>
139
105 </form>
140 <div class="form-submit"><input type="submit"
106 <div><a href="{% url "staticpage" name="help" %}">
141 value="{% trans "Post" %}"/></div>
107 {% trans 'Text syntax' %}</a></div>
142 <div><a href="{% url "staticpage" name="help" %}">
143 {% trans 'Text syntax' %}</a></div>
144 </div>
108 </div>
145 </form>
109 </div>
146
110
147 {% endspaceless %}
111 {% endspaceless %}
148 {% endblock %}
112 {% endblock %}
149
113
150 {% block metapanel %}
114 {% block metapanel %}
151
115
152 {% get_current_language as LANGUAGE_CODE %}
116 {% get_current_language as LANGUAGE_CODE %}
153
117
154 <span class="metapanel" data-last-update="{{ last_update }}">
118 <span class="metapanel" data-last-update="{{ last_update }}">
155 {% cache 600 thread_meta thread.last_edit_time moderator LANGUAGE_CODE %}
119 {% cache 600 thread_meta thread.last_edit_time moderator LANGUAGE_CODE %}
156 <span id="reply-count">{{ thread.get_reply_count }}</span> {% trans 'replies' %},
120 <span id="reply-count">{{ thread.get_reply_count }}</span> {% trans 'replies' %},
157 <span id="image-count">{{ thread.get_images_count }}</span> {% trans 'images' %}.
121 <span id="image-count">{{ thread.get_images_count }}</span> {% trans 'images' %}.
158 {% trans 'Last update: ' %}{{ thread.last_edit_time }}
122 {% trans 'Last update: ' %}{{ thread.last_edit_time }}
159 [<a href="rss/">RSS</a>]
123 [<a href="rss/">RSS</a>]
160 {% endcache %}
124 {% endcache %}
161 </span>
125 </span>
162
126
163 {% endblock %}
127 {% endblock %}
General Comments 0
You need to be logged in to leave comments. Login now