from django import forms from django.core.exceptions import PermissionDenied from django.db.models import Q from django.http import Http404 from django.utils import timezone from django.utils.translation import gettext_lazy as _, ngettext from misago.acl import algebra from misago.acl.decorators import return_boolean from misago.acl.models import Role from misago.acl.objectacl import add_acl_to_obj from misago.admin.forms import YesNoSwitch from misago.categories.models import Category, CategoryRole from misago.categories.permissions import get_categories_roles from misago.threads.models import Post, Thread __all__ = [ "allow_see_thread", "can_see_thread", "allow_start_thread", "can_start_thread", "allow_reply_thread", "can_reply_thread", "allow_edit_thread", "can_edit_thread", "allow_pin_thread", "can_pin_thread", "allow_unhide_thread", "can_unhide_thread", "allow_hide_thread", "can_hide_thread", "allow_delete_thread", "can_delete_thread", "allow_move_thread", "can_move_thread", "allow_merge_thread", "can_merge_thread", "allow_approve_thread", "can_approve_thread", "allow_see_post", "can_see_post", "allow_edit_post", "can_edit_post", "allow_unhide_post", "can_unhide_post", "allow_hide_post", "can_hide_post", "allow_delete_post", "can_delete_post", "allow_protect_post", "can_protect_post", "allow_approve_post", "can_approve_post", "allow_move_post", "can_move_post", "allow_merge_post", "can_merge_post", "allow_unhide_event", "can_unhide_event", "allow_split_post", "can_split_post", "allow_hide_event", "can_hide_event", "allow_delete_event", "can_delete_event", "exclude_invisible_threads", "exclude_invisible_posts", ] class RolePermissionsForm(forms.Form): legend = _("Threads") can_see_unapproved_content_lists = YesNoSwitch( label=_("Can see unapproved content list"), help_text=_( 'Allows access to "unapproved" tab on threads lists for ' "easy listing of threads that are unapproved or contain " "unapproved posts. Despite the tab being available on all " "threads lists, it will only display threads belonging to " "categories in which the user has permission to approve " "content." ), ) can_see_reported_content_lists = YesNoSwitch( label=_("Can see reported content list"), help_text=_( 'Allows access to "reported" tab on threads lists for ' "easy listing of threads that contain reported posts. " "Despite the tab being available on all categories " "threads lists, it will only display threads belonging to " "categories in which the user has permission to see posts " "reports." ), ) can_omit_flood_protection = YesNoSwitch( label=_("Can omit flood protection"), help_text=_("Allows posting more frequently than flood protection would."), ) class CategoryPermissionsForm(forms.Form): legend = _("Threads") can_see_all_threads = forms.TypedChoiceField( label=_("Can see threads"), coerce=int, initial=0, choices=[(0, _("Started threads")), (1, _("All threads"))], ) can_start_threads = YesNoSwitch(label=_("Can start threads")) can_reply_threads = YesNoSwitch(label=_("Can reply to threads")) can_edit_threads = forms.TypedChoiceField( label=_("Can edit threads"), coerce=int, initial=0, choices=[(0, _("No")), (1, _("Own threads")), (2, _("All threads"))], ) can_hide_own_threads = forms.TypedChoiceField( label=_("Can hide own threads"), help_text=_( "Only threads started within time limit and " "with no replies can be hidden." ), coerce=int, initial=0, choices=[(0, _("No")), (1, _("Hide threads")), (2, _("Delete threads"))], ) thread_edit_time = forms.IntegerField( label=_("Time limit for own threads edits, in minutes"), help_text=_("Enter 0 to don't limit time for editing own threads."), initial=0, min_value=0, ) can_hide_threads = forms.TypedChoiceField( label=_("Can hide all threads"), coerce=int, initial=0, choices=[(0, _("No")), (1, _("Hide threads")), (2, _("Delete threads"))], ) can_pin_threads = forms.TypedChoiceField( label=_("Can pin threads"), coerce=int, initial=0, choices=[(0, _("No")), (1, _("Locally")), (2, _("Globally"))], ) can_close_threads = YesNoSwitch(label=_("Can close threads")) can_move_threads = YesNoSwitch(label=_("Can move threads")) can_merge_threads = YesNoSwitch(label=_("Can merge threads")) can_edit_posts = forms.TypedChoiceField( label=_("Can edit posts"), coerce=int, initial=0, choices=[(0, _("No")), (1, _("Own posts")), (2, _("All posts"))], ) can_hide_own_posts = forms.TypedChoiceField( label=_("Can hide own posts"), help_text=_( "Only last posts to thread made within edit time limit can be hidden." ), coerce=int, initial=0, choices=[(0, _("No")), (1, _("Hide posts")), (2, _("Delete posts"))], ) post_edit_time = forms.IntegerField( label=_("Time limit for own post edits, in minutes"), help_text=_("Enter 0 to don't limit time for editing own posts."), initial=0, min_value=0, ) can_hide_posts = forms.TypedChoiceField( label=_("Can hide all posts"), coerce=int, initial=0, choices=[(0, _("No")), (1, _("Hide posts")), (2, _("Delete posts"))], ) can_see_posts_likes = forms.TypedChoiceField( label=_("Can see posts likes"), coerce=int, initial=0, choices=[ (0, _("No")), (1, _("Number only")), (2, _("Number and list of likers")), ], ) can_like_posts = YesNoSwitch( label=_("Can like posts"), help_text=_("Only users with this permission to see likes can like posts."), ) can_protect_posts = YesNoSwitch( label=_("Can protect posts"), help_text=_("Only users with this permission can edit protected posts."), ) can_move_posts = YesNoSwitch( label=_("Can move posts"), help_text=_("Will be able to move posts to other threads."), ) can_merge_posts = YesNoSwitch(label=_("Can merge posts")) can_approve_content = YesNoSwitch( label=_("Can approve content"), help_text=_("Will be able to see and approve unapproved content."), ) can_report_content = YesNoSwitch(label=_("Can report posts")) can_see_reports = YesNoSwitch(label=_("Can see reports")) can_hide_events = forms.TypedChoiceField( label=_("Can hide events"), coerce=int, initial=0, choices=[(0, _("No")), (1, _("Hide events")), (2, _("Delete events"))], ) require_threads_approval = YesNoSwitch(label=_("Require threads approval")) require_replies_approval = YesNoSwitch(label=_("Require replies approval")) require_edits_approval = YesNoSwitch(label=_("Require edits approval")) def change_permissions_form(role): if isinstance(role, Role) and role.special_role != "anonymous": return RolePermissionsForm elif isinstance(role, CategoryRole): return CategoryPermissionsForm else: return None def build_acl(acl, roles, key_name): acl.update( { "can_see_unapproved_content_lists": False, "can_see_reported_content_lists": False, "can_omit_flood_protection": False, "can_approve_content": [], "can_see_reports": [], } ) acl = algebra.sum_acls( acl, roles=roles, key=key_name, can_see_unapproved_content_lists=algebra.greater, can_see_reported_content_lists=algebra.greater, can_omit_flood_protection=algebra.greater, ) categories_roles = get_categories_roles(roles) categories = list(Category.objects.all_categories(include_root=True)) for category in categories: category_acl = acl["categories"].get(category.pk, {"can_browse": 0}) if category_acl["can_browse"]: category_acl = acl["categories"][category.pk] = build_category_acl( category_acl, category, categories_roles, key_name ) if category_acl.get("can_approve_content"): acl["can_approve_content"].append(category.pk) if category_acl.get("can_see_reports"): acl["can_see_reports"].append(category.pk) return acl def build_category_acl(acl, category, categories_roles, key_name): category_roles = categories_roles.get(category.pk, []) final_acl = { "can_see_all_threads": 0, "can_start_threads": 0, "can_reply_threads": 0, "can_edit_threads": 0, "can_edit_posts": 0, "can_hide_own_threads": 0, "can_hide_own_posts": 0, "thread_edit_time": 0, "post_edit_time": 0, "can_hide_threads": 0, "can_hide_posts": 0, "can_protect_posts": 0, "can_move_posts": 0, "can_merge_posts": 0, "can_pin_threads": 0, "can_close_threads": 0, "can_move_threads": 0, "can_merge_threads": 0, "can_report_content": 0, "can_see_reports": 0, "can_see_posts_likes": 0, "can_like_posts": 0, "can_approve_content": 0, "require_threads_approval": 0, "require_replies_approval": 0, "require_edits_approval": 0, "can_hide_events": 0, } final_acl.update(acl) algebra.sum_acls( final_acl, roles=category_roles, key=key_name, can_see_all_threads=algebra.greater, can_start_threads=algebra.greater, can_reply_threads=algebra.greater, can_edit_threads=algebra.greater, can_edit_posts=algebra.greater, can_hide_threads=algebra.greater, can_hide_posts=algebra.greater, can_hide_own_threads=algebra.greater, can_hide_own_posts=algebra.greater, thread_edit_time=algebra.greater_or_zero, post_edit_time=algebra.greater_or_zero, can_protect_posts=algebra.greater, can_move_posts=algebra.greater, can_merge_posts=algebra.greater, can_pin_threads=algebra.greater, can_close_threads=algebra.greater, can_move_threads=algebra.greater, can_merge_threads=algebra.greater, can_report_content=algebra.greater, can_see_reports=algebra.greater, can_see_posts_likes=algebra.greater, can_like_posts=algebra.greater, can_approve_content=algebra.greater, require_threads_approval=algebra.greater, require_replies_approval=algebra.greater, require_edits_approval=algebra.greater, can_hide_events=algebra.greater, ) return final_acl def add_acl_to_category(user_acl, category): category_acl = user_acl["categories"].get(category.pk, {}) category.acl.update( { "can_see_all_threads": 0, "can_see_own_threads": 0, "can_start_threads": 0, "can_reply_threads": 0, "can_edit_threads": 0, "can_edit_posts": 0, "can_hide_own_threads": 0, "can_hide_own_posts": 0, "thread_edit_time": 0, "post_edit_time": 0, "can_hide_threads": 0, "can_hide_posts": 0, "can_protect_posts": 0, "can_move_posts": 0, "can_merge_posts": 0, "can_pin_threads": 0, "can_close_threads": 0, "can_move_threads": 0, "can_merge_threads": 0, "can_report_content": 0, "can_see_reports": 0, "can_see_posts_likes": 0, "can_like_posts": 0, "can_approve_content": 0, "require_threads_approval": category.require_threads_approval, "require_replies_approval": category.require_replies_approval, "require_edits_approval": category.require_edits_approval, "can_hide_events": 0, } ) algebra.sum_acls( category.acl, acls=[category_acl], can_see_all_threads=algebra.greater, can_see_posts_likes=algebra.greater, ) if user_acl["is_authenticated"]: algebra.sum_acls( category.acl, acls=[category_acl], can_start_threads=algebra.greater, can_reply_threads=algebra.greater, can_edit_threads=algebra.greater, can_edit_posts=algebra.greater, can_hide_threads=algebra.greater, can_hide_posts=algebra.greater, can_hide_own_threads=algebra.greater, can_hide_own_posts=algebra.greater, thread_edit_time=algebra.greater_or_zero, post_edit_time=algebra.greater_or_zero, can_protect_posts=algebra.greater, can_move_posts=algebra.greater, can_merge_posts=algebra.greater, can_pin_threads=algebra.greater, can_close_threads=algebra.greater, can_move_threads=algebra.greater, can_merge_threads=algebra.greater, can_report_content=algebra.greater, can_see_reports=algebra.greater, can_like_posts=algebra.greater, can_approve_content=algebra.greater, require_threads_approval=algebra.greater, require_replies_approval=algebra.greater, require_edits_approval=algebra.greater, can_hide_events=algebra.greater, ) if user_acl["can_approve_content"]: category.acl.update( { "require_threads_approval": 0, "require_replies_approval": 0, "require_edits_approval": 0, } ) category.acl["can_see_own_threads"] = not category.acl["can_see_all_threads"] def add_acl_to_thread(user_acl, thread): category_acl = user_acl["categories"].get(thread.category_id, {}) thread.acl.update( { "can_reply": can_reply_thread(user_acl, thread), "can_edit": can_edit_thread(user_acl, thread), "can_pin": can_pin_thread(user_acl, thread), "can_pin_globally": False, "can_hide": can_hide_thread(user_acl, thread), "can_unhide": can_unhide_thread(user_acl, thread), "can_delete": can_delete_thread(user_acl, thread), "can_close": category_acl.get("can_close_threads", False), "can_move": can_move_thread(user_acl, thread), "can_merge": can_merge_thread(user_acl, thread), "can_move_posts": category_acl.get("can_move_posts", False), "can_merge_posts": category_acl.get("can_merge_posts", False), "can_approve": can_approve_thread(user_acl, thread), "can_see_reports": category_acl.get("can_see_reports", False), } ) if thread.acl["can_pin"] and category_acl.get("can_pin_threads") == 2: thread.acl["can_pin_globally"] = True def add_acl_to_post(user_acl, post): if post.is_event: add_acl_to_event(user_acl, post) else: add_acl_to_reply(user_acl, post) def add_acl_to_event(user_acl, event): can_hide_events = 0 if user_acl["is_authenticated"]: category_acl = user_acl["categories"].get( event.category_id, {"can_hide_events": 0} ) can_hide_events = category_acl["can_hide_events"] event.acl.update( { "can_see_hidden": can_hide_events > 0, "can_hide": can_hide_event(user_acl, event), "can_delete": can_delete_event(user_acl, event), } ) def add_acl_to_reply(user_acl, post): category_acl = user_acl["categories"].get(post.category_id, {}) post.acl.update( { "can_reply": can_reply_thread(user_acl, post.thread), "can_edit": can_edit_post(user_acl, post), "can_see_hidden": post.is_first_post or category_acl.get("can_hide_posts"), "can_unhide": can_unhide_post(user_acl, post), "can_hide": can_hide_post(user_acl, post), "can_delete": can_delete_post(user_acl, post), "can_protect": can_protect_post(user_acl, post), "can_approve": can_approve_post(user_acl, post), "can_move": can_move_post(user_acl, post), "can_merge": can_merge_post(user_acl, post), "can_report": category_acl.get("can_report_content", False), "can_see_reports": category_acl.get("can_see_reports", False), "can_see_likes": category_acl.get("can_see_posts_likes", 0), "can_like": False, } ) if not post.acl["can_see_hidden"]: post.acl["can_see_hidden"] = post.id == post.thread.first_post_id if user_acl["is_authenticated"] and post.acl["can_see_likes"]: post.acl["can_like"] = category_acl.get("can_like_posts", False) def register_with(registry): registry.acl_annotator(Category, add_acl_to_category) registry.acl_annotator(Thread, add_acl_to_thread) registry.acl_annotator(Post, add_acl_to_post) def allow_see_thread(user_acl, target): category_acl = user_acl["categories"].get( target.category_id, {"can_see": False, "can_browse": False} ) if not (category_acl["can_see"] and category_acl["can_browse"]): raise Http404() if target.is_hidden and ( user_acl["is_anonymous"] or not category_acl["can_hide_threads"] ): raise Http404() if user_acl["is_anonymous"] or user_acl["user_id"] != target.starter_id: if not category_acl["can_see_all_threads"]: raise Http404() if target.is_unapproved and not category_acl["can_approve_content"]: raise Http404() can_see_thread = return_boolean(allow_see_thread) def allow_start_thread(user_acl, target): if user_acl["is_anonymous"]: raise PermissionDenied(_("You have to sign in to start threads.")) category_acl = user_acl["categories"].get(target.pk, {"can_start_threads": False}) if not category_acl["can_start_threads"]: raise PermissionDenied( _("You don't have permission to start new threads in this category.") ) if target.is_closed and not category_acl["can_close_threads"]: raise PermissionDenied( _("This category is closed. You can't start new threads in it.") ) can_start_thread = return_boolean(allow_start_thread) def allow_reply_thread(user_acl, target): if user_acl["is_anonymous"]: raise PermissionDenied(_("You have to sign in to reply threads.")) category_acl = user_acl["categories"].get( target.category_id, {"can_reply_threads": False} ) if not category_acl["can_reply_threads"]: raise PermissionDenied(_("You can't reply to threads in this category.")) if not category_acl["can_close_threads"]: if target.category.is_closed: raise PermissionDenied( _("This category is closed. You can't reply to threads in it.") ) if target.is_closed: raise PermissionDenied( _("You can't reply to closed threads in this category.") ) can_reply_thread = return_boolean(allow_reply_thread) def allow_edit_thread(user_acl, target): if user_acl["is_anonymous"]: raise PermissionDenied(_("You have to sign in to edit threads.")) category_acl = user_acl["categories"].get( target.category_id, {"can_edit_threads": False} ) if not category_acl["can_edit_threads"]: raise PermissionDenied(_("You can't edit threads in this category.")) if category_acl["can_edit_threads"] == 1: if user_acl["user_id"] != target.starter_id: raise PermissionDenied( _("You can't edit other users threads in this category.") ) if not has_time_to_edit_thread(user_acl, target): message = ngettext( "You can't edit threads that are older than %(minutes)s minute.", "You can't edit threads that are older than %(minutes)s minutes.", category_acl["thread_edit_time"], ) raise PermissionDenied( message % {"minutes": category_acl["thread_edit_time"]} ) if not category_acl["can_close_threads"]: if target.category.is_closed: raise PermissionDenied( _("This category is closed. You can't edit threads in it.") ) if target.is_closed: raise PermissionDenied(_("This thread is closed. You can't edit it.")) can_edit_thread = return_boolean(allow_edit_thread) def allow_pin_thread(user_acl, target): if user_acl["is_anonymous"]: raise PermissionDenied(_("You have to sign in to change threads weights.")) category_acl = user_acl["categories"].get( target.category_id, {"can_pin_threads": 0} ) if not category_acl["can_pin_threads"]: raise PermissionDenied(_("You can't change threads weights in this category.")) if not category_acl["can_close_threads"]: if target.category.is_closed: raise PermissionDenied( _("This category is closed. You can't change threads weights in it.") ) if target.is_closed: raise PermissionDenied( _("This thread is closed. You can't change its weight.") ) can_pin_thread = return_boolean(allow_pin_thread) def allow_unhide_thread(user_acl, target): if user_acl["is_anonymous"]: raise PermissionDenied(_("You have to sign in to hide threads.")) category_acl = user_acl["categories"].get( target.category_id, {"can_close_threads": False} ) if not category_acl["can_close_threads"]: if target.category.is_closed: raise PermissionDenied( _("This category is closed. You can't reveal threads in it.") ) if target.is_closed: raise PermissionDenied(_("This thread is closed. You can't reveal it.")) can_unhide_thread = return_boolean(allow_unhide_thread) def allow_hide_thread(user_acl, target): if user_acl["is_anonymous"]: raise PermissionDenied(_("You have to sign in to hide threads.")) category_acl = user_acl["categories"].get( target.category_id, {"can_hide_threads": 0, "can_hide_own_threads": 0} ) if ( not category_acl["can_hide_threads"] and not category_acl["can_hide_own_threads"] ): raise PermissionDenied(_("You can't hide threads in this category.")) if not category_acl["can_hide_threads"] and category_acl["can_hide_own_threads"]: if user_acl["user_id"] != target.starter_id: raise PermissionDenied( _("You can't hide other users theads in this category.") ) if not has_time_to_edit_thread(user_acl, target): message = ngettext( "You can't hide threads that are older than %(minutes)s minute.", "You can't hide threads that are older than %(minutes)s minutes.", category_acl["thread_edit_time"], ) raise PermissionDenied( message % {"minutes": category_acl["thread_edit_time"]} ) if not category_acl["can_close_threads"]: if target.category.is_closed: raise PermissionDenied( _("This category is closed. You can't hide threads in it.") ) if target.is_closed: raise PermissionDenied(_("This thread is closed. You can't hide it.")) can_hide_thread = return_boolean(allow_hide_thread) def allow_delete_thread(user_acl, target): if user_acl["is_anonymous"]: raise PermissionDenied(_("You have to sign in to delete threads.")) category_acl = user_acl["categories"].get( target.category_id, {"can_hide_threads": 0, "can_hide_own_threads": 0} ) if ( category_acl["can_hide_threads"] != 2 and category_acl["can_hide_own_threads"] != 2 ): raise PermissionDenied(_("You can't delete threads in this category.")) if ( category_acl["can_hide_threads"] != 2 and category_acl["can_hide_own_threads"] == 2 ): if user_acl["user_id"] != target.starter_id: raise PermissionDenied( _("You can't delete other users theads in this category.") ) if not has_time_to_edit_thread(user_acl, target): message = ngettext( "You can't delete threads that are older than %(minutes)s minute.", "You can't delete threads that are older than %(minutes)s minutes.", category_acl["thread_edit_time"], ) raise PermissionDenied( message % {"minutes": category_acl["thread_edit_time"]} ) if not category_acl["can_close_threads"]: if target.category.is_closed: raise PermissionDenied( _("This category is closed. You can't delete threads in it.") ) if target.is_closed: raise PermissionDenied(_("This thread is closed. You can't delete it.")) can_delete_thread = return_boolean(allow_delete_thread) def allow_move_thread(user_acl, target): if user_acl["is_anonymous"]: raise PermissionDenied(_("You have to sign in to move threads.")) category_acl = user_acl["categories"].get( target.category_id, {"can_move_threads": 0} ) if not category_acl["can_move_threads"]: raise PermissionDenied(_("You can't move threads in this category.")) if not category_acl["can_close_threads"]: if target.category.is_closed: raise PermissionDenied( _("This category is closed. You can't move it's threads.") ) if target.is_closed: raise PermissionDenied(_("This thread is closed. You can't move it.")) can_move_thread = return_boolean(allow_move_thread) def allow_merge_thread(user_acl, target, otherthread=False): if user_acl["is_anonymous"]: raise PermissionDenied(_("You have to sign in to merge threads.")) category_acl = user_acl["categories"].get( target.category_id, {"can_merge_threads": 0} ) if not category_acl["can_merge_threads"]: if otherthread: raise PermissionDenied(_("Other thread can't be merged with.")) raise PermissionDenied(_("You can't merge threads in this category.")) if not category_acl["can_close_threads"]: if target.category.is_closed: if otherthread: raise PermissionDenied( _("Other thread's category is closed. You can't merge with it.") ) raise PermissionDenied( _("This category is closed. You can't merge it's threads.") ) if target.is_closed: if otherthread: raise PermissionDenied( _("Other thread is closed and can't be merged with.") ) raise PermissionDenied( _("This thread is closed. You can't merge it with other threads.") ) can_merge_thread = return_boolean(allow_merge_thread) def allow_approve_thread(user_acl, target): if user_acl["is_anonymous"]: raise PermissionDenied(_("You have to sign in to approve threads.")) category_acl = user_acl["categories"].get( target.category_id, {"can_approve_content": 0} ) if not category_acl["can_approve_content"]: raise PermissionDenied(_("You can't approve threads in this category.")) if not category_acl["can_close_threads"]: if target.category.is_closed: raise PermissionDenied( _("This category is closed. You can't approve threads in it.") ) if target.is_closed: raise PermissionDenied(_("This thread is closed. You can't approve it.")) can_approve_thread = return_boolean(allow_approve_thread) def allow_see_post(user_acl, target): category_acl = user_acl["categories"].get( target.category_id, {"can_approve_content": False, "can_hide_events": False} ) if not target.is_event and target.is_unapproved: if user_acl["is_anonymous"]: raise Http404() if ( not category_acl["can_approve_content"] and user_acl["user_id"] != target.poster_id ): raise Http404() if target.is_event and target.is_hidden and not category_acl["can_hide_events"]: raise Http404() can_see_post = return_boolean(allow_see_post) def allow_edit_post(user_acl, target): if user_acl["is_anonymous"]: raise PermissionDenied(_("You have to sign in to edit posts.")) if target.is_event: raise PermissionDenied(_("Events can't be edited.")) category_acl = user_acl["categories"].get( target.category_id, {"can_edit_posts": False} ) if not category_acl["can_edit_posts"]: raise PermissionDenied(_("You can't edit posts in this category.")) if ( target.is_hidden and not target.is_first_post and not category_acl["can_hide_posts"] ): raise PermissionDenied(_("This post is hidden, you can't edit it.")) if category_acl["can_edit_posts"] == 1: if target.poster_id != user_acl["user_id"]: raise PermissionDenied( _("You can't edit other users posts in this category.") ) if target.is_protected and not category_acl["can_protect_posts"]: raise PermissionDenied(_("This post is protected. You can't edit it.")) if not has_time_to_edit_post(user_acl, target): message = ngettext( "You can't edit posts that are older than %(minutes)s minute.", "You can't edit posts that are older than %(minutes)s minutes.", category_acl["post_edit_time"], ) raise PermissionDenied( message % {"minutes": category_acl["post_edit_time"]} ) if not category_acl["can_close_threads"]: if target.category.is_closed: raise PermissionDenied( _("This category is closed. You can't edit posts in it.") ) if target.thread.is_closed: raise PermissionDenied( _("This thread is closed. You can't edit posts in it.") ) can_edit_post = return_boolean(allow_edit_post) def allow_unhide_post(user_acl, target): if user_acl["is_anonymous"]: raise PermissionDenied(_("You have to sign in to reveal posts.")) category_acl = user_acl["categories"].get( target.category_id, {"can_hide_posts": 0, "can_hide_own_posts": 0} ) if not category_acl["can_hide_posts"]: if not category_acl["can_hide_own_posts"]: raise PermissionDenied(_("You can't reveal posts in this category.")) if user_acl["user_id"] != target.poster_id: raise PermissionDenied( _("You can't reveal other users posts in this category.") ) if target.is_protected and not category_acl["can_protect_posts"]: raise PermissionDenied(_("This post is protected. You can't reveal it.")) if not has_time_to_edit_post(user_acl, target): message = ngettext( "You can't reveal posts that are older than %(minutes)s minute.", "You can't reveal posts that are older than %(minutes)s minutes.", category_acl["post_edit_time"], ) raise PermissionDenied( message % {"minutes": category_acl["post_edit_time"]} ) if target.is_first_post: raise PermissionDenied(_("You can't reveal thread's first post.")) if not category_acl["can_close_threads"]: if target.category.is_closed: raise PermissionDenied( _("This category is closed. You can't reveal posts in it.") ) if target.thread.is_closed: raise PermissionDenied( _("This thread is closed. You can't reveal posts in it.") ) can_unhide_post = return_boolean(allow_unhide_post) def allow_hide_post(user_acl, target): if user_acl["is_anonymous"]: raise PermissionDenied(_("You have to sign in to hide posts.")) category_acl = user_acl["categories"].get( target.category_id, {"can_hide_posts": 0, "can_hide_own_posts": 0} ) if not category_acl["can_hide_posts"]: if not category_acl["can_hide_own_posts"]: raise PermissionDenied(_("You can't hide posts in this category.")) if user_acl["user_id"] != target.poster_id: raise PermissionDenied( _("You can't hide other users posts in this category.") ) if target.is_protected and not category_acl["can_protect_posts"]: raise PermissionDenied(_("This post is protected. You can't hide it.")) if not has_time_to_edit_post(user_acl, target): message = ngettext( "You can't hide posts that are older than %(minutes)s minute.", "You can't hide posts that are older than %(minutes)s minutes.", category_acl["post_edit_time"], ) raise PermissionDenied( message % {"minutes": category_acl["post_edit_time"]} ) if target.is_first_post: raise PermissionDenied(_("You can't hide thread's first post.")) if not category_acl["can_close_threads"]: if target.category.is_closed: raise PermissionDenied( _("This category is closed. You can't hide posts in it.") ) if target.thread.is_closed: raise PermissionDenied( _("This thread is closed. You can't hide posts in it.") ) can_hide_post = return_boolean(allow_hide_post) def allow_delete_post(user_acl, target): if user_acl["is_anonymous"]: raise PermissionDenied(_("You have to sign in to delete posts.")) category_acl = user_acl["categories"].get( target.category_id, {"can_hide_posts": 0, "can_hide_own_posts": 0} ) if category_acl["can_hide_posts"] != 2: if category_acl["can_hide_own_posts"] != 2: raise PermissionDenied(_("You can't delete posts in this category.")) if user_acl["user_id"] != target.poster_id: raise PermissionDenied( _("You can't delete other users posts in this category.") ) if target.is_protected and not category_acl["can_protect_posts"]: raise PermissionDenied(_("This post is protected. You can't delete it.")) if not has_time_to_edit_post(user_acl, target): message = ngettext( "You can't delete posts that are older than %(minutes)s minute.", "You can't delete posts that are older than %(minutes)s minutes.", category_acl["post_edit_time"], ) raise PermissionDenied( message % {"minutes": category_acl["post_edit_time"]} ) if target.is_first_post: raise PermissionDenied(_("You can't delete thread's first post.")) if not category_acl["can_close_threads"]: if target.category.is_closed: raise PermissionDenied( _("This category is closed. You can't delete posts in it.") ) if target.thread.is_closed: raise PermissionDenied( _("This thread is closed. You can't delete posts in it.") ) can_delete_post = return_boolean(allow_delete_post) def allow_protect_post(user_acl, target): if user_acl["is_anonymous"]: raise PermissionDenied(_("You have to sign in to protect posts.")) category_acl = user_acl["categories"].get( target.category_id, {"can_protect_posts": False} ) if not category_acl["can_protect_posts"]: raise PermissionDenied(_("You can't protect posts in this category.")) if not can_edit_post(user_acl, target): raise PermissionDenied(_("You can't protect posts you can't edit.")) can_protect_post = return_boolean(allow_protect_post) def allow_approve_post(user_acl, target): if user_acl["is_anonymous"]: raise PermissionDenied(_("You have to sign in to approve posts.")) category_acl = user_acl["categories"].get( target.category_id, {"can_approve_content": False} ) if not category_acl["can_approve_content"]: raise PermissionDenied(_("You can't approve posts in this category.")) if target.is_first_post: raise PermissionDenied(_("You can't approve thread's first post.")) if ( not target.is_first_post and not category_acl["can_hide_posts"] and target.is_hidden ): raise PermissionDenied(_("You can't approve posts the content you can't see.")) if not category_acl["can_close_threads"]: if target.category.is_closed: raise PermissionDenied( _("This category is closed. You can't approve posts in it.") ) if target.thread.is_closed: raise PermissionDenied( _("This thread is closed. You can't approve posts in it.") ) can_approve_post = return_boolean(allow_approve_post) def allow_move_post(user_acl, target): if user_acl["is_anonymous"]: raise PermissionDenied(_("You have to sign in to move posts.")) category_acl = user_acl["categories"].get( target.category_id, {"can_move_posts": False} ) if not category_acl["can_move_posts"]: raise PermissionDenied(_("You can't move posts in this category.")) if target.is_event: raise PermissionDenied(_("Events can't be moved.")) if target.is_first_post: raise PermissionDenied(_("You can't move thread's first post.")) if not category_acl["can_hide_posts"] and target.is_hidden: raise PermissionDenied(_("You can't move posts the content you can't see.")) if not category_acl["can_close_threads"]: if target.category.is_closed: raise PermissionDenied( _("This category is closed. You can't move posts in it.") ) if target.thread.is_closed: raise PermissionDenied( _("This thread is closed. You can't move posts in it.") ) can_move_post = return_boolean(allow_move_post) def allow_merge_post(user_acl, target): if user_acl["is_anonymous"]: raise PermissionDenied(_("You have to sign in to merge posts.")) category_acl = user_acl["categories"].get( target.category_id, {"can_merge_posts": False} ) if not category_acl["can_merge_posts"]: raise PermissionDenied(_("You can't merge posts in this category.")) if target.is_event: raise PermissionDenied(_("Events can't be merged.")) if ( target.is_hidden and not category_acl["can_hide_posts"] and not target.is_first_post ): raise PermissionDenied(_("You can't merge posts the content you can't see.")) if not category_acl["can_close_threads"]: if target.category.is_closed: raise PermissionDenied( _("This category is closed. You can't merge posts in it.") ) if target.thread.is_closed: raise PermissionDenied( _("This thread is closed. You can't merge posts in it.") ) can_merge_post = return_boolean(allow_merge_post) def allow_split_post(user_acl, target): if user_acl["is_anonymous"]: raise PermissionDenied(_("You have to sign in to split posts.")) category_acl = user_acl["categories"].get( target.category_id, {"can_move_posts": False} ) if not category_acl["can_move_posts"]: raise PermissionDenied(_("You can't split posts in this category.")) if target.is_event: raise PermissionDenied(_("Events can't be split.")) if target.is_first_post: raise PermissionDenied(_("You can't split thread's first post.")) if not category_acl["can_hide_posts"] and target.is_hidden: raise PermissionDenied(_("You can't split posts the content you can't see.")) if not category_acl["can_close_threads"]: if target.category.is_closed: raise PermissionDenied( _("This category is closed. You can't split posts in it.") ) if target.thread.is_closed: raise PermissionDenied( _("This thread is closed. You can't split posts in it.") ) can_split_post = return_boolean(allow_split_post) def allow_unhide_event(user_acl, target): if user_acl["is_anonymous"]: raise PermissionDenied(_("You have to sign in to reveal events.")) category_acl = user_acl["categories"].get( target.category_id, {"can_hide_events": 0} ) if not category_acl["can_hide_events"]: raise PermissionDenied(_("You can't reveal events in this category.")) if not category_acl["can_close_threads"]: if target.category.is_closed: raise PermissionDenied( _("This category is closed. You can't reveal events in it.") ) if target.thread.is_closed: raise PermissionDenied( _("This thread is closed. You can't reveal events in it.") ) can_unhide_event = return_boolean(allow_unhide_event) def allow_hide_event(user_acl, target): if user_acl["is_anonymous"]: raise PermissionDenied(_("You have to sign in to hide events.")) category_acl = user_acl["categories"].get( target.category_id, {"can_hide_events": 0} ) if not category_acl["can_hide_events"]: raise PermissionDenied(_("You can't hide events in this category.")) if not category_acl["can_close_threads"]: if target.category.is_closed: raise PermissionDenied( _("This category is closed. You can't hide events in it.") ) if target.thread.is_closed: raise PermissionDenied( _("This thread is closed. You can't hide events in it.") ) can_hide_event = return_boolean(allow_hide_event) def allow_delete_event(user_acl, target): if user_acl["is_anonymous"]: raise PermissionDenied(_("You have to sign in to delete events.")) category_acl = user_acl["categories"].get( target.category_id, {"can_hide_events": 0} ) if category_acl["can_hide_events"] != 2: raise PermissionDenied(_("You can't delete events in this category.")) if not category_acl["can_close_threads"]: if target.category.is_closed: raise PermissionDenied( _("This category is closed. You can't delete events in it.") ) if target.thread.is_closed: raise PermissionDenied( _("This thread is closed. You can't delete events in it.") ) can_delete_event = return_boolean(allow_delete_event) def can_change_owned_thread(user_acl, target): if user_acl["is_anonymous"] or user_acl["user_id"] != target.starter_id: return False if target.category.is_closed or target.is_closed: return False return has_time_to_edit_thread(user_acl, target) def has_time_to_edit_thread(user_acl, target): edit_time = ( user_acl["categories"].get(target.category_id, {}).get("thread_edit_time", 0) ) if edit_time: diff = timezone.now() - target.started_on diff_minutes = int(diff.total_seconds() / 60) return diff_minutes < edit_time else: return True def has_time_to_edit_post(user_acl, target): edit_time = ( user_acl["categories"].get(target.category_id, {}).get("post_edit_time", 0) ) if edit_time: diff = timezone.now() - target.posted_on diff_minutes = int(diff.total_seconds() / 60) return diff_minutes < edit_time else: return True def exclude_invisible_threads(user_acl, categories, queryset): show_all = [] show_accepted_visible = [] show_accepted = [] show_visible = [] show_owned = [] show_owned_visible = [] for category in categories: add_acl_to_obj(user_acl, category) if not (category.acl["can_see"] and category.acl["can_browse"]): continue can_hide = category.acl["can_hide_threads"] if category.acl["can_see_all_threads"]: can_mod = category.acl["can_approve_content"] if can_mod and can_hide: show_all.append(category) elif user_acl["is_authenticated"]: if not can_mod and not can_hide: show_accepted_visible.append(category) elif not can_mod: show_accepted.append(category) elif not can_hide: show_visible.append(category) else: show_accepted_visible.append(category) elif user_acl["is_authenticated"]: if can_hide: show_owned.append(category) else: show_owned_visible.append(category) conditions = None if show_all: conditions = Q(category__in=show_all) if show_accepted_visible: if user_acl["is_authenticated"]: condition = Q( Q(starter_id=user_acl["user_id"]) | Q(is_unapproved=False), category__in=show_accepted_visible, is_hidden=False, ) else: condition = Q( category__in=show_accepted_visible, is_hidden=False, is_unapproved=False ) if conditions: conditions = conditions | condition else: conditions = condition if show_accepted: condition = Q( Q(starter_id=user_acl["user_id"]) | Q(is_unapproved=False), category__in=show_accepted, ) if conditions: conditions = conditions | condition else: conditions = condition if show_visible: condition = Q(category__in=show_visible, is_hidden=False) if conditions: conditions = conditions | condition else: conditions = condition if show_owned: condition = Q(category__in=show_owned, starter_id=user_acl["user_id"]) if conditions: conditions = conditions | condition else: conditions = condition if show_owned_visible: condition = Q( category__in=show_owned_visible, starter_id=user_acl["user_id"], is_hidden=False, ) if conditions: conditions = conditions | condition else: conditions = condition if conditions: return queryset.filter(conditions) else: return Thread.objects.none() def exclude_invisible_posts(user_acl, categories, queryset): if hasattr(categories, "__iter__"): return exclude_invisible_posts_in_categories(user_acl, categories, queryset) else: return exclude_invisible_posts_in_category(user_acl, categories, queryset) def exclude_invisible_posts_in_categories(user_acl, categories, queryset): show_all = [] show_approved = [] show_approved_owned = [] hide_invisible_events = [] for category in categories: add_acl_to_obj(user_acl, category) if category.acl["can_approve_content"]: show_all.append(category.pk) else: if user_acl["is_authenticated"]: show_approved_owned.append(category.pk) else: show_approved.append(category.pk) if not category.acl["can_hide_events"]: hide_invisible_events.append(category.pk) conditions = None if show_all: conditions = Q(category__in=show_all) if show_approved: condition = Q(category__in=show_approved, is_unapproved=False) if conditions: conditions = conditions | condition else: conditions = condition if show_approved_owned: condition = Q( Q(poster_id=user_acl["user_id"]) | Q(is_unapproved=False), category__in=show_approved_owned, ) if conditions: conditions = conditions | condition else: conditions = condition if hide_invisible_events: queryset = queryset.exclude( category__in=hide_invisible_events, is_event=True, is_hidden=True ) if conditions: return queryset.filter(conditions) else: return Post.objects.none() def exclude_invisible_posts_in_category(user_acl, category, queryset): add_acl_to_obj(user_acl, category) if not category.acl["can_approve_content"]: if user_acl["is_authenticated"]: queryset = queryset.filter( Q(is_unapproved=False) | Q(poster_id=user_acl["user_id"]) ) else: queryset = queryset.exclude(is_unapproved=True) if not category.acl["can_hide_events"]: queryset = queryset.exclude(is_event=True, is_hidden=True) return queryset