ФУНКЦИИ - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: блокировка и удаление участников воркспейса в God Mode
This commit is contained in:
@@ -26,5 +26,6 @@ from .workspace import (
|
||||
InstanceWorkSpaceAvailabilityCheckEndpoint,
|
||||
InstanceWorkSpaceEndpoint,
|
||||
InstanceWorkSpaceFeatureEndpoint,
|
||||
InstanceWorkSpaceMemberBanEndpoint,
|
||||
InstanceWorkSpaceMemberEndpoint,
|
||||
)
|
||||
|
||||
@@ -8,6 +8,7 @@ from rest_framework import status
|
||||
from django.db import IntegrityError
|
||||
from django.db.models import Count, OuterRef, Func, F, Q
|
||||
from django.utils import timezone
|
||||
from django.utils.dateparse import parse_datetime
|
||||
|
||||
# Module imports
|
||||
from plane.app.views.base import BaseAPIView
|
||||
@@ -23,6 +24,7 @@ from plane.db.models import (
|
||||
)
|
||||
from plane.license.api.serializers import WorkspaceSerializer
|
||||
from plane.utils.constants import RESTRICTED_WORKSPACE_SLUGS
|
||||
from plane.utils.workspace_bans import release_expired_workspace_bans, release_workspace_member_ban
|
||||
|
||||
|
||||
WORKSPACE_ROLES = {5, 15, 20}
|
||||
@@ -46,6 +48,9 @@ def serialize_instance_workspace_member(workspace_member):
|
||||
},
|
||||
"role": workspace_member.role,
|
||||
"is_active": workspace_member.is_active,
|
||||
"is_banned": workspace_member.is_banned,
|
||||
"banned_at": workspace_member.banned_at,
|
||||
"banned_until": workspace_member.banned_until,
|
||||
"created_at": workspace_member.created_at,
|
||||
"active_project_count": getattr(workspace_member, "active_project_count", 0),
|
||||
"admin_project_count": getattr(workspace_member, "admin_project_count", 0),
|
||||
@@ -57,6 +62,7 @@ def has_other_workspace_admin(workspace_member):
|
||||
workspace=workspace_member.workspace,
|
||||
role=20,
|
||||
is_active=True,
|
||||
is_banned=False,
|
||||
member__is_bot=False,
|
||||
).exclude(pk=workspace_member.pk).exists()
|
||||
|
||||
@@ -87,8 +93,10 @@ def is_only_project_admin(workspace_member):
|
||||
|
||||
|
||||
def get_workspace_member_queryset(workspace_id):
|
||||
release_expired_workspace_bans(workspace_id=workspace_id)
|
||||
return (
|
||||
WorkspaceMember.objects.filter(workspace_id=workspace_id, member__is_bot=False, is_active=True)
|
||||
WorkspaceMember.objects.filter(workspace_id=workspace_id, member__is_bot=False)
|
||||
.filter(Q(is_active=True) | Q(is_banned=True))
|
||||
.select_related("workspace", "member", "member__avatar_asset")
|
||||
.annotate(
|
||||
active_project_count=Count(
|
||||
@@ -276,6 +284,77 @@ class InstanceWorkSpaceMemberEndpoint(BaseAPIView):
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
|
||||
class InstanceWorkSpaceMemberBanEndpoint(BaseAPIView):
|
||||
permission_classes = [InstanceAdminPermission]
|
||||
|
||||
def patch(self, request, workspace_id, member_id):
|
||||
workspace_member = get_workspace_member_queryset(workspace_id).filter(pk=member_id).first()
|
||||
if not workspace_member:
|
||||
return Response({"error": "Workspace member not found"}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
is_banned = request.data.get("is_banned")
|
||||
if not isinstance(is_banned, bool):
|
||||
return Response({"error": "is_banned must be a boolean"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
banned_until = None
|
||||
if is_banned:
|
||||
raw_banned_until = request.data.get("banned_until")
|
||||
if raw_banned_until not in (None, ""):
|
||||
banned_until = parse_datetime(str(raw_banned_until))
|
||||
if banned_until is None:
|
||||
return Response({"error": "banned_until must be an ISO datetime"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
if timezone.is_naive(banned_until):
|
||||
banned_until = timezone.make_aware(banned_until, timezone.get_current_timezone())
|
||||
if banned_until <= timezone.now():
|
||||
return Response({"error": "banned_until must be in the future"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
if workspace_member.role == 20 and not has_other_workspace_admin(workspace_member):
|
||||
return Response(
|
||||
{"error": "Cannot ban the only workspace admin"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
if is_only_project_admin(workspace_member):
|
||||
return Response(
|
||||
{"error": "Cannot ban a member who is the only admin in one or more projects"},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
project_member_ids = list(
|
||||
ProjectMember.objects.filter(
|
||||
workspace_id=workspace_id,
|
||||
member_id=workspace_member.member_id,
|
||||
is_active=True,
|
||||
).values_list("id", flat=True)
|
||||
)
|
||||
ProjectMember.objects.filter(
|
||||
workspace_id=workspace_id,
|
||||
member_id=workspace_member.member_id,
|
||||
is_active=True,
|
||||
).update(is_active=False, updated_at=timezone.now())
|
||||
|
||||
workspace_member.is_banned = True
|
||||
workspace_member.banned_at = timezone.now()
|
||||
workspace_member.banned_until = banned_until
|
||||
workspace_member.ban_project_member_ids = [str(project_member_id) for project_member_id in project_member_ids]
|
||||
workspace_member.is_active = False
|
||||
workspace_member.save(
|
||||
update_fields=[
|
||||
"is_banned",
|
||||
"banned_at",
|
||||
"banned_until",
|
||||
"ban_project_member_ids",
|
||||
"is_active",
|
||||
"updated_at",
|
||||
]
|
||||
)
|
||||
else:
|
||||
release_workspace_member_ban(workspace_member)
|
||||
|
||||
workspace_member = get_workspace_member_queryset(workspace_id).get(pk=member_id)
|
||||
return Response(serialize_instance_workspace_member(workspace_member), status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
class InstanceWorkSpaceFeatureEndpoint(BaseAPIView):
|
||||
permission_classes = [InstanceAdminPermission]
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ from plane.license.api.views import (
|
||||
InstanceWorkSpaceAvailabilityCheckEndpoint,
|
||||
InstanceWorkSpaceEndpoint,
|
||||
InstanceWorkSpaceFeatureEndpoint,
|
||||
InstanceWorkSpaceMemberBanEndpoint,
|
||||
InstanceWorkSpaceMemberEndpoint,
|
||||
)
|
||||
|
||||
@@ -83,6 +84,11 @@ urlpatterns = [
|
||||
InstanceWorkSpaceMemberEndpoint.as_view(),
|
||||
name="instance-workspace-member",
|
||||
),
|
||||
path(
|
||||
"workspaces/<uuid:workspace_id>/members/<uuid:member_id>/ban/",
|
||||
InstanceWorkSpaceMemberBanEndpoint.as_view(),
|
||||
name="instance-workspace-member-ban",
|
||||
),
|
||||
path(
|
||||
"workspaces/<uuid:workspace_id>/features/",
|
||||
InstanceWorkSpaceFeatureEndpoint.as_view(),
|
||||
|
||||
Reference in New Issue
Block a user