models.py 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. from django.conf import settings
  2. from django.core.cache import cache
  3. from django.db import models
  4. from django.utils.translation import ugettext_lazy as _
  5. from mptt.models import MPTTModel, TreeForeignKey
  6. from misago.forums.signals import move_forum_content, delete_forum_content
  7. from misago.roles.models import Role
  8. from misago.users.signals import rename_user
  9. class ForumManager(models.Manager):
  10. forums_tree = None
  11. def token_to_pk(self, token):
  12. self.populate_tree()
  13. try:
  14. return self.forums_tree[token].pk
  15. except KeyError:
  16. return 0
  17. def populate_tree(self, force=False):
  18. if not self.forums_tree:
  19. self.forums_tree = cache.get('forums_tree')
  20. if not self.forums_tree or force:
  21. self.forums_tree = {}
  22. for forum in Forum.objects.order_by('lft'):
  23. self.forums_tree[forum.pk] = forum
  24. if forum.token:
  25. self.forums_tree[forum.token] = forum
  26. cache.set('forums_tree', self.forums_tree)
  27. def forum_parents(self, forum, include_self=False):
  28. self.populate_tree()
  29. parents = []
  30. parent = self.forums_tree[forum]
  31. if include_self:
  32. parents.append(parent)
  33. while parent.level > 1:
  34. parent = self.forums_tree[parent.parent_id]
  35. parents.append(parent)
  36. return reversed(parents)
  37. def parents_aware_forum(self, forum):
  38. self.populate_tree()
  39. proxy = Forum()
  40. try:
  41. proxy.id = forum.pk
  42. proxy.pk = forum.pk
  43. except AttributeError:
  44. proxy.id = forum
  45. proxy.pk = forum
  46. proxy.closed = False
  47. for parent in self.forum_parents(proxy.pk):
  48. if parent.closed:
  49. proxy.closed = True
  50. return proxy
  51. return proxy
  52. def treelist(self, acl, parent=None, tracker=None):
  53. complete_list = []
  54. forums_list = []
  55. parents = {}
  56. if parent:
  57. queryset = Forum.objects.filter(pk__in=acl.known_forums).filter(lft__gt=parent.lft).filter(rght__lt=parent.rght).order_by('lft')
  58. else:
  59. queryset = Forum.objects.filter(pk__in=acl.known_forums).order_by('lft')
  60. for forum in queryset:
  61. forum.subforums = []
  62. forum.is_read = False
  63. if tracker:
  64. forum.is_read = tracker.is_read(forum)
  65. parents[forum.pk] = forum
  66. complete_list.append(forum)
  67. if forum.parent_id in parents:
  68. parents[forum.parent_id].subforums.append(forum)
  69. else:
  70. forums_list.append(forum)
  71. # Second iteration - sum up forum counters
  72. for forum in reversed(complete_list):
  73. if forum.parent_id in parents and parents[forum.parent_id].type != 'redirect':
  74. parents[forum.parent_id].threads += forum.threads
  75. parents[forum.parent_id].threads_delta += forum.threads_delta
  76. parents[forum.parent_id].posts += forum.posts
  77. parents[forum.parent_id].posts_delta += forum.posts_delta
  78. if acl.can_browse(forum.pk):
  79. # If forum is unread, make parent unread too
  80. if not forum.is_read:
  81. parents[forum.parent_id].is_read = False
  82. # Sum stats
  83. if forum.last_thread_date and (not parents[forum.parent_id].last_thread_date or forum.last_thread_date > parents[forum.parent_id].last_thread_date):
  84. parents[forum.parent_id].last_thread_id = forum.last_thread_id
  85. parents[forum.parent_id].last_thread_name = forum.last_thread_name
  86. parents[forum.parent_id].last_thread_slug = forum.last_thread_slug
  87. parents[forum.parent_id].last_thread_date = forum.last_thread_date
  88. parents[forum.parent_id].last_poster_id = forum.last_poster_id
  89. parents[forum.parent_id].last_poster_name = forum.last_poster_name
  90. parents[forum.parent_id].last_poster_slug = forum.last_poster_slug
  91. parents[forum.parent_id].last_poster_style = forum.last_poster_style
  92. return forums_list
  93. def ignored_users(self, user, forums):
  94. check_ids = []
  95. for forum in forums:
  96. forum.last_poster_ignored = False
  97. if user.is_authenticated() and user.pk != forum.last_poster_id and forum.last_poster_id and not forum.last_poster_id in check_ids:
  98. check_ids.append(forum.last_poster_id)
  99. ignored_ids = []
  100. if check_ids and user.is_authenticated():
  101. for user in user.ignores.filter(id__in=check_ids).values('id'):
  102. ignored_ids.append(user['id'])
  103. print ignored_ids
  104. class Forum(MPTTModel):
  105. parent = TreeForeignKey('self', null=True, blank=True, related_name='children')
  106. type = models.CharField(max_length=12)
  107. token = models.CharField(max_length=255, null=True, blank=True)
  108. name = models.CharField(max_length=255)
  109. slug = models.SlugField(max_length=255)
  110. description = models.TextField(null=True, blank=True)
  111. description_preparsed = models.TextField(null=True, blank=True)
  112. threads = models.PositiveIntegerField(default=0)
  113. threads_delta = models.PositiveIntegerField(default=0)
  114. posts = models.PositiveIntegerField(default=0)
  115. posts_delta = models.IntegerField(default=0)
  116. redirects = models.PositiveIntegerField(default=0)
  117. redirects_delta = models.IntegerField(default=0)
  118. last_thread = models.ForeignKey('threads.Thread', related_name='+', null=True, blank=True, on_delete=models.SET_NULL)
  119. last_thread_name = models.CharField(max_length=255, null=True, blank=True)
  120. last_thread_slug = models.SlugField(max_length=255, null=True, blank=True)
  121. last_thread_date = models.DateTimeField(null=True, blank=True)
  122. last_poster = models.ForeignKey('users.User', related_name='+', null=True, blank=True, on_delete=models.SET_NULL)
  123. last_poster_name = models.CharField(max_length=255, null=True, blank=True)
  124. last_poster_slug = models.SlugField(max_length=255, null=True, blank=True)
  125. last_poster_style = models.CharField(max_length=255, null=True, blank=True)
  126. prune_start = models.PositiveIntegerField(default=0)
  127. prune_last = models.PositiveIntegerField(default=0)
  128. redirect = models.CharField(max_length=255, null=True, blank=True)
  129. template = models.CharField(default='row', max_length=255, null=True, blank=True)
  130. show_details = models.BooleanField(default=True)
  131. style = models.CharField(max_length=255, null=True, blank=True)
  132. closed = models.BooleanField(default=False)
  133. objects = ForumManager()
  134. def __unicode__(self):
  135. if self.token == 'root':
  136. return unicode(_('Root Category'))
  137. return unicode(self.name)
  138. def set_description(self, description):
  139. self.description = description.strip()
  140. self.description_preparsed = ''
  141. if self.description:
  142. import markdown
  143. self.description_preparsed = markdown.markdown(description, safe_mode='escape', output_format=settings.OUTPUT_FORMAT)
  144. def copy_permissions(self, target):
  145. if target.pk != self.pk:
  146. for role in Role.objects.all():
  147. perms = role.get_permissions()
  148. try:
  149. perms['forums'][self.pk] = perms['forums'][target.pk]
  150. role.set_permissions(perms)
  151. role.save(force_update=True)
  152. except KeyError:
  153. pass
  154. def move_content(self, target):
  155. move_forum_content.send(sender=self, move_to=target)
  156. def sync(self):
  157. self.threads = self.thread_set.filter(moderated=False).filter(deleted=False).count()
  158. self.posts = self.post_set.filter(moderated=False).filter(deleted=False).count()
  159. self.last_poster = None
  160. self.last_poster_name = None
  161. self.last_poster_slug = None
  162. self.last_poster_style = None
  163. self.last_thread = None
  164. self.last_thread_date = None
  165. self.last_thread_name = None
  166. self.last_thread_slug = None
  167. try:
  168. last_thread = self.thread_set.filter(moderated=False).filter(deleted=False).order_by('-last').all()[0:][0]
  169. self.last_poster_name = last_thread.last_poster_name
  170. self.last_poster_slug = last_thread.last_poster_slug
  171. self.last_poster_style = last_thread.last_poster_style
  172. if last_thread.last_poster:
  173. self.last_poster = last_thread.last_poster
  174. self.last_thread = last_thread
  175. self.last_thread_date = last_thread.start
  176. self.last_thread_name = last_thread.name
  177. self.last_thread_slug = last_thread.slug
  178. except (IndexError, AttributeError):
  179. pass
  180. def prune(self):
  181. pass
  182. def delete(self, *args, **kwargs):
  183. delete_forum_content.send(sender=self)
  184. super(Forum, self).delete(*args, **kwargs)
  185. """
  186. Signals
  187. """
  188. def rename_user_handler(sender, **kwargs):
  189. Forum.objects.filter(last_poster=sender).update(
  190. last_poster_name=sender.username,
  191. last_poster_slug=sender.username_slug,
  192. )
  193. rename_user.connect(rename_user_handler, dispatch_uid="rename_forums_last_poster")