feat: approve users, edit users through manage users page (#1383)

This commit is contained in:
Markos Gogoulos
2025-09-20 15:16:52 +03:00
committed by GitHub
parent 8e8454d8c2
commit cbef629baf
30 changed files with 1384 additions and 790 deletions

View File

@@ -1,3 +1,4 @@
from django.conf import settings
from django.contrib import admin
from .models import User
@@ -5,20 +6,7 @@ from .models import User
class UserAdmin(admin.ModelAdmin):
search_fields = ["email", "username", "name"]
exclude = (
"user_permissions",
"title",
"password",
"groups",
"last_login",
"is_featured",
"location",
"first_name",
"last_name",
"media_count",
"date_joined",
"is_active",
)
exclude = ["user_permissions", "title", "password", "groups", "last_login", "is_featured", "location", "first_name", "last_name", "media_count", "date_joined", "is_active", "is_approved"]
list_display = [
"username",
"name",
@@ -33,5 +21,10 @@ class UserAdmin(admin.ModelAdmin):
list_filter = ["is_superuser", "is_editor", "is_manager"]
ordering = ("-date_added",)
if settings.USERS_NEEDS_TO_BE_APPROVED:
list_display.append("is_approved")
list_filter.append("is_approved")
exclude.remove("is_approved")
admin.site.register(User, UserAdmin)

View File

@@ -1,4 +1,5 @@
from django import forms
from django.conf import settings
from files.methods import is_mediacms_manager
@@ -25,6 +26,7 @@ class UserForm(forms.ModelForm):
"advancedUser",
"is_manager",
"is_editor",
"is_approved",
# "allow_contact",
)
@@ -44,6 +46,11 @@ class UserForm(forms.ModelForm):
self.fields.pop("advancedUser")
self.fields.pop("is_manager")
self.fields.pop("is_editor")
if not settings.USERS_NEEDS_TO_BE_APPROVED or not is_mediacms_manager(user):
if "is_approved" in self.fields:
self.fields.pop("is_approved")
if user.socialaccount_set.exists():
# for Social Accounts do not allow to edit the name
self.fields["name"].widget.attrs['readonly'] = True

View File

@@ -0,0 +1,17 @@
# Generated by Django 5.1.6 on 2025-09-19 14:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='user',
name='is_approved',
field=models.BooleanField(blank=True, db_index=True, default=False, null=True, verbose_name='Is approved'),
),
]

View File

@@ -29,6 +29,7 @@ class User(AbstractUser):
name = models.CharField("full name", max_length=250, db_index=True)
date_added = models.DateTimeField("date added", default=timezone.now, db_index=True)
is_featured = models.BooleanField("Is featured", default=False, db_index=True)
is_approved = models.BooleanField("Is approved", default=False, null=True, blank=True, db_index=True)
title = models.CharField("Title", max_length=250, blank=True)
advancedUser = models.BooleanField("advanced user", default=False, db_index=True)

View File

@@ -22,7 +22,7 @@ class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
read_only_fields = (
read_only_fields = [
"date_added",
"is_featured",
"uid",
@@ -31,8 +31,8 @@ class UserSerializer(serializers.ModelSerializer):
"is_editor",
"is_manager",
"email_is_verified",
)
fields = (
]
fields = [
"description",
"date_added",
"name",
@@ -45,7 +45,11 @@ class UserSerializer(serializers.ModelSerializer):
"is_editor",
"is_manager",
"email_is_verified",
)
]
if settings.USERS_NEEDS_TO_BE_APPROVED:
fields.append("is_approved")
read_only_fields.append("is_approved")
class UserDetailSerializer(serializers.ModelSerializer):

View File

