Update database

Correctly link user to their posts and groups
Change the table names to Group, Post and User
Remove unused Bans and Logs table, possibly will return later
This commit is contained in:
Michał 2023-04-12 15:16:43 +00:00
parent 9a21064dd5
commit d36699bd1f
12 changed files with 185 additions and 270 deletions

View file

@ -17,8 +17,8 @@ from werkzeug.exceptions import HTTPException
from werkzeug.security import generate_password_hash
from gallery.extensions import db, migrate, login_manager, assets, compress, cache
from gallery.models import Users
from gallery.views import index, image, group, settings, profile
from gallery.models import User
from gallery import api
from gallery import auth
@ -44,7 +44,7 @@ def create_app(): # pylint: disable=R0914
with app.app_context():
db.create_all()
register_user = Users(
register_user = User(
username=app.config["ADMIN_CONF"]["username"],
email=app.config["ADMIN_CONF"]["email"],
password=generate_password_hash('changeme!', method="sha256"),
@ -81,7 +81,7 @@ def create_app(): # pylint: disable=R0914
@login_manager.user_loader
def load_user(user_id):
return Users.query.filter_by(alt_id=user_id).first()
return User.query.filter_by(alt_id=user_id).first()
@login_manager.unauthorized_handler
def unauthorized():

View file

@ -14,7 +14,7 @@ from flask_login import login_required, current_user
from colorthief import ColorThief
from gallery.extensions import db
from gallery.models import Posts, Groups, GroupJunction
from gallery.models import Post, Group, GroupJunction
from gallery.utils import metadata as mt
from gallery.utils.generate_image import generate_thumbnail
@ -83,7 +83,7 @@ def upload():
img_colors = ColorThief(img_path).get_palette(color_count=3) # Get color palette
# Save to database
query = Posts(
query = Post(
author_id=current_user.id,
filename=img_name + "." + img_ext,
mimetype=img_ext,
@ -105,39 +105,33 @@ def delete_image(image_id):
"""
Deletes an image from the server and database
"""
img = Posts.query.filter_by(id=image_id).first()
post = Post.query.filter_by(id=image_id).first()
# Check if image exists and if user is allowed to delete it (author)
if img is None:
if post is None:
abort(404)
if img.author_id != current_user.id:
if post.author_id != current_user.id:
abort(403)
# Delete file
try:
os.remove(os.path.join(current_app.config["UPLOAD_FOLDER"], img.filename))
os.remove(os.path.join(current_app.config["UPLOAD_FOLDER"], post.filename))
except FileNotFoundError:
logging.warning(
"File not found: %s, already deleted or never existed", img.filename
"File not found: %s, already deleted or never existed", post.filename
)
# Delete cached files
cache_path = os.path.join(platformdirs.user_config_dir("onlylegs"), "cache")
cache_name = img.filename.rsplit(".")[0]
cache_name = post.filename.rsplit(".")[0]
for cache_file in pathlib.Path(cache_path).glob(cache_name + "*"):
os.remove(cache_file)
post = Posts.query.filter_by(id=image_id).first()
GroupJunction.query.filter_by(post_id=image_id).delete()
db.session.delete(post)
groups = GroupJunction.query.filter_by(post_id=image_id).all()
for group in groups:
db.session.delete(group)
# Commit all changes
db.session.commit()
logging.info("Removed image (%s) %s", image_id, img.filename)
logging.info("Removed image (%s) %s", image_id, post.filename)
flash(["Image was all in Le Head!", "1"])
return "Gwa Gwa"
@ -148,7 +142,7 @@ def create_group():
"""
Creates a group
"""
new_group = Groups(
new_group = Group(
name=request.form["name"],
description=request.form["description"],
author_id=current_user.id,
@ -170,25 +164,18 @@ def modify_group():
image_id = request.form["image"]
action = request.form["action"]
group = Groups.query.filter_by(id=group_id).first()
group = db.get_or_404(Group, group_id)
image = db.get_or_404(Post, image_id)
if group is None:
abort(404)
elif group.author_id != current_user.id:
if group.author_id != current_user.id:
abort(403)
if action == "add":
if not GroupJunction.query.filter_by(
group_id=group_id, post_id=image_id
).first():
db.session.add(GroupJunction(group_id=group_id, post_id=image_id))
if action == "add" and not GroupJunction.query.filter_by(group_id=group_id, post_id=image_id).first():
db.session.add(GroupJunction(group_id=group_id, post_id=image_id))
elif request.form["action"] == "remove":
db.session.delete(
GroupJunction.query.filter_by(group_id=group_id, post_id=image_id).first()
)
GroupJunction.query.filter_by(group_id=group_id, post_id=image_id).delete()
db.session.commit()
return ":3"
@ -198,21 +185,15 @@ def delete_group():
Deletes a group
"""
group_id = request.form["group"]
group = Groups.query.filter_by(id=group_id).first()
group = Group.query.filter_by(id=group_id).first()
if group is None:
abort(404)
elif group.author_id != current_user.id:
abort(403)
group_del = Groups.query.filter_by(id=group_id).first()
db.session.delete(group_del)
junction_del = GroupJunction.query.filter_by(group_id=group_id).all()
for junction in junction_del:
db.session.delete(junction)
GroupJunction.query.filter_by(group_id=group_id).delete()
db.session.delete(group)
db.session.commit()
flash(["Group yeeted!", "1"])

View file

@ -11,7 +11,7 @@ from werkzeug.security import check_password_hash, generate_password_hash
from flask_login import login_user, logout_user, login_required
from gallery.extensions import db
from gallery.models import Users
from gallery.models import User
blueprint = Blueprint("auth", __name__, url_prefix="/auth")
@ -28,7 +28,7 @@ def login():
password = request.form["password"].strip()
remember = bool(request.form["remember-me"])
user = Users.query.filter_by(username=username).first()
user = User.query.filter_by(username=username).first()
if not user or not check_password_hash(user.password, password):
logging.error("Login attempt from %s", request.remote_addr)
@ -77,7 +77,7 @@ def register():
elif password_repeat != password:
error.append("Passwords do not match!")
user_exists = Users.query.filter_by(username=username).first()
user_exists = User.query.filter_by(username=username).first()
if user_exists:
error.append("User already exists!")
@ -86,7 +86,7 @@ def register():
print(error)
return jsonify(error), 400
register_user = Users(
register_user = User(
username=username,
email=email,
password=generate_password_hash(password, method="sha256"),

View file

@ -2,137 +2,99 @@
OnlyLegs - Database models and ions for SQLAlchemy
"""
from uuid import uuid4
from flask_login import UserMixin
from .extensions import db
class Users(db.Model, UserMixin): # pylint: disable=too-few-public-methods, C0103
"""
User table
Joins with post, groups, session and log
"""
__tablename__ = "users"
# Gallery used information
id = db.Column(db.Integer, primary_key=True)
alt_id = db.Column(db.String, unique=True, nullable=False, default=str(uuid4()))
profile_picture = db.Column(db.String, nullable=True, default=None)
username = db.Column(db.String, unique=True, nullable=False)
email = db.Column(db.String, unique=True, nullable=False)
password = db.Column(db.String, nullable=False)
joined_at = db.Column(
db.DateTime,
nullable=False,
server_default=db.func.now(), # pylint: disable=E1102
)
posts = db.relationship("Posts", backref="users")
groups = db.relationship("Groups", backref="users")
log = db.relationship("Logs", backref="users")
def get_id(self):
return str(self.alt_id)
class Posts(db.Model): # pylint: disable=too-few-public-methods, C0103
"""
Post table
Joins with group_junction
"""
__tablename__ = "posts"
id = db.Column(db.Integer, primary_key=True)
author_id = db.Column(db.Integer, db.ForeignKey("users.id"))
created_at = db.Column(
db.DateTime,
nullable=False,
server_default=db.func.now(), # pylint: disable=E1102
)
filename = db.Column(db.String, unique=True, nullable=False)
mimetype = db.Column(db.String, nullable=False)
exif = db.Column(db.PickleType, nullable=False)
colours = db.Column(db.PickleType, nullable=False)
description = db.Column(db.String, nullable=False)
alt = db.Column(db.String, nullable=False)
junction = db.relationship("GroupJunction", backref="posts")
class Groups(db.Model): # pylint: disable=too-few-public-methods, C0103
"""
Group table
Joins with group_junction
"""
__tablename__ = "groups"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False)
description = db.Column(db.String, nullable=False)
author_id = db.Column(db.Integer, db.ForeignKey("users.id"))
created_at = db.Column(
db.DateTime,
nullable=False,
server_default=db.func.now(), # pylint: disable=E1102
)
junction = db.relationship("GroupJunction", backref="groups")
class GroupJunction(db.Model): # pylint: disable=too-few-public-methods, C0103
"""
Junction table for posts and groups
Joins with posts and groups
"""
__tablename__ = "group_junction"
id = db.Column(db.Integer, primary_key=True)
group_id = db.Column(db.Integer, db.ForeignKey("group.id"))
post_id = db.Column(db.Integer, db.ForeignKey("post.id"))
date_added = db.Column(
db.DateTime,
nullable=False,
server_default=db.func.now(), # pylint: disable=E1102
)
group_id = db.Column(db.Integer, db.ForeignKey("groups.id"))
post_id = db.Column(db.Integer, db.ForeignKey("posts.id"))
class Logs(db.Model): # pylint: disable=too-few-public-methods, C0103
class Post(db.Model): # pylint: disable=too-few-public-methods, C0103
"""
Log table
Joins with user
Post table
"""
__tablename__ = "logs"
__tablename__ = "post"
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey("users.id"))
ip_address = db.Column(db.String, nullable=False)
code = db.Column(db.Integer, nullable=False)
note = db.Column(db.String, nullable=False)
author_id = db.Column(db.Integer, db.ForeignKey("user.id"))
filename = db.Column(db.String, unique=True, nullable=False)
mimetype = db.Column(db.String, nullable=False)
exif = db.Column(db.PickleType, nullable=False)
colours = db.Column(db.PickleType, nullable=False)
description = db.Column(db.String, nullable=False)
alt = db.Column(db.String, nullable=False)
created_at = db.Column(
db.DateTime,
nullable=False,
server_default=db.func.now(), # pylint: disable=E1102
)
junction = db.relationship("GroupJunction", backref="posts")
class Bans(db.Model): # pylint: disable=too-few-public-methods, C0103
class Group(db.Model): # pylint: disable=too-few-public-methods, C0103
"""
Bans table
Group table
"""
__tablename__ = "bans"
__tablename__ = "group"
id = db.Column(db.Integer, primary_key=True)
ip_address = db.Column(db.String, nullable=False)
code = db.Column(db.Integer, nullable=False)
note = db.Column(db.String, nullable=False)
banned_at = db.Column(
name = db.Column(db.String, nullable=False)
description = db.Column(db.String, nullable=False)
author_id = db.Column(db.Integer, db.ForeignKey("user.id"))
created_at = db.Column(
db.DateTime,
nullable=False,
server_default=db.func.now(), # pylint: disable=E1102
)
junction = db.relationship("GroupJunction", backref="groups")
class User(db.Model, UserMixin): # pylint: disable=too-few-public-methods, C0103
"""
User table
"""
__tablename__ = "user"
# Gallery used information
id = db.Column(db.Integer, primary_key=True)
alt_id = db.Column(db.String, unique=True, nullable=False, default=str(uuid4()))
profile_picture = db.Column(db.String, nullable=True, default=None)
username = db.Column(db.String, unique=True, nullable=False)
email = db.Column(db.String, unique=True, nullable=False)
password = db.Column(db.String, nullable=False)
joined_at = db.Column(
db.DateTime,
nullable=False,
server_default=db.func.now(), # pylint: disable=E1102
)
posts = db.relationship('Post', backref='author')
groups = db.relationship('Group', backref='author')
def get_id(self):
return str(self.alt_id)

View file

@ -15,7 +15,7 @@
}
}
{% if current_user.id == group.author_id %}
{% if current_user.id == group.author.id %}
function groupDelete() {
cancelBtn = document.createElement('button');
cancelBtn.classList.add('btn-block');
@ -223,7 +223,7 @@
<img src="{{ url_for('api.file', file_name=images.0.filename ) }}?r=prev" onload="imgFade(this)" style="opacity:0;" alt="{% if images.0.alt %}{{ images.0.alt }}{% else %}Group Banner{% endif %}"/>
<span class="banner-filter"></span>
<div class="banner-content">
<p class="banner-info"><a href="{{ url_for('profile.profile', id=group.author_id) }}" class="link">By {{ group.author_username }}</a></p>
<p class="banner-info"><a href="{{ url_for('profile.profile', id=group.author.id) }}" class="link">By {{ group.author.username }}</a></p>
<h1 class="banner-header">{{ group.name }}</h1>
<p class="banner-subtitle">{{ images|length }} Images · {{ group.description }}</p>
<div class="pill-row">
@ -232,7 +232,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M216,112v96a16,16,0,0,1-16,16H56a16,16,0,0,1-16-16V112A16,16,0,0,1,56,96H80a8,8,0,0,1,0,16H56v96H200V112H176a8,8,0,0,1,0-16h24A16,16,0,0,1,216,112ZM93.66,69.66,120,43.31V136a8,8,0,0,0,16,0V43.31l26.34,26.35a8,8,0,0,0,11.32-11.32l-40-40a8,8,0,0,0-11.32,0l-40,40A8,8,0,0,0,93.66,69.66Z"></path></svg>
</button>
</div>
{% if current_user.id == group.author_id %}
{% if current_user.id == group.author.id %}
<div>
<button class="pill-item pill__critical" onclick="groupDelete()">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M216,48H176V40a24,24,0,0,0-24-24H104A24,24,0,0,0,80,40v8H40a8,8,0,0,0,0,16h8V208a16,16,0,0,0,16,16H192a16,16,0,0,0,16-16V64h8a8,8,0,0,0,0-16ZM96,40a8,8,0,0,1,8-8h48a8,8,0,0,1,8,8v8H96Zm96,168H64V64H192ZM112,104v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm48,0v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Z"></path></svg>
@ -249,14 +249,14 @@
<div class="banner-small">
<div class="banner-content">
<h1 class="banner-header">{{ group.name }}</h1>
<p class="banner-info">By {{ group.author_username }}</p>
<p class="banner-info">By {{ group.author.username }}</p>
<div class="pill-row">
<div>
<button class="pill-item" onclick="groupShare()">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M216,112v96a16,16,0,0,1-16,16H56a16,16,0,0,1-16-16V112A16,16,0,0,1,56,96H80a8,8,0,0,1,0,16H56v96H200V112H176a8,8,0,0,1,0-16h24A16,16,0,0,1,216,112ZM93.66,69.66,120,43.31V136a8,8,0,0,0,16,0V43.31l26.34,26.35a8,8,0,0,0,11.32-11.32l-40-40a8,8,0,0,0-11.32,0l-40,40A8,8,0,0,0,93.66,69.66Z"></path></svg>
</button>
</div>
{% if current_user.id == group.author_id %}
{% if current_user.id == group.author.id %}
<div>
<button class="pill-item pill__critical" onclick="groupDelete()">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M216,48H176V40a24,24,0,0,0-24-24H104A24,24,0,0,0,80,40v8H40a8,8,0,0,0,0,16h8V208a16,16,0,0,0,16,16H192a16,16,0,0,0,16-16V64h8a8,8,0,0,0,0-16ZM96,40a8,8,0,0,1,8-8h48a8,8,0,0,1,8,8v8H96Zm96,168H64V64H192ZM112,104v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm48,0v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Z"></path></svg>

View file

@ -27,7 +27,7 @@
}
}
{% if current_user.id == image.author_id %}
{% if current_user.id == image.author.id %}
function imageDelete() {
cancelBtn = document.createElement('button');
cancelBtn.classList.add('btn-block');
@ -120,7 +120,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M213.66,82.34l-56-56A8,8,0,0,0,152,24H56A16,16,0,0,0,40,40V216a16,16,0,0,0,16,16H200a16,16,0,0,0,16-16V88A8,8,0,0,0,213.66,82.34ZM160,51.31,188.69,80H160ZM200,216H56V40h88V88a8,8,0,0,0,8,8h48V216Zm-42.34-61.66a8,8,0,0,1,0,11.32l-24,24a8,8,0,0,1-11.32,0l-24-24a8,8,0,0,1,11.32-11.32L120,164.69V120a8,8,0,0,1,16,0v44.69l10.34-10.35A8,8,0,0,1,157.66,154.34Z"></path></svg>
</a>
</div>
{% if current_user.id == image.author_id %}
{% if current_user.id == image.author.id %}
<div>
<button class="pill-item pill__critical" onclick="imageDelete()">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M216,48H176V40a24,24,0,0,0-24-24H104A24,24,0,0,0,80,40v8H40a8,8,0,0,0,0,16h8V208a16,16,0,0,0,16,16H192a16,16,0,0,0,16-16V64h8a8,8,0,0,0,0-16ZM96,40a8,8,0,0,1,8-8h48a8,8,0,0,1,8,8v8H96Zm96,168H64V64H192ZM112,104v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm48,0v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Z"></path></svg>
@ -155,7 +155,7 @@
<table>
<tr>
<td>Author</td>
<td><a href="{{ url_for('profile.profile', id=image.author_id) }}" class="link">{{ image.author_username }}</a></td>
<td><a href="{{ url_for('profile.profile', id=image.author.id) }}" class="link">{{ image.author.username }}</a></td>
</tr>
<tr>
<td>Upload date</td>

View file

@ -70,7 +70,7 @@
<div class="navigation">
<!--<img src="{{url_for('static', filename='icon.png')}}" alt="Logo" class="logo" onload="this.style.opacity=1;" style="opacity:0">-->
<a href="{{url_for('gallery.index')}}{% block page_index %}{% endblock %}" class="navigation-item {% block nav_home %}{% endblock %}">
<a href="{{ url_for('gallery.index') }}{% block page_index %}{% endblock %}" class="navigation-item {% block nav_home %}{% endblock %}">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M208,32H80A16,16,0,0,0,64,48V64H48A16,16,0,0,0,32,80V208a16,16,0,0,0,16,16H176a16,16,0,0,0,16-16V192h16a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM80,48H208v69.38l-16.7-16.7a16,16,0,0,0-22.62,0L93.37,176H80Zm96,160H48V80H64v96a16,16,0,0,0,16,16h96ZM104,88a16,16,0,1,1,16,16A16,16,0,0,1,104,88Z"></path></svg>
<span class="tool-tip">
Home
@ -78,7 +78,7 @@
</span>
</a>
<a href="{{url_for('group.groups')}}" class="navigation-item {% block nav_groups %}{% endblock %}">
<a href="{{ url_for('group.groups') }}" class="navigation-item {% block nav_groups %}{% endblock %}">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M245,110.64A16,16,0,0,0,232,104H216V88a16,16,0,0,0-16-16H130.67L102.94,51.2a16.14,16.14,0,0,0-9.6-3.2H40A16,16,0,0,0,24,64V208h0a8,8,0,0,0,8,8H211.1a8,8,0,0,0,7.59-5.47l28.49-85.47A16.05,16.05,0,0,0,245,110.64ZM93.34,64l27.73,20.8a16.12,16.12,0,0,0,9.6,3.2H200v16H146.43a16,16,0,0,0-8.88,2.69l-20,13.31H69.42a15.94,15.94,0,0,0-14.86,10.06L40,166.46V64Z"></path></svg>
<span class="tool-tip">
Groups
@ -87,66 +87,66 @@
</a>
{% if current_user.is_authenticated %}
<button class="navigation-item {% block nav_upload %}{% endblock %}" onclick="toggleUploadTab()">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M74.34,77.66a8,8,0,0,1,0-11.32l48-48a8,8,0,0,1,11.32,0l48,48a8,8,0,0,1-11.32,11.32L136,43.31V128a8,8,0,0,1-16,0V43.31L85.66,77.66A8,8,0,0,1,74.34,77.66ZM240,136v64a16,16,0,0,1-16,16H32a16,16,0,0,1-16-16V136a16,16,0,0,1,16-16h68a4,4,0,0,1,4,4v3.46c0,13.45,11,24.79,24.46,24.54A24,24,0,0,0,152,128v-4a4,4,0,0,1,4-4h68A16,16,0,0,1,240,136Zm-40,32a12,12,0,1,0-12,12A12,12,0,0,0,200,168Z"></path></svg>
<span class="tool-tip">
Upload
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M168,48V208a8,8,0,0,1-13.66,5.66l-80-80a8,8,0,0,1,0-11.32l80-80A8,8,0,0,1,168,48Z"></path></svg>
</span>
</button>
<button class="navigation-item {% block nav_upload %}{% endblock %}" onclick="toggleUploadTab()">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M74.34,77.66a8,8,0,0,1,0-11.32l48-48a8,8,0,0,1,11.32,0l48,48a8,8,0,0,1-11.32,11.32L136,43.31V128a8,8,0,0,1-16,0V43.31L85.66,77.66A8,8,0,0,1,74.34,77.66ZM240,136v64a16,16,0,0,1-16,16H32a16,16,0,0,1-16-16V136a16,16,0,0,1,16-16h68a4,4,0,0,1,4,4v3.46c0,13.45,11,24.79,24.46,24.54A24,24,0,0,0,152,128v-4a4,4,0,0,1,4-4h68A16,16,0,0,1,240,136Zm-40,32a12,12,0,1,0-12,12A12,12,0,0,0,200,168Z"></path></svg>
<span class="tool-tip">
Upload
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M168,48V208a8,8,0,0,1-13.66,5.66l-80-80a8,8,0,0,1,0-11.32l80-80A8,8,0,0,1,168,48Z"></path></svg>
</span>
</button>
{% endif %}
<span class="navigation-spacer"></span>
{% if current_user.is_authenticated %}
<a href="{{url_for('profile.profile')}}" class="navigation-item {% block nav_profile %}{% endblock %}">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M231.73,221.94A8,8,0,0,1,224,232H160A8,8,0,0,1,152.27,222a40,40,0,0,1,17.11-23.33,32,32,0,1,1,45.24,0A40,40,0,0,1,231.73,221.94ZM216,72H130.67L102.93,51.2a16.12,16.12,0,0,0-9.6-3.2H40A16,16,0,0,0,24,64V200a16,16,0,0,0,16,16h80a8,8,0,0,0,0-16H40V64H93.33l27.74,20.8a16.12,16.12,0,0,0,9.6,3.2H216v32a8,8,0,0,0,16,0V88A16,16,0,0,0,216,72Z"></path></svg>
<span class="tool-tip">
Profile
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M168,48V208a8,8,0,0,1-13.66,5.66l-80-80a8,8,0,0,1,0-11.32l80-80A8,8,0,0,1,168,48Z"></path></svg>
</span>
</a>
<a href="{{url_for('settings.general')}}" class="navigation-item {% block nav_settings %}{% endblock %}">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M216,130.16q.06-2.16,0-4.32l14.92-18.64a8,8,0,0,0,1.48-7.06,107.6,107.6,0,0,0-10.88-26.25,8,8,0,0,0-6-3.93l-23.72-2.64q-1.48-1.56-3-3L186,40.54a8,8,0,0,0-3.94-6,107.29,107.29,0,0,0-26.25-10.86,8,8,0,0,0-7.06,1.48L130.16,40Q128,40,125.84,40L107.2,25.11a8,8,0,0,0-7.06-1.48A107.6,107.6,0,0,0,73.89,34.51a8,8,0,0,0-3.93,6L67.32,64.27q-1.56,1.49-3,3L40.54,70a8,8,0,0,0-6,3.94,107.71,107.71,0,0,0-10.87,26.25,8,8,0,0,0,1.49,7.06L40,125.84Q40,128,40,130.16L25.11,148.8a8,8,0,0,0-1.48,7.06,107.6,107.6,0,0,0,10.88,26.25,8,8,0,0,0,6,3.93l23.72,2.64q1.49,1.56,3,3L70,215.46a8,8,0,0,0,3.94,6,107.71,107.71,0,0,0,26.25,10.87,8,8,0,0,0,7.06-1.49L125.84,216q2.16.06,4.32,0l18.64,14.92a8,8,0,0,0,7.06,1.48,107.21,107.21,0,0,0,26.25-10.88,8,8,0,0,0,3.93-6l2.64-23.72q1.56-1.48,3-3L215.46,186a8,8,0,0,0,6-3.94,107.71,107.71,0,0,0,10.87-26.25,8,8,0,0,0-1.49-7.06ZM128,168a40,40,0,1,1,40-40A40,40,0,0,1,128,168Z"></path></svg>
<span class="tool-tip">
Settings
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M168,48V208a8,8,0,0,1-13.66,5.66l-80-80a8,8,0,0,1,0-11.32l80-80A8,8,0,0,1,168,48Z"></path></svg>
</span>
</a>
<a href="{{ url_for('profile.profile') }}" class="navigation-item {% block nav_profile %}{% endblock %}">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M231.73,221.94A8,8,0,0,1,224,232H160A8,8,0,0,1,152.27,222a40,40,0,0,1,17.11-23.33,32,32,0,1,1,45.24,0A40,40,0,0,1,231.73,221.94ZM216,72H130.67L102.93,51.2a16.12,16.12,0,0,0-9.6-3.2H40A16,16,0,0,0,24,64V200a16,16,0,0,0,16,16h80a8,8,0,0,0,0-16H40V64H93.33l27.74,20.8a16.12,16.12,0,0,0,9.6,3.2H216v32a8,8,0,0,0,16,0V88A16,16,0,0,0,216,72Z"></path></svg>
<span class="tool-tip">
Profile
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M168,48V208a8,8,0,0,1-13.66,5.66l-80-80a8,8,0,0,1,0-11.32l80-80A8,8,0,0,1,168,48Z"></path></svg>
</span>
</a>
<a href="{{ url_for('settings.general') }}" class="navigation-item {% block nav_settings %}{% endblock %}">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M216,130.16q.06-2.16,0-4.32l14.92-18.64a8,8,0,0,0,1.48-7.06,107.6,107.6,0,0,0-10.88-26.25,8,8,0,0,0-6-3.93l-23.72-2.64q-1.48-1.56-3-3L186,40.54a8,8,0,0,0-3.94-6,107.29,107.29,0,0,0-26.25-10.86,8,8,0,0,0-7.06,1.48L130.16,40Q128,40,125.84,40L107.2,25.11a8,8,0,0,0-7.06-1.48A107.6,107.6,0,0,0,73.89,34.51a8,8,0,0,0-3.93,6L67.32,64.27q-1.56,1.49-3,3L40.54,70a8,8,0,0,0-6,3.94,107.71,107.71,0,0,0-10.87,26.25,8,8,0,0,0,1.49,7.06L40,125.84Q40,128,40,130.16L25.11,148.8a8,8,0,0,0-1.48,7.06,107.6,107.6,0,0,0,10.88,26.25,8,8,0,0,0,6,3.93l23.72,2.64q1.49,1.56,3,3L70,215.46a8,8,0,0,0,3.94,6,107.71,107.71,0,0,0,26.25,10.87,8,8,0,0,0,7.06-1.49L125.84,216q2.16.06,4.32,0l18.64,14.92a8,8,0,0,0,7.06,1.48,107.21,107.21,0,0,0,26.25-10.88,8,8,0,0,0,3.93-6l2.64-23.72q1.56-1.48,3-3L215.46,186a8,8,0,0,0,6-3.94,107.71,107.71,0,0,0,10.87-26.25,8,8,0,0,0-1.49-7.06ZM128,168a40,40,0,1,1,40-40A40,40,0,0,1,128,168Z"></path></svg>
<span class="tool-tip">
Settings
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M168,48V208a8,8,0,0,1-13.66,5.66l-80-80a8,8,0,0,1,0-11.32l80-80A8,8,0,0,1,168,48Z"></path></svg>
</span>
</a>
{% else %}
<button class="navigation-item {% block nav_login %}{% endblock %}" onclick="showLogin()">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M141.66,133.66l-40,40A8,8,0,0,1,88,168V136H24a8,8,0,0,1,0-16H88V88a8,8,0,0,1,13.66-5.66l40,40A8,8,0,0,1,141.66,133.66ZM192,32H136a8,8,0,0,0,0,16h56V208H136a8,8,0,0,0,0,16h56a16,16,0,0,0,16-16V48A16,16,0,0,0,192,32Z"></path></svg>
<span class="tool-tip">
Login
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M168,48V208a8,8,0,0,1-13.66,5.66l-80-80a8,8,0,0,1,0-11.32l80-80A8,8,0,0,1,168,48Z"></path></svg>
</span>
</button>
<button class="navigation-item {% block nav_login %}{% endblock %}" onclick="showLogin()">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M141.66,133.66l-40,40A8,8,0,0,1,88,168V136H24a8,8,0,0,1,0-16H88V88a8,8,0,0,1,13.66-5.66l40,40A8,8,0,0,1,141.66,133.66ZM192,32H136a8,8,0,0,0,0,16h56V208H136a8,8,0,0,0,0,16h56a16,16,0,0,0,16-16V48A16,16,0,0,0,192,32Z"></path></svg>
<span class="tool-tip">
Login
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M168,48V208a8,8,0,0,1-13.66,5.66l-80-80a8,8,0,0,1,0-11.32l80-80A8,8,0,0,1,168,48Z"></path></svg>
</span>
</button>
{% endif %}
</div>
{% if current_user.is_authenticated %}
<div class="upload-panel">
<span class="click-off" onclick="closeUploadTab()"></span>
<div class="container">
<span id="dragIndicator"></span>
<h3>Upload stuffs</h3>
<p>May the world see your stuff 👀</p>
<form id="uploadForm">
<button class="fileDrop-block" type="button">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M240,136v64a16,16,0,0,1-16,16H32a16,16,0,0,1-16-16V136a16,16,0,0,1,16-16H80a8,8,0,0,1,0,16H32v64H224V136H176a8,8,0,0,1,0-16h48A16,16,0,0,1,240,136ZM85.66,77.66,120,43.31V128a8,8,0,0,0,16,0V43.31l34.34,34.35a8,8,0,0,0,11.32-11.32l-48-48a8,8,0,0,0-11.32,0l-48,48A8,8,0,0,0,85.66,77.66ZM200,168a12,12,0,1,0-12,12A12,12,0,0,0,200,168Z"></path></svg>
<span class="status">Choose or Drop file</span>
<input type="file" id="file" tab-index="-1"/>
</button>
<div class="upload-panel">
<span class="click-off" onclick="closeUploadTab()"></span>
<div class="container">
<span id="dragIndicator"></span>
<h3>Upload stuffs</h3>
<p>May the world see your stuff 👀</p>
<form id="uploadForm">
<button class="fileDrop-block" type="button">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 256 256"><path d="M240,136v64a16,16,0,0,1-16,16H32a16,16,0,0,1-16-16V136a16,16,0,0,1,16-16H80a8,8,0,0,1,0,16H32v64H224V136H176a8,8,0,0,1,0-16h48A16,16,0,0,1,240,136ZM85.66,77.66,120,43.31V128a8,8,0,0,0,16,0V43.31l34.34,34.35a8,8,0,0,0,11.32-11.32l-48-48a8,8,0,0,0-11.32,0l-48,48A8,8,0,0,0,85.66,77.66ZM200,168a12,12,0,1,0-12,12A12,12,0,0,0,200,168Z"></path></svg>
<span class="status">Choose or Drop file</span>
<input type="file" id="file" tab-index="-1"/>
</button>
<input class="input-block" type="text" placeholder="alt" id="alt"/>
<input class="input-block" type="text" placeholder="description" id="description"/>
<input class="input-block" type="text" placeholder="tags" id="tags"/>
<button class="btn-block" type="submit">Upload</button>
</form>
<div class="upload-jobs"></div>
<input class="input-block" type="text" placeholder="alt" id="alt"/>
<input class="input-block" type="text" placeholder="description" id="description"/>
<input class="input-block" type="text" placeholder="tags" id="tags"/>
<button class="btn-block primary" type="submit">Upload</button>
</form>
<div class="upload-jobs"></div>
</div>
</div>
</div>
{% endif %}
<div class="content">

View file

@ -121,7 +121,7 @@
{% for group in groups %}
<a id="group-{{ group.id }}" class="group-item" href="{{ url_for('group.group', group_id=group.id) }}" {% if group.images|length > 0 %} style="background-color: rgba({{ group.images.0.colours.0.0 }}, {{ group.images.0.colours.0.1 }}, {{ group.images.0.colours.0.2 }}, 0.4);" {% endif %}>
<div class="image-filter">
<p class="image-subtitle">By {{ group.author_username }}</p>
<p class="image-subtitle">By {{ group.author.username }}</p>
<p class="image-title">{{ group.name }}</p>
</div>
<div class="images size-{{ group.images|length }}">

View file

@ -5,7 +5,8 @@ sounds more limiting that it actually is in this gallery
"""
from flask import Blueprint, abort, render_template, url_for
from gallery.models import Posts, Users, GroupJunction, Groups
from gallery.models import Post, User, GroupJunction, Group
from gallery.extensions import db
from gallery.utils import contrast
@ -17,19 +18,20 @@ def groups():
"""
Group overview, shows all image groups
"""
groups = Groups.query.all()
groups = Group.query.all()
# For each group, get the 3 most recent images
for group in groups:
group.author_username = (
Users.query.with_entities(Users.username)
.filter(Users.id == group.author_id)
User.query.with_entities(User.username)
.filter(User.id == group.author_id)
.first()[0]
)
# Get the 3 most recent images
images = (
GroupJunction.query.with_entities(GroupJunction.post_id)
GroupJunction.query
.with_entities(GroupJunction.post_id)
.filter(GroupJunction.group_id == group.id)
.order_by(GroupJunction.date_added.desc())
.limit(3)
@ -39,10 +41,9 @@ def groups():
group.images = []
for image in images:
group.images.append(
Posts.query.with_entities(
Posts.filename, Posts.alt, Posts.colours, Posts.id
)
.filter(Posts.id == image[0])
Post.query
.with_entities(Post.filename, Post.alt, Post.colours, Post.id)
.filter(Post.id == image[0])
.first()
)
@ -55,21 +56,12 @@ def group(group_id):
Group view, shows all images in a group
"""
# Get the group, if it doesn't exist, 404
group = Groups.query.filter(Groups.id == group_id).first()
if group is None:
abort(404, "Group not found! D:")
# Get the group's author username
group.author_username = (
Users.query.with_entities(Users.username)
.filter(Users.id == group.author_id)
.first()[0]
)
group = db.get_or_404(Group, group_id, description="Group not found! D:")
# Get all images in the group from the junction table
junction = (
GroupJunction.query.with_entities(GroupJunction.post_id)
GroupJunction.query
.with_entities(GroupJunction.post_id)
.filter(GroupJunction.group_id == group_id)
.order_by(GroupJunction.date_added.desc())
.all()
@ -78,7 +70,7 @@ def group(group_id):
# Get the image data for each image in the group
images = []
for image in junction:
images.append(Posts.query.filter(Posts.id == image[0]).first())
images.append(Post.query.filter(Post.id == image[0]).first())
# Check contrast for the first image in the group for the banner
text_colour = "rgb(var(--fg-black))"
@ -98,16 +90,7 @@ def group_post(group_id, image_id):
Image view, shows the image and its metadata from a specific group
"""
# Get the image, if it doesn't exist, 404
image = Posts.query.filter(Posts.id == image_id).first()
if image is None:
abort(404, "Image not found")
# Get the image's author username
image.author_username = (
Users.query.with_entities(Users.username)
.filter(Users.id == image.author_id)
.first()[0]
)
image = db.get_or_404(Post, image_id, description="Image not found :<")
# Get all groups the image is in
groups = (
@ -120,8 +103,8 @@ def group_post(group_id, image_id):
image.groups = []
for group in groups:
image.groups.append(
Groups.query.with_entities(Groups.id, Groups.name)
.filter(Groups.id == group[0])
Group.query.with_entities(Group.id, Group.name)
.filter(Group.id == group[0])
.first()
)

View file

@ -2,10 +2,9 @@
Onlylegs - Image View
"""
from math import ceil
from flask import Blueprint, abort, render_template, url_for, current_app
from gallery.models import Posts, Users, GroupJunction, Groups
from flask import Blueprint, render_template, url_for, current_app
from gallery.models import Post, GroupJunction, Group
from gallery.extensions import db
blueprint = Blueprint("image", __name__, url_prefix="/image")
@ -17,45 +16,36 @@ def image(image_id):
Image view, shows the image and its metadata
"""
# Get the image, if it doesn't exist, 404
image = Posts.query.filter(Posts.id == image_id).first()
if not image:
abort(404, "Image not found :<")
image = db.get_or_404(Post, image_id, description="Image not found :<")
# Get the image's author username
image.author_username = (
Users.query.with_entities(Users.username)
.filter(Users.id == image.author_id)
.first()[0]
)
# Get the image's groups
# Get all groups the image is in
groups = (
GroupJunction.query.with_entities(GroupJunction.group_id)
.filter(GroupJunction.post_id == image_id)
.all()
)
# For each group, get the group data and add it to the image item
# Get the group data for each group the image is in
image.groups = []
for group in groups:
image.groups.append(
Groups.query.with_entities(Groups.name, Groups.id)
.filter(Groups.id == group[0])
Group.query.with_entities(Group.id, Group.name)
.filter(Group.id == group[0])
.first()
)
# Get the next and previous images
# Check if there is a group ID set
next_url = (
Posts.query.with_entities(Posts.id)
.filter(Posts.id > image_id)
.order_by(Posts.id.asc())
Post.query.with_entities(Post.id)
.filter(Post.id > image_id)
.order_by(Post.id.asc())
.first()
)
prev_url = (
Posts.query.with_entities(Posts.id)
.filter(Posts.id < image_id)
.order_by(Posts.id.desc())
Post.query.with_entities(Post.id)
.filter(Post.id < image_id)
.order_by(Post.id.desc())
.first()
)
@ -66,7 +56,7 @@ def image(image_id):
prev_url = url_for("image.image", image_id=prev_url[0])
# Yoink all the images in the database
total_images = Posts.query.with_entities(Posts.id).order_by(Posts.id.desc()).all()
total_images = Post.query.with_entities(Post.id).order_by(Post.id.desc()).all()
limit = current_app.config["UPLOAD_CONF"]["max-load"]
# If the number of items is less than the limit, no point of calculating the page

View file

@ -6,7 +6,7 @@ from math import ceil
from flask import Blueprint, render_template, request, current_app
from werkzeug.exceptions import abort
from gallery.models import Posts
from gallery.models import Post
blueprint = Blueprint("gallery", __name__)
@ -27,7 +27,7 @@ def index():
# get the total number of images in the database
# calculate the total number of pages, and make sure the page number is valid
total_images = Posts.query.with_entities(Posts.id).count()
total_images = Post.query.with_entities(Post.id).count()
pages = ceil(max(total_images, limit) / limit)
if page > pages:
abort(
@ -38,10 +38,9 @@ def index():
# get the images for the current page
images = (
Posts.query.with_entities(
Posts.filename, Posts.alt, Posts.colours, Posts.created_at, Posts.id
)
.order_by(Posts.id.desc())
Post.query
.with_entities( Post.filename, Post.alt, Post.colours, Post.created_at, Post.id)
.order_by(Post.id.desc())
.offset((page - 1) * limit)
.limit(limit)
.all()

View file

@ -5,7 +5,7 @@ from flask import Blueprint, render_template, request
from werkzeug.exceptions import abort
from flask_login import current_user
from gallery.models import Posts, Users
from gallery.models import Post, User
blueprint = Blueprint("profile", __name__, url_prefix="/profile")
@ -26,11 +26,11 @@ def profile():
abort(404, "You must be logged in to view your own profile!")
# Get the user's data
user = Users.query.filter(Users.id == user_id).first()
user = User.query.filter(User.id == user_id).first()
if not user:
abort(404, "User not found :c")
images = Posts.query.filter(Posts.author_id == user_id).all()
images = Post.query.filter(Post.author_id == user_id).all()
return render_template("profile.html", user=user, images=images)