thread.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. from django.db import models, transaction
  2. from misago.conf import settings
  3. from misago.core.utils import slugify
  4. class Thread(models.Model):
  5. category = models.ForeignKey('misago_categories.Category')
  6. label = models.ForeignKey('misago_threads.Label',
  7. null=True, blank=True,
  8. on_delete=models.SET_NULL)
  9. title = models.CharField(max_length=255)
  10. slug = models.CharField(max_length=255)
  11. replies = models.PositiveIntegerField(default=0, db_index=True)
  12. has_reported_posts = models.BooleanField(default=False)
  13. has_open_reports = models.BooleanField(default=False)
  14. has_moderated_posts = models.BooleanField(default=False)
  15. has_hidden_posts = models.BooleanField(default=False)
  16. has_events = models.BooleanField(default=False)
  17. started_on = models.DateTimeField(db_index=True)
  18. first_post = models.ForeignKey('misago_threads.Post', related_name='+',
  19. null=True, blank=True,
  20. on_delete=models.SET_NULL)
  21. starter = models.ForeignKey(settings.AUTH_USER_MODEL,
  22. null=True, blank=True,
  23. on_delete=models.SET_NULL)
  24. starter_name = models.CharField(max_length=255)
  25. starter_slug = models.CharField(max_length=255)
  26. last_post_on = models.DateTimeField(db_index=True)
  27. last_post = models.ForeignKey('misago_threads.Post', related_name='+',
  28. null=True, blank=True,
  29. on_delete=models.SET_NULL)
  30. last_poster = models.ForeignKey(settings.AUTH_USER_MODEL,
  31. related_name='last_poster_set',
  32. null=True, blank=True,
  33. on_delete=models.SET_NULL)
  34. last_poster_name = models.CharField(max_length=255, null=True, blank=True)
  35. last_poster_slug = models.CharField(max_length=255, null=True, blank=True)
  36. is_pinned = models.BooleanField(default=False, db_index=True)
  37. is_poll = models.BooleanField(default=False)
  38. is_moderated = models.BooleanField(default=False, db_index=True)
  39. is_hidden = models.BooleanField(default=False)
  40. is_closed = models.BooleanField(default=False)
  41. participants = models.ManyToManyField(settings.AUTH_USER_MODEL,
  42. related_name='private_thread_set',
  43. through='ThreadParticipant',
  44. through_fields=('thread', 'user'))
  45. class Meta:
  46. index_together = [
  47. ['category', 'id'],
  48. ['category', 'last_post_on'],
  49. ['category', 'replies'],
  50. ]
  51. def __unicode__(self):
  52. return self.title
  53. def lock(self):
  54. return Thread.objects.select_for_update().get(id=self.id)
  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_category):
  65. from misago.threads.signals import move_thread
  66. self.category = new_category
  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(has_reports=True)
  73. self.has_reported_posts = reported_post_qs.exists()
  74. if self.has_reported_posts:
  75. open_reports_qs = self.post_set.filter(has_open_reports=True)
  76. self.has_open_reports = open_reports_qs.exists()
  77. else:
  78. self.has_open_reports = False
  79. moderated_post_qs = self.post_set.filter(is_moderated=True)
  80. self.has_moderated_posts = moderated_post_qs.exists()
  81. hidden_post_qs = self.post_set.filter(is_hidden=True)[:1]
  82. self.has_hidden_posts = hidden_post_qs.exists()
  83. self.has_events = self.event_set.exists()
  84. first_post = self.post_set.order_by('id')[:1][0]
  85. self.set_first_post(first_post)
  86. self.is_moderated = first_post.is_moderated
  87. self.is_hidden = first_post.is_hidden
  88. last_post_qs = self.post_set.filter(is_moderated=False).order_by('-id')
  89. last_post = last_post_qs[:1]
  90. if last_post:
  91. self.set_last_post(last_post[0])
  92. else:
  93. self.set_last_post(first_post)
  94. @property
  95. def thread_type(self):
  96. return self.category.thread_type
  97. def get_absolute_url(self):
  98. return self.thread_type.get_thread_absolute_url(self)
  99. def get_post_url(self, post, page):
  100. try:
  101. post_id = post.id
  102. except AttributeError:
  103. post_id = post
  104. return self.thread_type.get_thread_post_url(self, post_id, page)
  105. def get_last_reply_url(self):
  106. return self.thread_type.get_thread_last_reply_url(self)
  107. def get_new_reply_url(self):
  108. return self.thread_type.get_thread_new_reply_url(self)
  109. def get_moderated_url(self):
  110. return self.thread_type.get_thread_moderated_url(self)
  111. def get_reported_url(self):
  112. return self.thread_type.get_thread_reported_url(self)
  113. def get_reply_api_url(self):
  114. return self.thread_type.get_reply_url(self)
  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)