goto.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. from math import ceil
  2. from django.core.exceptions import PermissionDenied
  3. from django.db.models import Q
  4. from django.shortcuts import get_object_or_404, redirect
  5. from django.utils.translation import ugettext as _
  6. from django.views import View
  7. from misago.conf import settings
  8. from misago.readtracker.dates import get_cutoff_date
  9. from misago.threads.permissions import exclude_invisible_posts
  10. from misago.threads.viewmodels import ForumThread, PrivateThread
  11. class GotoView(View):
  12. thread = None
  13. def get(self, request, pk, slug, **kwargs):
  14. thread = self.get_thread(request, pk, slug).unwrap()
  15. self.test_permissions(request, thread)
  16. posts_queryset = exclude_invisible_posts(request.user, thread.category, thread.post_set)
  17. target_post = self.get_target_post(request.user, thread, posts_queryset.order_by('id'), **kwargs)
  18. target_page = self.compute_post_page(target_post, posts_queryset)
  19. return self.get_redirect(thread, target_post, target_page)
  20. def get_thread(self, request, pk, slug):
  21. return self.thread(request, pk, slug)
  22. def test_permissions(self, request, thread):
  23. pass
  24. def get_target_post(self, user, thread, posts_queryset):
  25. raise NotImplementedError("goto views should define their own get_target_post method")
  26. def compute_post_page(self, target_post, posts_queryset):
  27. # filter out events, order queryset
  28. posts_queryset = posts_queryset.filter(is_event=False).order_by('id')
  29. thread_length = posts_queryset.count()
  30. # is target an event?
  31. if target_post.is_event:
  32. target_event = target_post
  33. previous_posts = posts_queryset.filter(id__lt=target_event.id)
  34. else:
  35. previous_posts = posts_queryset.filter(id__lte=target_post.id)
  36. post_position = previous_posts.count()
  37. per_page = settings.MISAGO_POSTS_PER_PAGE - 1
  38. orphans = settings.MISAGO_POSTS_TAIL
  39. if orphans:
  40. orphans += 1
  41. hits = max(1, thread_length - orphans)
  42. thread_pages = int(ceil(hits / float(per_page)))
  43. if post_position >= thread_pages * per_page:
  44. return thread_pages
  45. return int(ceil(float(post_position) / (per_page)))
  46. def get_redirect(self, thread, target_post, target_page):
  47. thread_url = thread.thread_type.get_thread_absolute_url(thread, target_page)
  48. return redirect('%s#post-%s' % (thread_url, target_post.pk))
  49. class ThreadGotoPostView(GotoView):
  50. thread = ForumThread
  51. def get_target_post(self, user, thread, posts_queryset, **kwargs):
  52. return get_object_or_404(posts_queryset, pk=kwargs['post'])
  53. class ThreadGotoLastView(GotoView):
  54. thread = ForumThread
  55. def get_target_post(self, user, thread, posts_queryset, **kwargs):
  56. return posts_queryset.order_by('id').last()
  57. class GetFirstUnreadPostMixin(object):
  58. def get_first_unread_post(self, user, posts_queryset):
  59. if user.is_authenticated:
  60. expired_posts = Q(posted_on__lt=get_cutoff_date(user))
  61. read_posts = Q(id__in=user.postread_set.values('post'))
  62. first_unread = posts_queryset.exclude(
  63. expired_posts | read_posts,
  64. ).order_by('id').first()
  65. if first_unread:
  66. return first_unread
  67. return posts_queryset.order_by('id').last()
  68. class ThreadGotoNewView(GotoView, GetFirstUnreadPostMixin):
  69. thread = ForumThread
  70. def get_target_post(self, user, thread, posts_queryset, **kwargs):
  71. return self.get_first_unread_post(user, posts_queryset)
  72. class ThreadGotoBestAnswerView(GotoView):
  73. thread = ForumThread
  74. def get_target_post(self, user, thread, posts_queryset, **kwargs):
  75. return thread.best_answer or thread.first_post
  76. class ThreadGotoUnapprovedView(GotoView):
  77. thread = ForumThread
  78. def test_permissions(self, request, thread):
  79. if not thread.acl['can_approve']:
  80. raise PermissionDenied(
  81. _(
  82. "You need permission to approve content to "
  83. "be able to go to first unapproved post."
  84. )
  85. )
  86. def get_target_post(self, user, thread, posts_queryset, **kwargs):
  87. unapproved_post = posts_queryset.filter(
  88. is_unapproved=True,
  89. ).order_by('id').first()
  90. if unapproved_post:
  91. return unapproved_post
  92. else:
  93. return posts_queryset.order_by('id').last()
  94. class PrivateThreadGotoPostView(GotoView):
  95. thread = PrivateThread
  96. def get_target_post(self, user, thread, posts_queryset, **kwargs):
  97. return get_object_or_404(posts_queryset, pk=kwargs['post'])
  98. class PrivateThreadGotoLastView(GotoView):
  99. thread = PrivateThread
  100. def get_target_post(self, user, thread, posts_queryset, **kwargs):
  101. return posts_queryset.order_by('id').last()
  102. class PrivateThreadGotoNewView(GotoView, GetFirstUnreadPostMixin):
  103. thread = PrivateThread
  104. def get_target_post(self, user, thread, posts_queryset, **kwargs):
  105. return self.get_first_unread_post(user, posts_queryset)