models.py 8.3 KB

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