##// END OF EJS Templates
Connect replies to the new post link format. Strip quote tag.
neko259 -
r753:b7096f08 default
parent child Browse files
Show More
@@ -1,152 +1,152 b''
1 # coding=utf-8
1 # coding=utf-8
2
2
3 import re
3 import re
4 import bbcode
4 import bbcode
5
5
6 import boards
6 import boards
7
7
8
8
9 __author__ = 'neko259'
9 __author__ = 'neko259'
10
10
11
11
12 REFLINK_PATTERN = re.compile(r'\d+')
12 REFLINK_PATTERN = re.compile(r'\d+')
13
13
14
14
15 class TextFormatter():
15 class TextFormatter():
16 """
16 """
17 An interface for formatter that can be used in the text format panel
17 An interface for formatter that can be used in the text format panel
18 """
18 """
19
19
20 def __init__(self):
20 def __init__(self):
21 pass
21 pass
22
22
23 name = ''
23 name = ''
24
24
25 # Left and right tags for the button preview
25 # Left and right tags for the button preview
26 preview_left = ''
26 preview_left = ''
27 preview_right = ''
27 preview_right = ''
28
28
29 # Left and right characters for the textarea input
29 # Left and right characters for the textarea input
30 format_left = ''
30 format_left = ''
31 format_right = ''
31 format_right = ''
32
32
33
33
34 class AutolinkPattern():
34 class AutolinkPattern():
35 def handleMatch(self, m):
35 def handleMatch(self, m):
36 link_element = etree.Element('a')
36 link_element = etree.Element('a')
37 href = m.group(2)
37 href = m.group(2)
38 link_element.set('href', href)
38 link_element.set('href', href)
39 link_element.text = href
39 link_element.text = href
40
40
41 return link_element
41 return link_element
42
42
43
43
44 class QuotePattern(TextFormatter):
44 class QuotePattern(TextFormatter):
45 name = 'q'
45 name = 'q'
46 preview_left = '<span class="multiquote">'
46 preview_left = '<span class="multiquote">'
47 preview_right = '</span>'
47 preview_right = '</span>'
48
48
49 format_left = '[quote]'
49 format_left = '[quote]'
50 format_right = '[/quote]'
50 format_right = '[/quote]'
51
51
52
52
53 class SpoilerPattern(TextFormatter):
53 class SpoilerPattern(TextFormatter):
54 name = 'spoiler'
54 name = 'spoiler'
55 preview_left = '<span class="spoiler">'
55 preview_left = '<span class="spoiler">'
56 preview_right = '</span>'
56 preview_right = '</span>'
57
57
58 format_left = '[spoiler]'
58 format_left = '[spoiler]'
59 format_right = '[/spoiler]'
59 format_right = '[/spoiler]'
60
60
61 def handleMatch(self, m):
61 def handleMatch(self, m):
62 quote_element = etree.Element('span')
62 quote_element = etree.Element('span')
63 quote_element.set('class', 'spoiler')
63 quote_element.set('class', 'spoiler')
64 quote_element.text = m.group(2)
64 quote_element.text = m.group(2)
65
65
66 return quote_element
66 return quote_element
67
67
68
68
69 class CommentPattern(TextFormatter):
69 class CommentPattern(TextFormatter):
70 name = ''
70 name = ''
71 preview_left = '<span class="comment">// '
71 preview_left = '<span class="comment">// '
72 preview_right = '</span>'
72 preview_right = '</span>'
73
73
74 format_left = '[comment]'
74 format_left = '[comment]'
75 format_right = '[/comment]'
75 format_right = '[/comment]'
76
76
77
77
78 class StrikeThroughPattern(TextFormatter):
78 class StrikeThroughPattern(TextFormatter):
79 name = 's'
79 name = 's'
80 preview_left = '<span class="strikethrough">'
80 preview_left = '<span class="strikethrough">'
81 preview_right = '</span>'
81 preview_right = '</span>'
82
82
83 format_left = '[s]'
83 format_left = '[s]'
84 format_right = '[/s]'
84 format_right = '[/s]'
85
85
86
86
87 class ItalicPattern(TextFormatter):
87 class ItalicPattern(TextFormatter):
88 name = 'i'
88 name = 'i'
89 preview_left = '<i>'
89 preview_left = '<i>'
90 preview_right = '</i>'
90 preview_right = '</i>'
91
91
92 format_left = '[i]'
92 format_left = '[i]'
93 format_right = '[/i]'
93 format_right = '[/i]'
94
94
95
95
96 class BoldPattern(TextFormatter):
96 class BoldPattern(TextFormatter):
97 name = 'b'
97 name = 'b'
98 preview_left = '<b>'
98 preview_left = '<b>'
99 preview_right = '</b>'
99 preview_right = '</b>'
100
100
101 format_left = '[b]'
101 format_left = '[b]'
102 format_right = '[/b]'
102 format_right = '[/b]'
103
103
104
104
105 class CodePattern(TextFormatter):
105 class CodePattern(TextFormatter):
106 name = 'code'
106 name = 'code'
107 preview_left = '<code>'
107 preview_left = '<code>'
108 preview_right = '</code>'
108 preview_right = '</code>'
109
109
110 format_left = '[code]'
110 format_left = '[code]'
111 format_right = '[/code]'
111 format_right = '[/code]'
112
112
113
113
114 def render_reflink(tag_name, value, options, parent, context):
114 def render_reflink(tag_name, value, options, parent, context):
115 if not REFLINK_PATTERN.match(value):
115 if not REFLINK_PATTERN.match(value):
116 return u'>>%s' % value
116 return u'>>%s' % value
117
117
118 post_id = int(value)
118 post_id = int(value)
119
119
120 posts = boards.models.Post.objects.filter(id=post_id)
120 posts = boards.models.Post.objects.filter(id=post_id)
121 if posts.exists():
121 if posts.exists():
122 post = posts[0]
122 post = posts[0]
123
123
124 return u'<a href=%s>&gt;&gt;%s</a>' % (post.get_url(), post_id)
124 return u'<a href=%s>&gt;&gt;%s</a>' % (post.get_url(), post_id)
125 else:
125 else:
126 return u'>>%s' % value
126 return u'>>%s' % value
127
127
128
128
129 def bbcode_extended(markup):
129 def bbcode_extended(markup):
130 parser = bbcode.Parser()
130 parser = bbcode.Parser()
131 parser.add_formatter('post', render_reflink, strip=True)
131 parser.add_formatter('post', render_reflink, strip=True)
132 parser.add_simple_formatter('quote',
132 parser.add_simple_formatter('quote',
133 u'<span class="multiquote">%(value)s</span>')
133 u'<span class="multiquote">%(value)s</span>', strip=True)
134 parser.add_simple_formatter('comment',
134 parser.add_simple_formatter('comment',
135 u'<span class="comment">//%(value)s</span>')
135 u'<span class="comment">//%(value)s</span>')
136 parser.add_simple_formatter('spoiler',
136 parser.add_simple_formatter('spoiler',
137 u'<span class="spoiler">%(value)s</span>')
137 u'<span class="spoiler">%(value)s</span>')
138 parser.add_simple_formatter('s',
138 parser.add_simple_formatter('s',
139 u'<span class="strikethrough">%(value)s</span>')
139 u'<span class="strikethrough">%(value)s</span>')
140 parser.add_simple_formatter('code',
140 parser.add_simple_formatter('code',
141 u'<pre><code>%(value)s</pre></code>')
141 u'<pre><code>%(value)s</pre></code>')
142 return parser.format(markup)
142 return parser.format(markup)
143
143
144 formatters = [
144 formatters = [
145 QuotePattern,
145 QuotePattern,
146 SpoilerPattern,
146 SpoilerPattern,
147 ItalicPattern,
147 ItalicPattern,
148 BoldPattern,
148 BoldPattern,
149 CommentPattern,
149 CommentPattern,
150 StrikeThroughPattern,
150 StrikeThroughPattern,
151 CodePattern,
151 CodePattern,
152 ]
152 ]
@@ -1,343 +1,343 b''
1 from datetime import datetime, timedelta, date
1 from datetime import datetime, timedelta, date
2 from datetime import time as dtime
2 from datetime import time as dtime
3 import logging
3 import logging
4 import re
4 import re
5
5
6 from django.core.cache import cache
6 from django.core.cache import cache
7 from django.core.urlresolvers import reverse
7 from django.core.urlresolvers import reverse
8 from django.db import models, transaction
8 from django.db import models, transaction
9 from django.template.loader import render_to_string
9 from django.template.loader import render_to_string
10 from django.utils import timezone
10 from django.utils import timezone
11 from markupfield.fields import MarkupField
11 from markupfield.fields import MarkupField
12
12
13 from boards.models import PostImage
13 from boards.models import PostImage
14 from boards.models.base import Viewable
14 from boards.models.base import Viewable
15 from boards.models.thread import Thread
15 from boards.models.thread import Thread
16
16
17
17
18 APP_LABEL_BOARDS = 'boards'
18 APP_LABEL_BOARDS = 'boards'
19
19
20 CACHE_KEY_PPD = 'ppd'
20 CACHE_KEY_PPD = 'ppd'
21 CACHE_KEY_POST_URL = 'post_url'
21 CACHE_KEY_POST_URL = 'post_url'
22
22
23 POSTS_PER_DAY_RANGE = 7
23 POSTS_PER_DAY_RANGE = 7
24
24
25 BAN_REASON_AUTO = 'Auto'
25 BAN_REASON_AUTO = 'Auto'
26
26
27 IMAGE_THUMB_SIZE = (200, 150)
27 IMAGE_THUMB_SIZE = (200, 150)
28
28
29 TITLE_MAX_LENGTH = 200
29 TITLE_MAX_LENGTH = 200
30
30
31 DEFAULT_MARKUP_TYPE = 'bbcode'
31 DEFAULT_MARKUP_TYPE = 'bbcode'
32
32
33 # TODO This should be removed
33 # TODO This should be removed
34 NO_IP = '0.0.0.0'
34 NO_IP = '0.0.0.0'
35
35
36 # TODO Real user agent should be saved instead of this
36 # TODO Real user agent should be saved instead of this
37 UNKNOWN_UA = ''
37 UNKNOWN_UA = ''
38
38
39 REGEX_REPLY = re.compile(r'&gt;&gt;(\d+)')
39 REGEX_REPLY = re.compile(ur'\[post\](\d+)\[/post\]')
40
40
41 logger = logging.getLogger(__name__)
41 logger = logging.getLogger(__name__)
42
42
43
43
44 class PostManager(models.Manager):
44 class PostManager(models.Manager):
45 def create_post(self, title, text, image=None, thread=None, ip=NO_IP,
45 def create_post(self, title, text, image=None, thread=None, ip=NO_IP,
46 tags=None):
46 tags=None):
47 """
47 """
48 Creates new post
48 Creates new post
49 """
49 """
50
50
51 posting_time = timezone.now()
51 posting_time = timezone.now()
52 if not thread:
52 if not thread:
53 thread = Thread.objects.create(bump_time=posting_time,
53 thread = Thread.objects.create(bump_time=posting_time,
54 last_edit_time=posting_time)
54 last_edit_time=posting_time)
55 new_thread = True
55 new_thread = True
56 else:
56 else:
57 thread.bump()
57 thread.bump()
58 thread.last_edit_time = posting_time
58 thread.last_edit_time = posting_time
59 thread.save()
59 thread.save()
60 new_thread = False
60 new_thread = False
61
61
62 post = self.create(title=title,
62 post = self.create(title=title,
63 text=text,
63 text=text,
64 pub_time=posting_time,
64 pub_time=posting_time,
65 thread_new=thread,
65 thread_new=thread,
66 poster_ip=ip,
66 poster_ip=ip,
67 poster_user_agent=UNKNOWN_UA, # TODO Get UA at
67 poster_user_agent=UNKNOWN_UA, # TODO Get UA at
68 # last!
68 # last!
69 last_edit_time=posting_time)
69 last_edit_time=posting_time)
70
70
71 if image:
71 if image:
72 post_image = PostImage.objects.create(image=image)
72 post_image = PostImage.objects.create(image=image)
73 post.images.add(post_image)
73 post.images.add(post_image)
74 logger.info('Created image #%d for post #%d' % (post_image.id,
74 logger.info('Created image #%d for post #%d' % (post_image.id,
75 post.id))
75 post.id))
76
76
77 thread.replies.add(post)
77 thread.replies.add(post)
78 if tags:
78 if tags:
79 map(thread.add_tag, tags)
79 map(thread.add_tag, tags)
80
80
81 if new_thread:
81 if new_thread:
82 Thread.objects.process_oldest_threads()
82 Thread.objects.process_oldest_threads()
83 self.connect_replies(post)
83 self.connect_replies(post)
84
84
85 logger.info('Created post #%d with title %s' % (post.id,
85 logger.info('Created post #%d with title %s' % (post.id,
86 post.get_title()))
86 post.get_title()))
87
87
88 return post
88 return post
89
89
90 def delete_post(self, post):
90 def delete_post(self, post):
91 """
91 """
92 Deletes post and update or delete its thread
92 Deletes post and update or delete its thread
93 """
93 """
94
94
95 post_id = post.id
95 post_id = post.id
96
96
97 thread = post.get_thread()
97 thread = post.get_thread()
98
98
99 if post.is_opening():
99 if post.is_opening():
100 thread.delete()
100 thread.delete()
101 else:
101 else:
102 thread.last_edit_time = timezone.now()
102 thread.last_edit_time = timezone.now()
103 thread.save()
103 thread.save()
104
104
105 post.delete()
105 post.delete()
106
106
107 logger.info('Deleted post #%d (%s)' % (post_id, post.get_title()))
107 logger.info('Deleted post #%d (%s)' % (post_id, post.get_title()))
108
108
109 def delete_posts_by_ip(self, ip):
109 def delete_posts_by_ip(self, ip):
110 """
110 """
111 Deletes all posts of the author with same IP
111 Deletes all posts of the author with same IP
112 """
112 """
113
113
114 posts = self.filter(poster_ip=ip)
114 posts = self.filter(poster_ip=ip)
115 map(self.delete_post, posts)
115 map(self.delete_post, posts)
116
116
117 def connect_replies(self, post):
117 def connect_replies(self, post):
118 """
118 """
119 Connects replies to a post to show them as a reflink map
119 Connects replies to a post to show them as a reflink map
120 """
120 """
121
121
122 for reply_number in re.finditer(REGEX_REPLY, post.text.rendered):
122 for reply_number in re.finditer(REGEX_REPLY, post.text.raw):
123 post_id = reply_number.group(1)
123 post_id = reply_number.group(1)
124 ref_post = self.filter(id=post_id)
124 ref_post = self.filter(id=post_id)
125 if ref_post.count() > 0:
125 if ref_post.count() > 0:
126 referenced_post = ref_post[0]
126 referenced_post = ref_post[0]
127 referenced_post.referenced_posts.add(post)
127 referenced_post.referenced_posts.add(post)
128 referenced_post.last_edit_time = post.pub_time
128 referenced_post.last_edit_time = post.pub_time
129 referenced_post.build_refmap()
129 referenced_post.build_refmap()
130 referenced_post.save(update_fields=['refmap', 'last_edit_time'])
130 referenced_post.save(update_fields=['refmap', 'last_edit_time'])
131
131
132 referenced_thread = referenced_post.get_thread()
132 referenced_thread = referenced_post.get_thread()
133 referenced_thread.last_edit_time = post.pub_time
133 referenced_thread.last_edit_time = post.pub_time
134 referenced_thread.save(update_fields=['last_edit_time'])
134 referenced_thread.save(update_fields=['last_edit_time'])
135
135
136 def get_posts_per_day(self):
136 def get_posts_per_day(self):
137 """
137 """
138 Gets average count of posts per day for the last 7 days
138 Gets average count of posts per day for the last 7 days
139 """
139 """
140
140
141 day_end = date.today()
141 day_end = date.today()
142 day_start = day_end - timedelta(POSTS_PER_DAY_RANGE)
142 day_start = day_end - timedelta(POSTS_PER_DAY_RANGE)
143
143
144 cache_key = CACHE_KEY_PPD + str(day_end)
144 cache_key = CACHE_KEY_PPD + str(day_end)
145 ppd = cache.get(cache_key)
145 ppd = cache.get(cache_key)
146 if ppd:
146 if ppd:
147 return ppd
147 return ppd
148
148
149 day_time_start = timezone.make_aware(datetime.combine(
149 day_time_start = timezone.make_aware(datetime.combine(
150 day_start, dtime()), timezone.get_current_timezone())
150 day_start, dtime()), timezone.get_current_timezone())
151 day_time_end = timezone.make_aware(datetime.combine(
151 day_time_end = timezone.make_aware(datetime.combine(
152 day_end, dtime()), timezone.get_current_timezone())
152 day_end, dtime()), timezone.get_current_timezone())
153
153
154 posts_per_period = float(self.filter(
154 posts_per_period = float(self.filter(
155 pub_time__lte=day_time_end,
155 pub_time__lte=day_time_end,
156 pub_time__gte=day_time_start).count())
156 pub_time__gte=day_time_start).count())
157
157
158 ppd = posts_per_period / POSTS_PER_DAY_RANGE
158 ppd = posts_per_period / POSTS_PER_DAY_RANGE
159
159
160 cache.set(cache_key, ppd)
160 cache.set(cache_key, ppd)
161 return ppd
161 return ppd
162
162
163
163
164 class Post(models.Model, Viewable):
164 class Post(models.Model, Viewable):
165 """A post is a message."""
165 """A post is a message."""
166
166
167 objects = PostManager()
167 objects = PostManager()
168
168
169 class Meta:
169 class Meta:
170 app_label = APP_LABEL_BOARDS
170 app_label = APP_LABEL_BOARDS
171 ordering = ('id',)
171 ordering = ('id',)
172
172
173 title = models.CharField(max_length=TITLE_MAX_LENGTH)
173 title = models.CharField(max_length=TITLE_MAX_LENGTH)
174 pub_time = models.DateTimeField()
174 pub_time = models.DateTimeField()
175 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
175 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
176 escape_html=False)
176 escape_html=False)
177
177
178 images = models.ManyToManyField(PostImage, null=True, blank=True,
178 images = models.ManyToManyField(PostImage, null=True, blank=True,
179 related_name='ip+', db_index=True)
179 related_name='ip+', db_index=True)
180
180
181 poster_ip = models.GenericIPAddressField()
181 poster_ip = models.GenericIPAddressField()
182 poster_user_agent = models.TextField()
182 poster_user_agent = models.TextField()
183
183
184 thread_new = models.ForeignKey('Thread', null=True, default=None,
184 thread_new = models.ForeignKey('Thread', null=True, default=None,
185 db_index=True)
185 db_index=True)
186 last_edit_time = models.DateTimeField()
186 last_edit_time = models.DateTimeField()
187
187
188 referenced_posts = models.ManyToManyField('Post', symmetrical=False,
188 referenced_posts = models.ManyToManyField('Post', symmetrical=False,
189 null=True,
189 null=True,
190 blank=True, related_name='rfp+',
190 blank=True, related_name='rfp+',
191 db_index=True)
191 db_index=True)
192 refmap = models.TextField(null=True, blank=True)
192 refmap = models.TextField(null=True, blank=True)
193
193
194 def __unicode__(self):
194 def __unicode__(self):
195 return '#' + str(self.id) + ' ' + self.title + ' (' + \
195 return '#' + str(self.id) + ' ' + self.title + ' (' + \
196 self.text.raw[:50] + ')'
196 self.text.raw[:50] + ')'
197
197
198 def get_title(self):
198 def get_title(self):
199 """
199 """
200 Gets original post title or part of its text.
200 Gets original post title or part of its text.
201 """
201 """
202
202
203 title = self.title
203 title = self.title
204 if not title:
204 if not title:
205 title = self.text.rendered
205 title = self.text.rendered
206
206
207 return title
207 return title
208
208
209 def build_refmap(self):
209 def build_refmap(self):
210 """
210 """
211 Builds a replies map string from replies list. This is a cache to stop
211 Builds a replies map string from replies list. This is a cache to stop
212 the server from recalculating the map on every post show.
212 the server from recalculating the map on every post show.
213 """
213 """
214 map_string = ''
214 map_string = ''
215
215
216 first = True
216 first = True
217 for refpost in self.referenced_posts.all():
217 for refpost in self.referenced_posts.all():
218 if not first:
218 if not first:
219 map_string += ', '
219 map_string += ', '
220 map_string += '<a href="%s">&gt;&gt;%s</a>' % (refpost.get_url(),
220 map_string += '<a href="%s">&gt;&gt;%s</a>' % (refpost.get_url(),
221 refpost.id)
221 refpost.id)
222 first = False
222 first = False
223
223
224 self.refmap = map_string
224 self.refmap = map_string
225
225
226 def get_sorted_referenced_posts(self):
226 def get_sorted_referenced_posts(self):
227 return self.refmap
227 return self.refmap
228
228
229 def is_referenced(self):
229 def is_referenced(self):
230 return len(self.refmap) > 0
230 return len(self.refmap) > 0
231
231
232 def is_opening(self):
232 def is_opening(self):
233 """
233 """
234 Checks if this is an opening post or just a reply.
234 Checks if this is an opening post or just a reply.
235 """
235 """
236
236
237 return self.get_thread().get_opening_post_id() == self.id
237 return self.get_thread().get_opening_post_id() == self.id
238
238
239 @transaction.atomic
239 @transaction.atomic
240 def add_tag(self, tag):
240 def add_tag(self, tag):
241 edit_time = timezone.now()
241 edit_time = timezone.now()
242
242
243 thread = self.get_thread()
243 thread = self.get_thread()
244 thread.add_tag(tag)
244 thread.add_tag(tag)
245 self.last_edit_time = edit_time
245 self.last_edit_time = edit_time
246 self.save(update_fields=['last_edit_time'])
246 self.save(update_fields=['last_edit_time'])
247
247
248 thread.last_edit_time = edit_time
248 thread.last_edit_time = edit_time
249 thread.save(update_fields=['last_edit_time'])
249 thread.save(update_fields=['last_edit_time'])
250
250
251 @transaction.atomic
251 @transaction.atomic
252 def remove_tag(self, tag):
252 def remove_tag(self, tag):
253 edit_time = timezone.now()
253 edit_time = timezone.now()
254
254
255 thread = self.get_thread()
255 thread = self.get_thread()
256 thread.remove_tag(tag)
256 thread.remove_tag(tag)
257 self.last_edit_time = edit_time
257 self.last_edit_time = edit_time
258 self.save(update_fields=['last_edit_time'])
258 self.save(update_fields=['last_edit_time'])
259
259
260 thread.last_edit_time = edit_time
260 thread.last_edit_time = edit_time
261 thread.save(update_fields=['last_edit_time'])
261 thread.save(update_fields=['last_edit_time'])
262
262
263 def get_url(self, thread=None):
263 def get_url(self, thread=None):
264 """
264 """
265 Gets full url to the post.
265 Gets full url to the post.
266 """
266 """
267
267
268 cache_key = CACHE_KEY_POST_URL + str(self.id)
268 cache_key = CACHE_KEY_POST_URL + str(self.id)
269 link = cache.get(cache_key)
269 link = cache.get(cache_key)
270
270
271 if not link:
271 if not link:
272 if not thread:
272 if not thread:
273 thread = self.get_thread()
273 thread = self.get_thread()
274
274
275 opening_id = thread.get_opening_post_id()
275 opening_id = thread.get_opening_post_id()
276
276
277 if self.id != opening_id:
277 if self.id != opening_id:
278 link = reverse('thread', kwargs={
278 link = reverse('thread', kwargs={
279 'post_id': opening_id}) + '#' + str(self.id)
279 'post_id': opening_id}) + '#' + str(self.id)
280 else:
280 else:
281 link = reverse('thread', kwargs={'post_id': self.id})
281 link = reverse('thread', kwargs={'post_id': self.id})
282
282
283 cache.set(cache_key, link)
283 cache.set(cache_key, link)
284
284
285 return link
285 return link
286
286
287 def get_thread(self):
287 def get_thread(self):
288 """
288 """
289 Gets post's thread.
289 Gets post's thread.
290 """
290 """
291
291
292 return self.thread_new
292 return self.thread_new
293
293
294 def get_referenced_posts(self):
294 def get_referenced_posts(self):
295 return self.referenced_posts.only('id', 'thread_new')
295 return self.referenced_posts.only('id', 'thread_new')
296
296
297 def get_text(self):
297 def get_text(self):
298 return self.text
298 return self.text
299
299
300 def get_view(self, moderator=False, need_open_link=False,
300 def get_view(self, moderator=False, need_open_link=False,
301 truncated=False, *args, **kwargs):
301 truncated=False, *args, **kwargs):
302 if 'is_opening' in kwargs:
302 if 'is_opening' in kwargs:
303 is_opening = kwargs['is_opening']
303 is_opening = kwargs['is_opening']
304 else:
304 else:
305 is_opening = self.is_opening()
305 is_opening = self.is_opening()
306
306
307 if 'thread' in kwargs:
307 if 'thread' in kwargs:
308 thread = kwargs['thread']
308 thread = kwargs['thread']
309 else:
309 else:
310 thread = self.get_thread()
310 thread = self.get_thread()
311
311
312 if 'can_bump' in kwargs:
312 if 'can_bump' in kwargs:
313 can_bump = kwargs['can_bump']
313 can_bump = kwargs['can_bump']
314 else:
314 else:
315 can_bump = thread.can_bump()
315 can_bump = thread.can_bump()
316
316
317 if is_opening:
317 if is_opening:
318 opening_post_id = self.id
318 opening_post_id = self.id
319 else:
319 else:
320 opening_post_id = thread.get_opening_post_id()
320 opening_post_id = thread.get_opening_post_id()
321
321
322 return render_to_string('boards/post.html', {
322 return render_to_string('boards/post.html', {
323 'post': self,
323 'post': self,
324 'moderator': moderator,
324 'moderator': moderator,
325 'is_opening': is_opening,
325 'is_opening': is_opening,
326 'thread': thread,
326 'thread': thread,
327 'bumpable': can_bump,
327 'bumpable': can_bump,
328 'need_open_link': need_open_link,
328 'need_open_link': need_open_link,
329 'truncated': truncated,
329 'truncated': truncated,
330 'opening_post_id': opening_post_id,
330 'opening_post_id': opening_post_id,
331 })
331 })
332
332
333 def get_first_image(self):
333 def get_first_image(self):
334 return self.images.earliest('id')
334 return self.images.earliest('id')
335
335
336 def delete(self, using=None):
336 def delete(self, using=None):
337 """
337 """
338 Deletes all post images and the post itself.
338 Deletes all post images and the post itself.
339 """
339 """
340
340
341 self.images.all().delete()
341 self.images.all().delete()
342
342
343 super(Post, self).delete(using)
343 super(Post, self).delete(using)
General Comments 0
You need to be logged in to leave comments. Login now