thread.py 5.8 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__ = ['Thread']
  8. class Thread(models.Model):
  9. forum = models.ForeignKey('misago_forums.Forum')
  10. label = models.ForeignKey('misago_threads.Label',
  11. null=True, blank=True,
  12. on_delete=models.SET_NULL)
  13. title = models.CharField(max_length=255)
  14. slug = models.CharField(max_length=255)
  15. replies = models.PositiveIntegerField(default=0, db_index=True)
  16. has_reported_posts = models.BooleanField(default=False)
  17. has_moderated_posts = models.BooleanField(default=False)
  18. has_hidden_posts = models.BooleanField(default=False)
  19. has_events = models.BooleanField(default=False)
  20. started_on = models.DateTimeField(db_index=True)
  21. first_post = models.ForeignKey('misago_threads.Post', related_name='+',
  22. null=True, blank=True,
  23. on_delete=models.SET_NULL)
  24. starter = models.ForeignKey(settings.AUTH_USER_MODEL,
  25. null=True, blank=True,
  26. on_delete=models.SET_NULL)
  27. starter_name = models.CharField(max_length=255)
  28. starter_slug = models.CharField(max_length=255)
  29. last_post_on = models.DateTimeField(db_index=True)
  30. last_post = models.ForeignKey('misago_threads.Post', related_name='+',
  31. null=True, blank=True,
  32. on_delete=models.SET_NULL)
  33. last_poster = models.ForeignKey(settings.AUTH_USER_MODEL,
  34. related_name='last_poster_set',
  35. null=True, blank=True,
  36. on_delete=models.SET_NULL)
  37. last_poster_name = models.CharField(max_length=255, null=True, blank=True)
  38. last_poster_slug = models.CharField(max_length=255, null=True, blank=True)
  39. is_pinned = models.BooleanField(default=False, db_index=True)
  40. is_poll = models.BooleanField(default=False)
  41. is_moderated = models.BooleanField(default=False, db_index=True)
  42. is_hidden = models.BooleanField(default=False)
  43. is_closed = models.BooleanField(default=False)
  44. class Meta:
  45. index_together = [
  46. ['forum', 'id'],
  47. ['forum', 'last_post_on'],
  48. ['forum', 'replies'],
  49. ]
  50. def __unicode__(self):
  51. return self.title
  52. def lock(self):
  53. return Forum.objects.select_for_update().get(id=self.id)
  54. def delete(self, *args, **kwargs):
  55. from misago.threads.signals import delete_thread
  56. delete_thread.send(sender=self)
  57. super(Thread, self).delete(*args, **kwargs)
  58. def merge(self, other_thread):
  59. if self.pk == other_thread.pk:
  60. raise ValueError("thread can't be merged with itself")
  61. from misago.threads.signals import merge_thread
  62. merge_thread.send(sender=self, other_thread=other_thread)
  63. def move(self, new_forum):
  64. from misago.threads.signals import move_thread
  65. self.forum = new_forum
  66. move_thread.send(sender=self)
  67. def synchronize(self):
  68. self.replies = self.post_set.filter(is_moderated=False).count()
  69. if self.replies > 0:
  70. self.replies -= 1
  71. reported_post_qs = self.post_set.filter(is_reported=True)[:1]
  72. self.has_reported_posts = reported_post_qs.exists()
  73. moderated_post_qs = self.post_set.filter(is_moderated=True)[:1]
  74. self.has_moderated_posts = moderated_post_qs.exists()
  75. hidden_post_qs = self.post_set.filter(is_hidden=True)[:1]
  76. self.has_hidden_posts = hidden_post_qs.exists()
  77. self.has_events = self.event_set.exists()
  78. first_post = self.post_set.order_by('id')[:1][0]
  79. self.set_first_post(first_post)
  80. self.is_moderated = first_post.is_moderated
  81. self.is_hidden = first_post.is_hidden
  82. last_post_qs = self.post_set.filter(is_moderated=False).order_by('-id')
  83. last_post = last_post_qs[:1]
  84. if last_post:
  85. self.set_last_post(last_post[0])
  86. else:
  87. self.set_last_post(first_post)
  88. @property
  89. def link_prefix(self):
  90. if self.forum.special_role == 'private_threads':
  91. return 'private_thread'
  92. else:
  93. return 'thread'
  94. def get_url_name(self, suffix=None):
  95. link = 'misago:%s' % self.link_prefix
  96. if suffix:
  97. link = '%s_%s' % (link, suffix)
  98. return link
  99. def get_url(self, suffix=None):
  100. return reverse(self.get_url_name(suffix), 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)