mirror of
https://github.com/Derpy-Leggies/OnlyLegs.git
synced 2025-01-29 01:28:24 +00:00
Allow for styling
This commit is contained in:
parent
3496a3bbe9
commit
b4bc8c61ec
|
@ -17,41 +17,32 @@ startup.check_conf()
|
|||
|
||||
# Set dirs
|
||||
APPLICATION_ROOT = platformdirs.user_config_dir("onlylegs")
|
||||
|
||||
# Load environment variables
|
||||
# print("Loading environment variables...")
|
||||
load_dotenv(os.path.join(APPLICATION_ROOT, ".env"))
|
||||
|
||||
# Load config from user dir
|
||||
# print("Loading config...")
|
||||
with open(
|
||||
os.path.join(APPLICATION_ROOT, "conf.yml"), encoding="utf-8", mode="r"
|
||||
) as file:
|
||||
conf = safe_load(file)
|
||||
|
||||
|
||||
# Flask config
|
||||
SECRET_KEY = os.environ.get("FLASK_SECRET")
|
||||
DATABASE_NAME = "gallery.sqlite3"
|
||||
SQLALCHEMY_DATABASE_URI = "sqlite:///" + DATABASE_NAME
|
||||
MAX_CONTENT_LENGTH = 1024 * 1024 * conf["upload"]["max-size"]
|
||||
ALLOWED_EXTENSIONS = conf["upload"]["allowed-extensions"]
|
||||
|
||||
# Pass YAML config to app
|
||||
ADMIN_CONF = conf["admin"]
|
||||
UPLOAD_CONF = conf["upload"]
|
||||
WEBSITE_CONF = conf["website"]
|
||||
|
||||
# Directories
|
||||
UPLOAD_FOLDER = os.path.join(APPLICATION_ROOT, "media", "uploads")
|
||||
MEDIA_FOLDER = os.path.join(APPLICATION_ROOT, "media")
|
||||
CACHE_FOLDER = os.path.join(APPLICATION_ROOT, "media", "cache")
|
||||
PFP_FOLDER = os.path.join(APPLICATION_ROOT, "media", "pfp")
|
||||
BANNER_FOLDER = os.path.join(APPLICATION_ROOT, "media", "banner")
|
||||
|
||||
# Load env and config files
|
||||
load_dotenv(os.path.join(APPLICATION_ROOT, ".env"))
|
||||
|
||||
config_file = os.path.join(APPLICATION_ROOT, "conf.yml")
|
||||
with open(config_file, encoding="utf-8", mode="r") as file:
|
||||
conf = safe_load(file)
|
||||
|
||||
# Flask config
|
||||
SECRET_KEY = os.environ.get("FLASK_SECRET")
|
||||
MAX_CONTENT_LENGTH = 1024 * 1024 * conf["upload"]["max-size"]
|
||||
ALLOWED_EXTENSIONS = conf["upload"]["allowed-extensions"]
|
||||
APP_VERSION = importlib.metadata.version("OnlyLegs")
|
||||
|
||||
# Database
|
||||
DATABASE_NAME = "gallery.sqlite3"
|
||||
SQLALCHEMY_DATABASE_URI = "sqlite:///" + DATABASE_NAME
|
||||
INSTANCE_DIR = os.path.join(APPLICATION_ROOT, "instance")
|
||||
MIGRATIONS_DIR = os.path.join(INSTANCE_DIR, "migrations")
|
||||
|
||||
# App
|
||||
APP_VERSION = importlib.metadata.version("OnlyLegs")
|
||||
# Pass YAML config to app
|
||||
ADMIN_CONF = conf["admin"]
|
||||
UPLOAD_CONF = conf["upload"]
|
||||
WEBSITE_CONF = conf["website"]
|
||||
|
|
|
@ -64,12 +64,11 @@
|
|||
|
||||
&:hover
|
||||
cursor: pointer
|
||||
|
||||
color: var(--primary)
|
||||
|
||||
&.disabled, &:disabled
|
||||
color: var(--foreground-dim)
|
||||
cursor: unset
|
||||
&:disabled, &[disabled], &.disabled
|
||||
color: var(--foreground-gray)
|
||||
cursor: default
|
||||
|
||||
.pill__critical
|
||||
color: var(--danger)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
.info-container
|
||||
padding: 0.5rem 0 0 0.5rem
|
||||
padding: 0.5rem 0 0.5rem 0.5rem
|
||||
width: 27rem
|
||||
position: absolute
|
||||
top: 0
|
||||
|
|
|
@ -75,9 +75,11 @@ main
|
|||
main
|
||||
margin: 0 0.5rem
|
||||
|
||||
header
|
||||
position: sticky
|
||||
top: 0
|
||||
// This is very broken, as it breaks when you open any context menu/popup
|
||||
// I need to fix this at some point as it looks really nice
|
||||
//header
|
||||
// position: sticky
|
||||
// top: 0
|
||||
|
||||
.error-page
|
||||
min-height: 100%
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
--red-transparent: rgba(182, 100, 103, 0.1)
|
||||
--orange: rgb(217, 140, 95)
|
||||
--orange-transparent: rgba(217, 140, 95, 0.1)
|
||||
--yellow: rgb(217, 188, 140)
|
||||
--yellow-transparent: rgba(217, 188, 140, 0.1)
|
||||
--yellow: rgb(198, 185, 166)
|
||||
--yellow-transparent: rgba(198, 185, 166, 0.1)
|
||||
--green: rgb(140, 151, 125)
|
||||
--green-transparent: rgba(140, 151, 125, 0.1)
|
||||
--blue: rgb(141, 163, 185)
|
||||
|
|
|
@ -28,10 +28,20 @@
|
|||
{% assets "scripts" %} <script type="text/javascript" src="{{ ASSET_URL }}"></script> {% endassets %}
|
||||
{% assets "styles" %} <link rel="stylesheet" href="{{ ASSET_URL }}" type="text/css" defer> {% endassets %}
|
||||
|
||||
<style type="text/css">
|
||||
:root{
|
||||
--primary: {{ config.WEBSITE_CONF.primary_color | default('rgb(198, 185, 166)') }};
|
||||
}
|
||||
<style>
|
||||
{% if config.WEBSITE_CONF.styling.force %}
|
||||
:root{
|
||||
--background-hsl-hue: {{ config.WEBSITE_CONF.styling.hue }} !important;
|
||||
--background-hsl-saturation: {{ config.WEBSITE_CONF.styling.saturation }}% !important;
|
||||
--rad: {{ config.WEBSITE_CONF.styling.rad }} !important;
|
||||
}
|
||||
{% else %}
|
||||
:root{
|
||||
--background-hsl-hue: {{ config.WEBSITE_CONF.styling.hue }};
|
||||
--background-hsl-saturation: {{ config.WEBSITE_CONF.styling.saturation }}%;
|
||||
--rad: {{ config.WEBSITE_CONF.styling.rad }};
|
||||
}
|
||||
{% endif %}
|
||||
</style>
|
||||
|
||||
{% block head %}{% endblock %}
|
||||
|
|
|
@ -24,37 +24,6 @@
|
|||
--background-hsl-hue: {{ images.0.colours.0 | hsl_hue }};
|
||||
--background-hsl-saturation: {{ images.0.colours.0 | hsl_saturation }}%;
|
||||
}
|
||||
|
||||
/*
|
||||
body {
|
||||
color: {{ text_colour }} !important;
|
||||
}
|
||||
|
||||
.navigation-item.selected {
|
||||
color: {{ text_colour }} !important;
|
||||
}
|
||||
|
||||
.banner .banner-content .banner-header,
|
||||
.banner .banner-content .banner-info,
|
||||
.banner .banner-content .banner-subtitle {
|
||||
color: {{ text_colour }} !important;
|
||||
}
|
||||
.banner-content .link {
|
||||
color: rgb{{ images.0.colours.0 }} !important;
|
||||
}
|
||||
.banner-content .link:hover {
|
||||
color: {{ text_colour }} !important;
|
||||
}
|
||||
|
||||
.banner-filter {
|
||||
background: linear-gradient(90deg, rgb{{ images.0.colours.0 }}, rgba({{ images.0.colours.1.0 }}, {{ images.0.colours.1.1 }}, {{ images.0.colours.1.2 }}, 0.3)) !important;
|
||||
}
|
||||
@media (max-width: 800px) {
|
||||
.banner-filter {
|
||||
background: linear-gradient(180deg, rgba({{ images.0.colours.1.0 }}, {{ images.0.colours.1.1 }}, {{ images.0.colours.1.2 }}, 0.4), rgba({{ images.0.colours.0.0 }}, {{ images.0.colours.0.1 }}, {{ images.0.colours.0.2 }}, 0.3)) !important;
|
||||
}
|
||||
}
|
||||
*/
|
||||
{% endif %}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
|
|
@ -18,6 +18,10 @@
|
|||
</script>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--background-hsl-hue: {{ image.colours.2 | hsl_hue }};
|
||||
--background-hsl-saturation: {{ image.colours.2 | hsl_saturation }}%;
|
||||
}
|
||||
.background::after {
|
||||
background-image: linear-gradient(to top, rgba({{ image.colours.0.0 }}, {{ image.colours.0.1 }}, {{ image.colours.0.2 }}, 0.8),
|
||||
rgba({{ image.colours.1.0 }}, {{ image.colours.1.1 }}, {{ image.colours.1.2 }}, 0.2));
|
||||
|
@ -30,7 +34,11 @@
|
|||
<div class="banner-content">
|
||||
<h1 class="banner-header">{{ config.WEBSITE_CONF.name }}</h1>
|
||||
<div class="pill-row">
|
||||
{% if next_url %}<div><a class="pill-item" href="{{ next_url }}"><i class="ph ph-arrow-left"></i></a></div>{% endif %}
|
||||
<div>
|
||||
<a {% if next_url %}class="pill-item" href="{{ next_url }}"{% else %}class="pill-item disabled"{% endif %}>
|
||||
<i class="ph ph-arrow-left"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<button class="pill-item" onclick="imageFullscreen()" id="fullscreenImage"><i class="ph ph-info"></i></button>
|
||||
<button class="pill-item" onclick="copyToClipboard(window.location.href)"><i class="ph ph-export"></i></button>
|
||||
|
@ -38,7 +46,11 @@
|
|||
<button class="pill-item" onclick="imageShowOptionsPopup(this)"><i class="ph ph-list"></i></button>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if prev_url %}<div><a class="pill-item" href="{{ prev_url }}"><i class="ph ph-arrow-right"></i></a></div>{% endif %}
|
||||
<div>
|
||||
<a {% if prev_url %}class="pill-item" href="{{ prev_url }}"{% else %}class="pill-item disabled"{% endif %}>
|
||||
<i class="ph ph-arrow-right"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -66,6 +66,36 @@
|
|||
<i class="ph ph-caret-down collapse-indicator"></i>
|
||||
</summary>
|
||||
|
||||
<p>Nothing here :3</p>
|
||||
<form method="POST" action="{{ url_for('settings.website_style') }}" enctype="multipart/form-data">
|
||||
<h3>Style</h3>
|
||||
<input type="range" name="hue" class="input-block" placeholder="Website Hue" value="{{ config.WEBSITE_CONF.styling.hue }}" min="0" max="360" />
|
||||
<input type="range" name="saturation" class="input-block" placeholder="Strength of Color" value="{{ config.WEBSITE_CONF.styling.saturation }}" min="0" max="100" />
|
||||
<input type="text" name="rad" class="input-block" placeholder="Roundy roundy" value="{{ config.WEBSITE_CONF.styling.rad }}" />
|
||||
<input type="checkbox" name="force" class="input-block" placeholder="Force styling?" {% if config.WEBSITE_CONF.styling.force %}checked{% endif %} />
|
||||
<button type="submit" class="btn-block">Update style</button>
|
||||
</form>
|
||||
</details>
|
||||
|
||||
<footer>
|
||||
<p>Built on coffee and love, by Fluffy</p>
|
||||
</footer>
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<script>
|
||||
let hue = document.querySelector('input[name=hue]');
|
||||
let saturation = document.querySelector('input[name=saturation]');
|
||||
let rad = document.querySelector('input[name=rad]');
|
||||
|
||||
hue.addEventListener('input', () => {
|
||||
document.documentElement.style.setProperty('--background-hsl-hue', hue.value, 'important');
|
||||
});
|
||||
saturation.addEventListener('input', () => {
|
||||
document.documentElement.style.setProperty('--background-hsl-saturation', saturation.value + '%', 'important');
|
||||
});
|
||||
rad.addEventListener('input', () => {
|
||||
document.documentElement.style.setProperty('--rad', rad.value, 'important');
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -22,12 +22,42 @@ REQUIRED_DIRS = {
|
|||
EMAIL_REGEX = re.compile(r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b")
|
||||
USERNAME_REGEX = re.compile(r"\b[A-Za-z0-9._%+-]+\b")
|
||||
|
||||
config = {
|
||||
# Version of the config file
|
||||
"version": "0.1.7",
|
||||
# Not really used much, but good to have for future use
|
||||
"admin": {
|
||||
"username": "admin",
|
||||
"email": "admin@example.com",
|
||||
},
|
||||
"upload": {
|
||||
"allowed-extensions": {
|
||||
"jpg": "jpeg",
|
||||
"jpeg": "jpeg",
|
||||
"png": "png",
|
||||
"webp": "webp",
|
||||
},
|
||||
# Max size in MB
|
||||
"max-size": 69,
|
||||
# Max images to load per page
|
||||
"max-load": 50,
|
||||
},
|
||||
"website": {
|
||||
# Website name and motto
|
||||
# Also CSS styling, hue is the color offset for hsl
|
||||
"name": "OnlyLegs",
|
||||
"motto": "A gallery built for fast and simple image management!",
|
||||
"styling": {
|
||||
"force": False,
|
||||
"hue": "69",
|
||||
"saturation": "25%",
|
||||
"rad": "0.4rem",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def check_dirs():
|
||||
"""
|
||||
Create the user directory
|
||||
"""
|
||||
|
||||
for directory in REQUIRED_DIRS.values():
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
|
@ -36,9 +66,6 @@ def check_dirs():
|
|||
|
||||
|
||||
def check_env():
|
||||
"""
|
||||
Create the .env file with default values
|
||||
"""
|
||||
if os.path.exists(os.path.join(APPLICATION_ROOT, ".env")):
|
||||
print("Environment file already exists at:", APPLICATION_ROOT)
|
||||
return
|
||||
|
@ -65,30 +92,23 @@ def check_env():
|
|||
|
||||
|
||||
def check_conf():
|
||||
"""
|
||||
Create the YAML config file with default values
|
||||
"""
|
||||
if os.path.exists(os.path.join(APPLICATION_ROOT, "conf.yml")):
|
||||
config_file = os.path.join(APPLICATION_ROOT, "conf.yml")
|
||||
if os.path.exists(config_file):
|
||||
print("Config file already exists at:", APPLICATION_ROOT)
|
||||
return
|
||||
|
||||
cant_continue = True
|
||||
username = "admin"
|
||||
name = "Admin"
|
||||
email = "admin@example.com"
|
||||
|
||||
print("No config file found, please enter the following information:")
|
||||
while cant_continue:
|
||||
username = input("Admin username: ").strip()
|
||||
name = input("Admin name: ").strip()
|
||||
email = input("Admin email: ").strip()
|
||||
|
||||
if not username or not USERNAME_REGEX.match(username):
|
||||
print("Username is invalid!")
|
||||
continue
|
||||
if not name:
|
||||
print("Name is invalid!")
|
||||
continue
|
||||
if not email or not EMAIL_REGEX.match(email):
|
||||
print("Email is invalid!")
|
||||
continue
|
||||
|
@ -98,34 +118,11 @@ def check_conf():
|
|||
if is_correct == "y" or not is_correct:
|
||||
cant_continue = False
|
||||
|
||||
yaml_conf = {
|
||||
"admin": {
|
||||
"name": name,
|
||||
"username": username,
|
||||
"email": email,
|
||||
},
|
||||
"upload": {
|
||||
"allowed-extensions": {
|
||||
"jpg": "jpeg",
|
||||
"jpeg": "jpeg",
|
||||
"png": "png",
|
||||
"webp": "webp",
|
||||
},
|
||||
"max-size": 69,
|
||||
"max-load": 50,
|
||||
"rename": "GWA_{{username}}_{{time}}",
|
||||
},
|
||||
"website": {
|
||||
"name": "OnlyLegs",
|
||||
"motto": "A gallery built for fast and simple image management!",
|
||||
"language": "en",
|
||||
},
|
||||
}
|
||||
config["admin"]["username"] = username
|
||||
config["admin"]["email"] = email
|
||||
|
||||
with open(
|
||||
os.path.join(APPLICATION_ROOT, "conf.yml"), encoding="utf-8", mode="w+"
|
||||
) as file:
|
||||
yaml.dump(yaml_conf, file, default_flow_style=False)
|
||||
with open(config_file, encoding="utf-8", mode="w+") as file:
|
||||
yaml.dump(config, file, default_flow_style=False)
|
||||
|
||||
print(
|
||||
"####################################################",
|
||||
|
|
|
@ -5,6 +5,7 @@ import os
|
|||
import pathlib
|
||||
import re
|
||||
import logging
|
||||
import yaml
|
||||
from colorthief import ColorThief
|
||||
from flask import (
|
||||
Blueprint,
|
||||
|
@ -19,6 +20,7 @@ from flask_login import login_required, current_user
|
|||
from werkzeug.security import check_password_hash, generate_password_hash
|
||||
from onlylegs.extensions import db
|
||||
from onlylegs.models import Users
|
||||
from onlylegs.config import APPLICATION_ROOT
|
||||
|
||||
|
||||
blueprint = Blueprint("settings", __name__, url_prefix="/settings")
|
||||
|
@ -157,3 +159,33 @@ def account_password():
|
|||
|
||||
flash(["Password changed! You must login now", 0])
|
||||
return redirect(url_for("auth.logout"))
|
||||
|
||||
|
||||
@blueprint.route("/website/style", methods=["POST"])
|
||||
@login_required
|
||||
def website_style():
|
||||
config_file = os.path.join(APPLICATION_ROOT, "conf.yml")
|
||||
|
||||
website_hue = request.form.get("hue", 69, type=int)
|
||||
website_saturation = request.form.get("saturation", 25, type=int)
|
||||
website_rad = request.form.get("rad", "0.4rem").strip()
|
||||
website_force_styling = request.form.get("force", False)
|
||||
|
||||
config = None
|
||||
with open(config_file, "r") as file:
|
||||
config = yaml.safe_load(file)
|
||||
|
||||
current_app.config["WEBSITE_CONF"]["styling"]["hue"] = website_hue
|
||||
current_app.config["WEBSITE_CONF"]["styling"]["saturation"] = website_saturation
|
||||
current_app.config["WEBSITE_CONF"]["styling"]["rad"] = website_rad
|
||||
current_app.config["WEBSITE_CONF"]["styling"]["force"] = website_force_styling
|
||||
|
||||
config["website"]["styling"]["hue"] = website_hue
|
||||
config["website"]["styling"]["saturation"] = website_saturation
|
||||
config["website"]["styling"]["rad"] = website_rad
|
||||
config["website"]["styling"]["force"] = website_force_styling
|
||||
|
||||
with open(config_file, "w") as file:
|
||||
yaml.dump(config, file)
|
||||
|
||||
return "Website style changed", 200
|
||||
|
|
Loading…
Reference in a new issue