##// END OF EJS Templates
Sort tags alphabetically in metadata
neko259 -
r315:0647dc8f default
parent child Browse files
Show More
@@ -1,375 +1,380 b''
1 import os
1 import os
2 from random import random
2 from random import random
3 import time
3 import time
4 import math
4 import math
5
5
6 from django.db import models
6 from django.db import models
7 from django.db.models import Count
7 from django.db.models import Count
8 from django.http import Http404
8 from django.http import Http404
9 from django.utils import timezone
9 from django.utils import timezone
10 from markupfield.fields import MarkupField
10 from markupfield.fields import MarkupField
11
11
12 from neboard import settings
12 from neboard import settings
13 import thumbs
13 import thumbs
14
14
15 import re
15 import re
16
16
17 IMAGE_THUMB_SIZE = (200, 150)
17 IMAGE_THUMB_SIZE = (200, 150)
18
18
19 TITLE_MAX_LENGTH = 50
19 TITLE_MAX_LENGTH = 50
20
20
21 DEFAULT_MARKUP_TYPE = 'markdown'
21 DEFAULT_MARKUP_TYPE = 'markdown'
22
22
23 NO_PARENT = -1
23 NO_PARENT = -1
24 NO_IP = '0.0.0.0'
24 NO_IP = '0.0.0.0'
25 UNKNOWN_UA = ''
25 UNKNOWN_UA = ''
26 ALL_PAGES = -1
26 ALL_PAGES = -1
27 OPENING_POST_POPULARITY_WEIGHT = 2
27 OPENING_POST_POPULARITY_WEIGHT = 2
28 IMAGES_DIRECTORY = 'images/'
28 IMAGES_DIRECTORY = 'images/'
29 FILE_EXTENSION_DELIMITER = '.'
29 FILE_EXTENSION_DELIMITER = '.'
30
30
31 RANK_ADMIN = 0
31 RANK_ADMIN = 0
32 RANK_MODERATOR = 10
32 RANK_MODERATOR = 10
33 RANK_USER = 100
33 RANK_USER = 100
34
34
35 SETTING_MODERATE = "moderate"
35 SETTING_MODERATE = "moderate"
36
36
37 REGEX_REPLY = re.compile('>>(\d+)')
37 REGEX_REPLY = re.compile('>>(\d+)')
38
38
39
39
40 class PostManager(models.Manager):
40 class PostManager(models.Manager):
41
41
42 def create_post(self, title, text, image=None, thread=None,
42 def create_post(self, title, text, image=None, thread=None,
43 ip=NO_IP, tags=None, user=None):
43 ip=NO_IP, tags=None, user=None):
44 post = self.create(title=title,
44 post = self.create(title=title,
45 text=text,
45 text=text,
46 pub_time=timezone.now(),
46 pub_time=timezone.now(),
47 thread=thread,
47 thread=thread,
48 image=image,
48 image=image,
49 poster_ip=ip,
49 poster_ip=ip,
50 poster_user_agent=UNKNOWN_UA,
50 poster_user_agent=UNKNOWN_UA,
51 last_edit_time=timezone.now(),
51 last_edit_time=timezone.now(),
52 bump_time=timezone.now(),
52 bump_time=timezone.now(),
53 user=user)
53 user=user)
54
54
55 if tags:
55 if tags:
56 map(post.tags.add, tags)
56 map(post.tags.add, tags)
57 for tag in tags:
57 for tag in tags:
58 tag.threads.add(post)
58 tag.threads.add(post)
59
59
60 if thread:
60 if thread:
61 thread.replies.add(post)
61 thread.replies.add(post)
62 thread.bump()
62 thread.bump()
63 thread.last_edit_time = timezone.now()
63 thread.last_edit_time = timezone.now()
64 thread.save()
64 thread.save()
65 else:
65 else:
66 self._delete_old_threads()
66 self._delete_old_threads()
67
67
68 self.connect_replies(post)
68 self.connect_replies(post)
69
69
70 return post
70 return post
71
71
72 def delete_post(self, post):
72 def delete_post(self, post):
73 if post.replies.count() > 0:
73 if post.replies.count() > 0:
74 map(self.delete_post, post.replies.all())
74 map(self.delete_post, post.replies.all())
75
75
76 # Update thread's last edit time (used as cache key)
76 # Update thread's last edit time (used as cache key)
77 thread = post.thread
77 thread = post.thread
78 if thread:
78 if thread:
79 thread.last_edit_time = timezone.now()
79 thread.last_edit_time = timezone.now()
80 thread.save()
80 thread.save()
81
81
82 post.delete()
82 post.delete()
83
83
84 def delete_posts_by_ip(self, ip):
84 def delete_posts_by_ip(self, ip):
85 posts = self.filter(poster_ip=ip)
85 posts = self.filter(poster_ip=ip)
86 map(self.delete_post, posts)
86 map(self.delete_post, posts)
87
87
88 def get_threads(self, tag=None, page=ALL_PAGES,
88 def get_threads(self, tag=None, page=ALL_PAGES,
89 order_by='-bump_time'):
89 order_by='-bump_time'):
90 if tag:
90 if tag:
91 threads = tag.threads
91 threads = tag.threads
92
92
93 if threads.count() == 0:
93 if threads.count() == 0:
94 raise Http404
94 raise Http404
95 else:
95 else:
96 threads = self.filter(thread=None)
96 threads = self.filter(thread=None)
97
97
98 threads = threads.order_by(order_by)
98 threads = threads.order_by(order_by)
99
99
100 if page != ALL_PAGES:
100 if page != ALL_PAGES:
101 thread_count = threads.count()
101 thread_count = threads.count()
102
102
103 if page < self.get_thread_page_count(tag=tag):
103 if page < self.get_thread_page_count(tag=tag):
104 start_thread = page * settings.THREADS_PER_PAGE
104 start_thread = page * settings.THREADS_PER_PAGE
105 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
105 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
106 thread_count)
106 thread_count)
107 threads = threads[start_thread:end_thread]
107 threads = threads[start_thread:end_thread]
108
108
109 return threads
109 return threads
110
110
111 def get_thread(self, opening_post_id):
111 def get_thread(self, opening_post_id):
112 try:
112 try:
113 opening_post = self.get(id=opening_post_id, thread=None)
113 opening_post = self.get(id=opening_post_id, thread=None)
114 except Post.DoesNotExist:
114 except Post.DoesNotExist:
115 raise Http404
115 raise Http404
116
116
117 if opening_post.replies:
117 if opening_post.replies:
118 thread = [opening_post]
118 thread = [opening_post]
119 thread.extend(opening_post.replies.all().order_by('pub_time'))
119 thread.extend(opening_post.replies.all().order_by('pub_time'))
120
120
121 return thread
121 return thread
122
122
123 def exists(self, post_id):
123 def exists(self, post_id):
124 posts = self.filter(id=post_id)
124 posts = self.filter(id=post_id)
125
125
126 return posts.count() > 0
126 return posts.count() > 0
127
127
128 def get_thread_page_count(self, tag=None):
128 def get_thread_page_count(self, tag=None):
129 if tag:
129 if tag:
130 threads = self.filter(thread=None, tags=tag)
130 threads = self.filter(thread=None, tags=tag)
131 else:
131 else:
132 threads = self.filter(thread=None)
132 threads = self.filter(thread=None)
133
133
134 return int(math.ceil(threads.count() / float(
134 return int(math.ceil(threads.count() / float(
135 settings.THREADS_PER_PAGE)))
135 settings.THREADS_PER_PAGE)))
136
136
137 def _delete_old_threads(self):
137 def _delete_old_threads(self):
138 """
138 """
139 Preserves maximum thread count. If there are too many threads,
139 Preserves maximum thread count. If there are too many threads,
140 delete the old ones.
140 delete the old ones.
141 """
141 """
142
142
143 # TODO Move old threads to the archive instead of deleting them.
143 # TODO Move old threads to the archive instead of deleting them.
144 # Maybe make some 'old' field in the model to indicate the thread
144 # Maybe make some 'old' field in the model to indicate the thread
145 # must not be shown and be able for replying.
145 # must not be shown and be able for replying.
146
146
147 threads = self.get_threads()
147 threads = self.get_threads()
148 thread_count = threads.count()
148 thread_count = threads.count()
149
149
150 if thread_count > settings.MAX_THREAD_COUNT:
150 if thread_count > settings.MAX_THREAD_COUNT:
151 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
151 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
152 old_threads = threads[thread_count - num_threads_to_delete:]
152 old_threads = threads[thread_count - num_threads_to_delete:]
153
153
154 map(self.delete_post, old_threads)
154 map(self.delete_post, old_threads)
155
155
156 def connect_replies(self, post):
156 def connect_replies(self, post):
157 """Connect replies to a post to show them as a refmap"""
157 """Connect replies to a post to show them as a refmap"""
158
158
159 for reply_number in re.finditer(REGEX_REPLY, post.text.raw):
159 for reply_number in re.finditer(REGEX_REPLY, post.text.raw):
160 id = reply_number.group(1)
160 id = reply_number.group(1)
161 ref_post = self.filter(id=id)
161 ref_post = self.filter(id=id)
162 if ref_post.count() > 0:
162 if ref_post.count() > 0:
163 ref_post[0].referenced_posts.add(post)
163 ref_post[0].referenced_posts.add(post)
164
164
165
165
166 class TagManager(models.Manager):
166 class TagManager(models.Manager):
167
167
168 def get_not_empty_tags(self):
168 def get_not_empty_tags(self):
169 tags = self.annotate(Count('threads')) \
169 tags = self.annotate(Count('threads')) \
170 .filter(threads__count__gt=0).order_by('name')
170 .filter(threads__count__gt=0).order_by('name')
171
171
172 return tags
172 return tags
173
173
174
174
175 class Tag(models.Model):
175 class Tag(models.Model):
176 """
176 """
177 A tag is a text node assigned to the post. The tag serves as a board
177 A tag is a text node assigned to the post. The tag serves as a board
178 section. There can be multiple tags for each message
178 section. There can be multiple tags for each message
179 """
179 """
180
180
181 objects = TagManager()
181 objects = TagManager()
182
182
183 name = models.CharField(max_length=100)
183 name = models.CharField(max_length=100)
184 threads = models.ManyToManyField('Post', null=True,
184 threads = models.ManyToManyField('Post', null=True,
185 blank=True, related_name='tag+')
185 blank=True, related_name='tag+')
186 linked = models.ForeignKey('Tag', null=True, blank=True)
186 linked = models.ForeignKey('Tag', null=True, blank=True)
187
187
188 def __unicode__(self):
188 def __unicode__(self):
189 return self.name
189 return self.name
190
190
191 def is_empty(self):
191 def is_empty(self):
192 return self.get_post_count() == 0
192 return self.get_post_count() == 0
193
193
194 def get_post_count(self):
194 def get_post_count(self):
195 return self.threads.count()
195 return self.threads.count()
196
196
197 def get_popularity(self):
197 def get_popularity(self):
198 posts_with_tag = Post.objects.get_threads(tag=self)
198 posts_with_tag = Post.objects.get_threads(tag=self)
199 reply_count = 0
199 reply_count = 0
200 for post in posts_with_tag:
200 for post in posts_with_tag:
201 reply_count += post.get_reply_count()
201 reply_count += post.get_reply_count()
202 reply_count += OPENING_POST_POPULARITY_WEIGHT
202 reply_count += OPENING_POST_POPULARITY_WEIGHT
203
203
204 return reply_count
204 return reply_count
205
205
206 def get_linked_tags(self):
206 def get_linked_tags(self):
207 tag_list = []
207 tag_list = []
208 self.get_linked_tags_list(tag_list)
208 self.get_linked_tags_list(tag_list)
209
209
210 return tag_list
210 return tag_list
211
211
212 def get_linked_tags_list(self, tag_list=[]):
212 def get_linked_tags_list(self, tag_list=[]):
213 """
213 """
214 Returns the list of tags linked to current. The list can be got
214 Returns the list of tags linked to current. The list can be got
215 through returned value or tag_list parameter
215 through returned value or tag_list parameter
216 """
216 """
217
217
218 linked_tag = self.linked
218 linked_tag = self.linked
219
219
220 if linked_tag and not (linked_tag in tag_list):
220 if linked_tag and not (linked_tag in tag_list):
221 tag_list.append(linked_tag)
221 tag_list.append(linked_tag)
222
222
223 linked_tag.get_linked_tags_list(tag_list)
223 linked_tag.get_linked_tags_list(tag_list)
224
224
225
225
226 class Post(models.Model):
226 class Post(models.Model):
227 """A post is a message."""
227 """A post is a message."""
228
228
229 objects = PostManager()
229 objects = PostManager()
230
230
231 def _update_image_filename(self, filename):
231 def _update_image_filename(self, filename):
232 """Get unique image filename"""
232 """Get unique image filename"""
233
233
234 path = IMAGES_DIRECTORY
234 path = IMAGES_DIRECTORY
235 new_name = str(int(time.mktime(time.gmtime())))
235 new_name = str(int(time.mktime(time.gmtime())))
236 new_name += str(int(random() * 1000))
236 new_name += str(int(random() * 1000))
237 new_name += FILE_EXTENSION_DELIMITER
237 new_name += FILE_EXTENSION_DELIMITER
238 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
238 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
239
239
240 return os.path.join(path, new_name)
240 return os.path.join(path, new_name)
241
241
242 title = models.CharField(max_length=TITLE_MAX_LENGTH)
242 title = models.CharField(max_length=TITLE_MAX_LENGTH)
243 pub_time = models.DateTimeField()
243 pub_time = models.DateTimeField()
244 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
244 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
245 escape_html=False)
245 escape_html=False)
246
246
247 image_width = models.IntegerField(default=0)
247 image_width = models.IntegerField(default=0)
248 image_height = models.IntegerField(default=0)
248 image_height = models.IntegerField(default=0)
249
249
250 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
250 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
251 blank=True, sizes=(IMAGE_THUMB_SIZE,),
251 blank=True, sizes=(IMAGE_THUMB_SIZE,),
252 width_field='image_width',
252 width_field='image_width',
253 height_field='image_height')
253 height_field='image_height')
254
254
255 poster_ip = models.GenericIPAddressField()
255 poster_ip = models.GenericIPAddressField()
256 poster_user_agent = models.TextField()
256 poster_user_agent = models.TextField()
257
257
258 thread = models.ForeignKey('Post', null=True, default=None)
258 thread = models.ForeignKey('Post', null=True, default=None)
259 tags = models.ManyToManyField(Tag)
259 tags = models.ManyToManyField(Tag)
260 last_edit_time = models.DateTimeField()
260 last_edit_time = models.DateTimeField()
261 bump_time = models.DateTimeField()
261 bump_time = models.DateTimeField()
262 user = models.ForeignKey('User', null=True, default=None)
262 user = models.ForeignKey('User', null=True, default=None)
263
263
264 replies = models.ManyToManyField('Post', symmetrical=False, null=True,
264 replies = models.ManyToManyField('Post', symmetrical=False, null=True,
265 blank=True, related_name='re+')
265 blank=True, related_name='re+')
266 referenced_posts = models.ManyToManyField('Post', symmetrical=False, null=True,
266 referenced_posts = models.ManyToManyField('Post', symmetrical=False, null=True,
267 blank=True, related_name='rfp+')
267 blank=True, related_name='rfp+')
268
268
269 def __unicode__(self):
269 def __unicode__(self):
270 return '#' + str(self.id) + ' ' + self.title + ' (' + \
270 return '#' + str(self.id) + ' ' + self.title + ' (' + \
271 self.text.raw[:50] + ')'
271 self.text.raw[:50] + ')'
272
272
273 def get_title(self):
273 def get_title(self):
274 title = self.title
274 title = self.title
275 if len(title) == 0:
275 if len(title) == 0:
276 title = self.text.raw[:20]
276 title = self.text.raw[:20]
277
277
278 return title
278 return title
279
279
280 def get_reply_count(self):
280 def get_reply_count(self):
281 return self.replies.count()
281 return self.replies.count()
282
282
283 def get_images_count(self):
283 def get_images_count(self):
284 images_count = 1 if self.image else 0
284 images_count = 1 if self.image else 0
285 images_count += self.replies.filter(image_width__gt=0).count()
285 images_count += self.replies.filter(image_width__gt=0).count()
286
286
287 return images_count
287 return images_count
288
288
289 def can_bump(self):
289 def can_bump(self):
290 """Check if the thread can be bumped by replying"""
290 """Check if the thread can be bumped by replying"""
291
291
292 post_count = self.get_reply_count() + 1
292 post_count = self.get_reply_count() + 1
293
293
294 return post_count <= settings.MAX_POSTS_PER_THREAD
294 return post_count <= settings.MAX_POSTS_PER_THREAD
295
295
296 def bump(self):
296 def bump(self):
297 """Bump (move to up) thread"""
297 """Bump (move to up) thread"""
298
298
299 if self.can_bump():
299 if self.can_bump():
300 self.bump_time = timezone.now()
300 self.bump_time = timezone.now()
301
301
302 def get_last_replies(self):
302 def get_last_replies(self):
303 if settings.LAST_REPLIES_COUNT > 0:
303 if settings.LAST_REPLIES_COUNT > 0:
304 reply_count = self.get_reply_count()
304 reply_count = self.get_reply_count()
305
305
306 if reply_count > 0:
306 if reply_count > 0:
307 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
307 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
308 reply_count)
308 reply_count)
309 last_replies = self.replies.all().order_by('pub_time')[reply_count -
309 last_replies = self.replies.all().order_by('pub_time')[reply_count -
310 reply_count_to_show:]
310 reply_count_to_show:]
311
311
312 return last_replies
312 return last_replies
313
313
314 def get_tags(self):
315 """Get a sorted tag list"""
316
317 return self.tags.order_by('name')
318
314
319
315 class User(models.Model):
320 class User(models.Model):
316
321
317 user_id = models.CharField(max_length=50)
322 user_id = models.CharField(max_length=50)
318 rank = models.IntegerField()
323 rank = models.IntegerField()
319
324
320 registration_time = models.DateTimeField()
325 registration_time = models.DateTimeField()
321
326
322 fav_tags = models.ManyToManyField(Tag, null=True, blank=True)
327 fav_tags = models.ManyToManyField(Tag, null=True, blank=True)
323 fav_threads = models.ManyToManyField(Post, related_name='+', null=True,
328 fav_threads = models.ManyToManyField(Post, related_name='+', null=True,
324 blank=True)
329 blank=True)
325
330
326 def save_setting(self, name, value):
331 def save_setting(self, name, value):
327 setting, created = Setting.objects.get_or_create(name=name, user=self)
332 setting, created = Setting.objects.get_or_create(name=name, user=self)
328 setting.value = str(value)
333 setting.value = str(value)
329 setting.save()
334 setting.save()
330
335
331 return setting
336 return setting
332
337
333 def get_setting(self, name):
338 def get_setting(self, name):
334 if Setting.objects.filter(name=name, user=self).exists():
339 if Setting.objects.filter(name=name, user=self).exists():
335 setting = Setting.objects.get(name=name, user=self)
340 setting = Setting.objects.get(name=name, user=self)
336 setting_value = setting.value
341 setting_value = setting.value
337 else:
342 else:
338 setting_value = None
343 setting_value = None
339
344
340 return setting_value
345 return setting_value
341
346
342 def is_moderator(self):
347 def is_moderator(self):
343 return RANK_MODERATOR >= self.rank
348 return RANK_MODERATOR >= self.rank
344
349
345 def get_sorted_fav_tags(self):
350 def get_sorted_fav_tags(self):
346 tags = self.fav_tags.annotate(Count('threads'))\
351 tags = self.fav_tags.annotate(Count('threads'))\
347 .filter(threads__count__gt=0).order_by('name')
352 .filter(threads__count__gt=0).order_by('name')
348
353
349 return tags
354 return tags
350
355
351 def get_post_count(self):
356 def get_post_count(self):
352 return Post.objects.filter(user=self).count()
357 return Post.objects.filter(user=self).count()
353
358
354 def __unicode__(self):
359 def __unicode__(self):
355 return self.user_id + '(' + str(self.rank) + ')'
360 return self.user_id + '(' + str(self.rank) + ')'
356
361
357 def get_last_access_time(self):
362 def get_last_access_time(self):
358 posts = Post.objects.filter(user=self)
363 posts = Post.objects.filter(user=self)
359 if posts.count() > 0:
364 if posts.count() > 0:
360 return posts.latest('pub_time').pub_time
365 return posts.latest('pub_time').pub_time
361
366
362
367
363 class Setting(models.Model):
368 class Setting(models.Model):
364
369
365 name = models.CharField(max_length=50)
370 name = models.CharField(max_length=50)
366 value = models.CharField(max_length=50)
371 value = models.CharField(max_length=50)
367 user = models.ForeignKey(User)
372 user = models.ForeignKey(User)
368
373
369
374
370 class Ban(models.Model):
375 class Ban(models.Model):
371
376
372 ip = models.GenericIPAddressField()
377 ip = models.GenericIPAddressField()
373
378
374 def __unicode__(self):
379 def __unicode__(self):
375 return self.ip
380 return self.ip
@@ -1,223 +1,223 b''
1 {% extends "boards/base.html" %}
1 {% extends "boards/base.html" %}
2
2
3 {% load i18n %}
3 {% load i18n %}
4 {% load markup %}
4 {% load markup %}
5 {% load cache %}
5 {% load cache %}
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 {% endblock %}
13 {% endblock %}
14
14
15 {% block content %}
15 {% block content %}
16
16
17 {% if tag %}
17 {% if tag %}
18 <div class="tag_info">
18 <div class="tag_info">
19 <h2>
19 <h2>
20 {% if tag in user.fav_tags.all %}
20 {% if tag in user.fav_tags.all %}
21 <a href="{% url 'tag_unsubscribe' tag.name %}?next={{ request.path }}"
21 <a href="{% url 'tag_unsubscribe' tag.name %}?next={{ request.path }}"
22 class="fav">β˜…</a>
22 class="fav">β˜…</a>
23 {% else %}
23 {% else %}
24 <a href="{% url 'tag_subscribe' tag.name %}?next={{ request.path }}"
24 <a href="{% url 'tag_subscribe' tag.name %}?next={{ request.path }}"
25 class="not_fav">β˜…</a>
25 class="not_fav">β˜…</a>
26 {% endif %}
26 {% endif %}
27 #{{ tag.name }}
27 #{{ tag.name }}
28 </h2>
28 </h2>
29 </div>
29 </div>
30 {% endif %}
30 {% endif %}
31
31
32 {% if threads %}
32 {% if threads %}
33 {% for thread in threads %}
33 {% for thread in threads %}
34 <div class="thread">
34 <div class="thread">
35 {% if thread.bumpable %}
35 {% if thread.bumpable %}
36 <div class="post" id="{{ thread.thread.id }}">
36 <div class="post" id="{{ thread.thread.id }}">
37 {% else %}
37 {% else %}
38 <div class="post dead_post" id="{{ thread.thread.id }}">
38 <div class="post dead_post" id="{{ thread.thread.id }}">
39 {% endif %}
39 {% endif %}
40 {% if thread.thread.image %}
40 {% if thread.thread.image %}
41 <div class="image">
41 <div class="image">
42 <a class="thumb"
42 <a class="thumb"
43 href="{{ thread.thread.image.url }}"><img
43 href="{{ thread.thread.image.url }}"><img
44 src="{{ thread.thread.image.url_200x150 }}"
44 src="{{ thread.thread.image.url_200x150 }}"
45 alt="{{ thread.thread.id }}"
45 alt="{{ thread.thread.id }}"
46 data-width="{{ thread.thread.image_width }}"
46 data-width="{{ thread.thread.image_width }}"
47 data-height="{{ thread.thread.image_height }}" />
47 data-height="{{ thread.thread.image_height }}" />
48 </a>
48 </a>
49 </div>
49 </div>
50 {% endif %}
50 {% endif %}
51 <div class="message">
51 <div class="message">
52 <div class="post-info">
52 <div class="post-info">
53 <span class="title">{{ thread.thread.title }}</span>
53 <span class="title">{{ thread.thread.title }}</span>
54 <a class="post_id" href="{% url 'thread' thread.thread.id %}"
54 <a class="post_id" href="{% url 'thread' thread.thread.id %}"
55 >({{ thread.thread.id }})</a>
55 >({{ thread.thread.id }})</a>
56 [{{ thread.thread.pub_time }}]
56 [{{ thread.thread.pub_time }}]
57 [<a class="link" href="{% url 'thread' thread.thread.id %}#form"
57 [<a class="link" href="{% url 'thread' thread.thread.id %}#form"
58 >{% trans "Reply" %}</a>]
58 >{% trans "Reply" %}</a>]
59
59
60 {% if moderator %}
60 {% if moderator %}
61 <span class="moderator_info">
61 <span class="moderator_info">
62 [<a href="{% url 'delete' post_id=thread.thread.id %}?next={{ request.path }}"
62 [<a href="{% url 'delete' post_id=thread.thread.id %}?next={{ request.path }}"
63 >{% trans 'Delete' %}</a>]
63 >{% trans 'Delete' %}</a>]
64 ({{ thread.thread.poster_ip }})
64 ({{ thread.thread.poster_ip }})
65 [<a href="{% url 'ban' post_id=thread.thread.id %}?next={{ request.path }}"
65 [<a href="{% url 'ban' post_id=thread.thread.id %}?next={{ request.path }}"
66 >{% trans 'Ban IP' %}</a>]
66 >{% trans 'Ban IP' %}</a>]
67 </span>
67 </span>
68 {% endif %}
68 {% endif %}
69 </div>
69 </div>
70 {% autoescape off %}
70 {% autoescape off %}
71 {{ thread.thread.text.rendered|truncatewords_html:50 }}
71 {{ thread.thread.text.rendered|truncatewords_html:50 }}
72 {% endautoescape %}
72 {% endautoescape %}
73 {% if thread.thread.referenced_posts.all %}
73 {% if thread.thread.referenced_posts.all %}
74 <div class="refmap">
74 <div class="refmap">
75 {% trans "Replies" %}:
75 {% trans "Replies" %}:
76 {% for ref_post in thread.thread.referenced_posts.all %}
76 {% for ref_post in thread.thread.referenced_posts.all %}
77 <a href="{% url 'jumper' ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a>
77 <a href="{% url 'jumper' ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a>
78 {% endfor %}
78 {% endfor %}
79 </div>
79 </div>
80 {% endif %}
80 {% endif %}
81 </div>
81 </div>
82 <div class="metadata">
82 <div class="metadata">
83 {{ thread.thread.get_reply_count }} {% trans 'replies' %},
83 {{ thread.thread.get_reply_count }} {% trans 'replies' %},
84 {{ thread.thread.get_images_count }} {% trans 'images' %}.
84 {{ thread.thread.get_images_count }} {% trans 'images' %}.
85 {% if thread.thread.tags %}
85 {% if thread.thread.tags %}
86 <span class="tags">
86 <span class="tags">
87 {% for tag in thread.thread.tags.all %}
87 {% for tag in thread.thread.get_tags %}
88 <a class="tag" href="
88 <a class="tag" href="
89 {% url 'tag' tag_name=tag.name %}">
89 {% url 'tag' tag_name=tag.name %}">
90 #{{ tag.name }}</a>
90 #{{ tag.name }}</a>
91 {% endfor %}
91 {% endfor %}
92 </span>
92 </span>
93 {% endif %}
93 {% endif %}
94 </div>
94 </div>
95 </div>
95 </div>
96 {% if thread.thread.get_last_replies.exists %}
96 {% if thread.thread.get_last_replies.exists %}
97 <div class="last-replies">
97 <div class="last-replies">
98 {% for post in thread.thread.get_last_replies %}
98 {% for post in thread.thread.get_last_replies %}
99 {% if thread.bumpable %}
99 {% if thread.bumpable %}
100 <div class="post" id="{{ post.id }}">
100 <div class="post" id="{{ post.id }}">
101 {% else %}
101 {% else %}
102 <div class="post dead_post" id="{{ post.id }}">
102 <div class="post dead_post" id="{{ post.id }}">
103 {% endif %}
103 {% endif %}
104 {% if post.image %}
104 {% if post.image %}
105 <div class="image">
105 <div class="image">
106 <a class="thumb"
106 <a class="thumb"
107 href="{{ post.image.url }}"><img
107 href="{{ post.image.url }}"><img
108 src=" {{ post.image.url_200x150 }}"
108 src=" {{ post.image.url_200x150 }}"
109 alt="{{ post.id }}"
109 alt="{{ post.id }}"
110 data-width="{{ post.image_width }}"
110 data-width="{{ post.image_width }}"
111 data-height="{{ post.image_height }}"/>
111 data-height="{{ post.image_height }}"/>
112 </a>
112 </a>
113 </div>
113 </div>
114 {% endif %}
114 {% endif %}
115 <div class="message">
115 <div class="message">
116 <div class="post-info">
116 <div class="post-info">
117 <span class="title">{{ post.title }}</span>
117 <span class="title">{{ post.title }}</span>
118 <a class="post_id" href="
118 <a class="post_id" href="
119 {% url 'thread' thread.thread.id %}#{{ post.id }}">
119 {% url 'thread' thread.thread.id %}#{{ post.id }}">
120 ({{ post.id }})</a>
120 ({{ post.id }})</a>
121 [{{ post.pub_time }}]
121 [{{ post.pub_time }}]
122 </div>
122 </div>
123 {% autoescape off %}
123 {% autoescape off %}
124 {{ post.text.rendered|truncatewords_html:50 }}
124 {{ post.text.rendered|truncatewords_html:50 }}
125 {% endautoescape %}
125 {% endautoescape %}
126 </div>
126 </div>
127 {% if post.referenced_posts.all %}
127 {% if post.referenced_posts.all %}
128 <div class="refmap">
128 <div class="refmap">
129 {% trans "Replies" %}:
129 {% trans "Replies" %}:
130 {% for ref_post in post.referenced_posts.all %}
130 {% for ref_post in post.referenced_posts.all %}
131 <a href="{% url 'jumper' ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a>
131 <a href="{% url 'jumper' ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a>
132 {% endfor %}
132 {% endfor %}
133 </div>
133 </div>
134 {% endif %}
134 {% endif %}
135 </div>
135 </div>
136 {% endfor %}
136 {% endfor %}
137 </div>
137 </div>
138 {% endif %}
138 {% endif %}
139 </div>
139 </div>
140 {% endfor %}
140 {% endfor %}
141 {% else %}
141 {% else %}
142 <div class="post">
142 <div class="post">
143 {% trans 'No threads exist. Create the first one!' %}</div>
143 {% trans 'No threads exist. Create the first one!' %}</div>
144 {% endif %}
144 {% endif %}
145
145
146 <form enctype="multipart/form-data" method="post">{% csrf_token %}
146 <form enctype="multipart/form-data" method="post">{% csrf_token %}
147 <div class="post-form-w">
147 <div class="post-form-w">
148
148
149 <div class="form-title">{% trans "Create new thread" %}</div>
149 <div class="form-title">{% trans "Create new thread" %}</div>
150 <div class="post-form">
150 <div class="post-form">
151 <div class="form-row">
151 <div class="form-row">
152 <div class="form-label">{% trans 'Title' %}</div>
152 <div class="form-label">{% trans 'Title' %}</div>
153 <div class="form-input">{{ form.title }}</div>
153 <div class="form-input">{{ form.title }}</div>
154 <div class="form-errors">{{ form.title.errors }}</div>
154 <div class="form-errors">{{ form.title.errors }}</div>
155 </div>
155 </div>
156 <div class="form-row">
156 <div class="form-row">
157 <div class="form-label">{% trans 'Formatting' %}</div>
157 <div class="form-label">{% trans 'Formatting' %}</div>
158 <div class="form-input" id="mark_panel">
158 <div class="form-input" id="mark_panel">
159 <span class="mark_btn" id="quote"><span class="quote">&gt;{% trans 'quote' %}</span></span>
159 <span class="mark_btn" id="quote"><span class="quote">&gt;{% trans 'quote' %}</span></span>
160 <span class="mark_btn" id="italic"><i>{% trans 'italic' %}</i></span>
160 <span class="mark_btn" id="italic"><i>{% trans 'italic' %}</i></span>
161 <span class="mark_btn" id="bold"><b>{% trans 'bold' %}</b></span>
161 <span class="mark_btn" id="bold"><b>{% trans 'bold' %}</b></span>
162 <span class="mark_btn" id="spoiler"><span class="spoiler">{% trans 'spoiler' %}</span></span>
162 <span class="mark_btn" id="spoiler"><span class="spoiler">{% trans 'spoiler' %}</span></span>
163 <span class="mark_btn" id="comment"><span class="comment">// {% trans 'comment' %}</span></span>
163 <span class="mark_btn" id="comment"><span class="comment">// {% trans 'comment' %}</span></span>
164 </div>
164 </div>
165 </div>
165 </div>
166 <div class="form-row">
166 <div class="form-row">
167 <div class="form-label">{% trans 'Text' %}</div>
167 <div class="form-label">{% trans 'Text' %}</div>
168 <div class="form-input">{{ form.text }}</div>
168 <div class="form-input">{{ form.text }}</div>
169 <div class="form-errors">{{ form.text.errors }}</div>
169 <div class="form-errors">{{ form.text.errors }}</div>
170 </div>
170 </div>
171 <div class="form-row">
171 <div class="form-row">
172 <div class="form-label">{% trans 'Image' %}</div>
172 <div class="form-label">{% trans 'Image' %}</div>
173 <div class="form-input">{{ form.image }}</div>
173 <div class="form-input">{{ form.image }}</div>
174 <div class="form-errors">{{ form.image.errors }}</div>
174 <div class="form-errors">{{ form.image.errors }}</div>
175 </div>
175 </div>
176 <div class="form-row">
176 <div class="form-row">
177 <div class="form-label">{% trans 'Tags' %}</div>
177 <div class="form-label">{% trans 'Tags' %}</div>
178 <div class="form-input">{{ form.tags }}</div>
178 <div class="form-input">{{ form.tags }}</div>
179 <div class="form-errors">{{ form.tags.errors }}</div>
179 <div class="form-errors">{{ form.tags.errors }}</div>
180 </div>
180 </div>
181 <div class="form-row form-email">
181 <div class="form-row form-email">
182 <div class="form-label">{% trans 'e-mail' %}</div>
182 <div class="form-label">{% trans 'e-mail' %}</div>
183 <div class="form-input">{{ form.email }}</div>
183 <div class="form-input">{{ form.email }}</div>
184 <div class="form-errors">{{ form.email.errors }}</div>
184 <div class="form-errors">{{ form.email.errors }}</div>
185 </div>
185 </div>
186 <div class="form-row">
186 <div class="form-row">
187 {{ form.captcha }}
187 {{ form.captcha }}
188 <div class="form-errors">{{ form.captcha.errors }}</div>
188 <div class="form-errors">{{ form.captcha.errors }}</div>
189 </div>
189 </div>
190 <div class="form-row">
190 <div class="form-row">
191 <div class="form-errors">{{ form.other.errors }}</div>
191 <div class="form-errors">{{ form.other.errors }}</div>
192 </div>
192 </div>
193 </div>
193 </div>
194 <div class="form-submit">
194 <div class="form-submit">
195 <input type="submit" value="{% trans "Post" %}"/></div>
195 <input type="submit" value="{% trans "Post" %}"/></div>
196 <div>
196 <div>
197 {% trans 'Tags must be delimited by spaces. Text or image is required.' %}
197 {% trans 'Tags must be delimited by spaces. Text or image is required.' %}
198 </div>
198 </div>
199 <div><a href="{% url "staticpage" name="help" %}">
199 <div><a href="{% url "staticpage" name="help" %}">
200 {% trans 'Text syntax' %}</a></div>
200 {% trans 'Text syntax' %}</a></div>
201 </div>
201 </div>
202 </form>
202 </form>
203
203
204 {% endblock %}
204 {% endblock %}
205
205
206 {% block metapanel %}
206 {% block metapanel %}
207
207
208 <span class="metapanel">
208 <span class="metapanel">
209 <b><a href="{% url "authors" %}">Neboard</a> 1.3</b>
209 <b><a href="{% url "authors" %}">Neboard</a> 1.3</b>
210 {% trans "Pages:" %}
210 {% trans "Pages:" %}
211 {% for page in pages %}
211 {% for page in pages %}
212 [<a href="
212 [<a href="
213 {% if tag %}
213 {% if tag %}
214 {% url "tag" tag_name=tag page=page %}
214 {% url "tag" tag_name=tag page=page %}
215 {% else %}
215 {% else %}
216 {% url "index" page=page %}
216 {% url "index" page=page %}
217 {% endif %}
217 {% endif %}
218 ">{{ page }}</a>]
218 ">{{ page }}</a>]
219 {% endfor %}
219 {% endfor %}
220 [<a href="rss/">RSS</a>]
220 [<a href="rss/">RSS</a>]
221 </span>
221 </span>
222
222
223 {% endblock %}
223 {% endblock %}
@@ -1,160 +1,160 b''
1 {% extends "boards/base.html" %}
1 {% extends "boards/base.html" %}
2
2
3 {% load i18n %}
3 {% load i18n %}
4 {% load markup %}
4 {% load markup %}
5 {% load cache %}
5 {% load cache %}
6 {% load static from staticfiles %}
6 {% load static from staticfiles %}
7
7
8 {% block head %}
8 {% block head %}
9 <title>Neboard - {{ posts.0.get_title }}</title>
9 <title>Neboard - {{ posts.0.get_title }}</title>
10 {% endblock %}
10 {% endblock %}
11
11
12 {% block content %}
12 {% block content %}
13 {% get_current_language as LANGUAGE_CODE %}
13 {% get_current_language as LANGUAGE_CODE %}
14
14
15 <script src="{% static 'js/thread.js' %}"></script>
15 <script src="{% static 'js/thread.js' %}"></script>
16
16
17 {% if posts %}
17 {% if posts %}
18 {% cache 600 thread_view posts.0.last_edit_time moderator LANGUAGE_CODE %}
18 {% cache 600 thread_view posts.0.last_edit_time moderator LANGUAGE_CODE %}
19 {% if bumpable %}
19 {% if bumpable %}
20 <div class="bar-bg">
20 <div class="bar-bg">
21 <div class="bar-value" style="width:{{ bumplimit_progress }}%">
21 <div class="bar-value" style="width:{{ bumplimit_progress }}%">
22 </div>
22 </div>
23 <div class="bar-text">
23 <div class="bar-text">
24 {{ posts_left }} {% trans 'posts to bumplimit' %}
24 {{ posts_left }} {% trans 'posts to bumplimit' %}
25 </div>
25 </div>
26 </div>
26 </div>
27 {% endif %}
27 {% endif %}
28 <div class="thread">
28 <div class="thread">
29 {% for post in posts %}
29 {% for post in posts %}
30 {% if bumpable %}
30 {% if bumpable %}
31 <div class="post" id="{{ post.id }}">
31 <div class="post" id="{{ post.id }}">
32 {% else %}
32 {% else %}
33 <div class="post dead_post" id="{{ post.id }}">
33 <div class="post dead_post" id="{{ post.id }}">
34 {% endif %}
34 {% endif %}
35 {% if post.image %}
35 {% if post.image %}
36 <div class="image">
36 <div class="image">
37 <a
37 <a
38 class="thumb"
38 class="thumb"
39 href="{{ post.image.url }}"><img
39 href="{{ post.image.url }}"><img
40 src="{{ post.image.url_200x150 }}"
40 src="{{ post.image.url_200x150 }}"
41 alt="{{ post.id }}"
41 alt="{{ post.id }}"
42 data-width="{{ post.image_width }}"
42 data-width="{{ post.image_width }}"
43 data-height="{{ post.image_height }}"/>
43 data-height="{{ post.image_height }}"/>
44 </a>
44 </a>
45 </div>
45 </div>
46 {% endif %}
46 {% endif %}
47 <div class="message">
47 <div class="message">
48 <div class="post-info">
48 <div class="post-info">
49 <span class="title">{{ post.title }}</span>
49 <span class="title">{{ post.title }}</span>
50 <a class="post_id" href="#{{ post.id }}">
50 <a class="post_id" href="#{{ post.id }}">
51 ({{ post.id }})</a>
51 ({{ post.id }})</a>
52 [{{ post.pub_time }}]
52 [{{ post.pub_time }}]
53 [<a href="#" onclick="javascript:addQuickReply('{{ post.id }}')
53 [<a href="#" onclick="javascript:addQuickReply('{{ post.id }}')
54 ; return false;">&gt;&gt;</a>]
54 ; return false;">&gt;&gt;</a>]
55
55
56 {% if moderator %}
56 {% if moderator %}
57 <span class="moderator_info">
57 <span class="moderator_info">
58 [<a href="{% url 'delete' post_id=post.id %}"
58 [<a href="{% url 'delete' post_id=post.id %}"
59 >{% trans 'Delete' %}</a>]
59 >{% trans 'Delete' %}</a>]
60 ({{ post.poster_ip }})
60 ({{ post.poster_ip }})
61 [<a href="{% url 'ban' post_id=post.id %}?next={{ request.path }}"
61 [<a href="{% url 'ban' post_id=post.id %}?next={{ request.path }}"
62 >{% trans 'Ban IP' %}</a>]
62 >{% trans 'Ban IP' %}</a>]
63 </span>
63 </span>
64 {% endif %}
64 {% endif %}
65 </div>
65 </div>
66 {% autoescape off %}
66 {% autoescape off %}
67 {{ post.text.rendered }}
67 {{ post.text.rendered }}
68 {% endautoescape %}
68 {% endautoescape %}
69 {% if post.referenced_posts.all %}
69 {% if post.referenced_posts.all %}
70 <div class="refmap">
70 <div class="refmap">
71 {% trans "Replies" %}:
71 {% trans "Replies" %}:
72 {% for ref_post in post.referenced_posts.all %}
72 {% for ref_post in post.referenced_posts.all %}
73 <a href="{% url 'jumper' ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a>
73 <a href="{% url 'jumper' ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a>
74 {% endfor %}
74 {% endfor %}
75 </div>
75 </div>
76 {% endif %}
76 {% endif %}
77 </div>
77 </div>
78 {% if post.id == posts.0.id %}
78 {% if post.id == posts.0.id %}
79 <div class="metadata">
79 <div class="metadata">
80 <span class="tags">
80 <span class="tags">
81 {% for tag in post.tags.all %}
81 {% for tag in post.get_tags %}
82 <a class="tag" href="{% url 'tag' tag.name %}">
82 <a class="tag" href="{% url 'tag' tag.name %}">
83 #{{ tag.name }}</a>
83 #{{ tag.name }}</a>
84 {% endfor %}
84 {% endfor %}
85 </span>
85 </span>
86 </div>
86 </div>
87 {% endif %}
87 {% endif %}
88 </div>
88 </div>
89 {% endfor %}
89 {% endfor %}
90 </div>
90 </div>
91 {% endcache %}
91 {% endcache %}
92 {% endif %}
92 {% endif %}
93
93
94 <form id="form" enctype="multipart/form-data" method="post"
94 <form id="form" enctype="multipart/form-data" method="post"
95 >{% csrf_token %}
95 >{% csrf_token %}
96 <div class="post-form-w">
96 <div class="post-form-w">
97 <div class="form-title">{% trans "Reply to thread" %} #{{ posts.0.id }}</div>
97 <div class="form-title">{% trans "Reply to thread" %} #{{ posts.0.id }}</div>
98 <div class="post-form">
98 <div class="post-form">
99 <div class="form-row">
99 <div class="form-row">
100 <div class="form-label">{% trans 'Title' %}</div>
100 <div class="form-label">{% trans 'Title' %}</div>
101 <div class="form-input">{{ form.title }}</div>
101 <div class="form-input">{{ form.title }}</div>
102 <div class="form-errors">{{ form.title.errors }}</div>
102 <div class="form-errors">{{ form.title.errors }}</div>
103 </div>
103 </div>
104 <div class="form-row">
104 <div class="form-row">
105 <div class="form-label">{% trans 'Formatting' %}</div>
105 <div class="form-label">{% trans 'Formatting' %}</div>
106 <div class="form-input" id="mark_panel">
106 <div class="form-input" id="mark_panel">
107 <span class="mark_btn" id="quote"><span class="quote">&gt;{% trans 'quote' %}</span></span>
107 <span class="mark_btn" id="quote"><span class="quote">&gt;{% trans 'quote' %}</span></span>
108 <span class="mark_btn" id="italic"><i>{% trans 'italic' %}</i></span>
108 <span class="mark_btn" id="italic"><i>{% trans 'italic' %}</i></span>
109 <span class="mark_btn" id="bold"><b>{% trans 'bold' %}</b></span>
109 <span class="mark_btn" id="bold"><b>{% trans 'bold' %}</b></span>
110 <span class="mark_btn" id="spoiler"><span class="spoiler">{% trans 'spoiler' %}</span></span>
110 <span class="mark_btn" id="spoiler"><span class="spoiler">{% trans 'spoiler' %}</span></span>
111 <span class="mark_btn" id="comment"><span class="comment">// {% trans 'comment' %}</span></span>
111 <span class="mark_btn" id="comment"><span class="comment">// {% trans 'comment' %}</span></span>
112 </div>
112 </div>
113 </div>
113 </div>
114 <div class="form-row">
114 <div class="form-row">
115 <div class="form-label">{% trans 'Text' %}</div>
115 <div class="form-label">{% trans 'Text' %}</div>
116 <div class="form-input">{{ form.text }}</div>
116 <div class="form-input">{{ form.text }}</div>
117 <div class="form-errors">{{ form.text.errors }}</div>
117 <div class="form-errors">{{ form.text.errors }}</div>
118 </div>
118 </div>
119 <div class="form-row">
119 <div class="form-row">
120 <div class="form-label">{% trans 'Image' %}</div>
120 <div class="form-label">{% trans 'Image' %}</div>
121 <div class="form-input">{{ form.image }}</div>
121 <div class="form-input">{{ form.image }}</div>
122 <div class="form-errors">{{ form.image.errors }}</div>
122 <div class="form-errors">{{ form.image.errors }}</div>
123 </div>
123 </div>
124 <div class="form-row form-email">
124 <div class="form-row form-email">
125 <div class="form-label">{% trans 'e-mail' %}</div>
125 <div class="form-label">{% trans 'e-mail' %}</div>
126 <div class="form-input">{{ form.email }}</div>
126 <div class="form-input">{{ form.email }}</div>
127 <div class="form-errors">{{ form.email.errors }}</div>
127 <div class="form-errors">{{ form.email.errors }}</div>
128 </div>
128 </div>
129 <div class="form-row">
129 <div class="form-row">
130 {{ form.captcha }}
130 {{ form.captcha }}
131 <div class="form-errors">{{ form.captcha.errors }}</div>
131 <div class="form-errors">{{ form.captcha.errors }}</div>
132 </div>
132 </div>
133 <div class="form-row">
133 <div class="form-row">
134 <div class="form-errors">{{ form.other.errors }}</div>
134 <div class="form-errors">{{ form.other.errors }}</div>
135 </div>
135 </div>
136 </div>
136 </div>
137
137
138 <div class="form-submit"><input type="submit"
138 <div class="form-submit"><input type="submit"
139 value="{% trans "Post" %}"/></div>
139 value="{% trans "Post" %}"/></div>
140 <div><a href="{% url "staticpage" name="help" %}">
140 <div><a href="{% url "staticpage" name="help" %}">
141 {% trans 'Text syntax' %}</a></div>
141 {% trans 'Text syntax' %}</a></div>
142 </div>
142 </div>
143 </form>
143 </form>
144
144
145 {% endblock %}
145 {% endblock %}
146
146
147 {% block metapanel %}
147 {% block metapanel %}
148
148
149 {% get_current_language as LANGUAGE_CODE %}
149 {% get_current_language as LANGUAGE_CODE %}
150
150
151 <span class="metapanel">
151 <span class="metapanel">
152 {% cache 600 thread_meta posts.0.last_edit_time moderator LANGUAGE_CODE %}
152 {% cache 600 thread_meta posts.0.last_edit_time moderator LANGUAGE_CODE %}
153 {{ posts.0.get_reply_count }} {% trans 'replies' %},
153 {{ posts.0.get_reply_count }} {% trans 'replies' %},
154 {{ posts.0.get_images_count }} {% trans 'images' %}.
154 {{ posts.0.get_images_count }} {% trans 'images' %}.
155 {% trans 'Last update: ' %}{{ posts.0.last_edit_time }}
155 {% trans 'Last update: ' %}{{ posts.0.last_edit_time }}
156 [<a href="rss/">RSS</a>]
156 [<a href="rss/">RSS</a>]
157 {% endcache %}
157 {% endcache %}
158 </span>
158 </span>
159
159
160 {% endblock %}
160 {% endblock %}
General Comments 0
You need to be logged in to leave comments. Login now