From c5524b17b10865f655eb47e43cc5a9c9ee2c4125 Mon Sep 17 00:00:00 2001 From: Jason Date: Fri, 2 Feb 2024 21:20:31 -0500 Subject: [PATCH] added the templates. rest is Lukas job does most of the work for #251 --- core/admin.py | 1 + core/api/urls.py | 9 ++- core/api/v3/views/user.py | 64 +++++++++++--------- core/api/views/objects/user.py | 4 +- core/api/views/term.py | 12 ++-- core/models/user.py | 33 +++++++--- core/tasks.py | 9 +-- core/templates/core/email/user/deleted.html | 24 ++++++++ core/templates/core/email/user/deleted.txt | 18 ++++++ core/templates/core/email/user/restored.html | 25 ++++++++ core/templates/core/email/user/restored.txt | 18 ++++++ 11 files changed, 166 insertions(+), 51 deletions(-) create mode 100644 core/templates/core/email/user/deleted.html create mode 100644 core/templates/core/email/user/deleted.txt create mode 100644 core/templates/core/email/user/restored.html create mode 100644 core/templates/core/email/user/restored.txt diff --git a/core/admin.py b/core/admin.py index d590677f..3f1bc379 100644 --- a/core/admin.py +++ b/core/admin.py @@ -611,6 +611,7 @@ class UserAdmin(DjangoUserAdmin): "is_teacher", "groups", "graduating_year", + "is_deleted", ] search_fields = [ "username", diff --git a/core/api/urls.py b/core/api/urls.py index eaeff01d..5f2b04ef 100644 --- a/core/api/urls.py +++ b/core/api/urls.py @@ -55,11 +55,14 @@ path("v3/staff", staff, name="api_staff3"), path("v3/feeds", feeds, name="api_feeds3"), path( - "v3/obj/user//delete", UserDeleteView.as_view(), name="api3_user_delete" + "v3/obj/user/me/delete", UserDeleteView.as_view(), name="api3_user_delete" ), path( - "v3/obj/user//restore", UserRestoreView.as_view(), name="api3_user_restore" - ), path("v3/obj/", ObjectList.as_view(), name="api_object_list3"), + "v3/obj/user/me/restore", + UserRestoreView.as_view(), + name="api3_user_restore", + ), + path("v3/obj/", ObjectList.as_view(), name="api_object_list3"), path("v3/obj//new", ObjectNew.as_view(), name="api_object_new3"), path( "v3/obj//single/", # lookup is typically ID unless otherwise specified via the ?lookup= query parameter diff --git a/core/api/v3/views/user.py b/core/api/v3/views/user.py index ad074a34..d0f2f3a5 100644 --- a/core/api/v3/views/user.py +++ b/core/api/v3/views/user.py @@ -3,39 +3,45 @@ from rest_framework.response import Response from rest_framework.views import APIView -from core import models from core.models import User class UserDeleteView(APIView): - permission_classes = [permissions.IsAuthenticated] - - def post(self, id): - user: User = User.objects.filter(id=id).first() - if user is None: - return Response(status=status.HTTP_410_GONE) - elif user.is_deleted: - return JsonResponse( - status=status.HTTP_406_NOT_ACCEPTABLE, - data={"error": "User is already deleted, Please use the restore endpoint."}, - ) - else: - user.mark_deleted() - return JsonResponse(status=status.HTTP_200_OK, data={"message": "User deleted."}) + permission_classes = [permissions.IsAuthenticated] + + def post(self, request): + user: User = request.user + if user is None: # old code from id based delete + return Response(status=status.HTTP_410_GONE) + elif user.is_deleted: + return JsonResponse( + status=status.HTTP_406_NOT_ACCEPTABLE, + data={ + "error": "User is already deleted, Please use the restore endpoint." + }, + ) + else: + user.mark_deleted() + return JsonResponse( + status=status.HTTP_200_OK, data={"message": "User deleted."} + ) class UserRestoreView(APIView): - permission_classes = [permissions.IsAuthenticated] - - def post(self, id): - user: User = models.User.objects.filter(id=id).first() - if user is None: - return Response(status=status.HTTP_410_GONE) - elif not user.is_deleted: - return JsonResponse( - status=status.HTTP_406_NOT_ACCEPTABLE, - data={"error": "User is not marked for deletion, Please use the delete endpoint if you wish to delete your account."}, - ) - else: - user.mark_restored() - return Response(status=status.HTTP_200_OK) + permission_classes = [permissions.IsAuthenticated] + + def post(self, request): + #user: User = models.User.objects.filter(id=id).first() + user: User = request.user + if user is None: # old code from id based delete + return Response(status=status.HTTP_410_GONE) + elif not user.is_deleted: + return JsonResponse( + status=status.HTTP_406_NOT_ACCEPTABLE, + data={ + "error": "User is not marked for deletion, Please use the delete endpoint if you wish to delete your account." + }, + ) + else: + user.mark_restored() + return Response(status=status.HTTP_200_OK) diff --git a/core/api/views/objects/user.py b/core/api/views/objects/user.py index 51d78c0c..c7f84382 100644 --- a/core/api/views/objects/user.py +++ b/core/api/views/objects/user.py @@ -63,7 +63,6 @@ def save(self, **kwargs): return obj class Meta: - model = User fields = [ "id", "username", @@ -83,7 +82,10 @@ class Meta: "saved_announcements", "is_teacher", "is_superuser", + "is_deleted", + "deleted_at" ] + model = User class ListSerializer(serializers.ModelSerializer): diff --git a/core/api/views/term.py b/core/api/views/term.py index b17d7366..aace1788 100644 --- a/core/api/views/term.py +++ b/core/api/views/term.py @@ -39,7 +39,8 @@ class TermDetail(generics.RetrieveAPIView): class TermSchedule(APIView): - def get(self, request, pk, format=None): + @staticmethod + def get(request, pk, fmt=None): term = get_object_or_404(models.Term, pk=pk) date = utils.parse_date_query_param(request) @@ -47,7 +48,8 @@ def get(self, request, pk, format=None): class TermScheduleWeek(APIView): - def get(self, request, pk, format=None): + @staticmethod + def get(request, pk, fmt=None): term = get_object_or_404(models.Term, pk=pk) date = utils.parse_date_query_param(request) @@ -62,7 +64,8 @@ def get(self, request, pk, format=None): class TermCurrent(APIView): - def get(self, request, format=None): + @staticmethod + def get(request, fmt=None): term = models.Term.get_current() if term is None: @@ -73,7 +76,8 @@ def get(self, request, format=None): class TermCurrentSchedule(APIView): - def get(self, request, format=None): + @staticmethod + def get(request, fmt=None): term = models.Term.get_current() date = utils.parse_date_query_param(request) diff --git a/core/models/user.py b/core/models/user.py index 6ab5c825..aa92aa15 100644 --- a/core/models/user.py +++ b/core/models/user.py @@ -57,8 +57,18 @@ class User(AbstractUser): help_text="JSON object with keys as tokens and values as null.", # the length is not specified :( https://github.com/expo/expo/issues/1135#issuecomment-399622890 ) - is_deleted = models.BooleanField(default=False, help_text="If the user is deleted. Never change this in admin", null=False, blank=False) - deleted_at = models.DateTimeField(null=True, default=None, blank=True, help_text="When the user was deleted. Never change this in admin") + is_deleted = models.BooleanField( + default=False, + help_text="If the user is deleted. Never change this in admin", + null=False, + blank=False, + ) + deleted_at = models.DateTimeField( + null=True, + default=None, + blank=True, + help_text="When the user was deleted. Never change this in admin", + ) @property def qltrs2(self): @@ -113,24 +123,25 @@ def mark_deleted(self): self.save() email_template_context = { "user": self, - "time_deleted": timezone.now(), - "restore_link": settings.SITE_URL + reverse("restore", args=(self.id,)), + "deleted_at": timezone.now() + timezone.timedelta(days=14), + "restore_link": settings.SITE_URL + reverse("api3_user_restore"), } + print("hiosas", email_template_context) send_mail( # todo: frontend needs to make a page for this f"[ACTION REQUIRED] Your account has been marked for deletion.", render_to_string( - "core/email/restore_deleted_user.txt", + "core/email/user/deleted.txt", email_template_context, ), None, [self.email], html_message=render_to_string( - "core/email/restore_deleted_user.html", + "core/email/user/deleted.html", email_template_context, ), ) - + def mark_restored(self): self.is_deleted = False self.deleted_at = None @@ -138,20 +149,21 @@ def mark_restored(self): email_template_context = { "user": self, } - + send_mail( # todo: frontend needs to make a page for this f"Your account has successfully been restored.", render_to_string( - "core/email/restored_user.txt", + "core/email/user/restored.txt", email_template_context, ), None, [self.email], html_message=render_to_string( - "core/email/restored_user.html", + "core/email/user/restored.html", email_template_context, ), ) + @classmethod def all(cls): return cls.objects.filter(is_active=True) @@ -195,6 +207,7 @@ class StaffMember(models.Model): ) # if the user got kicked or smth def __str__(self): + self.user: User return f"{self.user.get_full_name()} ({self.user})" @property diff --git a/core/tasks.py b/core/tasks.py index bf98167e..051a7041 100644 --- a/core/tasks.py +++ b/core/tasks.py @@ -65,7 +65,9 @@ def delete_expired_users(): is_deleted=True, last_login__lt=dt.datetime.now() - dt.timedelta(days=14) ) comments = Comment.objects.filter(author__in=queryset) - comments.update(body=None, last_modified=timezone.now()) # if body is None "deleted on %last_modified% would be shown + comments.update( + body=None, last_modified=timezone.now() + ) # if body is None "deleted on %last_modified% would be shown queryset.update( # We need to object to not break posts or comments first_name="Deleted", last_name="User", @@ -81,9 +83,8 @@ def delete_expired_users(): saved_announcements=[], expo_notif_tokens={}, ) - queryset.update( - email=Concat(F("random_username"), Value("@maclyonsden.com")) - ) + queryset.update(email=Concat(F("random_username"), Value("@maclyonsden.com"))) + @app.task def run_group_migrations(): diff --git a/core/templates/core/email/user/deleted.html b/core/templates/core/email/user/deleted.html new file mode 100644 index 00000000..2df04c31 --- /dev/null +++ b/core/templates/core/email/user/deleted.html @@ -0,0 +1,24 @@ +{% extends '../base.html' %} +{% block recipient %} + {{ user.get_full_name }}, your account will be deleted on {{ deleted_at }} +{% endblock %} +{% block information %} + + + + + + +
+ If you wish to recover your account, please click the button below before the date mentioned above. +
+ + +{% endblock %} +{% block button %} + Recover +{% endblock %} diff --git a/core/templates/core/email/user/deleted.txt b/core/templates/core/email/user/deleted.txt new file mode 100644 index 00000000..6e24afe3 --- /dev/null +++ b/core/templates/core/email/user/deleted.txt @@ -0,0 +1,18 @@ +{% load settings_tags %} + +Hello {{ user.get_full_name }}, + +Your account is subjected to be deleted on {{ deleted_at }}. + +This action is reversible up to the time above. If you want to keep your account, please click or copy paste the following link to recover your account. + +{{ recover_link }} + +Please click or copy paste the following link to review this announcement. + +{{ review_link }} + +Sincerely, +Metropolis Team + +This email is automated. Please send us an email at {% settings_value 'DEFAULT_FROM_EMAIL' %} for any concerns. diff --git a/core/templates/core/email/user/restored.html b/core/templates/core/email/user/restored.html new file mode 100644 index 00000000..c60f7b06 --- /dev/null +++ b/core/templates/core/email/user/restored.html @@ -0,0 +1,25 @@ +{% extends '../base.html' %} +{% block recipient %} + {{ user.get_full_name }}, your account will be deleted on {{ deleted_at }} +{% endblock %} +{% block information %} + + + + + + +
+ If you wish to recover your account, please click the button below before the date mentioned + above. +
+ + +{% endblock %} +{% block button %} + Recover +{% endblock %} diff --git a/core/templates/core/email/user/restored.txt b/core/templates/core/email/user/restored.txt new file mode 100644 index 00000000..6e24afe3 --- /dev/null +++ b/core/templates/core/email/user/restored.txt @@ -0,0 +1,18 @@ +{% load settings_tags %} + +Hello {{ user.get_full_name }}, + +Your account is subjected to be deleted on {{ deleted_at }}. + +This action is reversible up to the time above. If you want to keep your account, please click or copy paste the following link to recover your account. + +{{ recover_link }} + +Please click or copy paste the following link to review this announcement. + +{{ review_link }} + +Sincerely, +Metropolis Team + +This email is automated. Please send us an email at {% settings_value 'DEFAULT_FROM_EMAIL' %} for any concerns.