@@ -205,6 +205,12 @@ class UserList(APIView):
operation_description='Paginated listing of users',
)
def get(self, request, format=None):
if settings.CAN_SEE_MEMBERS_PAGE == "editors" and not is_mediacms_editor(request.user):
raise PermissionDenied("You do not have permission to view this page.")
if settings.CAN_SEE_MEMBERS_PAGE == "admins" and not request.user.is_superuser:
raise PermissionDenied("You do not have permission to view this page.")
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
paginator = pagination_class()
users = User.objects.filter()
@@ -213,11 +219,57 @@ class UserList(APIView):
if name:
users = users.filter(Q(name__icontains=name) | Q(username__icontains=name))
if settings.USERS_NEEDS_TO_BE_APPROVED:
is_approved = request.GET.get("is_approved")
if is_approved == "true":
users = users.filter(is_approved=True)
elif is_approved == "false":
users = users.filter(Q(is_approved=False) | Q(is_approved__isnull=True))
page = paginator.paginate_queryset(users, request)
serializer = UserSerializer(page, many=True, context={"request": request})
return paginator.get_paginated_response(serializer.data)
@swagger_auto_schema(
request_body=openapi.Schema(
type=openapi.TYPE_OBJECT,
required=["username", "password", "email", "name"],
properties={
"username": openapi.Schema(type=openapi.TYPE_STRING),
"password": openapi.Schema(type=openapi.TYPE_STRING),
"email": openapi.Schema(type=openapi.TYPE_STRING, format=openapi.FORMAT_EMAIL),
"name": openapi.Schema(type=openapi.TYPE_STRING),
},
),
tags=["Users"],
operation_summary="Create user",
operation_description="Create a new user. Only for managers.",
responses={201: UserSerializer},
)
def post(self, request, format=None):
if not is_mediacms_manager(request.user):
raise PermissionDenied("You do not have permission to create users.")
username = request.data.get("username")
password = request.data.get("password")
email = request.data.get("email")
name = request.data.get("name")
if not all([username, password, email, name]):
return Response({"detail": "username, password, email, and name are required."}, status=status.HTTP_400_BAD_REQUEST)
if User.objects.filter(username=username).exists():
return Response({"detail": "A user with that username already exists."}, status=status.HTTP_400_BAD_REQUEST)
if User.objects.filter(email=email).exists():
return Response({"detail": "A user with that email already exists."}, status=status.HTTP_400_BAD_REQUEST)
user = User.objects.create_user(username=username, password=password, email=email, name=name)
serializer = UserSerializer(user, context={"request": request})
return Response(serializer.data, status=status.HTTP_201_CREATED)
class UserDetail(APIView):
""""""
@@ -284,27 +336,36 @@ class UserDetail(APIView):
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@swagger_auto_schema(
manual_parameters=[],
manual_parameters=[
openapi.Parameter(name='action', in_=openapi.IN_FORM, type=openapi.TYPE_STRING, required=True, description="action to perform ('change_password' or 'approve_user')"),
openapi.Parameter(name='password', in_=openapi.IN_FORM, type=openapi.TYPE_STRING, required=False, description="new password (if action is 'change_password')"),
],
tags=['Users'],
operation_summary='Xto_be_written',
operation_description='to_be_written',
operation_summary='Update user details',
operation_description='Allows a user to change their password. Allows a manager to approve a user.',
)
def put(self, request, uid, format=None):
# ADMIN
user = self.get_user(uid)
def put(self, request, username, format=None):
user = self.get_user(username)
if isinstance(user, Response):
return user
if not request.user.is_superuser:
return Response({"detail": "not allowed"}, status=status.HTTP_400_BAD_REQUEST)
action = request.data.get("action")
if action == "feature":
user.is_featured = True
if action == "change_password":
# Permission to edit user is already checked by self.get_user -> self.check_object_permissions
password = request.data.get("password")
if not password:
return Response({"detail": "Password is required"}, status=status.HTTP_400_BAD_REQUEST)
user.set_password(password)
user.save()
elif action == "unfeature":
user.is_featured = False
elif action == "approve_user":
if not is_mediacms_manager(request.user):
raise PermissionDenied("You do not have permission to approve users.")
user.is_approved = True
user.save()
else:
return Response({"detail": "Invalid action"}, status=status.HTTP_400_BAD_REQUEST)
serializer = UserDetailSerializer(user, context={"request": request})
return Response(serializer.data)