Skip to content

Commit

Permalink
Add hooks for posting in closed category and start thread permission …
Browse files Browse the repository at this point in the history
…checks
  • Loading branch information
rafalp committed Aug 6, 2024
1 parent 90036ea commit a6c7307
Show file tree
Hide file tree
Showing 5 changed files with 277 additions and 1 deletion.
8 changes: 8 additions & 0 deletions misago/permissions/hooks/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
from .build_user_category_permissions import build_user_category_permissions_hook
from .build_user_permissions import build_user_permissions_hook
from .check_browse_category_permission import check_browse_category_permission_hook
from .check_post_in_closed_category_permission import (
check_post_in_closed_category_permission_hook,
)
from .check_private_threads_permission import check_private_threads_permission_hook
from .check_see_category_permission import check_see_category_permission_hook
from .check_start_thread_in_category_permission import (
check_start_thread_in_category_permission_hook,
)
from .copy_category_permissions import copy_category_permissions_hook
from .copy_group_permissions import copy_group_permissions_hook
from .filter_private_threads_queryset import filter_private_threads_queryset_hook
Expand All @@ -22,8 +28,10 @@
"build_user_category_permissions_hook",
"build_user_permissions_hook",
"check_browse_category_permission_hook",
"check_post_in_closed_category_permission_hook",
"check_private_threads_permission_hook",
"check_see_category_permission_hook",
"check_start_thread_in_category_permission_hook",
"copy_category_permissions_hook",
"copy_group_permissions_hook",
"filter_private_threads_queryset_hook",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class CheckBrowseCategoryPermissionHookFilter(Protocol):
It raises Django's `Http404` if they can't see it or `PermissionDenied`
with an error message if they can't browse it.
Browse the [action](#action) section for details.
See the [action](#action) section for details.
## `user_permissions: UserPermissionsProxy`
Expand Down
121 changes: 121 additions & 0 deletions misago/permissions/hooks/check_post_in_closed_category_permission.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
from typing import TYPE_CHECKING, Protocol

from ...categories.models import Category
from ...plugins.hooks import FilterHook

if TYPE_CHECKING:
from ..proxy import UserPermissionsProxy


class CheckPostInClosedCategoryPermissionHookAction(Protocol):
"""
A standard Misago function used to check if the user has permission to
post in a closed category. It raises Django's `PermissionDenied` with an
error message if category is closed and they can't post in it.
# Arguments
## `user_permissions: UserPermissionsProxy`
A proxy object with the current user's permissions.
## `category: Category`
A category to check permissions for.
"""

def __call__(
self,
permissions: "UserPermissionsProxy",
category: Category,
) -> None: ...


class CheckPostInClosedCategoryPermissionHookFilter(Protocol):
"""
A function implemented by a plugin that can be registered in this hook.
# Arguments
## `action: CheckPostInClosedCategoryPermissionHookAction`
A standard Misago function used to check if the user has permission to
post in a closed category. It raises Django's `PermissionDenied` with an
error message if category is closed and they can't post in it.
See the [action](#action) section for details.
## `user_permissions: UserPermissionsProxy`
A proxy object with the current user's permissions.
## `category: Category`
A category to check permissions for.
"""

def __call__(
self,
action: CheckPostInClosedCategoryPermissionHookAction,
permissions: "UserPermissionsProxy",
category: Category,
) -> None: ...


class CheckPostInClosedCategoryPermissionHook(
FilterHook[
CheckPostInClosedCategoryPermissionHookAction,
CheckPostInClosedCategoryPermissionHookFilter,
]
):
"""
This hook wraps the standard function that Misago uses to check if the user
has permission to post in a closed category. It raises Django's `PermissionDenied`
with an error message if category is closed and they can't post in it.
# Example
The code below implements a custom filter function that permits a user to
post in the specific category if they have a custom flag set on their account.
```python
from misago.categories.models import Category
from misago.permissions.hooks import check_post_in_closed_category_permission_hook
from misago.permissions.proxy import UserPermissionsProxy
@check_post_in_closed_category_permission_hook.append_filter
def check_user_can_post_in_closed_category(
action,
permissions: UserPermissionsProxy,
category: Category,
) -> None:
user = permissions.user
if user.is_authenticated:
post_in_closed_categories = (
user.plugin_data.get("post_in_closed_categories") or []
)
else:
post_in_closed_categories = None
if (
not post_in_closed_categories
or category.id not in post_in_closed_categories
):
action(permissions, category)
```
"""

__slots__ = FilterHook.__slots__

def __call__(
self,
action: CheckPostInClosedCategoryPermissionHookAction,
permissions: "UserPermissionsProxy",
category: Category,
) -> None:
return super().__call__(action, permissions, category)


check_post_in_closed_category_permission_hook = (
CheckPostInClosedCategoryPermissionHook()
)
123 changes: 123 additions & 0 deletions misago/permissions/hooks/check_start_thread_in_category_permission.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
from typing import TYPE_CHECKING, Protocol

from ...categories.models import Category
from ...plugins.hooks import FilterHook

if TYPE_CHECKING:
from ..proxy import UserPermissionsProxy


class CheckStartThreadInCategoryPermissionHookAction(Protocol):
"""
A standard Misago function used to check if the user has permission to
start a new thread in a category. It raises Django's `PermissionDenied` with
an error message if they can't start thread in a category.
# Arguments
## `user_permissions: UserPermissionsProxy`
A proxy object with the current user's permissions.
## `category: Category`
A category to check permissions for.
"""

def __call__(
self,
permissions: "UserPermissionsProxy",
category: Category,
) -> None: ...


class CheckStartThreadInCategoryPermissionHookFilter(Protocol):
"""
A function implemented by a plugin that can be registered in this hook.
# Arguments
## `action: CheckStartThreadInCategoryPermissionHookAction`
A standard Misago function used to check if the user has permission to
start a new thread in a category. It raises Django's `PermissionDenied` with
an error message if they can't start thread in a category.
See the [action](#action) section for details.
## `user_permissions: UserPermissionsProxy`
A proxy object with the current user's permissions.
## `category: Category`
A category to check permissions for.
"""

def __call__(
self,
action: CheckStartThreadInCategoryPermissionHookAction,
permissions: "UserPermissionsProxy",
category: Category,
) -> None: ...


class CheckStartThreadInCategoryPermissionHook(
FilterHook[
CheckStartThreadInCategoryPermissionHookAction,
CheckStartThreadInCategoryPermissionHookFilter,
]
):
"""
This hook wraps the standard Misago function used to check if the user has
permission to start a new thread in a category. It raises Django's
`PermissionDenied` with an error message if they can't start thread in a category.
# Example
The code below implements a custom filter function that prevents the user from
starting a thread in category if their account is newer than 7 days.
```python
from datetime import timedelta
from django.core.exceptions import PermissionDenied
from django.utils import timezone
from misago.categories.models import Category
from misago.permissions.hooks import check_start_thread_in_category_permission_hook
from misago.permissions.proxy import UserPermissionsProxy
@check_start_thread_in_category_permission_hook.append_filter
def check_user_can_start_thread(
action,
permissions: UserPermissionsProxy,
category: Category,
) -> None:
action(permissions, category)
user = permissions.user
if (
user.is_authenticated
and user.joined_on > timezone.now() - timedelta(days=7):
):
raise PermissionDenied(
"Your account was created less than 7 days ago. "
"You can't start threads yet."
)
```
"""

__slots__ = FilterHook.__slots__

def __call__(
self,
action: CheckStartThreadInCategoryPermissionHookAction,
permissions: "UserPermissionsProxy",
category: Category,
) -> None:
return super().__call__(action, permissions, category)


check_start_thread_in_category_permission_hook = (
CheckStartThreadInCategoryPermissionHook()
)
24 changes: 24 additions & 0 deletions misago/permissions/threads/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,25 @@

from ...categories.models import Category
from ..enums import CategoryPermission
from ..hooks import (
check_post_in_closed_category_permission_hook,
check_start_thread_in_category_permission_hook,
)
from ..proxy import UserPermissionsProxy


def check_post_in_closed_category_permission(
permissions: UserPermissionsProxy, category: Category
):
check_post_in_closed_category_permission_hook(
_check_post_in_closed_category_permission_action,
permissions,
category,
)


def _check_post_in_closed_category_permission_action(
permissions: UserPermissionsProxy, category: Category
):
if category.is_closed and not (
permissions.is_global_moderator
Expand All @@ -23,6 +37,16 @@ def check_post_in_closed_category_permission(

def check_start_thread_in_category_permission(
permissions: UserPermissionsProxy, category: Category
):
check_start_thread_in_category_permission_hook(
_check_start_thread_in_category_permission_action,
permissions,
category,
)


def _check_start_thread_in_category_permission_action(
permissions: UserPermissionsProxy, category: Category
):
if category.id not in permissions.categories[CategoryPermission.START]:
raise PermissionDenied(
Expand Down

0 comments on commit a6c7307

Please sign in to comment.