models.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. from django.db import models
  2. from django.db.models import F
  3. from django.utils import timezone
  4. from django.utils.translation import ugettext_lazy as _
  5. from misago.forums.signals import move_forum_content
  6. from misago.threads.signals import move_thread, merge_thread, move_post, merge_post
  7. from misago.users.signals import delete_user_content, rename_user
  8. from misago.utils import slugify
  9. class ThreadManager(models.Manager):
  10. def filter_stats(self, start, end):
  11. return self.filter(start__gte=start).filter(start__lte=end)
  12. class Thread(models.Model):
  13. forum = models.ForeignKey('forums.Forum')
  14. weight = models.PositiveIntegerField(default=0, db_index=True)
  15. type = models.PositiveIntegerField(default=0)
  16. name = models.CharField(max_length=255)
  17. slug = models.SlugField(max_length=255)
  18. replies = models.PositiveIntegerField(default=0)
  19. replies_reported = models.PositiveIntegerField(default=0)
  20. replies_moderated = models.PositiveIntegerField(default=0)
  21. replies_deleted = models.PositiveIntegerField(default=0)
  22. merges = models.PositiveIntegerField(default=0, db_index=True)
  23. score = models.PositiveIntegerField(default=30, db_index=True)
  24. upvotes = models.PositiveIntegerField(default=0)
  25. downvotes = models.PositiveIntegerField(default=0)
  26. start = models.DateTimeField(db_index=True)
  27. start_post = models.ForeignKey('Post', related_name='+', null=True, blank=True, on_delete=models.SET_NULL)
  28. start_poster = models.ForeignKey('users.User', null=True, blank=True)
  29. start_poster_name = models.CharField(max_length=255)
  30. start_poster_slug = models.SlugField(max_length=255)
  31. start_poster_style = models.CharField(max_length=255, null=True, blank=True)
  32. last = models.DateTimeField(db_index=True)
  33. last_post = models.ForeignKey('Post', related_name='+', null=True, blank=True, on_delete=models.SET_NULL)
  34. last_poster = models.ForeignKey('users.User', related_name='+', null=True, blank=True)
  35. last_poster_name = models.CharField(max_length=255, null=True, blank=True)
  36. last_poster_slug = models.SlugField(max_length=255, null=True, blank=True)
  37. last_poster_style = models.CharField(max_length=255, null=True, blank=True)
  38. moderated = models.BooleanField(default=False, db_index=True)
  39. deleted = models.BooleanField(default=False, db_index=True)
  40. closed = models.BooleanField(default=False)
  41. objects = ThreadManager()
  42. statistics_name = _('New Threads')
  43. def get_date(self):
  44. return self.start
  45. def move_to(self, move_to):
  46. move_thread.send(sender=self, move_to=move_to)
  47. self.forum = move_to
  48. def merge_with(self, thread, merge):
  49. merge_thread.send(sender=self, new_thread=thread, merge=merge)
  50. def sync(self):
  51. # Counters
  52. self.replies = self.post_set.filter(moderated=False).filter(deleted=False).count() - 1
  53. if self.replies < 0:
  54. self.replies = 0
  55. self.replies_reported = self.post_set.filter(reported=True).count()
  56. self.replies_moderated = self.post_set.filter(moderated=True).count()
  57. self.replies_deleted = self.post_set.filter(deleted=True).count()
  58. # First post
  59. start_post = self.post_set.order_by('merge', 'id')[0:][0]
  60. self.start = start_post.date
  61. self.start_post = start_post
  62. self.start_poster = start_post.user
  63. self.start_poster_name = start_post.user_name
  64. self.start_poster_slug = slugify(start_post.user_name)
  65. self.start_poster_style = start_post.user.rank.style if start_post.user else ''
  66. self.upvotes = start_post.upvotes
  67. self.downvotes = start_post.downvotes
  68. # Last post
  69. if self.replies > 0:
  70. last_post = self.post_set.order_by('-merge', '-id').filter(moderated=False).filter(deleted=False)[0:][0]
  71. else:
  72. last_post = start_post
  73. self.last = last_post.date
  74. self.last_post = last_post
  75. self.last_poster = last_post.user
  76. self.last_poster_name = last_post.user_name
  77. self.last_poster_slug = slugify(last_post.user_name)
  78. self.last_poster_style = last_post.user.rank.style if last_post.user else ''
  79. # Flags
  80. self.moderated = start_post.moderated
  81. self.deleted = start_post.deleted
  82. self.merges = last_post.merge
  83. class PostManager(models.Manager):
  84. def filter_stats(self, start, end):
  85. return self.filter(date__gte=start).filter(date__lte=end)
  86. class Post(models.Model):
  87. forum = models.ForeignKey('forums.Forum')
  88. thread = models.ForeignKey(Thread)
  89. merge = models.PositiveIntegerField(default=0, db_index=True)
  90. user = models.ForeignKey('users.User', null=True, blank=True)
  91. user_name = models.CharField(max_length=255)
  92. ip = models.GenericIPAddressField()
  93. agent = models.CharField(max_length=255)
  94. post = models.TextField()
  95. post_preparsed = models.TextField()
  96. upvotes = models.PositiveIntegerField(default=0)
  97. downvotes = models.PositiveIntegerField(default=0)
  98. checkpoints = models.BooleanField(default=False, db_index=True)
  99. date = models.DateTimeField()
  100. edits = models.PositiveIntegerField(default=0)
  101. edit_date = models.DateTimeField(null=True, blank=True)
  102. edit_reason = models.CharField(max_length=255, null=True, blank=True)
  103. edit_user = models.ForeignKey('users.User', related_name='+', null=True)
  104. edit_user_name = models.CharField(max_length=255, null=True, blank=True)
  105. edit_user_slug = models.SlugField(max_length=255, null=True, blank=True)
  106. reported = models.BooleanField(default=False)
  107. moderated = models.BooleanField(default=False, db_index=True)
  108. deleted = models.BooleanField(default=False, db_index=True)
  109. protected = models.BooleanField(default=False)
  110. objects = PostManager()
  111. statistics_name = _('New Posts')
  112. def get_date(self):
  113. return self.date
  114. def move_to(self, thread):
  115. move_post.send(sender=self, move_to=thread)
  116. self.thread = thread
  117. self.forum = thread.forum
  118. def merge_with(self, post):
  119. post.post = '%s\n- - -\n%s' % (post.post, self.post)
  120. merge_post.send(sender=self, new_post=post)
  121. def set_checkpoint(self, request, action):
  122. if request.user.is_authenticated():
  123. self.checkpoints = True
  124. self.checkpoint_set.create(
  125. forum=self.forum,
  126. thread=self.thread,
  127. post=self,
  128. action=action,
  129. user=request.user,
  130. user_name=request.user.username,
  131. user_slug=request.user.username_slug,
  132. date=timezone.now(),
  133. ip=request.session.get_ip(request),
  134. agent=request.META.get('HTTP_USER_AGENT'),
  135. )
  136. class Change(models.Model):
  137. forum = models.ForeignKey('forums.Forum')
  138. thread = models.ForeignKey(Thread)
  139. post = models.ForeignKey(Post)
  140. user = models.ForeignKey('users.User', null=True, blank=True)
  141. user_name = models.CharField(max_length=255)
  142. user_slug = models.CharField(max_length=255)
  143. date = models.DateTimeField()
  144. ip = models.GenericIPAddressField()
  145. agent = models.CharField(max_length=255)
  146. reason = models.CharField(max_length=255, null=True, blank=True)
  147. thread_name_new = models.CharField(max_length=255, null=True, blank=True)
  148. thread_name_old = models.CharField(max_length=255, null=True, blank=True)
  149. post_content = models.TextField()
  150. size = models.IntegerField(default=0)
  151. change = models.IntegerField(default=0)
  152. class Checkpoint(models.Model):
  153. forum = models.ForeignKey('forums.Forum')
  154. thread = models.ForeignKey(Thread)
  155. post = models.ForeignKey(Post)
  156. action = models.CharField(max_length=255)
  157. user = models.ForeignKey('users.User', null=True, blank=True)
  158. user_name = models.CharField(max_length=255)
  159. user_slug = models.CharField(max_length=255)
  160. date = models.DateTimeField()
  161. ip = models.GenericIPAddressField()
  162. agent = models.CharField(max_length=255)
  163. """
  164. Signals
  165. """
  166. def rename_user_handler(sender, **kwargs):
  167. Thread.objects.filter(start_poster=sender).update(
  168. start_poster_name=sender.username,
  169. start_poster_slug=sender.username_slug,
  170. )
  171. Thread.objects.filter(last_poster=sender).update(
  172. last_poster_name=sender.username,
  173. last_poster_slug=sender.username_slug,
  174. )
  175. Post.objects.filter(user=sender).update(
  176. user_name=sender.username,
  177. )
  178. Post.objects.filter(edit_user=sender).update(
  179. edit_user_name=sender.username,
  180. edit_user_slug=sender.username_slug,
  181. )
  182. Change.objects.filter(user=sender).update(
  183. user_name=sender.username,
  184. user_slug=sender.username_slug,
  185. )
  186. Checkpoint.objects.filter(user=sender).update(
  187. user_name=sender.username,
  188. user_slug=sender.username_slug,
  189. )
  190. rename_user.connect(rename_user_handler, dispatch_uid="rename_user_threads")
  191. def delete_user_content_handler(sender, **kwargs):
  192. Thread.objects.filter(start_poster=sender).delete()
  193. threads = []
  194. prev_posts = []
  195. for post in sender.post_set.filter(checkpoints=True):
  196. threads.append(post.thread_id)
  197. prev_post = Post.objects.filter(thread=post.thread_id).exclude(merge__gt=post.merge).exclude(user=sender).order_by('merge', '-id')[:1][0]
  198. post.checkpoint_set.update(post=prev_post)
  199. if not prev_post.pk in prev_posts:
  200. prev_posts.append(prev_post.pk)
  201. sender.post_set.all().delete()
  202. Post.objects.filter(id__in=prev_posts).update(checkpoints=True)
  203. for post in sender.post_set.distinct().values('thread_id').iterator():
  204. if not post['thread_id'] in threads:
  205. threads.append(post['thread_id'])
  206. Post.objects.filter(user=sender).delete()
  207. for thread in Thread.objects.filter(id__in=threads):
  208. thread.sync()
  209. thread.save(force_update=True)
  210. delete_user_content.connect(delete_user_content_handler, dispatch_uid="delete_user_threads_posts")
  211. def move_forum_content_handler(sender, **kwargs):
  212. Thread.objects.filter(forum=sender).update(forum=kwargs['move_to'])
  213. Post.objects.filter(forum=sender).update(forum=kwargs['move_to'])
  214. Change.objects.filter(forum=sender).update(forum=kwargs['move_to'])
  215. Checkpoint.objects.filter(forum=sender).update(forum=kwargs['move_to'])
  216. move_forum_content.connect(move_forum_content_handler, dispatch_uid="move_forum_threads_posts")
  217. def move_thread_handler(sender, **kwargs):
  218. Post.objects.filter(forum=sender.forum_pk).update(forum=kwargs['move_to'])
  219. Change.objects.filter(forum=sender.forum_pk).update(forum=kwargs['move_to'])
  220. Checkpoint.objects.filter(forum=sender.forum_pk).update(forum=kwargs['move_to'])
  221. move_thread.connect(move_thread_handler, dispatch_uid="move_thread")
  222. def merge_thread_handler(sender, **kwargs):
  223. Post.objects.filter(thread=sender).update(thread=kwargs['new_thread'], merge=F('merge') + kwargs['merge'])
  224. Change.objects.filter(thread=sender).update(thread=kwargs['new_thread'])
  225. Checkpoint.objects.filter(thread=sender).delete()
  226. merge_thread.connect(merge_thread_handler, dispatch_uid="merge_threads")
  227. def move_posts_handler(sender, **kwargs):
  228. Change.objects.filter(post=sender).update(forum=kwargs['move_to'].forum, thread=kwargs['move_to'])
  229. if sender.checkpoints:
  230. prev_post = Post.objects.filter(thread=sender.thread_id).filter(merge__lte=sender.merge).exclude(id=sender.pk).order_by('merge', '-id')[:1][0]
  231. Checkpoint.objects.filter(post=sender).update(post=prev_post)
  232. prev_post.checkpoints = True
  233. prev_post.save(force_update=True)
  234. sender.checkpoints = False
  235. move_post.connect(move_posts_handler, dispatch_uid="move_posts")
  236. def merge_posts_handler(sender, **kwargs):
  237. Change.objects.filter(post=sender).update(post=kwargs['new_post'])
  238. Checkpoint.objects.filter(post=sender).update(post=kwargs['new_post'])
  239. if sender.checkpoints:
  240. kwargs['new_post'].checkpoints = True
  241. merge_post.connect(merge_posts_handler, dispatch_uid="merge_posts")