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