thread.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  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. prefix = models.ForeignKey('misago_threads.Prefix',
  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. counted_criteria = {'is_hidden': False, 'is_moderated': False}
  67. self.replies = self.post_set.filter(**counted_criteria).count()
  68. if self.replies > 0:
  69. self.replies -= 1
  70. reported_post_qs = self.post_set.filter(is_reported=True)[:1]
  71. self.has_reported_posts = reported_post_qs.exists()
  72. moderated_post_qs = self.post_set.filter(is_moderated=True)[:1]
  73. self.has_moderated_posts = moderated_post_qs.exists()
  74. hidden_post_qs = self.post_set.filter(is_hidden=True)[:1]
  75. self.has_hidden_posts = hidden_post_qs.exists()
  76. first_post = self.post_set.order_by('id')[:1][0]
  77. self.set_first_post(first_post)
  78. last_post_qs = self.post_set.filter(**counted_criteria).order_by('-id')
  79. last_post = last_post_qs[:1]
  80. if last_post:
  81. self.set_last_post(last_post[0])
  82. else:
  83. self.set_last_post(first_post)
  84. @property
  85. def is_announcement(self):
  86. return self.weight == ANNOUNCEMENT
  87. @property
  88. def is_pinned(self):
  89. return self.weight == PINNED
  90. @property
  91. def link_prefix(self):
  92. if self.forum.special_role == 'private_threads':
  93. return 'private_thread'
  94. else:
  95. return 'thread'
  96. def get_url(self, suffix=None):
  97. link = 'misago:%s' % self.link_prefix
  98. if suffix:
  99. link = '%s_%s' % (link, suffix)
  100. return reverse(link, kwargs={
  101. 'thread_slug': self.slug,
  102. 'thread_id': self.id
  103. })
  104. def get_absolute_url(self):
  105. return self.get_url()
  106. def get_new_reply_url(self):
  107. return self.get_url('new')
  108. def get_last_reply_url(self):
  109. return self.get_url('last')
  110. def set_title(self, title):
  111. self.title = title
  112. self.slug = slugify(title)
  113. def set_first_post(self, post):
  114. self.started_on = post.posted_on
  115. self.first_post = post
  116. self.starter = post.poster
  117. self.starter_name = post.poster_name
  118. if post.poster:
  119. self.starter_slug = post.poster.slug
  120. else:
  121. self.starter_slug = slugify(post.poster_name)
  122. self.is_moderated = post.is_moderated
  123. self.is_hidden = post.is_hidden
  124. def set_last_post(self, post):
  125. self.last_post_on = post.posted_on
  126. self.last_post = post
  127. self.last_poster = post.poster
  128. self.last_poster_name = post.poster_name
  129. if post.poster:
  130. self.last_poster_slug = post.poster.slug
  131. else:
  132. self.last_poster_slug = slugify(post.poster_name)