diff --git a/accounts/views/edit.py b/accounts/views/edit.py index 7741e3f..c62a201 100644 --- a/accounts/views/edit.py +++ b/accounts/views/edit.py @@ -13,7 +13,7 @@ class EditView(APIView): authentication_classes = (SessionAuthentication,) def get(self, request: HttpRequest): - return Response({"username": request.user.username}) + return Response({"username": request.user.username, "id": request.user.pk}) def patch(self, request: HttpRequest): data: dict = request.data diff --git a/accounts/views/logged.py b/accounts/views/logged.py index 3956cd2..8e2fecb 100644 --- a/accounts/views/logged.py +++ b/accounts/views/logged.py @@ -13,4 +13,6 @@ class LoggedView(APIView): authentication_classes = (SessionAuthentication,) def get(self, request: HttpRequest): - return Response(str(request.user.is_authenticated), status=status.HTTP_200_OK) \ No newline at end of file + if (request.user.is_authenticated): + return Response({'id': request.user.pk}, status=status.HTTP_200_OK) + return Response('false', status=status.HTTP_200_OK) \ No newline at end of file diff --git a/accounts/views/login.py b/accounts/views/login.py index 1ee192f..490f79b 100644 --- a/accounts/views/login.py +++ b/accounts/views/login.py @@ -20,4 +20,4 @@ class LoginView(APIView): if user is None: return Response({'user': ['Username or password wrong.']}, status.HTTP_200_OK) login(request, user) - return Response('user connected', status=status.HTTP_200_OK) \ No newline at end of file + return Response({'id': user.pk}, status=status.HTTP_200_OK) \ No newline at end of file diff --git a/frontend/static/js/api/client.js b/frontend/static/js/api/client.js index 4469896..537a99e 100644 --- a/frontend/static/js/api/client.js +++ b/frontend/static/js/api/client.js @@ -1,4 +1,5 @@ import { Account } from "./account.js"; +import { Profile } from "./profile.js"; function getCookie(name) { @@ -73,12 +74,26 @@ class Client return response; } + async _patch_file(uri, file) + { + let response = await fetch(this._url + uri, { + method: "PATCH", + headers: { + "X-CSRFToken": getCookie("csrftoken"), + }, + body: file, + }); + return response; + } + async login(username, password) { let response = await this._post("/api/accounts/login", {username: username, password: password}) let data = await response.json(); - if (data == "user connected") + if (data.id != undefined) { + this.me = new Profile(this) + await this.me.init(data.id) this.logged = true; return null; } @@ -95,7 +110,13 @@ class Client { let response = await this._get("/api/accounts/logged"); let data = await response.json(); - return data === "True"; + + if (data.id !== undefined) + { + this.me = new Profile(this) + await this.me.init(data.id) + } + return data.id !== undefined; } } diff --git a/frontend/static/js/api/profile.js b/frontend/static/js/api/profile.js new file mode 100644 index 0000000..e5ed1a5 --- /dev/null +++ b/frontend/static/js/api/profile.js @@ -0,0 +1,34 @@ +class Profile +{ + constructor (client) + { + this.client = client; + this.username = undefined; + this.avatar_url = undefined + } + + async init(id) + { + let response = await this.client._get(`/api/profiles/${id}`); + let response_data = await response.json(); + + this.id = id; + this.username = response_data.username; + this.avatar_url = response_data.avatar_url; + } + + async change_avatar(form_data) + { + let response = await this.client._patch_file(`/api/profiles/${this.id}`, form_data); + let response_data = await response.json() + + return response_data; + } + + async setData (data) + { + + } +} + +export {Profile} \ No newline at end of file diff --git a/frontend/static/js/views/MeView.js b/frontend/static/js/views/MeView.js index ce5c600..8ae6378 100644 --- a/frontend/static/js/views/MeView.js +++ b/frontend/static/js/views/MeView.js @@ -91,18 +91,31 @@ export default class extends AbstractAuthentificateView if (error_display != null) error_display.innerHTML = response_data[error_field]; }); + let avatar = document.getElementById("avatar"); + + if (avatar.files[0] !== undefined) + { + let form_data = new FormData(); + form_data.append("file", avatar.files[0]); + await client.me.change_avatar(form_data) + } } async getHtml() { return `

ME

- - - - - - +
+ + + + + + +
+
+ +
diff --git a/profiles/models.py b/profiles/models.py index f34eb29..3c12017 100644 --- a/profiles/models.py +++ b/profiles/models.py @@ -3,11 +3,15 @@ from django.db import models from django.contrib.auth.models import User from django.db.models.signals import post_save from django.dispatch import receiver +from django.conf import settings + +def upload_to(instance, filename: str): + return f"./profiles/static/avatars/{instance.pk}.{filename.split('.')[1]}" # Create your models here. class ProfileModel(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) - title = models.CharField(max_length=40) + avatar_url = models.ImageField(upload_to=upload_to, default="../static/avatars/default.avif") #blank=True, null=True) @receiver(post_save, sender=User) def on_user_created(sender, instance, created, **kwargs): diff --git a/profiles/serializers.py b/profiles/serializers.py new file mode 100644 index 0000000..15dde29 --- /dev/null +++ b/profiles/serializers.py @@ -0,0 +1,12 @@ +from rest_framework import serializers +from .models import ProfileModel + +class ProfileSerializer(serializers.ModelSerializer): + + username = serializers.ReadOnlyField(source='user.username') + #creator_id = serializers.ReadOnlyField(source='user.pk') + avatar_url = serializers.ImageField(required=False) + + class Meta: + model = ProfileModel + fields = ["username", "avatar_url"] \ No newline at end of file diff --git a/profiles/static/avatars/1.JPG b/profiles/static/avatars/1.JPG new file mode 100644 index 0000000..a4bc4b8 Binary files /dev/null and b/profiles/static/avatars/1.JPG differ diff --git a/profiles/static/avatars/default.avif b/profiles/static/avatars/default.avif new file mode 100644 index 0000000..d7f1413 Binary files /dev/null and b/profiles/static/avatars/default.avif differ diff --git a/profiles/status_code.py b/profiles/status_code.py deleted file mode 100644 index 187f78f..0000000 --- a/profiles/status_code.py +++ /dev/null @@ -1 +0,0 @@ -PROFILE_NOT_FOUND = "Profile Not Found" \ No newline at end of file diff --git a/profiles/urls.py b/profiles/urls.py index 9dc15a8..5b92202 100644 --- a/profiles/urls.py +++ b/profiles/urls.py @@ -1,7 +1,10 @@ from django.urls import path +from django.conf import settings +from django.conf.urls.static import static -from . import views +from . import viewsets urlpatterns = [ - path("", views.ProfileView.as_view(), name="profile_page"), -] \ No newline at end of file + path("", viewsets.ProfileViewSet.as_view({'get': 'retrieve', 'patch': 'partial_update'}), name="profile_page"), + #path("me", viewsets.ProfileViewSet.as_view(), name="my_profile_page"), +] + static("/static/avatars/", document_root="./avatars") \ No newline at end of file diff --git a/profiles/views.py b/profiles/views.py deleted file mode 100644 index 2a67b8e..0000000 --- a/profiles/views.py +++ /dev/null @@ -1,19 +0,0 @@ -from django.http import HttpRequest -from rest_framework.views import APIView -from rest_framework.response import Response -from rest_framework import permissions, status - -from .models import ProfileModel - -# Create your views here. -class ProfileView(APIView): - permission_classes = (permissions.AllowAny,) - - def get(self, request: HttpRequest, pk: int): - - profile: ProfileModel = ProfileModel.objects.get(pk=pk) - if (profile is None): - return Response(status=status.HTTP_404_NOT_FOUND) - - return Response(status=status.HTTP_200_OK, data={'name': profile.user.username, - 'title': profile.title}) \ No newline at end of file diff --git a/profiles/viewsets.py b/profiles/viewsets.py new file mode 100644 index 0000000..c9f7584 --- /dev/null +++ b/profiles/viewsets.py @@ -0,0 +1,33 @@ +from rest_framework import permissions +from rest_framework.parsers import MultiPartParser, FormParser +from rest_framework import permissions, status +from rest_framework import viewsets +from rest_framework.response import Response + + +from django.db.models import QuerySet + +from .serializers import ProfileSerializer +from .models import ProfileModel + +class ProfileViewSet(viewsets.ModelViewSet): + queryset = ProfileModel.objects.order_by('-pk') + serializer_class = ProfileSerializer + parser_classes = (MultiPartParser, FormParser) + permission_classes = (permissions.IsAuthenticatedOrReadOnly,) + + def perform_create(self, serializer): + serializer.save(user=self.request.user) + + def perform_update(self, serializer): + query: QuerySet = ProfileModel.objects.filter(pk=self.request.user.pk) + if (not query.exists()): + return Response("profile not found", status=status.HTTP_400_BAD_REQUEST) + profile: ProfileModel = ProfileModel.objects.get(pk=self.request.user.pk) + avatar = self.request.data.get("file", None) + if (avatar is not None): + print(profile.avatar_url.name) + if (profile.avatar_url.name != "default.avif"): + profile.avatar_url.storage.delete(profile.avatar_url.name) + profile.avatar_url = avatar + profile.save() \ No newline at end of file diff --git a/trancendence/settings.py b/trancendence/settings.py index e6f29dd..4e89778 100644 --- a/trancendence/settings.py +++ b/trancendence/settings.py @@ -10,6 +10,8 @@ For the full list of settings and their values, see https://docs.djangoproject.com/en/4.2/ref/settings/ """ +import os + from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. @@ -146,4 +148,4 @@ STATIC_URL = 'static/' # Default primary key field type # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field -DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' \ No newline at end of file diff --git a/trancendence/urls.py b/trancendence/urls.py index 4623fa9..0f136f1 100644 --- a/trancendence/urls.py +++ b/trancendence/urls.py @@ -22,4 +22,4 @@ urlpatterns = [ path('api/profiles/', include('profiles.urls')), path('api/accounts/', include('accounts.urls')), path('', include('frontend.urls')), -] +] \ No newline at end of file