##// END OF EJS Templates
Fixed tag view.
neko259 -
r164:390191f3 default
parent child Browse files
Show More
@@ -1,182 +1,182 b''
1 1 {% extends "boards/base.html" %}
2 2
3 3 {% load i18n %}
4 4 {% load markup %}
5 5
6 6 {% block head %}
7 7 {% if tag %}
8 8 <title>Neboard - {{ tag }}</title>
9 9 {% else %}
10 10 <title>Neboard</title>
11 11 {% endif %}
12 12 {% endblock %}
13 13
14 14 {% block content %}
15 15
16 16 {% if tag %}
17 17 <div class="tag_info">
18 18 <h2>{% trans 'Tag: ' %}{{ tag }}</h2>
19 19 </div>
20 20 {% endif %}
21 21
22 22 {% if threads %}
23 23 {% for thread in threads %}
24 24 <div class="thread">
25 25 {% if thread.bumpable %}
26 26 <div class="post" id="{{ thread.thread.id }}">
27 27 {% else %}
28 28 <div class="post dead_post" id="{{ thread.thread.id }}">
29 29 {% endif %}
30 30 {% if thread.thread.image %}
31 31 <div class="image">
32 32 <a class="fancy"
33 33 href="{{ thread.thread.image.url }}"><img
34 34 src="{{ thread.thread.image.url_200x150 }}"
35 35 alt="{% trans 'Post image' %}"
36 36 data-width="{{ thread.thread.image_width }}"
37 37 data-height="{{ thread.thread.image_height }}" />
38 38 </a>
39 39 </div>
40 40 {% endif %}
41 41 <div class="message">
42 42 <div class="post-info">
43 43 <span class="title">{{ thread.thread.title }}</span>
44 <a class="post_id" href="{% url 'thread' thread.thread.id %}"
45 >(#{{ thread.thread.id }})</a>
44 <a class="post_id" href="{% url 'thread' thread.thread.id %}"
45 >(#{{ thread.thread.id }})</a>
46 46 [{{ thread.thread.pub_time }}]
47 47 [<a class="link" href="{% url 'thread' thread.thread.id %}#form"
48 48 >{% trans "Reply" %}</a>]
49 49
50 50 {% if user.is_moderator %}
51 51 <span class="moderator_info">
52 52 [<a href="{% url 'delete' post_id=thread.thread.id %}?next={{ request.path }}"
53 53 >{% trans 'Delete' %}</a>]
54 54 ({{ thread.thread.poster_ip }})
55 55 [<a href="{% url 'ban' post_id=thread.thread.id %}?next={{ request.path }}"
56 56 >{% trans 'Ban IP' %}</a>]
57 57 </span>
58 58 {% endif %}
59 59 </div>
60 60 {% autoescape off %}
61 61 {{ thread.thread.text.rendered|truncatewords_html:50 }}
62 62 {% endautoescape %}
63 63 </div>
64 64 <div class="metadata">
65 65 {{ thread.thread.get_reply_count }} {% trans 'replies' %},
66 66 {{ thread.thread.get_images_count }} {% trans 'images' %}.
67 67 {% if thread.tags.exists %}
68 68 <span class="tags">{% trans 'Tags' %}:
69 69 {% for tag in thread.thread.tags.all %}
70 70 <a class="tag" href="
71 71 {% url 'tag' tag_name=tag.name %}">
72 72 {{ tag.name }}</a>
73 73 {% endfor %}
74 74 </span>
75 75 {% endif %}
76 76 </div>
77 77 </div>
78 78 {% if thread.thread.get_last_replies %}
79 79 <div class="last-replies">
80 80 {% for post in thread.thread.get_last_replies %}
81 81 {% if thread.bumpable %}
82 82 <div class="post" id="{{ post.id }}">
83 83 {% else %}
84 84 <div class="post dead_post" id="{{ post.id }}">
85 85 {% endif %}
86 86 {% if post.image %}
87 87 <div class="image">
88 88 <a class="fancy"
89 89 href="{{ post.image.url }}"><img
90 90 src=" {{ post.image.url_200x150 }}"
91 91 alt="{% trans 'Post image' %}"
92 92 data-width="{{ post.image_width }}"
93 93 data-height="{{ post.image_height }}"/>
94 94 </a>
95 95 </div>
96 96 {% endif %}
97 97 <div class="message">
98 98 <div class="post-info">
99 99 <span class="title">{{ post.title }}</span>
100 100 <a class="post_id" href="
101 101 {% url 'thread' thread.thread.id %}#{{ post.id }}">
102 102 (#{{ post.id }})</a>
103 103 [{{ post.pub_time }}]
104 104 </div>
105 105 {% autoescape off %}
106 106 {{ post.text.rendered|truncatewords_html:50 }}
107 107 {% endautoescape %}
108 108 </div>
109 109 </div>
110 110 {% endfor %}
111 111 </div>
112 112 {% endif %}
113 113 </div>
114 114 {% endfor %}
115 115 {% else %}
116 116 <div class="post">
117 117 {% trans 'No threads exist. Create the first one!' %}</div>
118 118 {% endif %}
119 119
120 120 <form enctype="multipart/form-data" method="post">{% csrf_token %}
121 121 <div class="post-form-w">
122 122
123 123 <div class="form-title">{% trans "Create new thread" %}</div>
124 124 <div class="post-form">
125 125 <div class="form-row">
126 126 <div class="form-label">{% trans 'Title' %}</div>
127 127 <div class="form-input">{{ form.title }}</div>
128 128 <div class="form-errors">{{ form.title.errors }}</div>
129 129 </div>
130 130 <div class="form-row">
131 131 <div class="form-label">{% trans 'Text' %}</div>
132 132 <div class="form-input">{{ form.text }}</div>
133 133 <div class="form-errors">{{ form.text.errors }}</div>
134 134 </div>
135 135 <div class="form-row">
136 136 <div class="form-label">{% trans 'Image' %}</div>
137 137 <div class="form-input">{{ form.image }}</div>
138 138 <div class="form-errors">{{ form.image.errors }}</div>
139 139 </div>
140 140 <div class="form-row">
141 141 <div class="form-label">{% trans 'Tags' %}</div>
142 142 <div class="form-input">{{ form.tags }}</div>
143 143 <div class="form-errors">{{ form.tags.errors }}</div>
144 144 </div>
145 145 <div class="form-row">
146 146 {{ form.captcha }}
147 147 <div class="form-errors">{{ form.captcha.errors }}</div>
148 148 </div>
149 149 <div class="form-row">
150 150 <div class="form-errors">{{ form.other.errors }}</div>
151 151 </div>
152 152 </div>
153 153 <div class="form-submit">
154 154 <input type="submit" value="{% trans "Post" %}"/></div>
155 155 <div>
156 156 {% trans 'Tags must be delimited by spaces. Text or image is required.' %}
157 157 </div>
158 158 <div><a href="{% url "staticpage" name="help" %}">
159 159 {% trans 'Text syntax' %}</a></div>
160 160 </div>
161 161 </form>
162 162
163 163 {% endblock %}
164 164
165 165 {% block metapanel %}
166 166
167 167 <span class="metapanel">
168 168 <b><a href="{% url "authors" %}">Neboard</a> 1.1</b>
169 169 {% trans "Pages:" %}
170 170 {% for page in pages %}
171 171 [<a href="
172 172 {% if tag %}
173 173 {% url "tag" tag_name=tag page=page %}
174 174 {% else %}
175 175 {% url "index" page=page %}
176 176 {% endif %}
177 177 ">{{ page }}</a>]
178 178 {% endfor %}
179 179 [<a href="rss/">RSS</a>]
180 180 </span>
181 181
182 182 {% endblock %}
@@ -1,355 +1,358 b''
1 1 import hashlib
2 2 from django.core.urlresolvers import reverse
3 3 from django.http import HttpResponseRedirect
4 4 from django.template import RequestContext
5 5 from django.shortcuts import render, redirect, get_object_or_404
6 6 from django.utils import timezone
7 7
8 8 from boards import forms
9 9 import boards
10 10 from boards import utils
11 11 from boards.forms import ThreadForm, PostForm, SettingsForm, PlainErrorList, \
12 12 ThreadCaptchaForm, PostCaptchaForm, LoginForm
13 13
14 14 from boards.models import Post, Tag, Ban, User, RANK_USER, NO_PARENT
15 15 from boards import authors
16 16 import neboard
17 17
18 18
19 19 def index(request, page=0):
20 20 context = _init_default_context(request)
21 21
22 22 if utils.need_include_captcha(request):
23 23 threadFormClass = ThreadCaptchaForm
24 24 kwargs = {'request': request}
25 25 else:
26 26 threadFormClass = ThreadForm
27 27 kwargs = {}
28 28
29 29 if request.method == 'POST':
30 30 form = threadFormClass(request.POST, request.FILES,
31 31 error_class=PlainErrorList, **kwargs)
32 32 form.session = request.session
33 33
34 34 if form.is_valid():
35 35 return _new_post(request, form)
36 36 else:
37 37 form = threadFormClass(error_class=PlainErrorList, **kwargs)
38 38
39 39 threads = []
40 40 for thread in Post.objects.get_threads(page=int(page)):
41 41 threads.append({'thread': thread,
42 42 'bumpable': thread.can_bump()})
43 43
44 44 context['threads'] = None if len(threads) == 0 else threads
45 45 context['form'] = form
46 46 context['pages'] = range(Post.objects.get_thread_page_count())
47 47
48 48 return render(request, 'boards/posting_general.html',
49 49 context)
50 50
51 51
52 52 def _new_post(request, form, thread_id=boards.models.NO_PARENT):
53 53 """Add a new post (in thread or as a reply)."""
54 54
55 55 ip = _get_client_ip(request)
56 56 is_banned = Ban.objects.filter(ip=ip).count() > 0
57 57
58 58 if is_banned:
59 59 return redirect(you_are_banned)
60 60
61 61 data = form.cleaned_data
62 62
63 63 title = data['title']
64 64 text = data['text']
65 65
66 66 if 'image' in data.keys():
67 67 image = data['image']
68 68 else:
69 69 image = None
70 70
71 71 tags = []
72 72
73 73 new_thread = thread_id == boards.models.NO_PARENT
74 74 if new_thread:
75 75 tag_strings = data['tags']
76 76
77 77 if tag_strings:
78 78 tag_strings = tag_strings.split(' ')
79 79 for tag_name in tag_strings:
80 80 tag_name = tag_name.strip()
81 81 if len(tag_name) > 0:
82 82 tag, created = Tag.objects.get_or_create(name=tag_name)
83 83 tags.append(tag)
84 84
85 85 # TODO Add a possibility to define a link image instead of an image file.
86 86 # If a link is given, download the image automatically.
87 87
88 88 post = Post.objects.create_post(title=title, text=text, ip=ip,
89 89 parent_id=thread_id, image=image,
90 90 tags=tags)
91 91
92 92 thread_to_show = (post.id if new_thread else thread_id)
93 93
94 94 if new_thread:
95 95 return redirect(thread, post_id=thread_to_show)
96 96 else:
97 97 return redirect(reverse(thread,
98 98 kwargs={'post_id': thread_to_show}) + '#'
99 99 + str(post.id))
100 100
101 101
102 102 def tag(request, tag_name, page=0):
103 103 """Get all tag threads (posts without a parent)."""
104 104
105 105 tag = get_object_or_404(Tag, name=tag_name)
106 threads = Post.objects.get_threads(tag=tag, page=int(page))
106 threads = []
107 for thread in Post.objects.get_threads(tag=tag, page=int(page)):
108 threads.append({'thread': thread,
109 'bumpable': thread.can_bump()})
107 110
108 111 if request.method == 'POST':
109 112 form = ThreadForm(request.POST, request.FILES,
110 113 error_class=PlainErrorList)
111 114 if form.is_valid():
112 115 return _new_post(request, form)
113 116 else:
114 117 form = forms.ThreadForm(initial={'tags': tag_name},
115 118 error_class=PlainErrorList)
116 119
117 120 context = _init_default_context(request)
118 121 context['threads'] = None if len(threads) == 0 else threads
119 122 context['tag'] = tag_name
120 123 context['pages'] = range(Post.objects.get_thread_page_count(tag=tag))
121 124
122 125 context['form'] = form
123 126
124 127 return render(request, 'boards/posting_general.html',
125 128 context)
126 129
127 130
128 131 def thread(request, post_id):
129 132 """Get all thread posts"""
130 133
131 134 if utils.need_include_captcha(request):
132 135 postFormClass = PostCaptchaForm
133 136 kwargs = {'request': request}
134 137 else:
135 138 postFormClass = PostForm
136 139 kwargs = {}
137 140
138 141 if request.method == 'POST':
139 142 form = postFormClass(request.POST, request.FILES,
140 143 error_class=PlainErrorList, **kwargs)
141 144 form.session = request.session
142 145
143 146 if form.is_valid():
144 147 return _new_post(request, form, post_id)
145 148 else:
146 149 form = postFormClass(error_class=PlainErrorList, **kwargs)
147 150
148 151 posts = Post.objects.get_thread(post_id)
149 152
150 153 context = _init_default_context(request)
151 154
152 155 context['posts'] = posts
153 156 context['form'] = form
154 157 context['bumpable'] = posts[0].can_bump()
155 158
156 159 return render(request, 'boards/thread.html', context)
157 160
158 161
159 162 def login(request):
160 163 """Log in with user id"""
161 164
162 165 context = _init_default_context(request)
163 166
164 167 if request.method == 'POST':
165 168 form = LoginForm(request.POST, request.FILES,
166 169 error_class=PlainErrorList)
167 170 if form.is_valid():
168 171 user = User.objects.get(user_id=form.cleaned_data['user_id'])
169 172 request.session['user_id'] = user.id
170 173 return redirect(index)
171 174
172 175 else:
173 176 form = LoginForm()
174 177
175 178 context['form'] = form
176 179
177 180 return render(request, 'boards/login.html', context)
178 181
179 182
180 183 def settings(request):
181 184 """User's settings"""
182 185
183 186 context = _init_default_context(request)
184 187
185 188 if request.method == 'POST':
186 189 form = SettingsForm(request.POST)
187 190 if form.is_valid():
188 191 selected_theme = form.cleaned_data['theme']
189 192
190 193 user = _get_user(request)
191 194 user.save_setting('theme', selected_theme)
192 195
193 196 return redirect(settings)
194 197 else:
195 198 selected_theme = _get_theme(request)
196 199 form = SettingsForm(initial={'theme': selected_theme})
197 200 context['form'] = form
198 201
199 202 return render(request, 'boards/settings.html', context)
200 203
201 204
202 205 def all_tags(request):
203 206 """All tags list"""
204 207
205 208 context = _init_default_context(request)
206 209 context['all_tags'] = Tag.objects.get_not_empty_tags()
207 210
208 211 return render(request, 'boards/tags.html', context)
209 212
210 213
211 214 def jump_to_post(request, post_id):
212 215 """Determine thread in which the requested post is and open it's page"""
213 216
214 217 post = get_object_or_404(Post, id=post_id)
215 218
216 219 if boards.models.NO_PARENT == post.parent:
217 220 return redirect(thread, post_id=post.id)
218 221 else:
219 222 parent_thread = get_object_or_404(Post, id=post.parent)
220 223 return redirect(reverse(thread, kwargs={'post_id': parent_thread.id})
221 224 + '#' + str(post.id))
222 225
223 226
224 227 def authors(request):
225 228 context = _init_default_context(request)
226 229 context['authors'] = boards.authors.authors
227 230
228 231 return render(request, 'boards/authors.html', context)
229 232
230 233
231 234 def delete(request, post_id):
232 235 user = _get_user(request)
233 236 post = get_object_or_404(Post, id=post_id)
234 237
235 238 if user.is_moderator():
236 239 # TODO Show confirmation page before deletion
237 240 Post.objects.delete_post(post)
238 241
239 242 if NO_PARENT == post.parent:
240 243 return _redirect_to_next(request)
241 244 else:
242 245 return redirect(thread, post_id=post.parent)
243 246
244 247
245 248 def ban(request, post_id):
246 249 user = _get_user(request)
247 250 post = get_object_or_404(Post, id=post_id)
248 251
249 252 if user.is_moderator():
250 253 # TODO Show confirmation page before ban
251 254 Ban.objects.get_or_create(ip=post.poster_ip)
252 255
253 256 return _redirect_to_next(request)
254 257
255 258
256 259 def you_are_banned(request):
257 260 context = _init_default_context(request)
258 261 return render(request, 'boards/staticpages/banned.html', context)
259 262
260 263
261 264 def page_404(request):
262 265 context = _init_default_context(request)
263 266 return render(request, 'boards/404.html', context)
264 267
265 268
266 269 def tag_subscribe(request, tag_name):
267 270 user = _get_user(request)
268 271 tag = get_object_or_404(Tag, name=tag_name)
269 272
270 273 if not tag in user.fav_tags.all():
271 274 user.fav_tags.add(tag)
272 275
273 276 return redirect(all_tags)
274 277
275 278
276 279 def tag_unsubscribe(request, tag_name):
277 280 user = _get_user(request)
278 281 tag = get_object_or_404(Tag, name=tag_name)
279 282
280 283 if tag in user.fav_tags.all():
281 284 user.fav_tags.remove(tag)
282 285
283 286 return redirect(all_tags)
284 287
285 288
286 289 def static_page(request, name):
287 290 context = _init_default_context(request)
288 291 return render(request, 'boards/staticpages/' + name + '.html', context)
289 292
290 293
291 294 def _get_theme(request, user=None):
292 295 """Get user's CSS theme"""
293 296
294 297 if not user:
295 298 user = _get_user(request)
296 299 theme = user.get_setting('theme')
297 300 if not theme:
298 301 theme = neboard.settings.DEFAULT_THEME
299 302
300 303 return theme
301 304
302 305
303 306 def _get_client_ip(request):
304 307 x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
305 308 if x_forwarded_for:
306 309 ip = x_forwarded_for.split(',')[-1].strip()
307 310 else:
308 311 ip = request.META.get('REMOTE_ADDR')
309 312 return ip
310 313
311 314
312 315 def _init_default_context(request):
313 316 """Create context with default values that are used in most views"""
314 317
315 318 context = RequestContext(request)
316 319
317 320 user = _get_user(request)
318 321 context['user'] = user
319 322 context['tags'] = user.get_sorted_fav_tags()
320 323 context['theme'] = _get_theme(request, user)
321 324
322 325 return context
323 326
324 327
325 328 def _get_user(request):
326 329 """Get current user from the session"""
327 330
328 331 session = request.session
329 332 if not 'user_id' in session:
330 333 request.session.save()
331 334
332 335 md5 = hashlib.md5()
333 336 md5.update(session.session_key)
334 337 new_id = md5.hexdigest()
335 338
336 339 time_now = timezone.now()
337 340 user = User.objects.create(user_id=new_id, rank=RANK_USER,
338 341 registration_time=time_now,
339 342 last_access_time=time_now)
340 343
341 344 session['user_id'] = user.id
342 345 else:
343 346 user = User.objects.get(id=session['user_id'])
344 347 user.last_access_time = timezone.now()
345 348 user.save()
346 349
347 350 return user
348 351
349 352
350 353 def _redirect_to_next(request):
351 354 if 'next' in request.GET:
352 355 next_page = request.GET['next']
353 356 return HttpResponseRedirect(next_page)
354 357 else:
355 358 return redirect(index)
General Comments 0
You need to be logged in to leave comments. Login now