mirror of
https://github.com/Michatec/MiniFaceBook.git
synced 2026-03-31 23:46:30 +02:00
Files
This commit is contained in:
330
routes/admin.py
Normal file
330
routes/admin.py
Normal file
@@ -0,0 +1,330 @@
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, abort, request
|
||||
from flask_login import login_required, current_user
|
||||
from models import db, User, Post, Friendship, Comment, Upload, Notification, Event, PasswordResetRequest, Like, UserShopItem, Reward, SupportComment, SupportRequest
|
||||
from werkzeug.security import generate_password_hash
|
||||
from flask_babel import gettext as _
|
||||
import os
|
||||
|
||||
__mapper_args__ = {"confirm_deleted_rows": False}
|
||||
admin_bp = Blueprint('admin', __name__, url_prefix='/admin')
|
||||
|
||||
@admin_bp.route('/reset_requests/delete_all', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def admin_delete_all_reset_requests():
|
||||
if not current_user.is_admin:
|
||||
abort(403)
|
||||
db.session.query(PasswordResetRequest).delete()
|
||||
db.session.commit()
|
||||
flash(_('All password reset requests have been deleted.'), 'success')
|
||||
return redirect(url_for('admin.reset_requests'))
|
||||
|
||||
@admin_bp.route('/')
|
||||
@login_required
|
||||
def admin():
|
||||
if not current_user.is_admin:
|
||||
abort(403)
|
||||
users = db.session.query(User).all()
|
||||
posts = db.session.query(Post).all()
|
||||
friendships = db.session.query(Friendship).all()
|
||||
comments = db.session.query(Comment).all()
|
||||
uploads = db.session.query(Upload).all()
|
||||
all_notifications = db.session.query(Notification).order_by(Notification.created_at.desc()).all()
|
||||
events = db.session.query(Event).order_by(Event.timestamp.desc()).limit(50).all()
|
||||
user_shop_items = db.session.query(UserShopItem).all()
|
||||
return render_template(
|
||||
'admin.html',
|
||||
users=users,
|
||||
posts=posts,
|
||||
friendships=friendships,
|
||||
comments=comments,
|
||||
uploads=uploads,
|
||||
all_notifications=all_notifications,
|
||||
events=events,
|
||||
user_shop_items=user_shop_items
|
||||
)
|
||||
|
||||
@admin_bp.route('/reset_requests')
|
||||
@login_required
|
||||
def reset_requests():
|
||||
if not current_user.is_admin:
|
||||
abort(403)
|
||||
requests = db.session.query(PasswordResetRequest).filter_by(status='pending').all()
|
||||
requests_done = db.session.query(PasswordResetRequest).filter_by(status='done').all()
|
||||
requests_rejected = db.session.query(PasswordResetRequest).filter_by(status='rejected').all()
|
||||
return render_template('reset_requests.html', requests=requests, requests_done=requests_done, requests_rejected=requests_rejected)
|
||||
|
||||
@admin_bp.route('/reset_requests/<int:req_id>/reject', methods=['POST'])
|
||||
@login_required
|
||||
def reject_reset_request(req_id):
|
||||
if not current_user.is_admin:
|
||||
abort(403)
|
||||
req = db.session.get(PasswordResetRequest, req_id)
|
||||
if req:
|
||||
req.status = 'rejected'
|
||||
db.session.commit()
|
||||
flash(_('Request rejected.'), 'info')
|
||||
return redirect(url_for('admin.reset_requests'))
|
||||
|
||||
@admin_bp.route('/reset_requests/<int:req_id>/reset', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def admin_reset_password(req_id):
|
||||
if not current_user.is_admin:
|
||||
abort(403)
|
||||
req = db.session.get(PasswordResetRequest, req_id)
|
||||
if req and req.status == 'pending':
|
||||
user = db.session.get(User, req.user_id)
|
||||
if request.method == 'POST':
|
||||
new_pw = request.form['new_password']
|
||||
if not new_pw or len(new_pw) < 4:
|
||||
flash(_('Password too short.'), 'danger')
|
||||
else:
|
||||
user.password = generate_password_hash(new_pw, method='pbkdf2:sha256')
|
||||
req.status = 'done'
|
||||
db.session.commit()
|
||||
flash(_(f'Password for {user.username} reset.'), 'success')
|
||||
return redirect(url_for('admin.reset_requests'))
|
||||
return render_template('admin_set_password.html', req=req, user=user)
|
||||
return redirect(url_for('admin.reset_requests'))
|
||||
|
||||
@admin_bp.route('/delete_post/<int:post_id>', methods=['POST'])
|
||||
@login_required
|
||||
def admin_delete_post(post_id):
|
||||
if not current_user.is_admin:
|
||||
abort(403)
|
||||
post = db.session.get(Post, post_id)
|
||||
if post:
|
||||
for upload in post.uploads:
|
||||
file_path = os.path.join('static/uploads', upload.filename)
|
||||
try:
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
except Exception:
|
||||
pass
|
||||
likes = db.session.query(Like).filter_by(post_id=post_id).all()
|
||||
for like in likes:
|
||||
db.session.delete(like)
|
||||
comments = db.session.query(Comment).filter_by(post_id=post_id).all()
|
||||
for comment in comments:
|
||||
db.session.delete(comment)
|
||||
db.session.delete(post)
|
||||
event = Event(message=_(f"Admin {current_user.username} has deleted post {post.id}."))
|
||||
db.session.add(event)
|
||||
notification = Notification(message=_(f"Your post {post.id} has been deleted by an admin."), user_id=post.user_id)
|
||||
db.session.add(notification)
|
||||
db.session.commit()
|
||||
flash(_('Post and associated files deleted.'), 'success')
|
||||
return redirect(url_for('admin.admin'))
|
||||
|
||||
@admin_bp.route('/delete_user/<int:user_id>', methods=['POST'])
|
||||
@login_required
|
||||
def admin_delete_user(user_id):
|
||||
user = db.session.get(User, user_id)
|
||||
if user.is_owner:
|
||||
flash(_('Cannot delete the owner account.'), 'danger')
|
||||
return redirect(url_for('admin.admin'))
|
||||
if user and not user.is_admin:
|
||||
event = Event(message=f"Admin {current_user.username} hat {user.username} gelöscht.")
|
||||
db.session.add(event)
|
||||
for post in user.posts:
|
||||
db.session.delete(post)
|
||||
for friendship in user.friendships_sent + user.friendships_received:
|
||||
db.session.delete(friendship)
|
||||
for comment in user.comments:
|
||||
db.session.delete(comment)
|
||||
for user_shop_item in user.shop_items:
|
||||
db.session.delete(user_shop_item)
|
||||
for reward in user.rewards:
|
||||
db.session.delete(reward)
|
||||
for like in user.likes:
|
||||
db.session.delete(like)
|
||||
for upload in user.uploads:
|
||||
file_path = os.path.join('static/uploads', upload.filename)
|
||||
try:
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
except Exception:
|
||||
pass
|
||||
db.session.delete(upload)
|
||||
if user.profile_pic and not user.profile_pic == 'default.png':
|
||||
try:
|
||||
os.remove(os.path.join('static/profile_pics', user.profile_pic))
|
||||
except Exception:
|
||||
pass
|
||||
notifications = db.session.query(Notification).filter_by(user_id=user.id).all()
|
||||
for notif in notifications:
|
||||
db.session.delete(notif)
|
||||
db.session.delete(user)
|
||||
db.session.commit()
|
||||
flash(_('User deleted.'), 'success')
|
||||
else:
|
||||
flash(_('Cannot delete admin or user not found.'), 'danger')
|
||||
return redirect(url_for('admin.admin'))
|
||||
|
||||
@admin_bp.route('/delete_pic/<int:user_id>', methods=['POST'])
|
||||
@login_required
|
||||
def admin_delete_pic(user_id):
|
||||
if not current_user.is_admin:
|
||||
abort(403)
|
||||
user = db.session.get(User, user_id)
|
||||
if user and user.profile_pic and user.profile_pic != 'default.png':
|
||||
try:
|
||||
os.remove(os.path.join('static/profile_pics', user.profile_pic))
|
||||
except Exception:
|
||||
pass
|
||||
user.profile_pic = "default.png"
|
||||
db.session.commit()
|
||||
flash(_(f'Profile picture of {user.username} deleted.'), 'success')
|
||||
return redirect(url_for('admin.admin'))
|
||||
|
||||
@admin_bp.route('/delete_all_notifications', methods=['POST'])
|
||||
@login_required
|
||||
def admin_delete_all_notifications():
|
||||
if not current_user.is_admin:
|
||||
abort(403)
|
||||
db.session.query(Notification).delete()
|
||||
db.session.commit()
|
||||
flash(_('All notifications have been deleted.'), 'success')
|
||||
return redirect(url_for('admin.admin'))
|
||||
|
||||
@admin_bp.route('/delete_all_events', methods=['POST'])
|
||||
@login_required
|
||||
def admin_delete_all_events():
|
||||
if not current_user.is_admin:
|
||||
abort(403)
|
||||
db.session.query(Event).delete()
|
||||
db.session.commit()
|
||||
flash(_('All events have been deleted.'), 'success')
|
||||
return redirect(url_for('admin.admin'))
|
||||
|
||||
@admin_bp.route('/delete_upload/<int:upload_id>', methods=['POST'])
|
||||
@login_required
|
||||
def admin_delete_upload(upload_id):
|
||||
if not current_user.is_admin:
|
||||
abort(403)
|
||||
upload = db.session.get(Upload, upload_id)
|
||||
if upload:
|
||||
file_path = os.path.join('static/uploads', upload.filename)
|
||||
try:
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
except Exception:
|
||||
pass
|
||||
db.session.delete(upload)
|
||||
db.session.commit()
|
||||
flash(_('Upload deleted.'), 'success')
|
||||
return redirect(url_for('admin.admin'))
|
||||
|
||||
@admin_bp.route('/delete_all_uploads', methods=['POST'])
|
||||
@login_required
|
||||
def admin_delete_all_uploads():
|
||||
if not current_user.is_admin:
|
||||
abort(403)
|
||||
db.session.query(Upload).delete()
|
||||
db.session.commit()
|
||||
upload_dir = 'static/uploads'
|
||||
for filename in os.listdir(upload_dir):
|
||||
file_path = os.path.join(upload_dir, filename)
|
||||
try:
|
||||
if os.path.isfile(file_path):
|
||||
os.remove(file_path)
|
||||
except Exception:
|
||||
pass
|
||||
flash(_('All uploads have been deleted.'), 'success')
|
||||
return redirect(url_for('admin.admin'))
|
||||
|
||||
@admin_bp.route('/admin/points/<int:user_id>', methods=['POST'])
|
||||
@login_required
|
||||
def admin_points(user_id):
|
||||
if not current_user.is_admin:
|
||||
abort(403)
|
||||
action = request.form.get('action')
|
||||
try:
|
||||
points = int(request.form['points'])
|
||||
except:
|
||||
flash(_('No Points entered!'))
|
||||
return redirect(url_for('admin.admin'))
|
||||
cuser = db.session.get(User, current_user.id)
|
||||
if not cuser.is_owner:
|
||||
abort(403)
|
||||
if action == 'add':
|
||||
db.session.add(Reward(user_id=user_id, type='admin', points=points))
|
||||
db.session.commit()
|
||||
flash(_('Points added!'), 'success')
|
||||
elif action == 'remove':
|
||||
user = db.session.get(User, user_id)
|
||||
if user.reward_points() >= points:
|
||||
db.session.add(Reward(user_id=user_id, type='admin', points=-points))
|
||||
db.session.commit()
|
||||
flash(_('Points removed!'), 'success')
|
||||
else:
|
||||
flash(_("The user has not enough points to take!"), 'danger')
|
||||
return redirect(url_for('admin.admin'))
|
||||
|
||||
@admin_bp.route('/make_admin/<int:user_id>', methods=['POST'])
|
||||
@login_required
|
||||
def make_admin(user_id):
|
||||
if not current_user.is_admin:
|
||||
abort(403)
|
||||
user = db.session.get(User, user_id)
|
||||
if user and not user.is_admin:
|
||||
user.is_admin = True
|
||||
db.session.commit()
|
||||
flash(_(f"{user.username} is now an admin."), "success")
|
||||
return redirect(url_for('admin.admin'))
|
||||
|
||||
@admin_bp.route('/remove_admin/<int:user_id>', methods=['POST'])
|
||||
@login_required
|
||||
def remove_admin(user_id):
|
||||
if not current_user.is_admin:
|
||||
abort(403)
|
||||
user = db.session.get(User, user_id)
|
||||
if user and user.is_admin and not user.is_owner:
|
||||
user.is_admin = False
|
||||
db.session.commit()
|
||||
flash(_(f"Admin rights of {user.username} removed."), "info")
|
||||
else:
|
||||
flash(_("Owner cannot be removed!"), "danger")
|
||||
return redirect(url_for('admin.admin'))
|
||||
|
||||
@admin_bp.route('/wipe_server', methods=['POST'])
|
||||
@login_required
|
||||
def wipe_server():
|
||||
if not current_user.is_admin and not current_user.is_owner:
|
||||
abort(403)
|
||||
|
||||
db.session.query(Reward).delete()
|
||||
db.session.query(UserShopItem).delete()
|
||||
db.session.query(Like).delete()
|
||||
db.session.query(Comment).delete()
|
||||
db.session.query(Friendship).delete()
|
||||
db.session.query(Post).delete()
|
||||
db.session.query(Upload).delete()
|
||||
db.session.query(Notification).delete()
|
||||
db.session.query(Event).delete()
|
||||
db.session.query(PasswordResetRequest).delete()
|
||||
db.session.query(User).delete()
|
||||
db.session.query(SupportComment).delete()
|
||||
db.session.query(SupportRequest).delete()
|
||||
db.session.commit()
|
||||
|
||||
upload_dir = 'static/uploads'
|
||||
for filename in os.listdir(upload_dir):
|
||||
file_path = os.path.join(upload_dir, filename)
|
||||
try:
|
||||
if os.path.isfile(file_path):
|
||||
os.remove(file_path)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
profile_dir = 'static/profile_pics'
|
||||
for filename in os.listdir(profile_dir):
|
||||
if filename != 'default.png':
|
||||
file_path = os.path.join(profile_dir, filename)
|
||||
try:
|
||||
if os.path.isfile(file_path):
|
||||
os.remove(file_path)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
flash(_('All Data has been deleted.'), 'success')
|
||||
return redirect(url_for('admin.admin'))
|
||||
14
routes/credits.py
Normal file
14
routes/credits.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request
|
||||
from flask_login import login_required, current_user
|
||||
from models import db
|
||||
from flask_babel import gettext as _
|
||||
|
||||
credits_bp = Blueprint('credit', __name__)
|
||||
|
||||
@credits_bp.route('/credits')
|
||||
def credits():
|
||||
return render_template('credits.html')
|
||||
|
||||
@credits_bp.route('/privacy-policy')
|
||||
def privacy_policy():
|
||||
return render_template('privacy_policy.html')
|
||||
87
routes/discord.py
Normal file
87
routes/discord.py
Normal file
@@ -0,0 +1,87 @@
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request
|
||||
from flask_login import login_required, current_user
|
||||
from models import db, User
|
||||
from werkzeug.security import generate_password_hash
|
||||
from flask_babel import gettext as _
|
||||
from routes.oauth import discord
|
||||
from routes.login import login_user
|
||||
|
||||
discord_bp = Blueprint('discord', __name__)
|
||||
|
||||
@discord_bp.route('/login/discord')
|
||||
def login_discord():
|
||||
redirect_uri = url_for('discord.discord_login_callback', _external=True)
|
||||
return discord.authorize_redirect(redirect_uri)
|
||||
|
||||
@discord_bp.route('/login/discord/callback', methods=['GET', 'POST'])
|
||||
def discord_login_callback():
|
||||
if request.method == 'GET':
|
||||
token = discord.authorize_access_token()
|
||||
user_data = discord.get('users/@me').json()
|
||||
user = User.query.filter_by(discord_id=user_data['id']).first()
|
||||
if user:
|
||||
login_user(user)
|
||||
flash(_('Logged in with Discord.'), 'success')
|
||||
return redirect(url_for('post.feed'))
|
||||
else:
|
||||
flash(_('No account linked with this Discord. Please register.'), 'info')
|
||||
return render_template(
|
||||
'discord_register.html',
|
||||
username=user_data['username'],
|
||||
email=user_data.get('email', ''),
|
||||
discord_id=user_data['id']
|
||||
)
|
||||
else:
|
||||
username = request.form.get('username')
|
||||
email = request.form.get('email')
|
||||
discord_id = request.form.get('discord_id')
|
||||
password = request.form.get('password')
|
||||
confirm_password = request.form.get('confirm_password')
|
||||
if not password or len(password) < 8:
|
||||
flash(_('Password must be at least 8 characters long.'), 'danger')
|
||||
return render_template('discord_register.html', username=username, email=email, discord_id=discord_id)
|
||||
if password != confirm_password:
|
||||
flash(_('Passwords do not match.'), 'danger')
|
||||
return render_template('discord_register.html', username=username, email=email, discord_id=discord_id)
|
||||
if db.session.query(User).filter_by(username=username).first():
|
||||
flash(_('Username already exists. Please Report It.'), 'danger')
|
||||
return render_template('discord_register.html', username="", email=email, discord_id=discord_id)
|
||||
hashed_password = generate_password_hash(password, method='pbkdf2:sha256')
|
||||
new_user = User(
|
||||
username=username,
|
||||
email=email,
|
||||
password=hashed_password,
|
||||
discord_id=discord_id,
|
||||
discord_linked=True
|
||||
)
|
||||
db.session.add(new_user)
|
||||
db.session.commit()
|
||||
login_user(new_user)
|
||||
flash(_('Account created and logged in with Discord.'), 'success')
|
||||
return redirect(url_for('post.feed'))
|
||||
|
||||
@discord_bp.route('/link_discord')
|
||||
@login_required
|
||||
def link_discord():
|
||||
redirect_uri = url_for('discord.authorize_discord', _external=True)
|
||||
return discord.authorize_redirect(redirect_uri)
|
||||
|
||||
@discord_bp.route('/authorize/discord')
|
||||
@login_required
|
||||
def authorize_discord():
|
||||
token = discord.authorize_access_token()
|
||||
user_data = discord.get('users/@me').json()
|
||||
current_user.discord_id = user_data['id']
|
||||
current_user.discord_linked = True
|
||||
db.session.commit()
|
||||
flash(_('Discord account linked!'), 'success')
|
||||
return redirect(url_for('profil.profile'))
|
||||
|
||||
@discord_bp.route('/unlink_discord', methods=['POST'])
|
||||
@login_required
|
||||
def unlink_discord():
|
||||
current_user.discord_id = None
|
||||
current_user.discord_linked = False
|
||||
db.session.commit()
|
||||
flash(_('Discord account unlinked!'), 'success')
|
||||
return redirect(url_for('profil.profile'))
|
||||
12
routes/example oauth.py
Normal file
12
routes/example oauth.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from authlib.integrations.flask_client import OAuth
|
||||
|
||||
oauth = OAuth()
|
||||
discord = oauth.register(
|
||||
name='discord',
|
||||
client_id='YOUR_CLIENT_ID',
|
||||
client_secret='YOUR_CLIENT_SECRET',
|
||||
access_token_url='https://discord.com/api/oauth2/token',
|
||||
authorize_url='https://discord.com/api/oauth2/authorize',
|
||||
api_base_url='https://discord.com/api/',
|
||||
client_kwargs={'scope': 'identify email'}
|
||||
)
|
||||
92
routes/friends.py
Normal file
92
routes/friends.py
Normal file
@@ -0,0 +1,92 @@
|
||||
from flask import Blueprint, redirect, url_for, flash, render_template
|
||||
from flask_login import login_required, current_user
|
||||
from models import db, Notification, Event, User, Friendship, Reward
|
||||
from flask_babel import gettext as _
|
||||
|
||||
friends_bp = Blueprint('friend', __name__)
|
||||
|
||||
@friends_bp.route('/add_friend/<int:user_id>', methods=['POST'])
|
||||
@login_required
|
||||
def add_friend(user_id):
|
||||
if user_id == current_user.id:
|
||||
flash(_('You cannot add yourself as a friend.'), 'warning')
|
||||
return redirect(url_for('user.users'))
|
||||
existing = db.session.query(Friendship).filter_by(requester_id=current_user.id, receiver_id=user_id).first()
|
||||
if existing:
|
||||
flash(_('Friend request already sent.'), 'info')
|
||||
else:
|
||||
friendship = Friendship(requester_id=current_user.id, receiver_id=user_id)
|
||||
db.session.add(friendship)
|
||||
db.session.commit()
|
||||
friend = db.session.get(User, user_id)
|
||||
event = Event(message=_(f"{current_user.username} sent a friend request to {friend.username}."))
|
||||
db.session.add(event)
|
||||
db.session.commit()
|
||||
notif = Notification(
|
||||
user_id=user_id,
|
||||
message=_(f"You have received a friend request from {current_user.username}.")
|
||||
)
|
||||
db.session.add(notif)
|
||||
db.session.commit()
|
||||
flash(_('Friend request sent!'), 'success')
|
||||
return redirect(url_for('user.users'))
|
||||
|
||||
@friends_bp.route('/accept_friend/<int:friendship_id>', methods=['POST'])
|
||||
@login_required
|
||||
def accept_friend(friendship_id):
|
||||
friendship = db.session.get(Friendship, friendship_id)
|
||||
if friendship and friendship.receiver_id == current_user.id:
|
||||
friendship.status = 'accepted'
|
||||
db.session.commit()
|
||||
friend = db.session.get(User, friendship.requester_id)
|
||||
event = Event(message=_(f"{current_user.username} und {friend.username} sind jetzt Freunde."))
|
||||
db.session.add(event)
|
||||
db.session.add(Reward(user_id=current_user.id, type='friendship', points=5))
|
||||
db.session.add(Reward(user_id=friendship.requester_id, type='friendship', points=5))
|
||||
db.session.commit()
|
||||
flash(_('Friend request accepted!'), 'success')
|
||||
else:
|
||||
flash(_('Invalid friend request.'), 'danger')
|
||||
return redirect(url_for('friend.friends'))
|
||||
|
||||
@friends_bp.route('/reject_friend/<int:friendship_id>', methods=['POST'])
|
||||
@login_required
|
||||
def reject_friend(friendship_id):
|
||||
friendship = db.session.get(Friendship, friendship_id)
|
||||
if friendship and friendship.receiver_id == current_user.id:
|
||||
friendship.status = 'rejected'
|
||||
db.session.commit()
|
||||
friend = db.session.get(User, friendship.requester_id)
|
||||
event = Event(message=_(f"{current_user.username} has rejected {friend.username}'s friend request."))
|
||||
db.session.add(event)
|
||||
db.session.commit()
|
||||
flash(_('Friend request rejected.'), 'info')
|
||||
else:
|
||||
flash(_('Invalid friend request.'), 'danger')
|
||||
return redirect(url_for('friend.friends'))
|
||||
|
||||
@friends_bp.route('/friends')
|
||||
@login_required
|
||||
def friends():
|
||||
friends = db.session.query(User).join(Friendship, ((Friendship.requester_id == User.id) | (Friendship.receiver_id == User.id)))\
|
||||
.filter(
|
||||
((Friendship.requester_id == current_user.id) | (Friendship.receiver_id == current_user.id)),
|
||||
Friendship.status == 'accepted',
|
||||
User.id != current_user.id
|
||||
).all()
|
||||
requests = db.session.query(Friendship).filter_by(receiver_id=current_user.id, status='pending').all()
|
||||
return render_template('friends.html', friends=friends, requests=requests)
|
||||
|
||||
@friends_bp.route('/remove_friend/<int:user_id>', methods=['POST'])
|
||||
@login_required
|
||||
def remove_friend(user_id):
|
||||
friendship = db.session.query(Friendship).filter(
|
||||
((Friendship.requester_id == current_user.id) & (Friendship.receiver_id == user_id)) |
|
||||
((Friendship.requester_id == user_id) & (Friendship.receiver_id == current_user.id)),
|
||||
Friendship.status == 'accepted'
|
||||
).first()
|
||||
if friendship:
|
||||
db.session.delete(friendship)
|
||||
db.session.commit()
|
||||
flash(_('Friendship ended.'), 'info')
|
||||
return redirect(url_for('friend.friends'))
|
||||
41
routes/like.py
Normal file
41
routes/like.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from flask import Blueprint, redirect, url_for, flash
|
||||
from flask_login import login_required, current_user
|
||||
from models import db, Post, Notification, Like
|
||||
from flask_babel import gettext as _
|
||||
|
||||
like_bp = Blueprint('like', __name__)
|
||||
|
||||
@like_bp.route('/like/<int:post_id>', methods=['POST', 'GET'])
|
||||
@login_required
|
||||
def like_post(post_id):
|
||||
post = db.session.get(Post, post_id)
|
||||
if not post:
|
||||
flash(_('Post does not exist.'), 'danger')
|
||||
return redirect(url_for('post.feed'))
|
||||
like = db.session.query(Like).filter_by(post_id=post_id, user_id=current_user.id).first()
|
||||
if not like:
|
||||
db.session.add(Like(post_id=post_id, user_id=current_user.id))
|
||||
db.session.commit()
|
||||
if post.user_id != current_user.id:
|
||||
notif = Notification(
|
||||
user_id=post.user_id,
|
||||
message=_(f"{current_user.username} liked your post.")
|
||||
)
|
||||
db.session.add(notif)
|
||||
db.session.commit()
|
||||
flash(_('Post liked.'), 'info')
|
||||
return redirect(url_for('post.feed'))
|
||||
|
||||
@like_bp.route('/unlike/<int:post_id>', methods=['POST', 'GET'])
|
||||
@login_required
|
||||
def unlike_post(post_id):
|
||||
post = db.session.get(Post, post_id)
|
||||
if not post:
|
||||
flash(_('Post does not exist.'), 'danger')
|
||||
return redirect(url_for('post.feed'))
|
||||
like = db.session.query(Like).filter_by(post_id=post_id, user_id=current_user.id).first()
|
||||
if like:
|
||||
db.session.delete(like)
|
||||
db.session.commit()
|
||||
flash(_('Like removed.'), 'info')
|
||||
return redirect(url_for('post.feed'))
|
||||
80
routes/login.py
Normal file
80
routes/login.py
Normal file
@@ -0,0 +1,80 @@
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request
|
||||
from models import db, User, PasswordResetRequest
|
||||
from flask_babel import gettext as _
|
||||
from flask_login import login_user, login_required, logout_user, current_user
|
||||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
import re
|
||||
|
||||
log_bp = Blueprint('log', __name__)
|
||||
|
||||
@log_bp.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
if current_user.is_authenticated:
|
||||
return redirect(url_for('post.feed'))
|
||||
if request.method == 'POST':
|
||||
username = request.form['username']
|
||||
password = request.form['password']
|
||||
user = db.session.query(User).filter_by(username=username).first()
|
||||
if user and check_password_hash(user.password, password):
|
||||
login_user(user)
|
||||
flash(_('Logged in successfully.'), 'success')
|
||||
next_url = request.args.get('next')
|
||||
if next_url:
|
||||
return redirect(next_url)
|
||||
return redirect(url_for('post.feed'))
|
||||
else:
|
||||
flash(_('Invalid username or password.'), 'danger')
|
||||
return render_template('login.html')
|
||||
|
||||
@log_bp.route('/logout')
|
||||
@login_required
|
||||
def logout():
|
||||
logout_user()
|
||||
flash(_('Logged out successfully.'), 'success')
|
||||
return redirect(url_for('index'))
|
||||
|
||||
@log_bp.route('/register', methods=['GET', 'POST'])
|
||||
def register():
|
||||
if current_user.is_authenticated:
|
||||
return redirect(url_for('post.feed'))
|
||||
if request.method == 'POST':
|
||||
username = request.form['username']
|
||||
email = request.form['email']
|
||||
password = request.form['password']
|
||||
confirm_password = request.form['confirm_password']
|
||||
if password != confirm_password:
|
||||
flash(_('Passwords do not match.'), 'danger')
|
||||
elif db.session.query(User).filter_by(username=username).first():
|
||||
flash(_('Username already exists.'), 'danger')
|
||||
elif db.session.query(User).filter_by(email=email).first():
|
||||
flash(_('E-Mail already exists.'), 'danger')
|
||||
elif len(password) < 8:
|
||||
flash(_('Password must be at least 8 characters long.'), 'danger')
|
||||
elif not re.match(r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$', email):
|
||||
flash(_('Invalid email address.'), 'danger')
|
||||
elif not re.match(r'^[a-zA-Z0-9_.+-]+$', username):
|
||||
flash(_('Invalid username. Only alphanumeric characters are allowed.'), 'danger')
|
||||
elif len(username) < 3 or len(username) > 20:
|
||||
flash(_('Username must be between 3 and 20 characters long.'), 'danger')
|
||||
else:
|
||||
hashed_password = generate_password_hash(password, method='pbkdf2:sha256')
|
||||
new_user = User(username=username, email=email, password=hashed_password)
|
||||
db.session.add(new_user)
|
||||
db.session.commit()
|
||||
flash(_('Registered successfully. You can now log in.'), 'success')
|
||||
return redirect(url_for('log.login'))
|
||||
return render_template('register.html')
|
||||
|
||||
@log_bp.route('/reset_password', methods=['GET', 'POST'])
|
||||
def reset_password():
|
||||
if request.method == 'POST':
|
||||
username = request.form['username']
|
||||
user = db.session.query(User).filter_by(username=username).first()
|
||||
if user:
|
||||
req = PasswordResetRequest(user_id=user.id)
|
||||
db.session.add(req)
|
||||
db.session.commit()
|
||||
flash(_('Reset request sent to admins.'), 'info')
|
||||
else:
|
||||
flash(_('No user with this email.'), 'danger')
|
||||
return render_template('reset_password.html')
|
||||
33
routes/notifications.py
Normal file
33
routes/notifications.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from flask import Blueprint, redirect, url_for, flash, render_template
|
||||
from flask_login import login_required, current_user
|
||||
from models import db, Notification
|
||||
from flask_babel import gettext as _
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
noti_bp = Blueprint('notif', __name__)
|
||||
|
||||
@noti_bp.route('/delete_all_notifications', methods=['POST'])
|
||||
@login_required
|
||||
def delete_all_notifications():
|
||||
db.session.query(Notification).filter_by(user_id=current_user.id).delete()
|
||||
db.session.commit()
|
||||
flash(_('All notifications have been deleted.'), 'success')
|
||||
return redirect(url_for('notif.notifications'))
|
||||
|
||||
@noti_bp.route('/delete_notification/<int:notif_id>', methods=['POST'])
|
||||
@login_required
|
||||
def delete_notification(notif_id):
|
||||
notif = db.session.get(Notification, notif_id)
|
||||
if notif and notif.user_id == current_user.id:
|
||||
db.session.delete(notif)
|
||||
db.session.commit()
|
||||
return redirect(url_for('notif.notifications'))
|
||||
|
||||
@noti_bp.route('/notifications')
|
||||
@login_required
|
||||
def notifications():
|
||||
expire_time = datetime.now() - timedelta(days=3)
|
||||
db.session.query(Notification).filter(Notification.created_at < expire_time).delete()
|
||||
db.session.commit()
|
||||
notifications = db.session.query(Notification).filter_by(user_id=current_user.id).order_by(Notification.created_at.desc()).all()
|
||||
return render_template('notifications.html', notifications=notifications)
|
||||
225
routes/post.py
Normal file
225
routes/post.py
Normal file
@@ -0,0 +1,225 @@
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request
|
||||
from flask_login import login_required, current_user
|
||||
from models import db, Post, Reward, Friendship, Upload, Notification, Event, SHOPITEM_ID_EXTRA_UPLOAD, SHOPITEM_ID_EXTRA_TYPES, Like, Comment
|
||||
from flask_babel import gettext as _
|
||||
from sqlalchemy import func
|
||||
from datetime import datetime, date
|
||||
import os
|
||||
|
||||
post_bp = Blueprint('post', __name__)
|
||||
|
||||
@post_bp.route('/post', methods=['POST'])
|
||||
@login_required
|
||||
def create_post():
|
||||
content = request.form['content']
|
||||
visibility = request.form.get('visibility', 'public')
|
||||
file = request.files.get('file')
|
||||
file2 = request.files.get('file2')
|
||||
post = None
|
||||
if content:
|
||||
post = Post(user_id=current_user.id, content=content, visibility=visibility)
|
||||
if not SHOPITEM_ID_EXTRA_TYPES in [usi.item_id for usi in current_user.shop_items]:
|
||||
if len(content) > 250:
|
||||
flash(_('Post content is too long. Please limit it to 250 characters.'), 'danger')
|
||||
return redirect(url_for('post.feed'))
|
||||
else:
|
||||
if len(content) > 500:
|
||||
flash(_('Post content is too long. Please limit it to 500 characters.'), 'danger')
|
||||
return redirect(url_for('post.feed'))
|
||||
db.session.add(post)
|
||||
db.session.commit()
|
||||
flash(_('Post created!'), 'success')
|
||||
if file and file.filename:
|
||||
ext = os.path.splitext(file.filename)[1]
|
||||
filename = f"user_{current_user.id}_{int(datetime.now().timestamp())}{ext}"
|
||||
filepath = os.path.join('static/uploads', filename)
|
||||
file.save(filepath)
|
||||
upload = Upload(user_id=current_user.id, post_id=post.id, filename=filename, filetype=file.content_type)
|
||||
db.session.add(upload)
|
||||
db.session.commit()
|
||||
if file2 and file2.filename and SHOPITEM_ID_EXTRA_UPLOAD in [usi.item_id for usi in current_user.shop_items]:
|
||||
ext = os.path.splitext(file2.filename)[1]
|
||||
filename = f"user_{current_user.id}_{int(datetime.now().timestamp())}_extra{ext}"
|
||||
filepath = os.path.join('static/uploads', filename)
|
||||
file2.save(filepath)
|
||||
upload2 = Upload(user_id=current_user.id, post_id=post.id, filename=filename, filetype=file2.content_type)
|
||||
db.session.add(upload2)
|
||||
db.session.commit()
|
||||
event = Event(message=_(f"{current_user.username} has created a new post."))
|
||||
db.session.add(event)
|
||||
db.session.commit()
|
||||
notif = Notification(user_id=current_user.id, message=_("You have created a new post."))
|
||||
db.session.add(notif)
|
||||
db.session.add(Reward(user_id=current_user.id, type='post', points=5))
|
||||
db.session.commit()
|
||||
|
||||
return redirect(url_for('post.feed'))
|
||||
|
||||
@post_bp.route('/edit_post/<int:post_id>', methods=['GET'])
|
||||
@login_required
|
||||
def edit_post(post_id):
|
||||
post = db.session.get(Post, post_id)
|
||||
if not post:
|
||||
flash(_('Post does not exist.'), 'danger')
|
||||
return redirect(url_for('post.feed'))
|
||||
if post.user_id != current_user.id:
|
||||
flash(_('You do not have permission to edit this post.'), 'danger')
|
||||
return redirect(url_for('post.feed'))
|
||||
return render_template('edit_post.html', post=post)
|
||||
|
||||
@post_bp.route('/update_post/<int:post_id>', methods=['POST', 'GET'])
|
||||
@login_required
|
||||
def update_post(post_id):
|
||||
post = db.session.get(Post, post_id)
|
||||
if not post:
|
||||
flash(_('Post does not exist.'), 'danger')
|
||||
return redirect(url_for('post.feed'))
|
||||
if post.user_id != current_user.id:
|
||||
flash(_('You do not have permission to edit this post.'), 'danger')
|
||||
return redirect(url_for('post.feed'))
|
||||
content = request.form['content']
|
||||
visibility = request.form.get('visibility', 'public')
|
||||
post.content = content
|
||||
post.visibility = visibility
|
||||
file = request.files.get('upload')
|
||||
file2 = request.files.get('upload2')
|
||||
if not SHOPITEM_ID_EXTRA_TYPES in [usi.item_id for usi in current_user.shop_items]:
|
||||
if len(post.content) > 250:
|
||||
flash(_('Post content is too long. Please limit it to 250 characters.'), 'danger')
|
||||
return redirect(url_for('post.feed'))
|
||||
else:
|
||||
if len(post.content) > 500:
|
||||
flash(_('Post content is too long. Please limit it to 500 characters.'), 'danger')
|
||||
return redirect(url_for('post.feed'))
|
||||
|
||||
if file:
|
||||
for upload in post.uploads:
|
||||
file_path = os.path.join('static/uploads', upload.filename)
|
||||
try:
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
db.session.delete(upload)
|
||||
|
||||
ext = os.path.splitext(file.filename)[1]
|
||||
filename = f"user_{current_user.id}_{int(datetime.now().timestamp())}{ext}"
|
||||
filepath = os.path.join('static/uploads', filename)
|
||||
file.save(filepath)
|
||||
upload = Upload(user_id=current_user.id, post_id=post.id, filename=filename, filetype=file.content_type)
|
||||
db.session.add(upload)
|
||||
|
||||
if file2 and SHOPITEM_ID_EXTRA_UPLOAD in [usi.item_id for usi in current_user.shop_items]:
|
||||
for upload in post.uploads:
|
||||
if upload.filename.endswith('_extra' + os.path.splitext(file2.filename)[1]):
|
||||
file_path = os.path.join('static/uploads', upload.filename)
|
||||
try:
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
db.session.delete(upload)
|
||||
|
||||
ext = os.path.splitext(file2.filename)[1]
|
||||
filename = f"user_{current_user.id}_{int(datetime.now().timestamp())}_extra{ext}"
|
||||
filepath = os.path.join('static/uploads', filename)
|
||||
file2.save(filepath)
|
||||
upload2 = Upload(user_id=current_user.id, post_id=post.id, filename=filename, filetype=file2.content_type)
|
||||
db.session.add(upload2)
|
||||
|
||||
notif = Notification(user_id=current_user.id, message=_("Your post has been updated."))
|
||||
db.session.add(notif)
|
||||
event = Event(message=f"{current_user.username} has updated post {post.id}.")
|
||||
db.session.add(event)
|
||||
db.session.commit()
|
||||
flash(_('Post updated!'), 'success')
|
||||
return redirect(url_for('post.feed'))
|
||||
|
||||
@post_bp.route('/feed')
|
||||
def feed():
|
||||
if current_user.is_authenticated:
|
||||
today = date.today()
|
||||
reward_today = db.session.query(Reward).filter(
|
||||
Reward.user_id == current_user.id,
|
||||
Reward.type == 'daily',
|
||||
func.date(Reward.created_at) == today
|
||||
).first()
|
||||
|
||||
if not reward_today:
|
||||
db.session.add(Reward(user_id=current_user.id, type='daily', points=10))
|
||||
db.session.commit()
|
||||
|
||||
if current_user.is_admin:
|
||||
posts = db.session.query(Post).order_by(Post.created_at.desc()).all()
|
||||
else:
|
||||
friend_ids = [
|
||||
f.requester_id if f.requester_id != current_user.id else f.receiver_id
|
||||
for f in db.session.query(Friendship).filter(
|
||||
((Friendship.requester_id == current_user.id) | (Friendship.receiver_id == current_user.id)),
|
||||
Friendship.status == 'accepted'
|
||||
).all()
|
||||
]
|
||||
posts = db.session.query(Post).filter(
|
||||
(Post.visibility == 'public') |
|
||||
((Post.visibility == 'friends') & (Post.user_id.in_(friend_ids + [current_user.id])))
|
||||
).order_by(Post.created_at.desc()).all()
|
||||
else:
|
||||
posts = db.session.query(Post).filter_by(visibility='public').order_by(Post.created_at.desc()).all()
|
||||
events = db.session.query(Event).order_by(Event.timestamp.desc()).limit(20).all()
|
||||
return render_template('feed.html', posts=posts, events=events)
|
||||
|
||||
@post_bp.route('/delete_post/<int:post_id>', methods=['POST'])
|
||||
@login_required
|
||||
def delete_post(post_id):
|
||||
post = db.session.get(Post, post_id)
|
||||
if post and post.user_id == current_user.id:
|
||||
for upload in post.uploads:
|
||||
file_path = os.path.join('static/uploads', upload.filename)
|
||||
try:
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
except Exception:
|
||||
pass
|
||||
likes = db.session.query(Like).filter_by(post_id=post_id).all()
|
||||
for like in likes:
|
||||
db.session.delete(like)
|
||||
db.session.delete(post)
|
||||
comments = db.session.query(Comment).filter_by(post_id=post_id).all()
|
||||
for comment in comments:
|
||||
db.session.delete(comment)
|
||||
notif = Notification(user_id=current_user.id, message=_("Your post has been deleted."))
|
||||
db.session.add(notif)
|
||||
event = Event(message=_(f"{current_user.username} has deleted post {post.id}."))
|
||||
db.session.add(event)
|
||||
db.session.commit()
|
||||
flash(_('Post and all uploads deleted.'), 'success')
|
||||
else:
|
||||
flash(_('Not allowed.'), 'danger')
|
||||
return redirect(url_for('post.feed'))
|
||||
|
||||
@post_bp.route('/delete_comment/<int:comment_id>', methods=['POST'])
|
||||
@login_required
|
||||
def delete_comment(comment_id):
|
||||
comment = db.session.get(Comment, comment_id)
|
||||
if comment and comment.user_id == current_user.id:
|
||||
db.session.delete(comment)
|
||||
db.session.commit()
|
||||
flash(_('Comment deleted.'), 'success')
|
||||
else:
|
||||
flash(_('Not allowed.'), 'danger')
|
||||
return redirect(url_for('post.feed'))
|
||||
|
||||
@post_bp.route('/comment/<int:post_id>', methods=['POST'])
|
||||
@login_required
|
||||
def comment_post(post_id):
|
||||
content = request.form['comment']
|
||||
if content:
|
||||
comment = Comment(post_id=post_id, user_id=current_user.id, content=content)
|
||||
db.session.add(comment)
|
||||
notif = Notification(user_id=current_user.id, message=_("You have written a comment."))
|
||||
db.session.add(notif)
|
||||
db.session.add(Reward(user_id=current_user.id, type='comment', points=2))
|
||||
db.session.commit()
|
||||
return redirect(url_for('post.feed'))
|
||||
66
routes/profile.py
Normal file
66
routes/profile.py
Normal file
@@ -0,0 +1,66 @@
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request
|
||||
from flask_login import login_required, current_user
|
||||
from models import db, User, Post
|
||||
from flask_babel import gettext as _
|
||||
from werkzeug.security import generate_password_hash
|
||||
import re
|
||||
|
||||
profile_bp = Blueprint('profil', __name__)
|
||||
|
||||
@profile_bp.route('/profile')
|
||||
@login_required
|
||||
def profile():
|
||||
return render_template('profile.html', user=current_user)
|
||||
|
||||
@profile_bp.route('/my_posts')
|
||||
@login_required
|
||||
def my_posts():
|
||||
posts = db.session.query(Post).filter_by(user_id=current_user.id).order_by(Post.created_at.desc()).all()
|
||||
return render_template('my_posts.html', posts=posts)
|
||||
|
||||
@profile_bp.route('/edit_profile', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def edit_profile():
|
||||
if request.method == 'POST':
|
||||
new_username = request.form['username']
|
||||
new_email = request.form['email']
|
||||
new_password = request.form['password']
|
||||
confirm_password = request.form['confirm_password']
|
||||
if not current_user.username or not current_user.email:
|
||||
flash(_('Username and email cannot be empty.'), 'danger')
|
||||
return redirect(url_for('profile.edit_profile'))
|
||||
else:
|
||||
if new_username and new_username != current_user.username:
|
||||
if db.session.query(User).filter_by(username=new_username).first():
|
||||
flash(_('Username already taken.'), 'danger')
|
||||
return redirect(url_for('profile.edit_profile'))
|
||||
elif not re.match(r'^[a-zA-Z0-9_.+-]+$', new_username):
|
||||
flash(_('Invalid username. Only alphanumeric characters are allowed.'), 'danger')
|
||||
return redirect(url_for('profile.edit_profile'))
|
||||
else:
|
||||
current_user.username = new_username
|
||||
elif new_email and new_email != current_user.email:
|
||||
if not re.match(r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$', new_email):
|
||||
flash(_('Invalid email address.'), 'danger')
|
||||
return redirect(url_for('profile.edit_profile'))
|
||||
elif db.session.query(User).filter_by(email=new_email).first():
|
||||
flash(_('E-Mail already taken.'), 'danger')
|
||||
return redirect(url_for('profile.edit_profile'))
|
||||
else:
|
||||
current_user.email = new_email
|
||||
elif new_password:
|
||||
if len(new_password) < 8:
|
||||
flash(_('Password must be at least 8 characters long.'), 'danger')
|
||||
return redirect(url_for('profile.edit_profile'))
|
||||
elif new_password != confirm_password:
|
||||
flash(_('Passwords do not match.'), 'danger')
|
||||
return redirect(url_for('profile.edit_profile'))
|
||||
else:
|
||||
current_user.password = generate_password_hash(new_password, method='pbkdf2:sha256')
|
||||
else:
|
||||
flash(_('No changes made.'), 'info')
|
||||
return redirect(url_for('profil.profile'))
|
||||
db.session.commit()
|
||||
flash(_('Profile updated.'), 'success')
|
||||
return redirect(url_for('profil.profile'))
|
||||
return render_template('edit_profile.html', user=current_user)
|
||||
77
routes/support.py
Normal file
77
routes/support.py
Normal file
@@ -0,0 +1,77 @@
|
||||
from flask import Blueprint, render_template, redirect, url_for, flash, request, abort
|
||||
from models import db, SupportComment, SupportRequest
|
||||
from flask_babel import gettext as _
|
||||
from flask_login import login_required, current_user
|
||||
from datetime import datetime
|
||||
|
||||
support_bp = Blueprint('support', __name__, url_prefix="/support")
|
||||
|
||||
@support_bp.route('/', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def support():
|
||||
if request.method == 'POST':
|
||||
title = request.form.get('title')
|
||||
description = request.form.get('description')
|
||||
if description and title:
|
||||
ticket = SupportRequest(
|
||||
user_id=current_user.id,
|
||||
title=title,
|
||||
status='open',
|
||||
created_at=datetime.now()
|
||||
)
|
||||
db.session.add(ticket)
|
||||
db.session.commit()
|
||||
db.session.add(SupportComment(request_id=ticket.id, user_id=current_user.id, message=description, created_at=datetime.now()))
|
||||
db.session.commit()
|
||||
flash(_('Support request created!'), 'success')
|
||||
else:
|
||||
flash(_('Title and message required!'), 'danger')
|
||||
|
||||
if current_user.is_admin:
|
||||
support_requests = db.session.query(SupportRequest).order_by(SupportRequest.created_at.desc()).all()
|
||||
else:
|
||||
support_requests = db.session.query(SupportRequest).filter_by(user_id=current_user.id).order_by(SupportRequest.created_at.desc()).all()
|
||||
return render_template('support.html', support_requests=support_requests)
|
||||
|
||||
@support_bp.route('/close/<int:request_id>', methods=['POST'])
|
||||
@login_required
|
||||
def support_close(request_id):
|
||||
ticket = db.session.get(SupportRequest, request_id)
|
||||
if not ticket or (not current_user.is_admin and ticket.user_id != current_user.id):
|
||||
abort(403)
|
||||
ticket.status = 'closed'
|
||||
db.session.commit()
|
||||
flash(_('Ticket closed.'), 'success')
|
||||
return redirect(url_for('support.support_thread', request_id=request_id))
|
||||
|
||||
@support_bp.route('/thread/<int:request_id>', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def support_thread(request_id):
|
||||
ticket = db.session.get(SupportRequest, request_id)
|
||||
if not ticket or (not current_user.is_admin and ticket.user_id != current_user.id):
|
||||
abort(403)
|
||||
if request.method == 'POST' and ticket.status == 'open':
|
||||
message = request.form.get('message')
|
||||
if message:
|
||||
db.session.add(SupportComment(request_id=request_id, user_id=current_user.id, message=message, created_at=datetime.now()))
|
||||
db.session.commit()
|
||||
flash(_('Comment added.'), 'success')
|
||||
else:
|
||||
flash(_('Message required!'), 'danger')
|
||||
comments = db.session.query(SupportComment).filter_by(request_id=request_id).order_by(SupportComment.created_at.asc()).all()
|
||||
return render_template('support_thread.html', ticket=ticket, comments=comments)
|
||||
|
||||
@support_bp.route('/delete/<int:request_id>', methods=['POST'])
|
||||
@login_required
|
||||
def support_delete(request_id):
|
||||
if not current_user.is_admin:
|
||||
abort(403)
|
||||
ticket = db.session.get(SupportRequest, request_id)
|
||||
if ticket:
|
||||
db.session.query(SupportComment).filter_by(request_id=request_id).delete()
|
||||
db.session.delete(ticket)
|
||||
db.session.commit()
|
||||
flash(_('Support ticket deleted.'), 'success')
|
||||
else:
|
||||
flash(_('Ticket not found.'), 'danger')
|
||||
return redirect(url_for('support.support'))
|
||||
104
routes/user.py
Normal file
104
routes/user.py
Normal file
@@ -0,0 +1,104 @@
|
||||
from flask import Blueprint, redirect, url_for, flash, request, render_template
|
||||
from flask_login import logout_user
|
||||
from flask_login import login_required, current_user
|
||||
from models import db, Notification, Upload, Event, User
|
||||
from flask_babel import gettext as _
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
__mapper_args__ = {"confirm_deleted_rows": False}
|
||||
user_bp = Blueprint('user', __name__)
|
||||
|
||||
@user_bp.route('/delete_pic', methods=['POST'])
|
||||
@login_required
|
||||
def delete_pic():
|
||||
if current_user.profile_pic and current_user.profile_pic != 'default.png':
|
||||
try:
|
||||
os.remove(os.path.join('static/profile_pics', current_user.profile_pic))
|
||||
except Exception:
|
||||
pass
|
||||
current_user.profile_pic = 'default.png'
|
||||
db.session.commit()
|
||||
flash(_('Profile picture deleted.'), 'success')
|
||||
return redirect(url_for('profil.profile'))
|
||||
|
||||
@user_bp.route('/upload_pic', methods=['POST'])
|
||||
@login_required
|
||||
def upload_pic():
|
||||
file = request.files['profile_pic']
|
||||
if file:
|
||||
if current_user.profile_pic and current_user.profile_pic != 'default.png':
|
||||
try:
|
||||
os.remove(os.path.join('static/profile_pics', current_user.profile_pic))
|
||||
except Exception:
|
||||
pass
|
||||
current_user.profile_pic = 'default.png'
|
||||
db.session.commit()
|
||||
|
||||
ext = os.path.splitext(file.filename)[1]
|
||||
filename = f"user_{current_user.id}_{int(datetime.now().timestamp())}{ext}"
|
||||
filepath = os.path.join('static/profile_pics', filename)
|
||||
file.save(filepath)
|
||||
current_user.profile_pic = filename
|
||||
db.session.commit()
|
||||
|
||||
notif = Notification(user_id=current_user.id, message=_("You have changed your profile picture."))
|
||||
db.session.add(notif)
|
||||
db.session.commit()
|
||||
|
||||
event = Event(message=_(f"{current_user.username} has changed their profile picture."))
|
||||
db.session.add(event)
|
||||
db.session.commit()
|
||||
flash(_('Profile picture updated.'), 'success')
|
||||
return redirect(url_for('profil.profile'))
|
||||
|
||||
@user_bp.route('/delete_account', methods=['POST'])
|
||||
@login_required
|
||||
def delete_account():
|
||||
if current_user.is_owner:
|
||||
flash(_('You cannot delete the owner account.'), 'danger')
|
||||
return redirect(url_for('profil.profile'))
|
||||
if current_user.is_admin:
|
||||
flash(_('You cannot delete an admin account.'), 'danger')
|
||||
return redirect(url_for('profil.profile'))
|
||||
event = Event(message=f"{current_user.username} hat sein Konto gelöscht.")
|
||||
db.session.add(event)
|
||||
for post in current_user.posts:
|
||||
db.session.delete(post)
|
||||
for friendship in current_user.friendships_sent + current_user.friendships_received:
|
||||
db.session.delete(friendship)
|
||||
for comment in current_user.comments:
|
||||
db.session.delete(comment)
|
||||
for like in current_user.likes:
|
||||
db.session.delete(like)
|
||||
for shop_item in current_user.shop_items:
|
||||
db.session.delete(shop_item)
|
||||
for reward in current_user.rewards:
|
||||
db.session.delete(reward)
|
||||
for upload in current_user.uploads:
|
||||
file_path = os.path.join('static/uploads', upload.filename)
|
||||
try:
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
except Exception:
|
||||
pass
|
||||
db.session.delete(upload)
|
||||
if current_user.profile_pic and not current_user.profile_pic == 'default.png':
|
||||
try:
|
||||
os.remove(os.path.join('static/profile_pics', current_user.profile_pic))
|
||||
except Exception:
|
||||
pass
|
||||
notifications = db.session.query(Notification).filter_by(user_id=current_user.id).all()
|
||||
for notif in notifications:
|
||||
db.session.delete(notif)
|
||||
db.session.delete(current_user)
|
||||
db.session.commit()
|
||||
logout_user()
|
||||
flash(_('Account and all your data deleted.'), 'success')
|
||||
return redirect(url_for('index'))
|
||||
|
||||
@user_bp.route('/users')
|
||||
@login_required
|
||||
def users():
|
||||
all_users = db.session.query(User).filter(User.id != current_user.id).all()
|
||||
return render_template('users.html', users=all_users)
|
||||
Reference in New Issue
Block a user