thread.py 5.6 KB

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