thread.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. from django.core.urlresolvers import reverse
  2. from django.db import models
  3. from django.dispatch import receiver
  4. from misago.conf import settings
  5. from misago.core.shortcuts import paginate
  6. from misago.core.utils import slugify
  7. __all__ = ['ANNOUNCEMENT', 'PINNED', 'Thread']
  8. ANNOUNCEMENT = 2
  9. PINNED = 1
  10. class Thread(models.Model):
  11. forum = models.ForeignKey('misago_forums.Forum')
  12. weight = models.PositiveIntegerField(default=0, db_index=True)
  13. label = models.ForeignKey('misago_threads.Label',
  14. null=True, blank=True,
  15. on_delete=models.SET_NULL)
  16. title = models.CharField(max_length=255)
  17. slug = models.CharField(max_length=255)
  18. replies = models.PositiveIntegerField(default=0, db_index=True)
  19. has_reported_posts = models.BooleanField(default=False)
  20. has_moderated_posts = models.BooleanField(default=False)
  21. has_hidden_posts = models.BooleanField(default=False)
  22. has_events = models.BooleanField(default=False)
  23. started_on = models.DateTimeField()
  24. first_post = models.ForeignKey('misago_threads.Post', related_name='+',
  25. null=True, blank=True,
  26. on_delete=models.SET_NULL)
  27. starter = models.ForeignKey(settings.AUTH_USER_MODEL,
  28. null=True, blank=True,
  29. on_delete=models.SET_NULL)
  30. starter_name = models.CharField(max_length=255)
  31. starter_slug = models.CharField(max_length=255)
  32. last_post_on = models.DateTimeField(db_index=True)
  33. last_post = models.ForeignKey('misago_threads.Post', related_name='+',
  34. null=True, blank=True,
  35. on_delete=models.SET_NULL)
  36. last_poster = models.ForeignKey(settings.AUTH_USER_MODEL,
  37. related_name='last_poster_set',
  38. null=True, blank=True,
  39. on_delete=models.SET_NULL)
  40. last_poster_name = models.CharField(max_length=255, null=True, blank=True)
  41. last_poster_slug = models.CharField(max_length=255, null=True, blank=True)
  42. is_poll = models.BooleanField(default=False)
  43. is_moderated = models.BooleanField(default=False, db_index=True)
  44. is_hidden = models.BooleanField(default=False)
  45. is_closed = models.BooleanField(default=False)
  46. class Meta:
  47. index_together = [
  48. ['forum', 'weight'],
  49. ['forum', 'weight', 'id'],
  50. ['forum', 'weight', 'last_post_on'],
  51. ['forum', 'weight', 'replies'],
  52. ]
  53. def __unicode__(self):
  54. return self.title
  55. def delete(self, *args, **kwargs):
  56. from misago.threads.signals import delete_thread
  57. delete_thread.send(sender=self)
  58. super(Thread, self).delete(*args, **kwargs)
  59. def merge(self, other_thread):
  60. if self.pk == other_thread.pk:
  61. raise ValueError("thread can't be merged with itself")
  62. from misago.threads.signals import merge_thread
  63. merge_thread.send(sender=self, other_thread=other_thread)
  64. def move(self, new_forum):
  65. from misago.threads.signals import move_thread
  66. self.forum = new_forum
  67. move_thread.send(sender=self)
  68. def synchronize(self):
  69. self.replies = self.post_set.filter(is_moderated=False).count()
  70. if self.replies > 0:
  71. self.replies -= 1
  72. reported_post_qs = self.post_set.filter(is_reported=True)[:1]
  73. self.has_reported_posts = reported_post_qs.exists()
  74. moderated_post_qs = self.post_set.filter(is_moderated=True)[:1]
  75. self.has_moderated_posts = moderated_post_qs.exists()
  76. hidden_post_qs = self.post_set.filter(is_hidden=True)[:1]
  77. self.has_hidden_posts = hidden_post_qs.exists()
  78. self.has_events = self.event_set.exists()
  79. first_post = self.post_set.order_by('id')[:1][0]
  80. self.set_first_post(first_post)
  81. self.is_moderated = first_post.is_moderated
  82. self.is_hidden = first_post.is_hidden
  83. last_post_qs = self.post_set.filter(is_moderated=False).order_by('-id')
  84. last_post = last_post_qs[:1]
  85. if last_post:
  86. self.set_last_post(last_post[0])
  87. else:
  88. self.set_last_post(first_post)
  89. @property
  90. def is_announcement(self):
  91. return self.weight == ANNOUNCEMENT
  92. @property
  93. def is_pinned(self):
  94. return self.weight == PINNED
  95. @property
  96. def link_prefix(self):
  97. if self.forum.special_role == 'private_threads':
  98. return 'private_thread'
  99. else:
  100. return 'thread'
  101. def get_url(self, suffix=None):
  102. link = 'misago:%s' % self.link_prefix
  103. if suffix:
  104. link = '%s_%s' % (link, suffix)
  105. return reverse(link, kwargs={
  106. 'thread_slug': self.slug,
  107. 'thread_id': self.id
  108. })
  109. def get_absolute_url(self):
  110. return self.get_url()
  111. def get_new_reply_url(self):
  112. return self.get_url('new')
  113. def get_last_reply_url(self):
  114. return self.get_url('last')
  115. def set_title(self, title):
  116. self.title = title
  117. self.slug = slugify(title)
  118. def set_first_post(self, post):
  119. self.started_on = post.posted_on
  120. self.first_post = post
  121. self.starter = post.poster
  122. self.starter_name = post.poster_name
  123. if post.poster:
  124. self.starter_slug = post.poster.slug
  125. else:
  126. self.starter_slug = slugify(post.poster_name)
  127. self.is_moderated = post.is_moderated
  128. self.is_hidden = post.is_hidden
  129. def set_last_post(self, post):
  130. self.last_post_on = post.posted_on
  131. self.last_post = post
  132. self.last_poster = post.poster
  133. self.last_poster_name = post.poster_name
  134. if post.poster:
  135. self.last_poster_slug = post.poster.slug
  136. else:
  137. self.last_poster_slug = slugify(post.poster_name)