settings: avatar & username (& little stuff too)
This commit is contained in:
parent
6d942eea09
commit
c65c986b25
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \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"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -17,6 +17,11 @@ msgstr ""
|
|||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n > 1);\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
|
#: views/login.py:22
|
||||||
msgid "Invalid username or password."
|
msgid "Invalid username or password."
|
||||||
msgstr "Nom d'utilisateur ou mot de passe incorect."
|
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 django.urls import path
|
||||||
|
|
||||||
from .views import register, login, logout, delete, edit, logged
|
from .views import register, login, logout, delete, logged, update_profile
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("register", register.RegisterView.as_view(), name="register"),
|
path("register", register.RegisterView.as_view(), name="register"),
|
||||||
@ -8,6 +8,5 @@ urlpatterns = [
|
|||||||
path("logout", logout.LogoutView.as_view(), name="logout"),
|
path("logout", logout.LogoutView.as_view(), name="logout"),
|
||||||
path("logged", logged.LoggedView.as_view(), name="logged"),
|
path("logged", logged.LoggedView.as_view(), name="logged"),
|
||||||
path("delete", delete.DeleteView.as_view(), name="delete"),
|
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 django.http import HttpRequest
|
||||||
from rest_framework.authentication import SessionAuthentication
|
from rest_framework.authentication import SessionAuthentication
|
||||||
|
|
||||||
|
|
||||||
class DeleteView(APIView):
|
class DeleteView(APIView):
|
||||||
permission_classes = (permissions.IsAuthenticated,)
|
permission_classes = (permissions.IsAuthenticated,)
|
||||||
authentication_classes = (SessionAuthentication,)
|
authentication_classes = (SessionAuthentication,)
|
||||||
|
|
||||||
def delete(self, request: HttpRequest):
|
def delete(self, request: HttpRequest):
|
||||||
data: dict = request.data
|
data: dict = request.data
|
||||||
|
|
||||||
password: str = data["password"]
|
password: str = data["password"]
|
||||||
if (password is None):
|
if (request.user.check_password(password) is False):
|
||||||
return Response({"password": ["This field may not be blank."]})
|
return Response({"password": ["Password incorrect."]},
|
||||||
if (request.user.check_password(password) == False):
|
status.HTTP_401_UNAUTHORIZED)
|
||||||
return Response({"password": ["Password wrong."]})
|
|
||||||
request.user.delete()
|
request.user.delete()
|
||||||
logout(request)
|
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.response import Response
|
||||||
from rest_framework import permissions, status
|
from rest_framework import permissions, status
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
from django.contrib.auth import login
|
|
||||||
from rest_framework.authentication import SessionAuthentication
|
from rest_framework.authentication import SessionAuthentication
|
||||||
|
|
||||||
from ..serializers.login import LoginSerializer
|
|
||||||
|
|
||||||
class LoggedView(APIView):
|
class LoggedView(APIView):
|
||||||
|
|
||||||
permission_classes = (permissions.AllowAny,)
|
permission_classes = (permissions.AllowAny,)
|
||||||
authentication_classes = (SessionAuthentication,)
|
authentication_classes = (SessionAuthentication,)
|
||||||
|
|
||||||
def get(self, request: HttpRequest):
|
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
|
from ..serializers.login import LoginSerializer
|
||||||
|
|
||||||
|
|
||||||
class LoginView(APIView):
|
class LoginView(APIView):
|
||||||
|
|
||||||
permission_classes = (permissions.AllowAny,)
|
permission_classes = (permissions.AllowAny,)
|
||||||
authentication_classes = (SessionAuthentication,)
|
authentication_classes = (SessionAuthentication,)
|
||||||
|
|
||||||
|
@ -5,9 +5,11 @@ from rest_framework.response import Response
|
|||||||
from django.http import HttpRequest
|
from django.http import HttpRequest
|
||||||
from rest_framework.authentication import SessionAuthentication
|
from rest_framework.authentication import SessionAuthentication
|
||||||
|
|
||||||
|
|
||||||
class LogoutView(APIView):
|
class LogoutView(APIView):
|
||||||
permission_classes = (permissions.IsAuthenticated,)
|
permission_classes = (permissions.IsAuthenticated,)
|
||||||
authentication_classes = (SessionAuthentication,)
|
authentication_classes = (SessionAuthentication,)
|
||||||
|
|
||||||
def get(self, request: HttpRequest):
|
def get(self, request: HttpRequest):
|
||||||
logout(request)
|
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.http import HttpRequest
|
||||||
from django.contrib.auth import login
|
from django.contrib.auth import login
|
||||||
|
|
||||||
|
|
||||||
class RegisterView(APIView):
|
class RegisterView(APIView):
|
||||||
permission_classes = (permissions.AllowAny,)
|
permission_classes = (permissions.AllowAny,)
|
||||||
|
|
||||||
def post(self, request: HttpRequest):
|
def post(self, request: HttpRequest):
|
||||||
data = request.data
|
data = request.data
|
||||||
serializer = RegisterSerialiser(data=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 {
|
#popup {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
font-size: 1.2em;
|
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)
|
* @param {String} newUsername
|
||||||
* @returns {?Promise<Object>}
|
* @returns {?Promise<Object>}
|
||||||
*/
|
*/
|
||||||
async get()
|
async updateUsername(newUsername)
|
||||||
{
|
{
|
||||||
let response = await this.client._get("/api/accounts/edit");
|
const data = {
|
||||||
let response_data = await response.json();
|
username: newUsername
|
||||||
|
};
|
||||||
|
const response = await this.client._patch_json(`/api/accounts/update_profile`, data);
|
||||||
|
const respondeData = await response.json();
|
||||||
|
|
||||||
if (response.status === 403)
|
if (response.status === 200) {
|
||||||
{
|
this.client.me.username = respondeData.username;
|
||||||
this.client._update_logged(false);
|
document.getElementById('navbarDropdownButton').innerHTML = respondeData.username;
|
||||||
|
document.getElementById('myProfileLink').href = '/profiles/' + respondeData.username;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return response_data;
|
return respondeData['authorize'] || respondeData['detail'] || respondeData['username']?.join(' ') || 'Error.';
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,15 +14,35 @@ class MyProfile extends Profile
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {*} form_data
|
* @param {File} selectedFile
|
||||||
* @returns {Promise<Object>}
|
* @returns {Promise<Response>}
|
||||||
*/
|
*/
|
||||||
async change_avatar(form_data)
|
async changeAvatar(selectedFile)
|
||||||
{
|
{
|
||||||
let response = await this.client._patch_file(`/api/profiles/settings`, form_data);
|
const formData = new FormData();
|
||||||
let response_data = await response.json();
|
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 { client, navigateTo } from '../index.js';
|
||||||
import { clear, fill_errors } from "../utils/formUtils.js";
|
import { clear, fill_errors } from '../utils/formUtils.js';
|
||||||
import AbstractAuthenticatedView from "./abstracts/AbstractAuthenticatedView.js";
|
import AbstractAuthenticatedView from './abstracts/AbstractAuthenticatedView.js';
|
||||||
|
|
||||||
export default class extends AbstractAuthenticatedView
|
export default class extends AbstractAuthenticatedView
|
||||||
{
|
{
|
||||||
constructor(params)
|
constructor(params)
|
||||||
{
|
{
|
||||||
super(params, "Settings");
|
super(params, 'Settings');
|
||||||
this.PROFILE_PICTURE_MAX_SIZE = 2 * 1024 * 1024; // 2MB
|
this.PROFILE_PICTURE_MAX_SIZE = 2 * 1024 * 1024; // 2MB
|
||||||
}
|
}
|
||||||
|
|
||||||
async postInit()
|
async postInit()
|
||||||
{
|
{
|
||||||
this.display_avatar();
|
this.avatarInit();
|
||||||
document.getElementById("save-account-button").onclick = () => this.save_account();
|
this.usernameInit();
|
||||||
document.getElementById("delete-account-button").onclick = () => this.delete_account();
|
|
||||||
document.getElementById("save-profile-button").onclick = () => this.save_profile();
|
// document.getElementById('delete-account-button').onclick = () => this.delete_account();
|
||||||
}
|
}
|
||||||
|
|
||||||
async display_avatar() {
|
usernameInit() {
|
||||||
let profile = await client.profiles.getProfile(client.me.username);
|
const usernameInput = document.getElementById('usernameInput');
|
||||||
if (profile !== undefined || profile !== null) {
|
const usernameSave = document.getElementById('usernameSave');
|
||||||
if (document.getElementById("avatar") != undefined)
|
|
||||||
document.getElementById("avatar").remove();
|
usernameInput.oninput = e => {
|
||||||
let avatar = document.createElement("img");
|
const value = e.target.value;
|
||||||
avatar.id = "avatar";
|
if (value != client.me.username && value.length)
|
||||||
avatar.src = profile.avatar_url + '?t=' +new Date().getTime();
|
usernameSave.classList.remove('disabled');
|
||||||
document.getElementsByClassName("avatar")[0].appendChild(avatar);
|
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()
|
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);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
clear("innerHTML", ["current_password-input"]);
|
clear('innerHTML', ['current_password-input']);
|
||||||
fill_errors({"current_password-input": response_data.password}, "innerHTML");
|
fill_errors({'current_password-input': response_data.password}, 'innerHTML');
|
||||||
}
|
}
|
||||||
|
|
||||||
async save_account()
|
async save_account()
|
||||||
{
|
{
|
||||||
let username = document.getElementById("username-input").value;
|
let username = document.getElementById('username-input').value;
|
||||||
let new_password = document.getElementById("new_password-input").value;
|
let new_password = document.getElementById('new_password-input').value;
|
||||||
let current_password = document.getElementById("current_password-input").value;
|
let current_password = document.getElementById('current_password-input').value;
|
||||||
|
|
||||||
let data = {};
|
let data = {};
|
||||||
|
|
||||||
@ -61,67 +88,139 @@ export default class extends AbstractAuthenticatedView
|
|||||||
|
|
||||||
if (response_data === null)
|
if (response_data === null)
|
||||||
{
|
{
|
||||||
navigateTo("/login");
|
navigateTo('/login');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response_data === "data has been alterate")
|
if (response_data === 'data has been alterate')
|
||||||
response_data = {"save-account": "saved"};
|
response_data = {'save-account': 'saved'};
|
||||||
|
|
||||||
clear("innerHTML", ["username", "new_password", "current_password", "save-account", "delete-account"]);
|
clear('innerHTML', ['username', 'new_password', 'current_password', 'save-account', 'delete-account']);
|
||||||
fill_errors(response_data, "innerHTML");
|
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";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getHtml()
|
async saveUsername()
|
||||||
{
|
{
|
||||||
return /* HTML */ `
|
const usernameInput = document.getElementById('usernameInput');
|
||||||
<link rel="stylesheet" href="/static/css/settings.css">
|
const username = usernameInput.value;
|
||||||
<h1>ME</h1>
|
const usernameDetail = document.getElementById('usernameDetail');
|
||||||
<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>
|
|
||||||
|
|
||||||
<input type="button" value="Save Credentials" id="save-account-button">
|
if (!username.length || username === client.me.username)
|
||||||
<span id="save-account"></span>
|
return;
|
||||||
<input type="button" value="Delete Account" id="delete-account-button">
|
|
||||||
<span id="delete-account"></span>
|
const error = await client.account.updateUsername(username);
|
||||||
</div>
|
if (!error) {
|
||||||
<div class="profile">
|
usernameDetail.classList.remove('text-danger');
|
||||||
<h3>Profile</h3>
|
usernameDetail.classList.add('text-success');
|
||||||
<input type="file" id="avatar-input" accept="image/*">
|
usernameDetail.innerHTML = 'Username Saved.';
|
||||||
<input type="button" value="Save profile" id="save-profile-button">
|
setTimeout(_ => usernameDetail.innerHTML = '', 2000);
|
||||||
<span id="save-profile"></span>
|
document.getElementById('usernameSave').classList.add('disabled');
|
||||||
</div>
|
} else {
|
||||||
<a href="/logout" class="nav__link" data-link>Logout</a>
|
usernameDetail.classList.remove('text-success');
|
||||||
</div>
|
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.contrib.auth.models import User
|
||||||
from django.db.models.signals import post_save, pre_delete
|
from django.db.models.signals import post_save, pre_delete
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.conf import settings
|
|
||||||
from django.db.models import IntegerField
|
from django.db.models import IntegerField
|
||||||
|
|
||||||
from games.consumers import game_manager
|
from games.consumers import game_manager
|
||||||
|
|
||||||
from os.path import splitext
|
from os.path import splitext
|
||||||
|
|
||||||
|
|
||||||
def upload_to(instance, filename: str):
|
def upload_to(instance, filename: str):
|
||||||
return f"./profiles/static/avatars/{instance.pk}{splitext(filename)[1]}"
|
return f"./profiles/static/avatars/{instance.pk}{splitext(filename)[1]}"
|
||||||
|
|
||||||
|
|
||||||
# Create your models here.
|
# Create your models here.
|
||||||
class ProfileModel(models.Model):
|
class ProfileModel(models.Model):
|
||||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from .models import ProfileModel
|
from .models import ProfileModel
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
|
|
||||||
class ProfileSerializer(serializers.ModelSerializer):
|
class ProfileSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
@ -16,5 +18,5 @@ class ProfileSerializer(serializers.ModelSerializer):
|
|||||||
Check that the image is not too large
|
Check that the image is not too large
|
||||||
'''
|
'''
|
||||||
if value.size > settings.PROFILE_PICTURE_MAX_SIZE:
|
if value.size > settings.PROFILE_PICTURE_MAX_SIZE:
|
||||||
raise serializers.ValidationError('Image is too large.');
|
raise serializers.ValidationError(_('Image is too large.'))
|
||||||
return value;
|
return value
|
||||||
|
@ -4,7 +4,7 @@ from . import viewsets
|
|||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
urlpatterns = [
|
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("me", viewsets.MyProfileViewSet.as_view({'get': 'retrieve'}), name="my_profile_page"),
|
||||||
path("", viewsets.ProfileViewSet.as_view({'get': 'list'}), name="profiles_list"),
|
path("", viewsets.ProfileViewSet.as_view({'get': 'list'}), name="profiles_list"),
|
||||||
path("block", views.BlocksView.as_view(), name="block_page"),
|
path("block", views.BlocksView.as_view(), name="block_page"),
|
||||||
@ -12,5 +12,4 @@ urlpatterns = [
|
|||||||
path("friend", views.FriendsView.as_view(), name="friend_page"),
|
path("friend", views.FriendsView.as_view(), name="friend_page"),
|
||||||
path("user/<str:username>", viewsets.ProfileViewSet.as_view({'get': 'retrieve'}), name="profile_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"),
|
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.parsers import MultiPartParser, FormParser
|
||||||
from rest_framework import permissions, status
|
from rest_framework import permissions, status
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
@ -42,28 +41,35 @@ class ProfileViewSet(viewsets.ModelViewSet):
|
|||||||
profile["avatar"] = profile["avatar"][profile["avatar"].find("static") - 1:]
|
profile["avatar"] = profile["avatar"][profile["avatar"].find("static") - 1:]
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
class MyProfileViewSet(viewsets.ModelViewSet):
|
|
||||||
|
|
||||||
|
class MyProfileViewSet(viewsets.ModelViewSet):
|
||||||
permission_classes = (permissions.IsAuthenticated,)
|
permission_classes = (permissions.IsAuthenticated,)
|
||||||
authentication_classes = (SessionAuthentication,)
|
authentication_classes = (SessionAuthentication,)
|
||||||
serializer_class = ProfileSerializer
|
serializer_class = ProfileSerializer
|
||||||
queryset = ProfileModel.objects.all
|
queryset = ProfileModel.objects.all()
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
obj = self.queryset().get(pk=self.request.user.pk)
|
obj = self.queryset.get(pk=self.request.user.pk)
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
def perform_update(self, serializer, pk=None):
|
def perform_update(self, serializer: ProfileSerializer, pk=None):
|
||||||
serializer.is_valid(raise_exception=True);
|
serializer.is_valid(raise_exception=True)
|
||||||
profile: ProfileModel = self.get_object();
|
avatar = serializer.validated_data.get('avatar')
|
||||||
avatar = serializer.validated_data.get('avatar');
|
profile: ProfileModel = self.get_object()
|
||||||
|
|
||||||
if (avatar is not None):
|
if (avatar is not None):
|
||||||
if (profile.avatar.name != "./profiles/static/avatars/default.avif"):
|
if (profile.avatar.name != "./profiles/static/avatars/default.avif"):
|
||||||
profile.avatar.storage.delete(profile.avatar.name)
|
profile.avatar.storage.delete(profile.avatar.name)
|
||||||
profile.avatar = avatar
|
serializer.save()
|
||||||
profile.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):
|
def retrieve(self, request: HttpRequest, pk=None):
|
||||||
instance: ProfileModel = self.get_object()
|
instance: ProfileModel = self.get_object()
|
||||||
instance.avatar.name = instance.avatar.name[instance.avatar.name.find("static") - 1:]
|
instance.avatar.name = instance.avatar.name[instance.avatar.name.find("static") - 1:]
|
||||||
|
Loading…
Reference in New Issue
Block a user