mirror of
https://github.com/partitioncloud/partitioncloud-server.git
synced 2025-02-03 10:48:00 +01:00
Compare commits
5 Commits
3f83f1c44a
...
bfb6a127f0
Author | SHA1 | Date | |
---|---|---|---|
bfb6a127f0 | |||
3cbc586c78 | |||
5d0535ef70 | |||
c702cb714e | |||
b9a5f92a56 |
16
Dockerfile
Normal file
16
Dockerfile
Normal file
@ -0,0 +1,16 @@
|
||||
FROM python:latest
|
||||
|
||||
WORKDIR /app
|
||||
EXPOSE 5000
|
||||
|
||||
COPY . /app
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y python3-pip sqlite3 ghostscript
|
||||
|
||||
RUN rm /app/instance -rf
|
||||
RUN bash make.sh init
|
||||
RUN pip3 install -r requirements.txt
|
||||
RUN pip3 install gunicorn
|
||||
|
||||
|
||||
CMD [ "bash", "make.sh" , "production"]
|
@ -92,8 +92,9 @@ pybabel update -i partitioncloud/translations/messages.pot -d partitioncloud/tra
|
||||
```
|
||||
|
||||
## TODO
|
||||
- [ ] Modifier son mot de passe
|
||||
- [ ] Supprimer un utilisateur
|
||||
- [x] Modifier son mot de passe
|
||||
- [x] Supprimer un utilisateur
|
||||
- [ ] Ajouter config:DISABLE_DARK_MODE
|
||||
- [x] Ajouter config:DISABLE_REGISTER
|
||||
- [ ] Ajouter config:ONLINE_SEARCH_BASE_QUERY pour la recherche google, actuellement 'filetype:pdf partition'
|
||||
- [x] Ajouter un Dockerfile
|
||||
|
@ -26,7 +26,7 @@ MAX_AGE=31
|
||||
INSTANCE_PATH="instance"
|
||||
|
||||
# Events to log
|
||||
ENABLED_LOGS=["NEW_GROUPE", "NEW_ALBUM", "NEW_PARTITION", "NEW_USER", "SERVER_RESTART", "FAILED_LOGIN"]
|
||||
ENABLED_LOGS=["NEW_GROUPE", "NEW_ALBUM", "NEW_PARTITION", "NEW_USER", "PASSWORD_CHANGE", "DELETE_ACCOUNT", "SERVER_RESTART", "FAILED_LOGIN"]
|
||||
|
||||
# Available languages
|
||||
LANGUAGES=['en', 'fr']
|
||||
|
11
make.sh
11
make.sh
@ -9,7 +9,7 @@ init () {
|
||||
mkdir -p "$INSTANCE_PATH/search-partitions"
|
||||
mkdir -p "$INSTANCE_PATH/cache/thumbnails"
|
||||
mkdir -p "$INSTANCE_PATH/cache/search-thumbnails"
|
||||
|
||||
|
||||
if ! test -f "$INSTANCE_PATH/config.py"; then
|
||||
echo "SECRET_KEY=\"$(python3 -c 'import secrets; print(secrets.token_hex())')\"" > "$INSTANCE_PATH/config.py"
|
||||
fi
|
||||
@ -18,6 +18,7 @@ init () {
|
||||
printf "Souhaitez vous supprimer la base de données existante ? [y/n] "
|
||||
read -r CONFIRMATION
|
||||
[[ $CONFIRMATION == y ]] || exit 1
|
||||
rm "$INSTANCE_PATH/partitioncloud.sqlite"
|
||||
fi
|
||||
sqlite3 "$INSTANCE_PATH/partitioncloud.sqlite" '.read partitioncloud/schema.sql'
|
||||
echo "Base de données créé"
|
||||
@ -40,7 +41,7 @@ start () {
|
||||
|
||||
production () {
|
||||
pybabel compile -d partitioncloud/translations/
|
||||
FLASK_APP=partitioncloud /usr/bin/gunicorn \
|
||||
FLASK_APP=partitioncloud gunicorn \
|
||||
wsgi:app \
|
||||
--bind 0.0.0.0:$PORT
|
||||
}
|
||||
@ -65,7 +66,11 @@ RESULT=$(type "$1")
|
||||
if [[ $1 && $RESULT = *"is a"*"function"* || $RESULT == *"est une fonction"* ]]; then
|
||||
# Import config
|
||||
load_config "default_config.py"
|
||||
[[ ! -x "$INSTANCE_PATH/config.py" ]] && load_config "$INSTANCE_PATH/config.py"
|
||||
|
||||
if test -f "instance/config.py"; then
|
||||
load_config "instance/config.py"
|
||||
fi
|
||||
|
||||
$1 ${*:2} # Call the function
|
||||
else
|
||||
usage
|
||||
|
@ -13,7 +13,7 @@ from werkzeug.security import generate_password_hash
|
||||
from flask_babel import Babel, _
|
||||
|
||||
from .modules.utils import User, Album, get_all_albums, user_count, partition_count
|
||||
from .modules import albums, auth, partition, admin, groupe, thumbnails, logging
|
||||
from .modules import albums, auth, partition, admin, groupe, thumbnails, logging, settings
|
||||
from .modules.auth import admin_required, login_required
|
||||
from .modules.db import get_db
|
||||
|
||||
@ -91,6 +91,7 @@ app.register_blueprint(auth.bp)
|
||||
app.register_blueprint(admin.bp)
|
||||
app.register_blueprint(groupe.bp)
|
||||
app.register_blueprint(albums.bp)
|
||||
app.register_blueprint(settings.bp)
|
||||
app.register_blueprint(partition.bp)
|
||||
app.register_blueprint(thumbnails.bp)
|
||||
|
||||
|
@ -19,7 +19,6 @@ def index():
|
||||
Admin panel home page
|
||||
"""
|
||||
current_user = User(user_id=session.get("user_id"))
|
||||
current_user.get_albums() # We need to do that before we close the db
|
||||
db = get_db()
|
||||
users_id = db.execute(
|
||||
"""
|
||||
@ -37,6 +36,23 @@ def index():
|
||||
user=current_user
|
||||
)
|
||||
|
||||
@bp.route("/user/<user_id>")
|
||||
@admin_required
|
||||
def user_inspect(user_id):
|
||||
"""
|
||||
Inspect user
|
||||
"""
|
||||
current_user = User(user_id=session.get("user_id"))
|
||||
db = get_db()
|
||||
inspected_user = User(user_id=user_id)
|
||||
|
||||
return render_template(
|
||||
"settings/index.html",
|
||||
skip_old_password=True,
|
||||
inspected_user=inspected_user,
|
||||
user=current_user
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/logs")
|
||||
@admin_required
|
||||
|
@ -3,7 +3,9 @@
|
||||
Albums module
|
||||
"""
|
||||
import os
|
||||
import pypdf
|
||||
import shutil
|
||||
|
||||
from uuid import uuid4
|
||||
from typing import TypeVar
|
||||
|
||||
@ -263,6 +265,12 @@ def add_partition(album_uuid):
|
||||
else:
|
||||
partition_type = "file"
|
||||
|
||||
try:
|
||||
pypdf.PdfReader(request.files["file"])
|
||||
request.files["file"].seek(0)
|
||||
except (pypdf.errors.PdfReadError, pypdf.errors.PdfStreamError):
|
||||
error = _("Invalid PDF file")
|
||||
|
||||
if error is not None:
|
||||
flash(error)
|
||||
return redirect(request.referrer)
|
||||
|
@ -114,7 +114,7 @@ class Album():
|
||||
"""
|
||||
SELECT partition.uuid FROM partition
|
||||
WHERE NOT EXISTS (
|
||||
SELECT NULL FROM contient_partition
|
||||
SELECT NULL FROM contient_partition
|
||||
WHERE partition.uuid = partition_uuid
|
||||
)
|
||||
"""
|
||||
@ -142,7 +142,7 @@ class Album():
|
||||
WHERE uuid IN (
|
||||
SELECT partition.uuid FROM partition
|
||||
WHERE NOT EXISTS (
|
||||
SELECT NULL FROM contient_partition
|
||||
SELECT NULL FROM contient_partition
|
||||
WHERE partition.uuid = partition_uuid
|
||||
)
|
||||
)
|
||||
|
@ -1,4 +1,5 @@
|
||||
from flask import current_app
|
||||
from werkzeug.security import generate_password_hash
|
||||
|
||||
from ..db import get_db
|
||||
from .album import Album
|
||||
@ -28,6 +29,7 @@ class User():
|
||||
def __init__(self, user_id=None, name=None):
|
||||
self.id = user_id
|
||||
self.username = name
|
||||
self.password = None
|
||||
self.albums = None
|
||||
self.groupes = None
|
||||
self.partitions = None
|
||||
@ -58,6 +60,7 @@ class User():
|
||||
|
||||
self.id = data["id"]
|
||||
self.username = data["username"]
|
||||
self.password = data["password"]
|
||||
self.access_level = data["access_level"]
|
||||
self.color = self.get_color()
|
||||
if self.access_level == 1:
|
||||
@ -198,12 +201,10 @@ class User():
|
||||
db.execute(
|
||||
"""
|
||||
DELETE FROM contient_user
|
||||
JOIN album
|
||||
ON album.id = album_id
|
||||
WHERE user_id = ?
|
||||
AND album.uuid = ?
|
||||
WHERE album_id IN (SELECT id FROM album WHERE uuid = ?)
|
||||
AND user_id = ?
|
||||
""",
|
||||
(self.id, album_uuid)
|
||||
(album_uuid, self.id)
|
||||
)
|
||||
db.commit()
|
||||
|
||||
@ -221,6 +222,49 @@ class User():
|
||||
)
|
||||
db.commit()
|
||||
|
||||
def update_password(self, new_password):
|
||||
db = get_db()
|
||||
|
||||
db.execute(
|
||||
"""
|
||||
UPDATE user SET password=?
|
||||
WHERE id=?
|
||||
""",
|
||||
(generate_password_hash(new_password), self.id)
|
||||
)
|
||||
|
||||
db.commit()
|
||||
|
||||
|
||||
def delete(self):
|
||||
instance_path = current_app.config["INSTANCE_PATH"]
|
||||
for groupe in self.get_groupes():
|
||||
self.quit_groupe(groupe.uuid)
|
||||
|
||||
if groupe.get_users() == []:
|
||||
groupe.delete(instance_path)
|
||||
|
||||
|
||||
for album_data in self.get_albums():
|
||||
uuid = album_data["uuid"]
|
||||
self.quit_album(uuid)
|
||||
|
||||
album = Album(uuid=uuid)
|
||||
if album.get_users() == []:
|
||||
album.delete(instance_path)
|
||||
|
||||
db = get_db()
|
||||
|
||||
db.execute(
|
||||
"""
|
||||
DELETE FROM user
|
||||
WHERE id=?
|
||||
""",
|
||||
(self.id,)
|
||||
)
|
||||
|
||||
db.commit()
|
||||
|
||||
|
||||
def get_color(self):
|
||||
if len(colors) == 0:
|
||||
|
@ -12,8 +12,10 @@ class LogEntry(Enum):
|
||||
NEW_ALBUM = 3
|
||||
NEW_PARTITION = 4
|
||||
NEW_USER = 5
|
||||
SERVER_RESTART = 6
|
||||
FAILED_LOGIN = 7
|
||||
PASSWORD_CHANGE = 6
|
||||
DELETE_ACCOUNT = 7
|
||||
SERVER_RESTART = 8
|
||||
FAILED_LOGIN = 9
|
||||
|
||||
def from_string(entry: str):
|
||||
mapping = {
|
||||
@ -22,6 +24,8 @@ class LogEntry(Enum):
|
||||
"NEW_ALBUM": LogEntry.NEW_ALBUM,
|
||||
"NEW_PARTITION": LogEntry.NEW_PARTITION,
|
||||
"NEW_USER": LogEntry.NEW_USER,
|
||||
"PASSWORD_CHANGE": LogEntry.PASSWORD_CHANGE,
|
||||
"DELETE_ACCOUNT": LogEntry.DELETE_ACCOUNT,
|
||||
"SERVER_RESTART": LogEntry.SERVER_RESTART,
|
||||
"FAILED_LOGIN": LogEntry.FAILED_LOGIN
|
||||
}
|
||||
@ -60,7 +64,19 @@ def log(content: list[Union[str, bool, int]], log_type: LogEntry) -> None:
|
||||
description = f"New user {content[0]}[{content[1]}]"
|
||||
else:
|
||||
description = f"New user {content[0]}[{content[1]}] added by {content[3]}"
|
||||
|
||||
|
||||
case LogEntry.PASSWORD_CHANGE: # content = (user.name, user.id, admin.name if relevant)
|
||||
if len(content) == 2:
|
||||
description = f"New password for {content[0]}[{content[1]}]"
|
||||
else:
|
||||
description = f"New password for {content[0]}[{content[1]}], changed by {content[2]}"
|
||||
|
||||
case LogEntry.DELETE_ACCOUNT: # content = (user.name, user.id, admin.name if relevant)
|
||||
if len(content) == 2:
|
||||
description = f"Account deleted {content[0]}[{content[1]}]"
|
||||
else:
|
||||
description = f"Account deleted {content[0]}[{content[1]}], by {content[2]}"
|
||||
|
||||
case LogEntry.SERVER_RESTART: # content = ()
|
||||
description = "Server just restarted"
|
||||
|
||||
|
101
partitioncloud/modules/settings.py
Normal file
101
partitioncloud/modules/settings.py
Normal file
@ -0,0 +1,101 @@
|
||||
#!/usr/bin/python3
|
||||
"""
|
||||
User Settings
|
||||
"""
|
||||
import os
|
||||
from flask import Blueprint, render_template, session, current_app, send_file, request, flash, redirect
|
||||
from werkzeug.security import check_password_hash
|
||||
|
||||
from flask_babel import _
|
||||
|
||||
from .db import get_db
|
||||
from .auth import login_required
|
||||
from .utils import User
|
||||
from . import logging
|
||||
|
||||
|
||||
bp = Blueprint("settings", __name__, url_prefix="/settings")
|
||||
|
||||
@bp.route("/")
|
||||
@login_required
|
||||
def index():
|
||||
"""
|
||||
Settings page
|
||||
"""
|
||||
user = User(user_id=session.get("user_id"))
|
||||
|
||||
return render_template(
|
||||
"settings/index.html",
|
||||
inspected_user=user,
|
||||
user=user
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/delete-account", methods=["POST"])
|
||||
@login_required
|
||||
def delete_account():
|
||||
log_data = None
|
||||
if "user_id" not in request.form:
|
||||
flash(_("Missing user id."))
|
||||
return redirect(request.referrer)
|
||||
|
||||
cur_user = User(user_id=session.get("user_id"))
|
||||
user_id = request.form["user_id"]
|
||||
mod_user = User(user_id=user_id)
|
||||
|
||||
if cur_user.access_level != 1:
|
||||
log_data = [mod_user.username, mod_user.id]
|
||||
if cur_user.id != mod_user.id:
|
||||
flash(_("Missing rights."))
|
||||
return redirect(request.referrer)
|
||||
else:
|
||||
log_data = [mod_user.username, mod_user.id, cur_user.username]
|
||||
|
||||
mod_user.delete()
|
||||
flash(_("User successfully deleted."))
|
||||
logging.log(log_data, logging.LogEntry.DELETE_ACCOUNT)
|
||||
if cur_user.id == mod_user.id:
|
||||
return redirect("/")
|
||||
return redirect("/admin")
|
||||
|
||||
|
||||
@bp.route("/change-password", methods=["POST"])
|
||||
@login_required
|
||||
def change_password():
|
||||
log_data = None
|
||||
if "user_id" not in request.form:
|
||||
flash(_("Missing user id."))
|
||||
return redirect(request.referrer)
|
||||
|
||||
cur_user = User(user_id=session.get("user_id"))
|
||||
user_id = request.form["user_id"]
|
||||
mod_user = User(user_id=user_id)
|
||||
|
||||
if cur_user.access_level != 1:
|
||||
log_data = [mod_user.username, mod_user.id]
|
||||
if cur_user.id != mod_user.id:
|
||||
flash(_("Missing rights."))
|
||||
return redirect(request.referrer)
|
||||
|
||||
if "old_password" not in request.form:
|
||||
flash(_("Missing old password."))
|
||||
return redirect(request.referrer)
|
||||
|
||||
if not check_password_hash(mod_user.password, request.form["old_password"]):
|
||||
flash(_("Incorrect password."))
|
||||
return redirect(request.referrer)
|
||||
else:
|
||||
log_data = [mod_user.username, mod_user.id, cur_user.username]
|
||||
|
||||
if "new_password" not in request.form or "confirm_new_password" not in request.form:
|
||||
flash(_("Missing password."))
|
||||
return redirect(request.referrer)
|
||||
|
||||
if request.form["new_password"] != request.form["confirm_new_password"]:
|
||||
flash(_("Password and its confirmation differ."))
|
||||
return redirect(request.referrer)
|
||||
|
||||
mod_user.update_password(request.form["new_password"])
|
||||
flash(_("Successfully updated password."))
|
||||
logging.log(log_data, logging.LogEntry.PASSWORD_CHANGE)
|
||||
return redirect(request.referrer)
|
@ -2,6 +2,7 @@
|
||||
Thumbnails
|
||||
"""
|
||||
import os
|
||||
import pypdf
|
||||
|
||||
from flask import current_app, abort, Blueprint, send_file
|
||||
|
||||
@ -14,13 +15,18 @@ def generate_thumbnail(source, dest):
|
||||
"""
|
||||
Generates a thumbnail with 'convert' (ImageMagick)
|
||||
"""
|
||||
os.system(
|
||||
f'/usr/bin/convert -thumbnail\
|
||||
"178^>" -background white -alpha \
|
||||
remove -crop 178x178+0+0 \
|
||||
{source}[0] \
|
||||
{dest}'
|
||||
try:
|
||||
pypdf.PdfReader(source) # Check if file is really a PDF
|
||||
except (pypdf.errors.PdfReadError, pypdf.errors.PdfStreamError):
|
||||
return
|
||||
|
||||
command = (
|
||||
f"gs -dQUIET -dSAFER -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 \
|
||||
-dAlignToPixels=0 -dGridFitTT=2 -sDEVICE=png16m -dBackgroundColor=16#FFFFFF -dTextAlphaBits=4 \
|
||||
-dGraphicsAlphaBits=4 -r72x72 -dPrinted=false -dFirstPage=1 -dPDFFitPage -g356x356 \
|
||||
-dLastPage=1 -sOutputFile={dest} {source}"
|
||||
)
|
||||
os.system(command)
|
||||
|
||||
def serve_thumbnail(partition_file, thumbnail_file):
|
||||
"""
|
||||
@ -32,6 +38,9 @@ def serve_thumbnail(partition_file, thumbnail_file):
|
||||
if not os.path.exists(thumbnail_file):
|
||||
generate_thumbnail(partition_file, thumbnail_file)
|
||||
|
||||
if not os.path.exists(thumbnail_file):
|
||||
abort(404)
|
||||
|
||||
return send_file(thumbnail_file)
|
||||
|
||||
|
||||
|
@ -92,5 +92,5 @@ def partition_count():
|
||||
SELECT COUNT(*) FROM partition
|
||||
"""
|
||||
).fetchone()
|
||||
|
||||
|
||||
return count[0]
|
@ -2,7 +2,7 @@ let logsEmbed = document.getElementById("logs-embed");
|
||||
|
||||
logsEmbed.addEventListener("load", () => {
|
||||
var cssLink = document.createElement("link");
|
||||
|
||||
|
||||
cssLink.href = "/static/style/logs.css";
|
||||
cssLink.rel = "stylesheet";
|
||||
cssLink.type = "text/css";
|
||||
|
@ -17,7 +17,7 @@ async function hideSidebarNoAnim () {
|
||||
/* The transition needs to be invisible as if it was loaded that way */
|
||||
content_container.style.transitionDuration = "0s";
|
||||
sidebar_indicator.style.transitionDuration = "0s";
|
||||
|
||||
|
||||
sidebar_toggle.checked = true;
|
||||
|
||||
/* We need to set a sleep because we want to reset the transition duration only once it ended*/
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
/** Various settings (variables) */
|
||||
:root {
|
||||
--sidebar-size: max(10vw, 160px);
|
||||
--sidebar-size: max(10vw, 250px);
|
||||
--sidebar-sz-plus10: calc(var(--sidebar-size) + 10px);
|
||||
--sidebar-sz-moins20: calc(var(--sidebar-size) - 20px);
|
||||
}
|
||||
@ -333,6 +333,7 @@ img.partition-thumbnail {
|
||||
|
||||
.user {
|
||||
display: flex;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.username {
|
||||
@ -387,12 +388,13 @@ a#delete-album {
|
||||
transform: translateY(-17%);
|
||||
}
|
||||
|
||||
#settings-container>.user {
|
||||
#settings-container > a > .user {
|
||||
margin-top: 6px;
|
||||
border-radius: 3px;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
#settings-container>.user:hover {
|
||||
#settings-container > a > .user:hover {
|
||||
background-color: var(--color-mantle);
|
||||
}
|
||||
|
||||
@ -538,6 +540,8 @@ input[type="file"] {
|
||||
/** Dangerous buttons */
|
||||
button#logout:hover,
|
||||
a#delete-album:hover,
|
||||
.red-confirm,
|
||||
input[type="submit"].red-confirm,
|
||||
#delete-partition {
|
||||
background-color: var(--color-red);
|
||||
color: var(--color-mantle);
|
||||
@ -652,7 +656,7 @@ td {
|
||||
/** Attachment page */
|
||||
#pdf-embed {
|
||||
margin: auto;
|
||||
|
||||
|
||||
width: 100%;
|
||||
width: -moz-available;
|
||||
width: -webkit-fill-available;
|
||||
|
@ -34,7 +34,9 @@
|
||||
title="{{ user.username }}">
|
||||
{{ user.username[0] | upper }}
|
||||
</div>
|
||||
<div class="table-username">{{ user.username }}</div>
|
||||
<div class="table-username">
|
||||
<a href="/admin/user/{{ user.id }}">{{ user.username }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ user.albums | length }}</td>
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
<body>
|
||||
<div id="dialogs">
|
||||
<!-- This div contains needed needed dialogs for the page
|
||||
<!-- This div contains needed needed dialogs for the page
|
||||
They will only appear if the user clicks a button for one of them -->
|
||||
{% block dialogs %}{% endblock %}
|
||||
{% if g.user %}
|
||||
@ -157,13 +157,13 @@
|
||||
</svg>{{ _("Admin Panel") }}
|
||||
</button></a><br/>
|
||||
{% endif %}
|
||||
<div class="user">
|
||||
<a href="/settings"><div class="user">
|
||||
<div class="user-profile-picture" style="background-color:{{ user.color }};"
|
||||
title="{{ user.username }}">
|
||||
{{ user.username[0] | upper }}
|
||||
</div>
|
||||
<div class="username">{{ user.username }}</div>
|
||||
</div>
|
||||
</div></a>
|
||||
{% else %}
|
||||
{% if not config.DISABLE_REGISTER %}
|
||||
<a href="{{ url_for('auth.register') }}"><button>{{ _("Create account") }}</button></a>
|
||||
|
@ -38,7 +38,7 @@
|
||||
{{ _("Let's go !") }}
|
||||
</button>
|
||||
</a>
|
||||
<a href="https://github.com/partitioncloud/partitioncloud-server" class="no-color-link">
|
||||
<a href="https://github.com/partitioncloud/partitioncloud-server" class="no-color-link">
|
||||
<button>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M16 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" /><path d="M12 8m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" /><path d="M12 16m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" /><path d="M12 15v-6" /><path d="M15 11l-2 -2" /><path d="M11 7l-1.9 -1.9" /><path d="M13.446 2.6l7.955 7.954a2.045 2.045 0 0 1 0 2.892l-7.955 7.955a2.045 2.045 0 0 1 -2.892 0l-7.955 -7.955a2.045 2.045 0 0 1 0 -2.892l7.955 -7.955a2.045 2.045 0 0 1 2.892 0z" /></svg>
|
||||
{{ _("Check code") }}
|
||||
|
@ -27,7 +27,7 @@
|
||||
<script src="https://cdn.jsdelivr.net/combine/npm/tone@14.7.58,npm/@magenta/music@1.23.1/es6/core.js,npm/focus-visible@5,npm/html-midi-player@1.5.0"></script>
|
||||
<midi-visualizer type="staff" id="midi-visualizer"></midi-visualizer>
|
||||
|
||||
|
||||
|
||||
{% if partition.attachments | length > 0 %}
|
||||
<div id="attachments">
|
||||
<table>
|
||||
@ -38,7 +38,7 @@
|
||||
<td><audio controls src="/partition/attachment/{{ attachment.uuid }}.mp3"></td>
|
||||
<td>🎙️ {{ attachment.name }}</td>
|
||||
{% elif attachment.filetype == "mid" %}
|
||||
|
||||
|
||||
<td><midi-player
|
||||
src="/partition/attachment/{{ attachment.uuid }}.mid"
|
||||
sound-font visualizer="#midi-visualizer" data-js-focus-visible>
|
||||
|
42
partitioncloud/templates/settings/index.html
Normal file
42
partitioncloud/templates/settings/index.html
Normal file
@ -0,0 +1,42 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %}{{ _("Settings") }}{% endblock %}
|
||||
|
||||
|
||||
{% block dialogs %}
|
||||
<dialog id="delete-account">
|
||||
<h2>{{ _("Delete account") }}</h2>
|
||||
{% set username %}
|
||||
<b>{{ inspected_user.username }}</b>
|
||||
{% endset %}
|
||||
{% set irreversible_bold %}
|
||||
<b>irreversible</b>
|
||||
{% endset %}
|
||||
{{ _("Do you really want to delete %(username)s's account ? This action is %(irreversible_bold)s.", username=username, irreversible_bold=irreversible_bold) }}
|
||||
<br/><br/>
|
||||
<form method="post" action="/settings/delete-account">
|
||||
<input type="hidden" id="user_id" name="user_id" value="{{ inspected_user.id }}">
|
||||
<input type="submit" class="red-confirm" value="{{ _('Delete') }}">
|
||||
</form>
|
||||
<a href="#!" class="close-dialog">Close</a>
|
||||
</dialog>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
{{ _("User %(username)s has %(album_count)s albums", username=inspected_user.username, album_count=(inspected_user.get_albums() | length)) }}
|
||||
<form action="/settings/change-password" method="post">
|
||||
<h3>{{ _("Change password") }}</h3>
|
||||
{% if not skip_old_password %}
|
||||
<input type="password" id="old-password" name="old_password" placeholder="{{ _('old password') }}"/><br/>
|
||||
{% endif %}
|
||||
<input type="password" id="new-password" name="new_password" placeholder="{{ _('new password') }}"/><br/>
|
||||
<input type="password" id="confirm-new-password" name="confirm_new_password" placeholder="{{ _('confirm new password') }}"/><br/>
|
||||
<input type="hidden" id="user_id" name="user_id" value="{{ inspected_user.id }}">
|
||||
<input type="Submit" value="{{ _('confirm') }}">
|
||||
</form>
|
||||
|
||||
<h3>{{ _("Delete account") }}</h3>
|
||||
<a href="#delete-account"><button class="red-confirm">{{ _("Delete account") }}</button></a>
|
||||
|
||||
{% endblock %}
|
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2024-01-29 18:32+0100\n"
|
||||
"POT-Creation-Date: 2024-02-25 15:18+0100\n"
|
||||
"PO-Revision-Date: 2024-01-22 15:38+0100\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: en\n"
|
||||
@ -18,12 +18,12 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.14.0\n"
|
||||
|
||||
#: partitioncloud/__init__.py:147
|
||||
#: partitioncloud/__init__.py:148
|
||||
#, python-format
|
||||
msgid "Created user %(username)s"
|
||||
msgstr "Created user %(username)s"
|
||||
|
||||
#: partitioncloud/__init__.py:150
|
||||
#: partitioncloud/__init__.py:151
|
||||
#, python-format
|
||||
msgid "This album does not exists, but user %(username)s has been created"
|
||||
msgstr "This album does not exists, but user %(username)s has been created"
|
||||
@ -116,7 +116,8 @@ msgstr "Unknown score type."
|
||||
msgid "You need to login to access this resource."
|
||||
msgstr "You need to login to access this resource."
|
||||
|
||||
#: partitioncloud/modules/auth.py:59
|
||||
#: partitioncloud/modules/auth.py:59 partitioncloud/modules/settings.py:46
|
||||
#: partitioncloud/modules/settings.py:69
|
||||
msgid "Missing rights."
|
||||
msgstr "Missing rights."
|
||||
|
||||
@ -124,7 +125,7 @@ msgstr "Missing rights."
|
||||
msgid "Missing username."
|
||||
msgstr "Missing username."
|
||||
|
||||
#: partitioncloud/modules/auth.py:87
|
||||
#: partitioncloud/modules/auth.py:87 partitioncloud/modules/settings.py:81
|
||||
msgid "Missing password."
|
||||
msgstr "Missing password."
|
||||
|
||||
@ -217,79 +218,103 @@ msgstr "You are not allowed to delete this score."
|
||||
msgid "Score deleted."
|
||||
msgstr "Score deleted."
|
||||
|
||||
#: partitioncloud/templates/base.html:25
|
||||
#: partitioncloud/modules/settings.py:37 partitioncloud/modules/settings.py:60
|
||||
msgid "Missing user id."
|
||||
msgstr "Missing user id."
|
||||
|
||||
#: partitioncloud/modules/settings.py:50
|
||||
msgid "User successfully deleted."
|
||||
msgstr "User successfully deleted."
|
||||
|
||||
#: partitioncloud/modules/settings.py:73
|
||||
msgid "Missing old password."
|
||||
msgstr "Missing old password."
|
||||
|
||||
#: partitioncloud/modules/settings.py:77
|
||||
msgid "Incorrect password."
|
||||
msgstr "Incorrect password."
|
||||
|
||||
#: partitioncloud/modules/settings.py:85
|
||||
msgid "Password and its confirmation differ."
|
||||
msgstr "Password and its confirmation differ."
|
||||
|
||||
#: partitioncloud/modules/settings.py:89
|
||||
msgid "Successfully updated password."
|
||||
msgstr "Successfully updated password."
|
||||
|
||||
#: partitioncloud/templates/base.html:26
|
||||
msgid "New Album"
|
||||
msgstr "New Album"
|
||||
|
||||
#: partitioncloud/templates/base.html:27 partitioncloud/templates/base.html:38
|
||||
#: partitioncloud/templates/base.html:28 partitioncloud/templates/base.html:39
|
||||
#: partitioncloud/templates/groupe/index.html:10
|
||||
#: partitioncloud/templates/partition/attachments.html:11
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
#: partitioncloud/templates/base.html:28 partitioncloud/templates/base.html:39
|
||||
#: partitioncloud/templates/base.html:29 partitioncloud/templates/base.html:40
|
||||
msgid "Create"
|
||||
msgstr "Create"
|
||||
|
||||
#: partitioncloud/templates/base.html:32
|
||||
#: partitioncloud/templates/base.html:33
|
||||
msgid "I want to create a collection of albums."
|
||||
msgstr "I want to create a collection of albums."
|
||||
|
||||
#: partitioncloud/templates/base.html:32
|
||||
#: partitioncloud/templates/base.html:33
|
||||
msgid "Create group"
|
||||
msgstr "Create group"
|
||||
|
||||
#: partitioncloud/templates/base.html:36
|
||||
#: partitioncloud/templates/base.html:37
|
||||
msgid "Create new group"
|
||||
msgstr "Create new group"
|
||||
|
||||
#: partitioncloud/templates/base.html:63
|
||||
#: partitioncloud/templates/base.html:64
|
||||
msgid "Search"
|
||||
msgstr "Search"
|
||||
|
||||
#: partitioncloud/templates/base.html:65
|
||||
#: partitioncloud/templates/base.html:66
|
||||
msgid "Number of online searches"
|
||||
msgstr "Number of online searches"
|
||||
|
||||
#: partitioncloud/templates/admin/index.html:23
|
||||
#: partitioncloud/templates/base.html:73
|
||||
#: partitioncloud/templates/base.html:74
|
||||
#: partitioncloud/templates/partition/details.html:41
|
||||
msgid "Albums"
|
||||
msgstr "Albums"
|
||||
|
||||
#: partitioncloud/templates/base.html:77
|
||||
#: partitioncloud/templates/base.html:78
|
||||
msgid "New album"
|
||||
msgstr "New album"
|
||||
|
||||
#: partitioncloud/templates/base.html:94
|
||||
#: partitioncloud/templates/base.html:95
|
||||
msgid "No albums"
|
||||
msgstr "No albums"
|
||||
|
||||
#: partitioncloud/templates/base.html:113
|
||||
#: partitioncloud/templates/base.html:114
|
||||
msgid "No album available"
|
||||
msgstr "No album available"
|
||||
|
||||
#: partitioncloud/templates/base.html:127
|
||||
#: partitioncloud/templates/base.html:128
|
||||
msgid "Log in to see your albums"
|
||||
msgstr "Log in to see your albums"
|
||||
|
||||
#: partitioncloud/templates/base.html:141
|
||||
#: partitioncloud/templates/base.html:142
|
||||
msgid "Log out"
|
||||
msgstr "Log out"
|
||||
|
||||
#: partitioncloud/templates/base.html:156
|
||||
#: partitioncloud/templates/base.html:157
|
||||
msgid "Admin Panel"
|
||||
msgstr "Admin Panel"
|
||||
|
||||
#: partitioncloud/templates/auth/register.html:5
|
||||
#: partitioncloud/templates/auth/register.html:20
|
||||
#: partitioncloud/templates/base.html:168
|
||||
#: partitioncloud/templates/base.html:169
|
||||
msgid "Create account"
|
||||
msgstr "Create account"
|
||||
|
||||
#: partitioncloud/templates/auth/login.html:5
|
||||
#: partitioncloud/templates/auth/login.html:10
|
||||
#: partitioncloud/templates/base.html:170
|
||||
#: partitioncloud/templates/base.html:171
|
||||
#: partitioncloud/templates/launch.html:26
|
||||
msgid "Log in"
|
||||
msgstr "Log in"
|
||||
@ -315,9 +340,7 @@ msgstr "Check code"
|
||||
msgid ""
|
||||
"This instance is used by %(users)s users with a total of %(scores)s "
|
||||
"scores."
|
||||
msgstr ""
|
||||
"This instance has %(users)s users with a total of %(scores)s "
|
||||
"scores."
|
||||
msgstr "This instance has %(users)s users with a total of %(scores)s scores."
|
||||
|
||||
#: partitioncloud/templates/admin/index.html:5
|
||||
msgid "Administration Panel"
|
||||
|
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2024-01-29 18:32+0100\n"
|
||||
"POT-Creation-Date: 2024-02-25 15:18+0100\n"
|
||||
"PO-Revision-Date: 2024-01-22 15:24+0100\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language: fr\n"
|
||||
@ -18,12 +18,12 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.14.0\n"
|
||||
|
||||
#: partitioncloud/__init__.py:147
|
||||
#: partitioncloud/__init__.py:148
|
||||
#, python-format
|
||||
msgid "Created user %(username)s"
|
||||
msgstr "Utilisateur %(username)s créé"
|
||||
|
||||
#: partitioncloud/__init__.py:150
|
||||
#: partitioncloud/__init__.py:151
|
||||
#, python-format
|
||||
msgid "This album does not exists, but user %(username)s has been created"
|
||||
msgstr "Cet album n'existe pas. L'utilisateur %(username)s a été créé"
|
||||
@ -116,7 +116,8 @@ msgstr "Type de partition inconnu."
|
||||
msgid "You need to login to access this resource."
|
||||
msgstr "Vous devez être connecté pour accéder à cette page."
|
||||
|
||||
#: partitioncloud/modules/auth.py:59
|
||||
#: partitioncloud/modules/auth.py:59 partitioncloud/modules/settings.py:46
|
||||
#: partitioncloud/modules/settings.py:69
|
||||
msgid "Missing rights."
|
||||
msgstr "Droits insuffisants."
|
||||
|
||||
@ -124,7 +125,7 @@ msgstr "Droits insuffisants."
|
||||
msgid "Missing username."
|
||||
msgstr "Un nom d'utilisateur est requis."
|
||||
|
||||
#: partitioncloud/modules/auth.py:87
|
||||
#: partitioncloud/modules/auth.py:87 partitioncloud/modules/settings.py:81
|
||||
msgid "Missing password."
|
||||
msgstr "Un mot de passe est requis."
|
||||
|
||||
@ -219,81 +220,105 @@ msgstr "Vous n'êtes pas autorisé à supprimer cette partition."
|
||||
msgid "Score deleted."
|
||||
msgstr "Partition supprimée."
|
||||
|
||||
#: partitioncloud/templates/base.html:25
|
||||
#: partitioncloud/modules/settings.py:37 partitioncloud/modules/settings.py:60
|
||||
msgid "Missing user id."
|
||||
msgstr "Identifiant d'utilisateur manquant."
|
||||
|
||||
#: partitioncloud/modules/settings.py:50
|
||||
msgid "User successfully deleted."
|
||||
msgstr "Utilisateur supprimée."
|
||||
|
||||
#: partitioncloud/modules/settings.py:73
|
||||
msgid "Missing old password."
|
||||
msgstr "Ancien mot de passe manquant."
|
||||
|
||||
#: partitioncloud/modules/settings.py:77
|
||||
msgid "Incorrect password."
|
||||
msgstr "Mot de passe incorrect."
|
||||
|
||||
#: partitioncloud/modules/settings.py:85
|
||||
msgid "Password and its confirmation differ."
|
||||
msgstr "Le mot de passe et sa confirmation diffèrent"
|
||||
|
||||
#: partitioncloud/modules/settings.py:89
|
||||
msgid "Successfully updated password."
|
||||
msgstr "Mot de passe mis à jour."
|
||||
|
||||
#: partitioncloud/templates/base.html:26
|
||||
msgid "New Album"
|
||||
msgstr "Créer un nouvel album"
|
||||
|
||||
#: partitioncloud/templates/base.html:27 partitioncloud/templates/base.html:38
|
||||
#: partitioncloud/templates/base.html:28 partitioncloud/templates/base.html:39
|
||||
#: partitioncloud/templates/groupe/index.html:10
|
||||
#: partitioncloud/templates/partition/attachments.html:11
|
||||
msgid "Name"
|
||||
msgstr "Nom"
|
||||
|
||||
#: partitioncloud/templates/base.html:28 partitioncloud/templates/base.html:39
|
||||
#: partitioncloud/templates/base.html:29 partitioncloud/templates/base.html:40
|
||||
msgid "Create"
|
||||
msgstr "Créer"
|
||||
|
||||
#: partitioncloud/templates/base.html:32
|
||||
#: partitioncloud/templates/base.html:33
|
||||
msgid "I want to create a collection of albums."
|
||||
msgstr ""
|
||||
"Je souhaite créer plusieurs albums et pouvoir tous les partager avec un "
|
||||
"seul lien."
|
||||
|
||||
#: partitioncloud/templates/base.html:32
|
||||
#: partitioncloud/templates/base.html:33
|
||||
msgid "Create group"
|
||||
msgstr "Créer un groupe"
|
||||
|
||||
#: partitioncloud/templates/base.html:36
|
||||
#: partitioncloud/templates/base.html:37
|
||||
msgid "Create new group"
|
||||
msgstr "Créer un nouveau groupe"
|
||||
|
||||
#: partitioncloud/templates/base.html:63
|
||||
#: partitioncloud/templates/base.html:64
|
||||
msgid "Search"
|
||||
msgstr "Rechercher"
|
||||
|
||||
#: partitioncloud/templates/base.html:65
|
||||
#: partitioncloud/templates/base.html:66
|
||||
msgid "Number of online searches"
|
||||
msgstr "Nombre de recherches en ligne"
|
||||
|
||||
#: partitioncloud/templates/admin/index.html:23
|
||||
#: partitioncloud/templates/base.html:73
|
||||
#: partitioncloud/templates/base.html:74
|
||||
#: partitioncloud/templates/partition/details.html:41
|
||||
msgid "Albums"
|
||||
msgstr "Albums"
|
||||
|
||||
#: partitioncloud/templates/base.html:77
|
||||
#: partitioncloud/templates/base.html:78
|
||||
msgid "New album"
|
||||
msgstr "Créer un album"
|
||||
|
||||
#: partitioncloud/templates/base.html:94
|
||||
#: partitioncloud/templates/base.html:95
|
||||
msgid "No albums"
|
||||
msgstr "Aucun album disponible"
|
||||
|
||||
#: partitioncloud/templates/base.html:113
|
||||
#: partitioncloud/templates/base.html:114
|
||||
msgid "No album available"
|
||||
msgstr "Aucun album disponible"
|
||||
|
||||
#: partitioncloud/templates/base.html:127
|
||||
#: partitioncloud/templates/base.html:128
|
||||
msgid "Log in to see your albums"
|
||||
msgstr "Connectez vous pour avoir accès à vos albums"
|
||||
|
||||
#: partitioncloud/templates/base.html:141
|
||||
#: partitioncloud/templates/base.html:142
|
||||
msgid "Log out"
|
||||
msgstr "Déconnexion"
|
||||
|
||||
#: partitioncloud/templates/base.html:156
|
||||
#: partitioncloud/templates/base.html:157
|
||||
msgid "Admin Panel"
|
||||
msgstr "Panneau admin"
|
||||
|
||||
#: partitioncloud/templates/auth/register.html:5
|
||||
#: partitioncloud/templates/auth/register.html:20
|
||||
#: partitioncloud/templates/base.html:168
|
||||
#: partitioncloud/templates/base.html:169
|
||||
msgid "Create account"
|
||||
msgstr "Créer un compte"
|
||||
|
||||
#: partitioncloud/templates/auth/login.html:5
|
||||
#: partitioncloud/templates/auth/login.html:10
|
||||
#: partitioncloud/templates/base.html:170
|
||||
#: partitioncloud/templates/base.html:171
|
||||
#: partitioncloud/templates/launch.html:26
|
||||
msgid "Log in"
|
||||
msgstr "Se connecter"
|
||||
|
@ -58,7 +58,7 @@ def install_package(package):
|
||||
return
|
||||
print(f"{Fore.RED}Installation with pip failed{Style.RESET_ALL}")
|
||||
sys.exit(return_value)
|
||||
|
||||
|
||||
elif choice == 2:
|
||||
input("Install via you preferred option, and hit [Enter] when done")
|
||||
return
|
||||
|
Loading…
Reference in New Issue
Block a user