models.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. from urlparse import urlparse
  2. from django.core.urlresolvers import reverse
  3. from django.db import models
  4. from django.dispatch import receiver
  5. from django.utils.translation import ugettext_lazy as _
  6. from mptt.managers import TreeManager
  7. from mptt.models import MPTTModel, TreeForeignKey
  8. from misago.acl import version as acl_version
  9. from misago.acl.models import BaseRole
  10. from misago.conf import settings
  11. from misago.core import serializer
  12. from misago.core.cache import cache
  13. from misago.core.signals import secret_key_changed
  14. from misago.core.utils import slugify
  15. CACHE_NAME = 'misago_forums_tree'
  16. FORUMS_TREE_ID = 1
  17. class ForumManager(TreeManager):
  18. def private_threads(self):
  19. return self.get(special_role='private_threads')
  20. def root_category(self):
  21. return self.get(special_role='root_category')
  22. def all_forums(self, include_root=False):
  23. qs = self.filter(tree_id=FORUMS_TREE_ID)
  24. if not include_root:
  25. qs = self.filter(lft__gt=3)
  26. return qs.order_by('lft')
  27. def get_cached_forums_dict(self):
  28. forums_dict = cache.get(CACHE_NAME, 'nada')
  29. if forums_dict == 'nada':
  30. forums_dict = self.get_forums_dict_from_db()
  31. cache.set(CACHE_NAME, forums_dict)
  32. return forums_dict
  33. def get_forums_dict_from_db(self):
  34. forums_dict = {}
  35. for forum in self.all_forums(include_root=True):
  36. forums_dict[forum.pk] = forum
  37. return forums_dict
  38. def clear_cache(self):
  39. cache.delete(CACHE_NAME)
  40. class Forum(MPTTModel):
  41. parent = TreeForeignKey(
  42. 'self', null=True, blank=True, related_name='children')
  43. special_role = models.CharField(max_length=255, null=True, blank=True)
  44. role = models.CharField(max_length=255, null=True, blank=True)
  45. name = models.CharField(max_length=255)
  46. slug = models.CharField(max_length=255)
  47. description = models.TextField(null=True, blank=True)
  48. is_closed = models.BooleanField(default=False)
  49. redirect_url = models.CharField(max_length=255, null=True, blank=True)
  50. redirects = models.PositiveIntegerField(default=0)
  51. threads = models.PositiveIntegerField(default=0)
  52. posts = models.PositiveIntegerField(default=0)
  53. last_post_on = models.DateTimeField(null=True, blank=True)
  54. last_thread = models.ForeignKey('misago_threads.Thread', related_name='+',
  55. null=True, blank=True,
  56. on_delete=models.SET_NULL)
  57. last_thread_title = models.CharField(max_length=255, null=True, blank=True)
  58. last_thread_slug = models.CharField(max_length=255, null=True, blank=True)
  59. last_poster = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='+',
  60. null=True, blank=True,
  61. on_delete=models.SET_NULL)
  62. last_poster_name = models.CharField(max_length=255, null=True, blank=True)
  63. last_poster_slug = models.SlugField(max_length=255, null=True, blank=True)
  64. prune_started_after = models.PositiveIntegerField(default=0)
  65. prune_replied_after = models.PositiveIntegerField(default=0)
  66. archive_pruned_in = models.ForeignKey('self',
  67. related_name='pruned_archive',
  68. null=True, blank=True,
  69. on_delete=models.SET_NULL)
  70. css_class = models.CharField(max_length=255, null=True, blank=True)
  71. objects = ForumManager()
  72. def __unicode__(self):
  73. if self.special_role == 'root_category':
  74. return unicode(_('None (will become top level category)'))
  75. elif self.special_role == 'private_threads':
  76. return unicode(_('Private Threads'))
  77. else:
  78. return self.name
  79. def save(self, *args, **kwargs):
  80. if self.pk:
  81. acl_version.invalidate()
  82. return super(Forum, self).save(*args, **kwargs)
  83. def delete(self, *args, **kwargs):
  84. Forum.objects.clear_cache()
  85. acl_version.invalidate()
  86. return super(Forum, self).delete(*args, **kwargs)
  87. @property
  88. def redirect_host(self):
  89. return urlparse(self.redirect_url).hostname
  90. def get_absolute_url(self):
  91. if not self.special_role:
  92. if self.level == 1:
  93. formats = (reverse('misago:index'), self.slug, self.id)
  94. return '%s#%s-%s' % formats
  95. else:
  96. return reverse('misago:%s' % self.role, kwargs={
  97. 'forum_id': self.id, 'forum_slug': self.slug
  98. })
  99. else:
  100. return None
  101. def set_name(self, name):
  102. self.name = name
  103. self.slug = slugify(name)
  104. def set_last_thread(self, thread):
  105. self.last_post_on = thread.last_post_on
  106. self.last_thread = thread
  107. self.last_thread_title = thread.title
  108. self.last_thread_slug = thread.slug
  109. self.last_poster = thread.last_poster
  110. self.last_poster_name = thread.last_poster_name
  111. self.last_poster_slug = thread.last_poster_slug
  112. def has_child(self, child):
  113. return child.lft > self.lft and child.rght < self.rght
  114. class ForumRole(BaseRole):
  115. pass
  116. class RoleForumACL(models.Model):
  117. role = models.ForeignKey('misago_acl.Role', related_name='forums_acls')
  118. forum = models.ForeignKey('Forum', related_name='forum_role_set')
  119. forum_role = models.ForeignKey(ForumRole)
  120. """
  121. Signal handlers
  122. """
  123. @receiver(secret_key_changed)
  124. def update_roles_pickles(sender, **kwargs):
  125. for role in ForumRole.objects.iterator():
  126. if role.pickled_permissions:
  127. role.pickled_permissions = serializer.regenerate_checksum(
  128. role.pickled_permissions)
  129. role.save(update_fields=['pickled_permissions'])