diff --git a/.gitignore b/.gitignore index a930a3f..f7f20ae 100644 --- a/.gitignore +++ b/.gitignore @@ -166,5 +166,5 @@ db/db.sqlite /HabitTracker.iml -static/profile_images - +static/profile_images/* +!/static/profile_images/no_avatar/ diff --git a/app.py b/app.py index 09188a7..cf07dec 100644 --- a/app.py +++ b/app.py @@ -3,7 +3,7 @@ import hashlib import os from PIL import Image -from flask import Flask, render_template, redirect, url_for, request +from flask import Flask, render_template, redirect, url_for, request, jsonify from flask_login import login_required, LoginManager, login_user, logout_user, current_user from models.Habit import Habit @@ -273,7 +273,6 @@ def profile(): name=current_user.name, email=current_user.email, profile_image_url=current_user.profile_image, - errors={} ) @@ -282,34 +281,37 @@ def profile(): def profile_change(): newName = request.form.get('newName') newEmail = request.form.get('newEmail') - newPassword = request.form.get('newPassword') - oldPassword = request.form.get('oldPassword') - - # Check for errors - errors = {} - if not newName: - errors['newName'] = 'Der Name ist erforderlich.' - - if not newEmail: - errors['newEmail'] = 'Die E-Mail Adresse ist erforderlich.' - - if not oldPassword: - errors['oldPassword'] = 'Du musst dein aktuelles Passwort angeben.' - elif hashlib.sha256(oldPassword.encode()).hexdigest() != current_user.password: - errors['oldPassword'] = 'Das Passwort ist falsch.' - - if errors: - return render_template( - "profile.html", - name=current_user.name, - email=current_user.email, - profile_image_url=current_user.profile_image, - errors=errors - ) # Update user current_user.name = newName current_user.email = newEmail + current_user.update() + + # Back to profile + return render_template( + "profile.html", + name=current_user.name, + email=current_user.email, + profile_image_url=current_user.profile_image, + ) + +@app.route('/check_password', methods=['POST']) +@login_required +def check_password(): + # Get the password sent from the client + password = request.json.get('password') + + if hashlib.sha256(password.encode()).hexdigest() == current_user.password: + return jsonify({"valid": True}) + else: + return jsonify({"valid": False}) + +@app.route('/password', methods=['POST']) +@login_required +def password_change(): + newPassword = request.form.get('newPassword') + + # Update user if newPassword: current_user.password = hashlib.sha256(newPassword.encode()).hexdigest() current_user.update() @@ -320,14 +322,12 @@ def profile_change(): name=current_user.name, email=current_user.email, profile_image_url=current_user.profile_image, - errors={} ) UPLOAD_FOLDER = 'static/profile_images/' # Folder to store profile images app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER def save_profile_image(image_file): - print("we are saving...") filename = image_file.filename if '.' not in filename: # Ensure the filename has an extension @@ -339,9 +339,15 @@ def save_profile_image(image_file): if file_extension not in allowed_extensions: raise ValueError("Invalid file extension") + # Get the filename from the image path saved in the user + filename = os.path.basename(current_user.profile_image) + # Open the uploaded image image = Image.open(image_file) + # Convert the image to RGB mode (required for JPEG) + image = image.convert('RGB') + # Determine the size of the square image min_dimension = min(image.size) square_size = (min_dimension, min_dimension) @@ -355,13 +361,11 @@ def save_profile_image(image_file): # Crop the image to a square and resize it image = image.crop((left, top, right, bottom)) image.thumbnail(square_size) - image = image.resize((128, 128)) + image = image.resize((256, 256)) # Save the processed image image_path = os.path.join(app.config['UPLOAD_FOLDER'], filename) - image.save(image_path) - - return filename + image.save(image_path, 'JPEG', quality=100) @app.route('/upload', methods=['POST']) def upload_profile_image(): @@ -369,12 +373,7 @@ def upload_profile_image(): return 'No file part' file = request.files['file'] - - filename = save_profile_image(file) - - # Update user - current_user.profile_image = url_for('static', filename=f'profile_images/{filename}') - current_user.update() + save_profile_image(file) # Back to profile return render_template( @@ -440,7 +439,6 @@ def delete_habit(): habit.delete() return {} - @app.route('/reorder', methods=['POST']) @login_required def reorder_habits(): @@ -462,4 +460,4 @@ def reorder_habits(): # Run the application if __name__ == '__main__': - app.run(port=5000, debug=True) + app.run(port=5000, debug=True) \ No newline at end of file diff --git a/db/SQLiteClient.py b/db/SQLiteClient.py index c407599..fb183f3 100644 --- a/db/SQLiteClient.py +++ b/db/SQLiteClient.py @@ -1,6 +1,8 @@ from datetime import datetime, timedelta import hashlib import sqlite3 +import os +import shutil def con3(): @@ -9,7 +11,23 @@ def con3(): ### User ### -def create_user(name: str, email: str, password: str, profile_image: str): +def create_user_profile_image(user_id): + script_directory = os.path.dirname(os.path.abspath(__file__)) + source_path = os.path.join(script_directory, '../static/profile_images/no_avatar/user.jpg') + destination_directory = os.path.join(script_directory, '../static/profile_images/') + new_filename = f'user{user_id}-profile.jpg' + + destination_path = os.path.join(destination_directory, new_filename) + shutil.copyfile(source_path, destination_path) + + # Just save the part after static + static_index = destination_path.find('static') + relative_destination_path = destination_path[static_index:] + + return relative_destination_path + + +def create_user(name: str, email: str, password: str, profile_image:str = None): password = hashlib.sha256(password.encode()).hexdigest() now = datetime.now().isoformat() query = (f"INSERT INTO users (name, email, password, profile_image, created_at, updated_at) VALUES " @@ -17,9 +35,15 @@ def create_user(name: str, email: str, password: str, profile_image: str): conn = con3() cursor = conn.cursor() cursor.execute(query) + + id = cursor.lastrowid + profile_image = create_user_profile_image(id) + query2 = f"UPDATE users SET profile_image = '{profile_image}' WHERE id = '{id}';" + cursor.execute(query2) + conn.commit() conn.close() - return cursor.lastrowid + return id, profile_image def get_user(id: int): @@ -42,14 +66,13 @@ def get_user_by_email(email: str): return user -def update_user(id: int, name: str, email: str, password: str, profile_image:str): +def update_user(id: int, name: str, email: str, password: str): now = datetime.now().isoformat() if password: - query = (f"UPDATE users SET name = '{name}', email = '{email}', password = '{password}', " - f"profile_image = '{profile_image}', updated_at = '{now}' WHERE id = {id};") + query = (f"UPDATE users SET name = '{name}', email = '{email}', password = '{password}', updated_at = '{now}' " + f"WHERE id = {id};") else: - query = (f"UPDATE users SET name = '{name}', email = '{email}', profile_image = '{profile_image}'," - f" updated_at = '{now}' WHERE id = {id};") + query = f"UPDATE users SET name = '{name}', email = '{email}', updated_at = '{now}' WHERE id = {id};" conn = con3() cursor = conn.cursor() cursor.execute(query) diff --git a/models/User.py b/models/User.py index 16a24d9..e322983 100644 --- a/models/User.py +++ b/models/User.py @@ -11,9 +11,10 @@ class User(UserMixin): self.password = password self.profile_image = profile_image + @staticmethod - def create(name: str, email: str, password: str, profile_image: str=None): - id = create_user(name, email, password, profile_image) + def create(name: str, email: str, password: str): + id, profile_image = create_user(name, email, password) return User(id=id, name=name, email=email, profile_image=profile_image) @staticmethod @@ -27,9 +28,9 @@ class User(UserMixin): return User(user[0], user[1], user[2], user[3], user[4]) if user else None - # Updates: name, email, password, profile_image + # Updates: name, email, password def update(self): - update_user(self.id, self.name, self.email, self.password if self.password else None, self.profile_image) + update_user(self.id, self.name, self.email, self.password if self.password else None) # Deletes the User diff --git a/static/css/profile.css b/static/css/profile.css index 55b1d07..af7832c 100644 --- a/static/css/profile.css +++ b/static/css/profile.css @@ -1,3 +1,4 @@ + /* Profile image */ .profile-image-container { position: relative; width: 150px; /* Adjust the size as needed */ @@ -42,4 +43,31 @@ .profile-image-overlay:hover span { /* Style the text when hovering */ color: white; +} + + + /* Edit-Modal Close Button */ +.close-icon { + fill: #aaa; + cursor: pointer; +} + +.close-icon:hover { + fill: #777; /* Change the color to whatever you'd like on hover */ +} + + +.fade-out { + -webkit-animation: fadeOut 3s forwards; + animation: fadeOut 3s forwards; +} + +@keyframes fadeOut { + 0% { opacity: 1; } + 100% { opacity: 0; display: none; } +} + +@-webkit-keyframes fadeOut { + 0% { opacity: 1; } + 100% { opacity: 0; display: none; } } \ No newline at end of file diff --git a/static/css/symbols/eye-closed.svg b/static/css/symbols/eye-closed.svg new file mode 100644 index 0000000..1176af5 --- /dev/null +++ b/static/css/symbols/eye-closed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/css/symbols/eye-opened.svg b/static/css/symbols/eye-opened.svg new file mode 100644 index 0000000..9caef02 --- /dev/null +++ b/static/css/symbols/eye-opened.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/profile_images/no_avatar/user.jpg b/static/profile_images/no_avatar/user.jpg new file mode 100644 index 0000000..dc70cad Binary files /dev/null and b/static/profile_images/no_avatar/user.jpg differ diff --git a/templates/layouts/main.html b/templates/layouts/main.html index 43d392c..4077001 100644 --- a/templates/layouts/main.html +++ b/templates/layouts/main.html @@ -6,14 +6,24 @@