settings: avatar & username (& little stuff too)
This commit is contained in:
parent
6d942eea09
commit
c65c986b25
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-01 13:59+0100\n"
|
||||
"POT-Creation-Date: 2024-03-11 11:02+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -17,6 +17,11 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
#: serializers/update_user.py:15
|
||||
msgid "You dont have permission for this user."
|
||||
msgstr "Vous n'avez pas de permissions pour cet utilisateur."
|
||||
|
||||
#: views/login.py:22
|
||||
msgid "Invalid username or password."
|
||||
msgstr "Nom d'utilisateur ou mot de passe incorect."
|
||||
|
20
accounts/serializers/update_user.py
Normal file
20
accounts/serializers/update_user.py
Normal file
@ -0,0 +1,20 @@
|
||||
from rest_framework.serializers import ModelSerializer, ValidationError
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
|
||||
class UpdateUserSerializer(ModelSerializer):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['username']
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
user = self.context['request'].user
|
||||
|
||||
if user.pk != instance.pk:
|
||||
raise ValidationError({'authorize': _('You dont have permission for this user.')})
|
||||
|
||||
instance.username = validated_data.get('username', instance.username)
|
||||
|
||||
instance.save()
|
||||
return instance
|
@ -1,6 +1,6 @@
|
||||
from django.urls import path
|
||||
|
||||
from .views import register, login, logout, delete, edit, logged
|
||||
from .views import register, login, logout, delete, logged, update_profile
|
||||
|
||||
urlpatterns = [
|
||||
path("register", register.RegisterView.as_view(), name="register"),
|
||||
@ -8,6 +8,5 @@ urlpatterns = [
|
||||
path("logout", logout.LogoutView.as_view(), name="logout"),
|
||||
path("logged", logged.LoggedView.as_view(), name="logged"),
|
||||
path("delete", delete.DeleteView.as_view(), name="delete"),
|
||||
path("edit", edit.EditView.as_view(), name="change_password")
|
||||
|
||||
]
|
||||
path('update_profile', update_profile.UpdateProfileView.as_view(), name='update_profile')
|
||||
]
|
||||
|
@ -5,17 +5,18 @@ from django.contrib.auth import logout
|
||||
from django.http import HttpRequest
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
|
||||
|
||||
class DeleteView(APIView):
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
|
||||
def delete(self, request: HttpRequest):
|
||||
data: dict = request.data
|
||||
|
||||
password: str = data["password"]
|
||||
if (password is None):
|
||||
return Response({"password": ["This field may not be blank."]})
|
||||
if (request.user.check_password(password) == False):
|
||||
return Response({"password": ["Password wrong."]})
|
||||
if (request.user.check_password(password) is False):
|
||||
return Response({"password": ["Password incorrect."]},
|
||||
status.HTTP_401_UNAUTHORIZED)
|
||||
request.user.delete()
|
||||
logout(request)
|
||||
return Response("user deleted", status=status.HTTP_200_OK)
|
||||
return Response(status=status.HTTP_200_OK)
|
||||
|
@ -1,45 +0,0 @@
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import permissions, status
|
||||
from django.http import HttpRequest
|
||||
from django.contrib.auth import login
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
from django.contrib.auth.models import User
|
||||
import re
|
||||
|
||||
class EditView(APIView):
|
||||
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
|
||||
def get(self, request: HttpRequest):
|
||||
return Response({"username": request.user.username, "id": request.user.pk})
|
||||
|
||||
def patch(self, request: HttpRequest):
|
||||
data: dict = request.data
|
||||
|
||||
current_password: str = data.get("current_password")
|
||||
if (current_password is None):
|
||||
return Response({"current_password": ["This field may not be blank."]})
|
||||
|
||||
user_object = request.user
|
||||
|
||||
if (user_object.check_password(current_password) == False):
|
||||
return Response({"current_password": ["Password is wrong."]})
|
||||
|
||||
new_username = data.get("username", user_object.username)
|
||||
if (new_username != user_object.username):
|
||||
if (User.objects.filter(username=new_username).exists()):
|
||||
return Response({"username": ["A user with that username already exists."]})
|
||||
if (set(new_username) == {' '}):
|
||||
return Response({"username": ["This field may not be blank."]})
|
||||
if (re.search('^([a-z]||\@||\+||\-||\_)+$', new_username) is None):
|
||||
return Response({"username":["Enter a valid username. This value may contain only letters, numbers, and @/./+/-/_ characters."]})
|
||||
|
||||
new_password: str = data.get("password")
|
||||
if (new_password is not None):
|
||||
user_object.set_password(new_password)
|
||||
|
||||
user_object.save()
|
||||
|
||||
return Response("data has been alterate")
|
@ -2,15 +2,13 @@ from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import permissions, status
|
||||
from django.http import HttpRequest
|
||||
from django.contrib.auth import login
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
|
||||
from ..serializers.login import LoginSerializer
|
||||
|
||||
class LoggedView(APIView):
|
||||
|
||||
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
|
||||
def get(self, request: HttpRequest):
|
||||
return Response(status = (status.HTTP_200_OK if request.user.is_authenticated else status.HTTP_400_BAD_REQUEST))
|
||||
return Response(status=status.HTTP_200_OK if request.user.is_authenticated else status.HTTP_400_BAD_REQUEST)
|
||||
|
@ -8,8 +8,9 @@ from django.utils.translation import gettext as _
|
||||
|
||||
from ..serializers.login import LoginSerializer
|
||||
|
||||
|
||||
class LoginView(APIView):
|
||||
|
||||
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
|
||||
|
@ -5,9 +5,11 @@ from rest_framework.response import Response
|
||||
from django.http import HttpRequest
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
|
||||
|
||||
class LogoutView(APIView):
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
|
||||
def get(self, request: HttpRequest):
|
||||
logout(request)
|
||||
return Response("user unlogged", status=status.HTTP_200_OK)
|
||||
return Response("user logged out", status.HTTP_200_OK)
|
||||
|
@ -5,8 +5,10 @@ from rest_framework.response import Response
|
||||
from django.http import HttpRequest
|
||||
from django.contrib.auth import login
|
||||
|
||||
|
||||
class RegisterView(APIView):
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
|
||||
def post(self, request: HttpRequest):
|
||||
data = request.data
|
||||
serializer = RegisterSerialiser(data=data)
|
||||
|
14
accounts/views/update_profile.py
Normal file
14
accounts/views/update_profile.py
Normal file
@ -0,0 +1,14 @@
|
||||
from ..serializers.update_user import UpdateUserSerializer
|
||||
from rest_framework.generics import UpdateAPIView
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
|
||||
class UpdateProfileView(UpdateAPIView):
|
||||
|
||||
queryset = User.objects.all()
|
||||
permission_classes = (IsAuthenticated,)
|
||||
serializer_class = UpdateUserSerializer
|
||||
|
||||
def get_object(self):
|
||||
return self.queryset.get(pk=self.request.user.pk)
|
@ -1,11 +1,3 @@
|
||||
#app #avatar {
|
||||
max-height: 10em;
|
||||
max-width: 10em;
|
||||
min-height: 6em;
|
||||
min-width: 6em;
|
||||
}
|
||||
|
||||
|
||||
#popup {
|
||||
position: fixed;
|
||||
font-size: 1.2em;
|
||||
|
@ -1,11 +0,0 @@
|
||||
#app * {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
|
||||
#app #main
|
||||
{
|
||||
width: 60%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
@ -48,41 +48,24 @@ class Account
|
||||
}
|
||||
|
||||
/**
|
||||
* Get account data (username)
|
||||
* @returns {?Promise<Object>}
|
||||
*/
|
||||
async get()
|
||||
* @param {String} newUsername
|
||||
* @returns {?Promise<Object>}
|
||||
*/
|
||||
async updateUsername(newUsername)
|
||||
{
|
||||
let response = await this.client._get("/api/accounts/edit");
|
||||
let response_data = await response.json();
|
||||
const data = {
|
||||
username: newUsername
|
||||
};
|
||||
const response = await this.client._patch_json(`/api/accounts/update_profile`, data);
|
||||
const respondeData = await response.json();
|
||||
|
||||
if (response.status === 403)
|
||||
{
|
||||
this.client._update_logged(false);
|
||||
if (response.status === 200) {
|
||||
this.client.me.username = respondeData.username;
|
||||
document.getElementById('navbarDropdownButton').innerHTML = respondeData.username;
|
||||
document.getElementById('myProfileLink').href = '/profiles/' + respondeData.username;
|
||||
return null;
|
||||
}
|
||||
return response_data;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} data
|
||||
* @param {Number} password
|
||||
* @returns {?Object}
|
||||
*/
|
||||
async update(data, password)
|
||||
{
|
||||
data.current_password = password;
|
||||
let response = await this.client._patch_json("/api/accounts/edit", data);
|
||||
let response_data = await response.json();
|
||||
|
||||
if (response.status === 403)
|
||||
{
|
||||
this.client._update_logged(false);
|
||||
return null;
|
||||
}
|
||||
|
||||
return response_data;
|
||||
return respondeData['authorize'] || respondeData['detail'] || respondeData['username']?.join(' ') || 'Error.';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,15 +14,35 @@ class MyProfile extends Profile
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} form_data
|
||||
* @returns {Promise<Object>}
|
||||
* @param {File} selectedFile
|
||||
* @returns {Promise<Response>}
|
||||
*/
|
||||
async change_avatar(form_data)
|
||||
async changeAvatar(selectedFile)
|
||||
{
|
||||
let response = await this.client._patch_file(`/api/profiles/settings`, form_data);
|
||||
let response_data = await response.json();
|
||||
const formData = new FormData();
|
||||
formData.append('avatar', selectedFile);
|
||||
|
||||
return response_data;
|
||||
const response = await this.client._patch_file(`/api/profiles/settings`, formData);
|
||||
const responseData = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
console.log('save', responseData);
|
||||
this.avatar_url = responseData.avatar.substr(responseData.avatar.indexOf('static') - 1);
|
||||
return null;
|
||||
}
|
||||
return responseData;
|
||||
}
|
||||
|
||||
async deleteAvatar() {
|
||||
const response = await this.client._delete('/api/profiles/settings');
|
||||
const responseData = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
console.log('delete', responseData);
|
||||
this.avatar_url = responseData.avatar.substr(responseData.avatar.indexOf('static') - 1);
|
||||
return null;
|
||||
}
|
||||
return responseData;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,55 +1,82 @@
|
||||
import { client, navigateTo } from "../index.js";
|
||||
import { clear, fill_errors } from "../utils/formUtils.js";
|
||||
import AbstractAuthenticatedView from "./abstracts/AbstractAuthenticatedView.js";
|
||||
import { client, navigateTo } from '../index.js';
|
||||
import { clear, fill_errors } from '../utils/formUtils.js';
|
||||
import AbstractAuthenticatedView from './abstracts/AbstractAuthenticatedView.js';
|
||||
|
||||
export default class extends AbstractAuthenticatedView
|
||||
{
|
||||
constructor(params)
|
||||
{
|
||||
super(params, "Settings");
|
||||
super(params, 'Settings');
|
||||
this.PROFILE_PICTURE_MAX_SIZE = 2 * 1024 * 1024; // 2MB
|
||||
}
|
||||
|
||||
async postInit()
|
||||
{
|
||||
this.display_avatar();
|
||||
document.getElementById("save-account-button").onclick = () => this.save_account();
|
||||
document.getElementById("delete-account-button").onclick = () => this.delete_account();
|
||||
document.getElementById("save-profile-button").onclick = () => this.save_profile();
|
||||
this.avatarInit();
|
||||
this.usernameInit();
|
||||
|
||||
// document.getElementById('delete-account-button').onclick = () => this.delete_account();
|
||||
}
|
||||
|
||||
async display_avatar() {
|
||||
let profile = await client.profiles.getProfile(client.me.username);
|
||||
if (profile !== undefined || profile !== null) {
|
||||
if (document.getElementById("avatar") != undefined)
|
||||
document.getElementById("avatar").remove();
|
||||
let avatar = document.createElement("img");
|
||||
avatar.id = "avatar";
|
||||
avatar.src = profile.avatar_url + '?t=' +new Date().getTime();
|
||||
document.getElementsByClassName("avatar")[0].appendChild(avatar);
|
||||
usernameInit() {
|
||||
const usernameInput = document.getElementById('usernameInput');
|
||||
const usernameSave = document.getElementById('usernameSave');
|
||||
|
||||
usernameInput.oninput = e => {
|
||||
const value = e.target.value;
|
||||
if (value != client.me.username && value.length)
|
||||
usernameSave.classList.remove('disabled');
|
||||
else
|
||||
usernameSave.classList.add('disabled');
|
||||
}
|
||||
usernameSave.onclick = _ => this.saveUsername();
|
||||
}
|
||||
|
||||
avatarInit() {
|
||||
const avatar = document.getElementById('avatar');
|
||||
const avatarInput = document.getElementById('avatarInput');
|
||||
const avatarUpload = document.getElementById('avatarUpload');
|
||||
const avatarDelete = document.getElementById('avatarDelete');
|
||||
|
||||
avatar.onclick = _ => avatarInput.click();
|
||||
avatarInput.onchange = function () {
|
||||
const selectedFile = this.files[0];
|
||||
if (!selectedFile)
|
||||
return;
|
||||
|
||||
avatar.src = URL.createObjectURL(selectedFile);
|
||||
avatarUpload.classList.remove('d-none');
|
||||
}
|
||||
avatarUpload.onclick = _ => this.saveAvatar();
|
||||
avatarDelete.onclick = _ => this.deleteAvatar();
|
||||
}
|
||||
|
||||
async displayAvatar() {
|
||||
let avatar = document.getElementById('avatar');
|
||||
avatar.src = client.me.avatar_url + '?t=' + new Date().getTime();
|
||||
console.log(avatar.src);
|
||||
}
|
||||
|
||||
async delete_account()
|
||||
{
|
||||
let current_password = document.getElementById("current_password-input").value;
|
||||
let current_password = document.getElementById('current_password-input').value;
|
||||
|
||||
let response_data = await client.account.delete(current_password);
|
||||
|
||||
if (response_data === null || response_data === "user deleted")
|
||||
if (response_data === null || response_data === 'user deleted')
|
||||
{
|
||||
navigateTo("/login");
|
||||
navigateTo('/login');
|
||||
return;
|
||||
}
|
||||
clear("innerHTML", ["current_password-input"]);
|
||||
fill_errors({"current_password-input": response_data.password}, "innerHTML");
|
||||
clear('innerHTML', ['current_password-input']);
|
||||
fill_errors({'current_password-input': response_data.password}, 'innerHTML');
|
||||
}
|
||||
|
||||
async save_account()
|
||||
{
|
||||
let username = document.getElementById("username-input").value;
|
||||
let new_password = document.getElementById("new_password-input").value;
|
||||
let current_password = document.getElementById("current_password-input").value;
|
||||
let username = document.getElementById('username-input').value;
|
||||
let new_password = document.getElementById('new_password-input').value;
|
||||
let current_password = document.getElementById('current_password-input').value;
|
||||
|
||||
let data = {};
|
||||
|
||||
@ -61,67 +88,139 @@ export default class extends AbstractAuthenticatedView
|
||||
|
||||
if (response_data === null)
|
||||
{
|
||||
navigateTo("/login");
|
||||
navigateTo('/login');
|
||||
return;
|
||||
}
|
||||
|
||||
if (response_data === "data has been alterate")
|
||||
response_data = {"save-account": "saved"};
|
||||
if (response_data === 'data has been alterate')
|
||||
response_data = {'save-account': 'saved'};
|
||||
|
||||
clear("innerHTML", ["username", "new_password", "current_password", "save-account", "delete-account"]);
|
||||
fill_errors(response_data, "innerHTML");
|
||||
}
|
||||
|
||||
async save_profile()
|
||||
{
|
||||
let avatar = document.getElementById("avatar-input");
|
||||
|
||||
if (avatar.files[0] !== undefined)
|
||||
{
|
||||
if (avatar.files[0].size > this.PROFILE_PICTURE_MAX_SIZE) {
|
||||
document.getElementById("save-profile").classList.add('text-danger');
|
||||
document.getElementById("save-profile").innerHTML = "Image too large :/";
|
||||
return;
|
||||
}
|
||||
let form_data = new FormData();
|
||||
form_data.append("avatar", avatar.files[0]);
|
||||
await client.me.change_avatar(form_data);
|
||||
this.display_avatar();
|
||||
}
|
||||
document.getElementById("save-profile").classList.remove('text-danger');
|
||||
document.getElementById("save-profile").innerHTML = "Saved";
|
||||
clear('innerHTML', ['username', 'new_password', 'current_password', 'save-account', 'delete-account']);
|
||||
fill_errors(response_data, 'innerHTML');
|
||||
}
|
||||
|
||||
async getHtml()
|
||||
{
|
||||
return /* HTML */ `
|
||||
<link rel="stylesheet" href="/static/css/settings.css">
|
||||
<h1>ME</h1>
|
||||
<div id="main">
|
||||
<div class="avatar">
|
||||
</div>
|
||||
<div class="account">
|
||||
<h3>Account</h3>
|
||||
<input type="text" placeholder="username" id="username-input" text=${client.me.username}>
|
||||
<span id="username"></span>
|
||||
<input type=password placeholder="new_password" id="new_password-input">
|
||||
<span id="new_password"></span>
|
||||
<input type=password placeholder="current_password" id="current_password-input">
|
||||
<span id="current_password"></span>
|
||||
async saveUsername()
|
||||
{
|
||||
const usernameInput = document.getElementById('usernameInput');
|
||||
const username = usernameInput.value;
|
||||
const usernameDetail = document.getElementById('usernameDetail');
|
||||
|
||||
<input type="button" value="Save Credentials" id="save-account-button">
|
||||
<span id="save-account"></span>
|
||||
<input type="button" value="Delete Account" id="delete-account-button">
|
||||
<span id="delete-account"></span>
|
||||
</div>
|
||||
<div class="profile">
|
||||
<h3>Profile</h3>
|
||||
<input type="file" id="avatar-input" accept="image/*">
|
||||
<input type="button" value="Save profile" id="save-profile-button">
|
||||
<span id="save-profile"></span>
|
||||
</div>
|
||||
<a href="/logout" class="nav__link" data-link>Logout</a>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
if (!username.length || username === client.me.username)
|
||||
return;
|
||||
|
||||
const error = await client.account.updateUsername(username);
|
||||
if (!error) {
|
||||
usernameDetail.classList.remove('text-danger');
|
||||
usernameDetail.classList.add('text-success');
|
||||
usernameDetail.innerHTML = 'Username Saved.';
|
||||
setTimeout(_ => usernameDetail.innerHTML = '', 2000);
|
||||
document.getElementById('usernameSave').classList.add('disabled');
|
||||
} else {
|
||||
usernameDetail.classList.remove('text-success');
|
||||
usernameDetail.classList.add('text-danger');
|
||||
usernameDetail.innerHTML = error;
|
||||
document.getElementById('usernameSave').classList.add('disabled');
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
async saveAvatar()
|
||||
{
|
||||
const avatarInput = document.getElementById('avatarInput');
|
||||
const selectedFile = avatarInput.files[0];
|
||||
const avatarDetail = document.getElementById('avatarDetail');
|
||||
|
||||
if (!selectedFile)
|
||||
return;
|
||||
|
||||
if (selectedFile.size > this.PROFILE_PICTURE_MAX_SIZE) {
|
||||
avatarDetail.classList.remove('text-success');
|
||||
avatarDetail.classList.add('text-danger');
|
||||
avatarDetail.innerHTML = 'Image is too large.'; //to translate
|
||||
return;
|
||||
}
|
||||
|
||||
const error = await client.me.changeAvatar(selectedFile);
|
||||
if (!error) {
|
||||
avatarDetail.classList.remove('text-danger');
|
||||
avatarDetail.classList.add('text-success');
|
||||
avatarDetail.innerHTML = 'Avatar saved.'; //to translate
|
||||
setTimeout(_ => avatarDetail.innerHTML = '', 2000);
|
||||
document.getElementById('avatarDelete').classList.remove('d-none');
|
||||
document.getElementById('avatarUpload').classList.add('d-none');
|
||||
avatarInput.value = null;
|
||||
} else {
|
||||
avatarDetail.classList.remove('text-success');
|
||||
avatarDetail.classList.add('text-danger');
|
||||
avatarDetail.innerHTML = error.avatar[0];
|
||||
document.getElementById('avatarUpload').classList.add('d-none');
|
||||
avatarInput.value = null;
|
||||
console.log(error);
|
||||
}
|
||||
this.displayAvatar();
|
||||
}
|
||||
|
||||
async deleteAvatar() {
|
||||
const avatarDetail = document.getElementById('avatarDetail');
|
||||
|
||||
const error = await client.me.deleteAvatar();
|
||||
if (!error) {
|
||||
avatarDetail.classList.remove('text-danger');
|
||||
avatarDetail.classList.add('text-success');
|
||||
avatarDetail.innerHTML = 'Avatar deleted.'; //to translate
|
||||
setTimeout(_ => avatarDetail.innerHTML = '', 2000);
|
||||
document.getElementById('avatarDelete').classList.add('d-none');
|
||||
} else {
|
||||
avatarDetail.classList.remove('text-success');
|
||||
avatarDetail.classList.add('text-danger');
|
||||
avatarDetail.innerHTML = 'Something went wrong.'; //to translate
|
||||
}
|
||||
this.displayAvatar();
|
||||
}
|
||||
|
||||
async getHtml()
|
||||
{
|
||||
const avatarUnchanged = client.me.avatar_url === '/static/avatars/default.avif';
|
||||
|
||||
return /* HTML */ `
|
||||
<div class='container col-sm-10 col-8 d-flex rounded border border-2 bg-light-subtle py-3'>
|
||||
<div class='row col-4 bg-body-tertiary border rounded p-2 m-2 d-flex justify-content-center align-items-center'>
|
||||
<h2 class='border-bottom'>Avatar</h2>
|
||||
<img id='avatar' class='rounded p-0' src=${client.me.avatar_url} style='cursor: pointer;'>
|
||||
<input id='avatarInput' class='d-none' type='file' accept='image/*'>
|
||||
<div class='d-flex gap-2 mt-1 px-0'>
|
||||
<span class='my-auto ms-1 me-auto' id='avatarDetail'></span>
|
||||
<button class='btn btn-primary d-none' id='avatarUpload'>Save</button>
|
||||
<button class='btn btn-danger${avatarUnchanged ? ' d-none' : ''}' id='avatarDelete'>Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class='flex-grow-1'>
|
||||
<h1 class='border-bottom ps-1 mb-3'>Account</h1>
|
||||
<div>
|
||||
<div class='input-group'>
|
||||
<div class='form-floating'>
|
||||
<input type='text' class='form-control' id='usernameInput' placeholder='username' value=${client.me.username}>
|
||||
<label for='usernameInput'>Username</label>
|
||||
</div>
|
||||
<button class='input-group-text btn btn-success disabled' id='usernameSave'>Save</button>
|
||||
</div>
|
||||
<span class='form-text' id='usernameDetail'></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
// <input class='form-control' type='text' placeholder='Username' id='username-input' value=${client.me.username}>
|
||||
// <h1>Settings</h1>
|
||||
// <input class='form-control d-inline-block' type='text' placeholder='Username' id='username-input' value=${client.me.username}>
|
||||
// <span id='username'></span>
|
||||
// <input type=password placeholder='New Password' id='new_password-input'>
|
||||
// <span id='new_password'></span>
|
||||
// <input type=password placeholder='Current Password' id='current_password-input'>
|
||||
// <span id='current_password'></span>
|
||||
// <input type='button' value='Save Credentials' id='save-account-button'>
|
||||
// <span id='save-account'></span>
|
||||
//
|
||||
// <input type='button' value='Delete Account' id='delete-account-button'>
|
||||
// <span id='delete-account'></span>
|
||||
}
|
||||
}
|
||||
|
@ -3,16 +3,17 @@ from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.db.models.signals import post_save, pre_delete
|
||||
from django.dispatch import receiver
|
||||
from django.conf import settings
|
||||
from django.db.models import IntegerField
|
||||
|
||||
from games.consumers import game_manager
|
||||
|
||||
from os.path import splitext
|
||||
|
||||
|
||||
def upload_to(instance, filename: str):
|
||||
return f"./profiles/static/avatars/{instance.pk}{splitext(filename)[1]}"
|
||||
|
||||
|
||||
# Create your models here.
|
||||
class ProfileModel(models.Model):
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
|
@ -1,6 +1,8 @@
|
||||
from rest_framework import serializers
|
||||
from .models import ProfileModel
|
||||
from django.conf import settings
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
|
||||
class ProfileSerializer(serializers.ModelSerializer):
|
||||
|
||||
@ -16,5 +18,5 @@ class ProfileSerializer(serializers.ModelSerializer):
|
||||
Check that the image is not too large
|
||||
'''
|
||||
if value.size > settings.PROFILE_PICTURE_MAX_SIZE:
|
||||
raise serializers.ValidationError('Image is too large.');
|
||||
return value;
|
||||
raise serializers.ValidationError(_('Image is too large.'))
|
||||
return value
|
||||
|
@ -4,7 +4,7 @@ from . import viewsets
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path("settings", viewsets.MyProfileViewSet.as_view({'patch': 'partial_update'}), name="my_profile_page"),
|
||||
path("settings", viewsets.MyProfileViewSet.as_view({'patch': 'partial_update', 'delete': 'delete_avatar'}), name="my_profile_page"),
|
||||
path("me", viewsets.MyProfileViewSet.as_view({'get': 'retrieve'}), name="my_profile_page"),
|
||||
path("", viewsets.ProfileViewSet.as_view({'get': 'list'}), name="profiles_list"),
|
||||
path("block", views.BlocksView.as_view(), name="block_page"),
|
||||
@ -12,5 +12,4 @@ urlpatterns = [
|
||||
path("friend", views.FriendsView.as_view(), name="friend_page"),
|
||||
path("user/<str:username>", viewsets.ProfileViewSet.as_view({'get': 'retrieve'}), name="profile_page"),
|
||||
path("id/<int:pk>", viewsets.ProfileViewSet.as_view({'get': 'retrieve_id'}), name="profile_page"),
|
||||
|
||||
]
|
||||
|
@ -1,4 +1,3 @@
|
||||
from rest_framework import permissions
|
||||
from rest_framework.parsers import MultiPartParser, FormParser
|
||||
from rest_framework import permissions, status
|
||||
from rest_framework import viewsets
|
||||
@ -42,28 +41,35 @@ class ProfileViewSet(viewsets.ModelViewSet):
|
||||
profile["avatar"] = profile["avatar"][profile["avatar"].find("static") - 1:]
|
||||
return Response(serializer.data)
|
||||
|
||||
class MyProfileViewSet(viewsets.ModelViewSet):
|
||||
|
||||
class MyProfileViewSet(viewsets.ModelViewSet):
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
serializer_class = ProfileSerializer
|
||||
queryset = ProfileModel.objects.all
|
||||
queryset = ProfileModel.objects.all()
|
||||
|
||||
def get_object(self):
|
||||
obj = self.queryset().get(pk=self.request.user.pk)
|
||||
obj = self.queryset.get(pk=self.request.user.pk)
|
||||
return obj
|
||||
|
||||
def perform_update(self, serializer, pk=None):
|
||||
serializer.is_valid(raise_exception=True);
|
||||
profile: ProfileModel = self.get_object();
|
||||
avatar = serializer.validated_data.get('avatar');
|
||||
def perform_update(self, serializer: ProfileSerializer, pk=None):
|
||||
serializer.is_valid(raise_exception=True)
|
||||
avatar = serializer.validated_data.get('avatar')
|
||||
profile: ProfileModel = self.get_object()
|
||||
|
||||
if (avatar is not None):
|
||||
if (profile.avatar.name != "./profiles/static/avatars/default.avif"):
|
||||
profile.avatar.storage.delete(profile.avatar.name)
|
||||
profile.avatar = avatar
|
||||
profile.save()
|
||||
|
||||
serializer.save()
|
||||
|
||||
def delete_avatar(self, request, pk=None):
|
||||
profile = self.get_object()
|
||||
if (profile.avatar.name != './profiles/static/avatars/default.avif'):
|
||||
profile.avatar.storage.delete(profile.avatar.name)
|
||||
profile.avatar.name = './profiles/static/avatars/default.avif'
|
||||
profile.save()
|
||||
return Response(ProfileSerializer(profile).data)
|
||||
|
||||
def retrieve(self, request: HttpRequest, pk=None):
|
||||
instance: ProfileModel = self.get_object()
|
||||
instance.avatar.name = instance.avatar.name[instance.avatar.name.find("static") - 1:]
|
||||
|
Loading…
Reference in New Issue
Block a user