##// END OF EJS Templates
Do not pass over perms variable manually to the post template. Fixed thread...
neko259 -
r1390:1252678f default
parent child Browse files
Show More
@@ -1,361 +1,360 b''
1 1 import logging
2 2 import re
3 3 import uuid
4 4
5 5 from django.core.exceptions import ObjectDoesNotExist
6 6 from django.core.urlresolvers import reverse
7 7 from django.db import models
8 8 from django.db.models import TextField, QuerySet
9 9 from django.template.defaultfilters import striptags, truncatewords
10 10 from django.template.loader import render_to_string
11 11 from django.utils import timezone
12 12
13 13 from boards import settings
14 14 from boards.abstracts.tripcode import Tripcode
15 15 from boards.mdx_neboard import Parser
16 16 from boards.models import PostImage, Attachment
17 17 from boards.models.base import Viewable
18 18 from boards.models.post.export import get_exporter, DIFF_TYPE_JSON
19 19 from boards.models.post.manager import PostManager
20 20 from boards.models.user import Notification
21 21
22 22 CSS_CLS_HIDDEN_POST = 'hidden_post'
23 23 CSS_CLS_DEAD_POST = 'dead_post'
24 24 CSS_CLS_ARCHIVE_POST = 'archive_post'
25 25 CSS_CLS_POST = 'post'
26 26
27 27 TITLE_MAX_WORDS = 10
28 28
29 29 APP_LABEL_BOARDS = 'boards'
30 30
31 31 BAN_REASON_AUTO = 'Auto'
32 32
33 33 IMAGE_THUMB_SIZE = (200, 150)
34 34
35 35 TITLE_MAX_LENGTH = 200
36 36
37 37 REGEX_REPLY = re.compile(r'\[post\](\d+)\[/post\]')
38 38 REGEX_NOTIFICATION = re.compile(r'\[user\](\w+)\[/user\]')
39 39
40 40 PARAMETER_TRUNCATED = 'truncated'
41 41 PARAMETER_TAG = 'tag'
42 42 PARAMETER_OFFSET = 'offset'
43 43 PARAMETER_DIFF_TYPE = 'type'
44 44 PARAMETER_CSS_CLASS = 'css_class'
45 45 PARAMETER_THREAD = 'thread'
46 46 PARAMETER_IS_OPENING = 'is_opening'
47 PARAMETER_MODERATOR = 'moderator'
48 47 PARAMETER_POST = 'post'
49 48 PARAMETER_OP_ID = 'opening_post_id'
50 49 PARAMETER_NEED_OPEN_LINK = 'need_open_link'
51 50 PARAMETER_REPLY_LINK = 'reply_link'
52 51 PARAMETER_NEED_OP_DATA = 'need_op_data'
53 52
54 53 POST_VIEW_PARAMS = (
55 54 'need_op_data',
56 55 'reply_link',
57 56 'need_open_link',
58 57 'truncated',
59 58 'mode_tree',
60 59 'perms',
61 60 )
62 61
63 62
64 63 class Post(models.Model, Viewable):
65 64 """A post is a message."""
66 65
67 66 objects = PostManager()
68 67
69 68 class Meta:
70 69 app_label = APP_LABEL_BOARDS
71 70 ordering = ('id',)
72 71
73 72 title = models.CharField(max_length=TITLE_MAX_LENGTH, null=True, blank=True)
74 73 pub_time = models.DateTimeField()
75 74 text = TextField(blank=True, null=True)
76 75 _text_rendered = TextField(blank=True, null=True, editable=False)
77 76
78 77 images = models.ManyToManyField(PostImage, null=True, blank=True,
79 78 related_name='post_images', db_index=True)
80 79 attachments = models.ManyToManyField(Attachment, null=True, blank=True,
81 80 related_name='attachment_posts')
82 81
83 82 poster_ip = models.GenericIPAddressField()
84 83
85 84 # TODO This field can be removed cause UID is used for update now
86 85 last_edit_time = models.DateTimeField()
87 86
88 87 referenced_posts = models.ManyToManyField('Post', symmetrical=False,
89 88 null=True,
90 89 blank=True, related_name='refposts',
91 90 db_index=True)
92 91 refmap = models.TextField(null=True, blank=True)
93 92 threads = models.ManyToManyField('Thread', db_index=True,
94 93 related_name='multi_replies')
95 94 thread = models.ForeignKey('Thread', db_index=True, related_name='pt+')
96 95
97 96 url = models.TextField()
98 97 uid = models.TextField(db_index=True)
99 98
100 99 tripcode = models.CharField(max_length=50, blank=True, default='')
101 100 opening = models.BooleanField(db_index=True)
102 101 hidden = models.BooleanField(default=False)
103 102
104 103 def __str__(self):
105 104 return 'P#{}/{}'.format(self.id, self.get_title())
106 105
107 106 def get_referenced_posts(self):
108 107 threads = self.get_threads().all()
109 108 return self.referenced_posts.filter(threads__in=threads)\
110 109 .order_by('pub_time').distinct().all()
111 110
112 111 def get_title(self) -> str:
113 112 return self.title
114 113
115 114 def get_title_or_text(self):
116 115 title = self.get_title()
117 116 if not title:
118 117 title = truncatewords(striptags(self.get_text()), TITLE_MAX_WORDS)
119 118
120 119 return title
121 120
122 121 def build_refmap(self) -> None:
123 122 """
124 123 Builds a replies map string from replies list. This is a cache to stop
125 124 the server from recalculating the map on every post show.
126 125 """
127 126
128 127 post_urls = [refpost.get_link_view()
129 128 for refpost in self.referenced_posts.all()]
130 129
131 130 self.refmap = ', '.join(post_urls)
132 131
133 132 def is_referenced(self) -> bool:
134 133 return self.refmap and len(self.refmap) > 0
135 134
136 135 def is_opening(self) -> bool:
137 136 """
138 137 Checks if this is an opening post or just a reply.
139 138 """
140 139
141 140 return self.opening
142 141
143 142 def get_absolute_url(self, thread=None):
144 143 url = None
145 144
146 145 if thread is None:
147 146 thread = self.get_thread()
148 147
149 148 # Url is cached only for the "main" thread. When getting url
150 149 # for other threads, do it manually.
151 150 if self.url:
152 151 url = self.url
153 152
154 153 if url is None:
155 154 opening_id = thread.get_opening_post_id()
156 155 url = reverse('thread', kwargs={'post_id': opening_id})
157 156 if self.id != opening_id:
158 157 url += '#' + str(self.id)
159 158
160 159 return url
161 160
162 161 def get_thread(self):
163 162 return self.thread
164 163
165 164 def get_threads(self) -> QuerySet:
166 165 """
167 166 Gets post's thread.
168 167 """
169 168
170 169 return self.threads
171 170
172 171 def get_view(self, *args, **kwargs) -> str:
173 172 """
174 173 Renders post's HTML view. Some of the post params can be passed over
175 174 kwargs for the means of caching (if we view the thread, some params
176 175 are same for every post and don't need to be computed over and over.
177 176 """
178 177
179 178 thread = self.get_thread()
180 179
181 180 css_classes = [CSS_CLS_POST]
182 181 if thread.archived:
183 182 css_classes.append(CSS_CLS_ARCHIVE_POST)
184 183 elif not thread.can_bump():
185 184 css_classes.append(CSS_CLS_DEAD_POST)
186 185 if self.is_hidden():
187 186 css_classes.append(CSS_CLS_HIDDEN_POST)
188 187
189 188 params = dict()
190 189 for param in POST_VIEW_PARAMS:
191 190 if param in kwargs:
192 191 params[param] = kwargs[param]
193 192
194 193 params.update({
195 194 PARAMETER_POST: self,
196 195 PARAMETER_IS_OPENING: self.is_opening(),
197 196 PARAMETER_THREAD: thread,
198 197 PARAMETER_CSS_CLASS: ' '.join(css_classes),
199 198 })
200 199
201 200 return render_to_string('boards/post.html', params)
202 201
203 202 def get_search_view(self, *args, **kwargs):
204 203 return self.get_view(need_op_data=True, *args, **kwargs)
205 204
206 205 def get_first_image(self) -> PostImage:
207 206 return self.images.earliest('id')
208 207
209 208 def delete(self, using=None):
210 209 """
211 210 Deletes all post images and the post itself.
212 211 """
213 212
214 213 for image in self.images.all():
215 214 image_refs_count = image.post_images.count()
216 215 if image_refs_count == 1:
217 216 image.delete()
218 217
219 218 for attachment in self.attachments.all():
220 219 attachment_refs_count = attachment.attachment_posts.count()
221 220 if attachment_refs_count == 1:
222 221 attachment.delete()
223 222
224 223 thread = self.get_thread()
225 224 thread.last_edit_time = timezone.now()
226 225 thread.save()
227 226
228 227 super(Post, self).delete(using)
229 228
230 229 logging.getLogger('boards.post.delete').info(
231 230 'Deleted post {}'.format(self))
232 231
233 232 def get_post_data(self, format_type=DIFF_TYPE_JSON, request=None,
234 233 include_last_update=False) -> str:
235 234 """
236 235 Gets post HTML or JSON data that can be rendered on a page or used by
237 236 API.
238 237 """
239 238
240 239 return get_exporter(format_type).export(self, request,
241 240 include_last_update)
242 241
243 242 def notify_clients(self, recursive=True):
244 243 """
245 244 Sends post HTML data to the thread web socket.
246 245 """
247 246
248 247 if not settings.get_bool('External', 'WebsocketsEnabled'):
249 248 return
250 249
251 250 thread_ids = list()
252 251 for thread in self.get_threads().all():
253 252 thread_ids.append(thread.id)
254 253
255 254 thread.notify_clients()
256 255
257 256 if recursive:
258 257 for reply_number in re.finditer(REGEX_REPLY, self.get_raw_text()):
259 258 post_id = reply_number.group(1)
260 259
261 260 try:
262 261 ref_post = Post.objects.get(id=post_id)
263 262
264 263 if ref_post.get_threads().exclude(id__in=thread_ids).exists():
265 264 # If post is in this thread, its thread was already notified.
266 265 # Otherwise, notify its thread separately.
267 266 ref_post.notify_clients(recursive=False)
268 267 except ObjectDoesNotExist:
269 268 pass
270 269
271 270 def build_url(self):
272 271 self.url = self.get_absolute_url()
273 272 self.save(update_fields=['url'])
274 273
275 274 def save(self, force_insert=False, force_update=False, using=None,
276 275 update_fields=None):
277 276 self._text_rendered = Parser().parse(self.get_raw_text())
278 277
279 278 self.uid = str(uuid.uuid4())
280 279 if update_fields is not None and 'uid' not in update_fields:
281 280 update_fields += ['uid']
282 281
283 282 if self.id:
284 283 for thread in self.get_threads().all():
285 284 thread.last_edit_time = self.last_edit_time
286 285
287 286 thread.save(update_fields=['last_edit_time', 'bumpable'])
288 287
289 288 super().save(force_insert, force_update, using, update_fields)
290 289
291 290 def get_text(self) -> str:
292 291 return self._text_rendered
293 292
294 293 def get_raw_text(self) -> str:
295 294 return self.text
296 295
297 296 def get_absolute_id(self) -> str:
298 297 """
299 298 If the post has many threads, shows its main thread OP id in the post
300 299 ID.
301 300 """
302 301
303 302 if self.get_threads().count() > 1:
304 303 return '{}/{}'.format(self.get_thread().get_opening_post_id(), self.id)
305 304 else:
306 305 return str(self.id)
307 306
308 307 def connect_notifications(self):
309 308 for reply_number in re.finditer(REGEX_NOTIFICATION, self.get_raw_text()):
310 309 user_name = reply_number.group(1).lower()
311 310 Notification.objects.get_or_create(name=user_name, post=self)
312 311
313 312 def connect_replies(self):
314 313 """
315 314 Connects replies to a post to show them as a reflink map
316 315 """
317 316
318 317 for reply_number in re.finditer(REGEX_REPLY, self.get_raw_text()):
319 318 post_id = reply_number.group(1)
320 319
321 320 try:
322 321 referenced_post = Post.objects.get(id=post_id)
323 322
324 323 referenced_post.referenced_posts.add(self)
325 324 referenced_post.last_edit_time = self.pub_time
326 325 referenced_post.build_refmap()
327 326 referenced_post.save(update_fields=['refmap', 'last_edit_time'])
328 327 except ObjectDoesNotExist:
329 328 pass
330 329
331 330 def connect_threads(self, opening_posts):
332 331 for opening_post in opening_posts:
333 332 threads = opening_post.get_threads().all()
334 333 for thread in threads:
335 334 if thread.can_bump():
336 335 thread.update_bump_status()
337 336
338 337 thread.last_edit_time = self.last_edit_time
339 338 thread.save(update_fields=['last_edit_time', 'bumpable'])
340 339 self.threads.add(opening_post.get_thread())
341 340
342 341 def get_tripcode(self):
343 342 if self.tripcode:
344 343 return Tripcode(self.tripcode)
345 344
346 345 def get_link_view(self):
347 346 """
348 347 Gets view of a reflink to the post.
349 348 """
350 349 result = '<a href="{}">&gt;&gt;{}</a>'.format(self.get_absolute_url(),
351 350 self.id)
352 351 if self.is_opening():
353 352 result = '<b>{}</b>'.format(result)
354 353
355 354 return result
356 355
357 356 def is_hidden(self) -> bool:
358 357 return self.hidden
359 358
360 359 def set_hidden(self, hidden):
361 360 self.hidden = hidden
@@ -1,55 +1,57 b''
1 from django.contrib.auth.context_processors import PermWrapper
2
1 3 from boards import utils
2 4
3 5
4 6 PARAMETER_TRUNCATED = 'truncated'
5 7
6 8 DIFF_TYPE_HTML = 'html'
7 9 DIFF_TYPE_JSON = 'json'
8 10
9 11
10 12 class Exporter():
11 13 @staticmethod
12 14 def export(post, request, include_last_update) -> str:
13 15 pass
14 16
15 17
16 18 class HtmlExporter(Exporter):
17 19 @staticmethod
18 20 def export(post, request, include_last_update):
19 21 if request is not None and PARAMETER_TRUNCATED in request.GET:
20 22 truncated = True
21 23 reply_link = False
22 24 else:
23 25 truncated = False
24 26 reply_link = True
25 27
26 28 return post.get_view(truncated=truncated, reply_link=reply_link,
27 moderator=utils.is_moderator(request))
29 perms=PermWrapper(request.user))
28 30
29 31
30 32 class JsonExporter(Exporter):
31 33 @staticmethod
32 34 def export(post, request, include_last_update):
33 35 post_json = {
34 36 'id': post.id,
35 37 'title': post.title,
36 38 'text': post.get_raw_text(),
37 39 }
38 40 if post.images.exists():
39 41 post_image = post.get_first_image()
40 42 post_json['image'] = post_image.image.url
41 43 post_json['image_preview'] = post_image.image.url_200x150
42 44 if include_last_update:
43 45 post_json['bump_time'] = utils.datetime_to_epoch(
44 46 post.get_thread().bump_time)
45 47 return post_json
46 48
47 49
48 50 EXPORTERS = {
49 51 DIFF_TYPE_HTML: HtmlExporter,
50 52 DIFF_TYPE_JSON: JsonExporter,
51 53 }
52 54
53 55
54 56 def get_exporter(export_type: str) -> Exporter:
55 57 return EXPORTERS[export_type]()
@@ -1,186 +1,186 b''
1 1 {% extends "boards/base.html" %}
2 2
3 3 {% load i18n %}
4 4 {% load board %}
5 5 {% load static %}
6 6 {% load tz %}
7 7
8 8 {% block head %}
9 9 <meta name="robots" content="noindex">
10 10
11 11 {% if tag %}
12 12 <title>{{ tag.name }} - {{ site_name }}</title>
13 13 {% else %}
14 14 <title>{{ site_name }}</title>
15 15 {% endif %}
16 16
17 17 {% if prev_page_link %}
18 18 <link rel="prev" href="{{ prev_page_link }}" />
19 19 {% endif %}
20 20 {% if next_page_link %}
21 21 <link rel="next" href="{{ next_page_link }}" />
22 22 {% endif %}
23 23
24 24 {% endblock %}
25 25
26 26 {% block content %}
27 27
28 28 {% get_current_language as LANGUAGE_CODE %}
29 29 {% get_current_timezone as TIME_ZONE %}
30 30
31 31 {% for banner in banners %}
32 32 <div class="post">
33 33 <div class="title">{{ banner.title }}</div>
34 34 <div>{{ banner.text }}</div>
35 35 <div>{% trans 'Related message' %}: <a href="{{ banner.post.get_absolute_url }}">>>{{ banner.post.id }}</a></div>
36 36 </div>
37 37 {% endfor %}
38 38
39 39 {% if tag %}
40 40 <div class="tag_info" style="border-bottom: solid .5ex #{{ tag.get_color }}">
41 41 {% if random_image_post %}
42 42 <div class="tag-image">
43 43 {% with image=random_image_post.images.first %}
44 44 <a href="{{ random_image_post.get_absolute_url }}"><img
45 45 src="{{ image.image.url_200x150 }}"
46 46 width="{{ image.pre_width }}"
47 47 height="{{ image.pre_height }}"/></a>
48 48 {% endwith %}
49 49 </div>
50 50 {% endif %}
51 51 <div class="tag-text-data">
52 52 <h2>
53 53 <form action="{% url 'tag' tag.name %}" method="post" class="post-button-form">
54 54 {% if is_favorite %}
55 55 <button name="method" value="unsubscribe" class="fav">β˜…</button>
56 56 {% else %}
57 57 <button name="method" value="subscribe" class="not_fav">β˜…</button>
58 58 {% endif %}
59 59 </form>
60 60 <form action="{% url 'tag' tag.name %}" method="post" class="post-button-form">
61 61 {% if is_hidden %}
62 62 <button name="method" value="unhide" class="fav">H</button>
63 63 {% else %}
64 64 <button name="method" value="hide" class="not_fav">H</button>
65 65 {% endif %}
66 66 </form>
67 67 {{ tag.get_view|safe }}
68 68 {% if moderator %}
69 69 <span class="moderator_info">| <a href="{% url 'admin:boards_tag_change' tag.id %}">{% trans 'Edit tag' %}</a></span>
70 70 {% endif %}
71 71 </h2>
72 72 {% if tag.get_description %}
73 73 <p>{{ tag.get_description|safe }}</p>
74 74 {% endif %}
75 75 <p>
76 76 {% blocktrans count count=tag.get_active_thread_count %}{{ count }} active thread{% plural %}active threads{% endblocktrans %},
77 77 {% blocktrans count count=tag.get_bumplimit_thread_count %}{{ count }} thread in bumplimit{% plural %} threads in bumplimit{% endblocktrans %},
78 78 {% blocktrans count count=tag.get_archived_thread_count %}{{ count }} archived thread{% plural %}archived threads{% endblocktrans %},
79 79 {% blocktrans count count=tag.get_post_count %}{{ count }} message{% plural %}messages{% endblocktrans %}.
80 80 </p>
81 81 {% if tag.get_all_parents %}
82 82 <p>
83 83 {% for parent in tag.get_all_parents %}
84 84 {{ parent.get_view|safe }} &gt;
85 85 {% endfor %}
86 86 {{ tag.get_view|safe }}
87 87 </p>
88 88 {% endif %}
89 89 </div>
90 90 </div>
91 91 {% endif %}
92 92
93 93 {% if threads %}
94 94 {% if prev_page_link %}
95 95 <div class="page_link">
96 96 <a href="{{ prev_page_link }}">{% trans "Previous page" %}</a>
97 97 </div>
98 98 {% endif %}
99 99
100 100 {% for thread in threads %}
101 101 <div class="thread">
102 {% post_view thread.get_opening_post perms=perms thread=thread truncated=True need_open_link=True %}
102 {% post_view thread.get_opening_post thread=thread truncated=True need_open_link=True %}
103 103 {% if not thread.archived %}
104 104 {% with last_replies=thread.get_last_replies %}
105 105 {% if last_replies %}
106 106 {% with skipped_replies_count=thread.get_skipped_replies_count %}
107 107 {% if skipped_replies_count %}
108 108 <div class="skipped_replies">
109 109 <a href="{% url 'thread' thread.get_opening_post_id %}">
110 110 {% blocktrans count count=skipped_replies_count %}Skipped {{ count }} reply. Open thread to see all replies.{% plural %}Skipped {{ count }} replies. Open thread to see all replies.{% endblocktrans %}
111 111 </a>
112 112 </div>
113 113 {% endif %}
114 114 {% endwith %}
115 115 <div class="last-replies">
116 116 {% for post in last_replies %}
117 {% post_view post perms=perms truncated=True %}
117 {% post_view post truncated=True %}
118 118 {% endfor %}
119 119 </div>
120 120 {% endif %}
121 121 {% endwith %}
122 122 {% endif %}
123 123 </div>
124 124 {% endfor %}
125 125
126 126 {% if next_page_link %}
127 127 <div class="page_link">
128 128 <a href="{{ next_page_link }}">{% trans "Next page" %}</a>
129 129 </div>
130 130 {% endif %}
131 131 {% else %}
132 132 <div class="post">
133 133 {% trans 'No threads exist. Create the first one!' %}</div>
134 134 {% endif %}
135 135
136 136 <div class="post-form-w">
137 137 <script src="{% static 'js/panel.js' %}"></script>
138 138 <div class="post-form">
139 139 <div class="form-title">{% trans "Create new thread" %}</div>
140 140 <div class="swappable-form-full">
141 141 <form enctype="multipart/form-data" method="post" id="form">{% csrf_token %}
142 142 {{ form.as_div }}
143 143 <div class="form-submit">
144 144 <input type="submit" value="{% trans "Post" %}"/>
145 145 <button id="preview-button" onclick="return false;">{% trans 'Preview' %}</button>
146 146 </div>
147 147 </form>
148 148 </div>
149 149 <div>
150 150 {% trans 'Tags must be delimited by spaces. Text or image is required.' %}
151 151 </div>
152 152 <div id="preview-text"></div>
153 153 <div><a href="{% url "staticpage" name="help" %}">{% trans 'Text syntax' %}</a></div>
154 154 <div><a href="{% url "tags" "required" %}">{% trans 'Tags' %}</a></div>
155 155 </div>
156 156 </div>
157 157
158 158 <script src="{% static 'js/form.js' %}"></script>
159 159 <script src="{% static 'js/thread_create.js' %}"></script>
160 160
161 161 {% endblock %}
162 162
163 163 {% block metapanel %}
164 164
165 165 <span class="metapanel">
166 166 <b><a href="{% url "authors" %}">{{ site_name }}</a> {{ version }}</b>
167 167 {% trans "Pages:" %}
168 168 [
169 169 {% with dividers=paginator.get_dividers %}
170 170 {% for page in paginator.get_divided_range %}
171 171 {% if page in dividers %}
172 172 …,
173 173 {% endif %}
174 174 <a
175 175 {% ifequal page current_page.number %}
176 176 class="current_page"
177 177 {% endifequal %}
178 178 href="{% page_url paginator page %}">{{ page }}</a>
179 179 {% if not forloop.last %},{% endif %}
180 180 {% endfor %}
181 181 {% endwith %}
182 182 ]
183 183 [<a href="rss/">RSS</a>]
184 184 </span>
185 185
186 186 {% endblock %}
@@ -1,70 +1,70 b''
1 1 {% extends "boards/thread.html" %}
2 2
3 3 {% load i18n %}
4 4 {% load static from staticfiles %}
5 5 {% load board %}
6 6 {% load tz %}
7 7
8 8 {% block thread_content %}
9 9 {% get_current_language as LANGUAGE_CODE %}
10 10 {% get_current_timezone as TIME_ZONE %}
11 11
12 12 <div class="tag_info">
13 13 <h2>
14 14 <form action="{% url 'thread' opening_post.id %}" method="post" class="post-button-form">
15 15 {% if is_favorite %}
16 16 <button name="method" value="unsubscribe" class="fav">β˜…</button>
17 17 {% else %}
18 18 <button name="method" value="subscribe" class="not_fav">β˜…</button>
19 19 {% endif %}
20 20 </form>
21 21 {{ opening_post.get_title_or_text }}
22 22 </h2>
23 23 </div>
24 24
25 25 {% if bumpable and thread.has_post_limit %}
26 26 <div class="bar-bg">
27 27 <div class="bar-value" style="width:{{ bumplimit_progress }}%" id="bumplimit_progress">
28 28 </div>
29 29 <div class="bar-text">
30 30 <span id="left_to_limit">{{ posts_left }}</span> {% trans 'posts to bumplimit' %}
31 31 </div>
32 32 </div>
33 33 {% endif %}
34 34
35 35 <div class="thread">
36 36 {% for post in thread.get_replies %}
37 {% post_view post perms=perms reply_link=True %}
37 {% post_view post reply_link=True %}
38 38 {% endfor %}
39 39 </div>
40 40
41 41 {% if not thread.archived %}
42 42 <div class="post-form-w">
43 43 <script src="{% static 'js/panel.js' %}"></script>
44 44 <div class="form-title">{% trans "Reply to thread" %} #{{ opening_post.id }}<span class="reply-to-message"> {% trans "to message " %} #<span id="reply-to-message-id"></span></span></div>
45 45 <div class="post-form" id="compact-form">
46 46 <div class="swappable-form-full">
47 47 <form enctype="multipart/form-data" method="post" id="form">{% csrf_token %}
48 48 <div class="compact-form-text"></div>
49 49 {{ form.as_div }}
50 50 <div class="form-submit">
51 51 <input type="submit" value="{% trans "Post" %}"/>
52 52 <button id="preview-button" onclick="return false;">{% trans 'Preview' %}</button>
53 53 </div>
54 54 </form>
55 55 </div>
56 56 <div id="preview-text"></div>
57 57 <div><a href="{% url "staticpage" name="help" %}">
58 58 {% trans 'Text syntax' %}</a></div>
59 59 <div><a id="form-close-button" href="#" onClick="resetFormPosition(); return false;">{% trans 'Close form' %}</a></div>
60 60 </div>
61 61 </div>
62 62
63 63 <script src="{% static 'js/jquery.form.min.js' %}"></script>
64 64 {% endif %}
65 65
66 66 <script src="{% static 'js/form.js' %}"></script>
67 67 <script src="{% static 'js/thread.js' %}"></script>
68 68 <script src="{% static 'js/thread_update.js' %}"></script>
69 69 <script src="{% static 'js/3party/centrifuge.js' %}"></script>
70 70 {% endblock %}
@@ -1,49 +1,50 b''
1 1 import re
2 2 from django.shortcuts import get_object_or_404
3 3 from django import template
4 4
5 5
6 6 IMG_ACTION_URL = '[<a href="{}">{}</a>]'
7 7
8 8
9 9 register = template.Library()
10 10
11 11 actions = [
12 12 {
13 13 'name': 'google',
14 14 'link': 'http://google.com/searchbyimage?image_url=%s',
15 15 },
16 16 {
17 17 'name': 'iqdb',
18 18 'link': 'http://iqdb.org/?url=%s',
19 19 },
20 20 ]
21 21
22 22
23 23 @register.simple_tag(name='post_url')
24 24 def post_url(*args, **kwargs):
25 25 post_id = args[0]
26 26
27 27 post = get_object_or_404('Post', id=post_id)
28 28
29 29 return post.get_absolute_url()
30 30
31 31
32 32 @register.simple_tag(name='image_actions')
33 33 def image_actions(*args, **kwargs):
34 34 image_link = args[0]
35 35 if len(args) > 1:
36 36 image_link = 'http://' + args[1] + image_link # TODO https?
37 37
38 38 return ', '.join([IMG_ACTION_URL.format(
39 39 action['link'] % image_link, action['name']) for action in actions])
40 40
41 41
42 @register.simple_tag(name='post_view')
43 def post_view(post, *args, **kwargs):
42 @register.simple_tag(name='post_view', takes_context=True)
43 def post_view(context, post, *args, **kwargs):
44 kwargs['perms'] = context['perms']
44 45 return post.get_view(*args, **kwargs)
45 46
46 47 @register.simple_tag(name='page_url')
47 48 def page_url(paginator, page_number, *args, **kwargs):
48 49 if paginator.supports_urls():
49 50 return paginator.get_page_url(page_number)
General Comments 0
You need to be logged in to leave comments. Login now