Compare commits

..

8 Commits

23 changed files with 284 additions and 130 deletions

View File

@ -10,7 +10,7 @@ Serveur web (basé sur Flask) pour gérer sa collection de partitions musicales
- Thème sombre - Thème sombre
- dashboard administrateur: gestion de tous les albums, partitions et utilisateurs - dashboard administrateur: gestion de tous les albums, partitions et utilisateurs
- [CLI](https://github.com/partitioncloud/partitioncloud-cli) uniquement à des fins de synchronisation. Il serait bon d'ajouter une BDD locale avec les UUIDs des partitions - [CLI](https://github.com/partitioncloud/partitioncloud-cli) uniquement à des fins de synchronisation. Il serait bon d'ajouter une BDD locale avec les UUIDs des partitions
- ~~Pas de Javascript~~ Complètement fonctionnel sans JavaScript, cela vient juste ajouter des [toutes petites améliorations](partitioncloud/static/main.js) - ~~Pas de Javascript~~ Complètement fonctionnel sans JavaScript, cela vient juste ajouter des [toutes petites améliorations](partitioncloud/static/scripts)
## Points à noter ## Points à noter
- Les partitions ajoutées sont accessibles à tous les utilisateurs depuis la recherche même si ils ne sont pas dans un album leur y donnant accès, pour limiter la redondance - Les partitions ajoutées sont accessibles à tous les utilisateurs depuis la recherche même si ils ne sont pas dans un album leur y donnant accès, pour limiter la redondance

View File

@ -1,2 +1,5 @@
[extractors]
jinja2 = jinja2.ext:babel_extract
[python: partitioncloud/**.py] [python: partitioncloud/**.py]
[jinja2: partitioncloud/templates/**.html] [jinja2: partitioncloud/templates/**.html]

View File

@ -89,7 +89,7 @@ def get_album(uuid):
except LookupError: except LookupError:
return abort(404) return abort(404)
album.users = [User(user_id=i["id"]) for i in album.get_users()] album.users = [User(user_id=u_id) for u_id in album.get_users()]
user = User(user_id=session.get("user_id")) user = User(user_id=session.get("user_id"))
partitions = album.get_partitions() partitions = album.get_partitions()
if user.id is None: if user.id is None:
@ -204,8 +204,9 @@ def quit_album(uuid):
""" """
user = User(user_id=session.get("user_id")) user = User(user_id=session.get("user_id"))
album = Album(uuid=uuid) album = Album(uuid=uuid)
users = album.get_users() users = album.get_users()
if user.id not in [u["id"] for u in users]: if user.id not in users:
flash(_("You are not a member of this album")) flash(_("You are not a member of this album"))
return redirect(request.referrer) return redirect(request.referrer)
@ -234,7 +235,7 @@ def delete_album(uuid):
users = album.get_users() users = album.get_users()
if len(users) > 1: if len(users) > 1:
error = _("You are not alone in this album.") error = _("You are not alone in this album.")
elif len(users) == 1 and users[0]["id"] != user.id: elif len(users) == 1 and users[0] != user.id:
error = _("You don't own this album.") error = _("You don't own this album.")
if user.access_level == 1: if user.access_level == 1:

View File

@ -51,11 +51,11 @@ class Album():
def get_users(self, force_reload=False): def get_users(self, force_reload=False):
""" """
Renvoie les utilisateurs liés à l'album Renvoie les data["id"] des utilisateurs liés à l'album
""" """
if self.users is None or force_reload: if self.users is None or force_reload:
db = get_db() db = get_db()
self.users = db.execute( data = db.execute(
""" """
SELECT * FROM user SELECT * FROM user
JOIN contient_user ON user_id = user.id JOIN contient_user ON user_id = user.id
@ -64,6 +64,7 @@ class Album():
""", """,
(self.uuid,) (self.uuid,)
).fetchall() ).fetchall()
self.users = [i["id"] for i in data]
return self.users return self.users
def get_partitions(self): def get_partitions(self):

View File

@ -75,21 +75,23 @@ class Groupe():
album.delete(instance_path) album.delete(instance_path)
def get_users(self): def get_users(self, force_reload=False):
""" """
Renvoie les data["id"] des utilisateurs liés au groupe Renvoie les data["id"] des utilisateurs liés au groupe
TODO: uniformiser le tout
""" """
db = get_db() if self.users is None or force_reload:
return db.execute( db = get_db()
""" data = db.execute(
SELECT * FROM user """
JOIN groupe_contient_user ON user_id = user.id SELECT * FROM user
JOIN groupe ON groupe.id = groupe_id JOIN groupe_contient_user ON user_id = user.id
WHERE groupe.id = ? JOIN groupe ON groupe.id = groupe_id
""", WHERE groupe.id = ?
(self.id,) """,
).fetchall() (self.id,)
).fetchall()
self.users = [i["id"] for i in data]
return self.users
def get_albums(self, force_reload=False): def get_albums(self, force_reload=False):
""" """
@ -126,6 +128,21 @@ class Groupe():
).fetchall() ).fetchall()
return [i["id"] for i in data] return [i["id"] for i in data]
def set_admin(self, user_id, value):
"""
Rend un utilisateur administrateur du groupe
"""
db = get_db()
data = db.execute(
"""
UPDATE groupe_contient_user
SET is_admin=?
WHERE user_id=? AND groupe_id=?
""",
(value, user_id, self.id)
)
db.commit()
def to_zip(self, instance_path): def to_zip(self, instance_path):
data = io.BytesIO() data = io.BytesIO()
with zipfile.ZipFile(data, mode="w") as z: with zipfile.ZipFile(data, mode="w") as z:

View File

@ -75,6 +75,16 @@ class Partition():
) )
db.commit() db.commit()
def update_file(self, file, instance_path):
partition_path = os.path.join(
instance_path,
"partitions",
f"{self.uuid}.pdf"
)
file.save(partition_path)
if os.path.exists(f"{instance_path}/cache/thumbnails/{self.uuid}.jpg"):
os.remove(f"{instance_path}/cache/thumbnails/{self.uuid}.jpg")
def get_user(self): def get_user(self):
db = get_db() db = get_db()
user = db.execute( user = db.execute(

View File

@ -182,7 +182,7 @@ class User():
else: else:
self.accessible_partitions = db.execute( self.accessible_partitions = db.execute(
""" """
SELECT partition.uuid, partition.name, SELECT DISTINCT partition.uuid, partition.name,
partition.author, partition.body, partition.author, partition.body,
partition.user_id, partition.source partition.user_id, partition.source
FROM partition FROM partition
@ -196,7 +196,7 @@ class User():
ON contient_user.user_id=? ON contient_user.user_id=?
AND album_id=album.id AND album_id=album.id
UNION UNION
SELECT album.id FROM album SELECT DISTINCT album.id FROM album
JOIN groupe_contient_user JOIN groupe_contient_user
JOIN groupe_contient_album JOIN groupe_contient_album
ON groupe_contient_user.user_id=? ON groupe_contient_user.user_id=?

View File

@ -35,7 +35,7 @@ def get_groupe(uuid):
except LookupError: except LookupError:
return abort(404) return abort(404)
groupe.users = [User(user_id=i["id"]) for i in groupe.get_users()] groupe.users = [User(user_id=u_id) for u_id in groupe.get_users()]
groupe.get_albums() groupe.get_albums()
user = User(user_id=session.get("user_id")) user = User(user_id=session.get("user_id"))
@ -131,7 +131,7 @@ def quit_groupe(uuid):
user = User(user_id=session.get("user_id")) user = User(user_id=session.get("user_id"))
groupe = Groupe(uuid=uuid) groupe = Groupe(uuid=uuid)
users = groupe.get_users() users = groupe.get_users()
if user.id not in [u["id"] for u in users]: if user.id not in users:
flash(_("You are not a member of this group.")) flash(_("You are not a member of this group."))
return redirect(f"/groupe/{uuid}") return redirect(f"/groupe/{uuid}")
@ -140,6 +140,11 @@ def quit_groupe(uuid):
return redirect(f"/groupe/{uuid}#delete") return redirect(f"/groupe/{uuid}#delete")
user.quit_groupe(groupe.uuid) user.quit_groupe(groupe.uuid)
if len(groupe.get_admins()) == 0: # On s'assure que le groupe contient toujours des administrateurs
for user_id in groupe.get_users(force_reload=True):
groupe.set_admin(user_id, True)
flash(_("Group quitted.")) flash(_("Group quitted."))
return redirect("/albums") return redirect("/albums")
@ -151,8 +156,7 @@ def delete_groupe(uuid):
user = User(user_id=session.get("user_id")) user = User(user_id=session.get("user_id"))
error = None error = None
users = groupe.get_users() if len(groupe.get_users()) > 1:
if len(users) > 1:
error = _("You are not alone in this group.") error = _("You are not alone in this group.")
if user.access_level == 1 or user.id not in groupe.get_admins(): if user.access_level == 1 or user.id not in groupe.get_admins():
@ -185,7 +189,7 @@ def create_album_req(groupe_uuid):
if not name or name.strip() == "": if not name or name.strip() == "":
error = _("Missing name.") error = _("Missing name.")
if user.id not in groupe.get_admins(): if user.id not in groupe.get_admins() and user.access_level != 1:
error = _("You are not admin of this group.") error = _("You are not admin of this group.")
if error is None: if error is None:
@ -241,7 +245,7 @@ def get_album(groupe_uuid, album_uuid):
user = User(user_id=session.get("user_id")) user = User(user_id=session.get("user_id"))
# List of users without duplicate # List of users without duplicate
users_id = list({i["id"] for i in album.get_users()+groupe.get_users()}) users_id = list(set(album.get_users()+groupe.get_users()))
album.users = [User(user_id=id) for id in users_id] album.users = [User(user_id=id) for id in users_id]
partitions = album.get_partitions() partitions = album.get_partitions()

View File

@ -3,6 +3,7 @@
Partition module Partition module
""" """
import os import os
import pypdf
from uuid import uuid4 from uuid import uuid4
from flask import (Blueprint, abort, send_file, render_template, from flask import (Blueprint, abort, send_file, render_template,
request, redirect, flash, session, current_app) request, redirect, flash, session, current_app)
@ -160,6 +161,17 @@ def edit(uuid):
flash(error) flash(error)
return redirect(f"/partition/{ uuid }/edit") return redirect(f"/partition/{ uuid }/edit")
if request.files.get('file', None):
new_file = request.files["file"]
try:
pypdf.PdfReader(new_file)
new_file.seek(0)
except (pypdf.errors.PdfReadError, pypdf.errors.PdfStreamError):
flash(_("Invalid PDF file"))
return redirect(request.referrer)
partition.update_file(new_file, current_app.instance_path)
partition.update( partition.update(
name=request.form["name"], name=request.form["name"],
author=request.form["author"], author=request.form["author"],

View File

@ -10,6 +10,7 @@ import os
import pypdf import pypdf
import googlesearch import googlesearch
from unidecode import unidecode
from .db import get_db from .db import get_db
@ -20,20 +21,20 @@ def local_search(query, partitions):
""" """
Renvoie les 5 résultats les plus pertinents parmi une liste donnée Renvoie les 5 résultats les plus pertinents parmi une liste donnée
""" """
query_words = [word.lower() for word in query.split(" ")] query_words = [word.lower() for word in unidecode(query).split()]
def score_attribution(partition): def score_attribution(partition):
score = 0 score = 0
for word in query_words: for word in query_words:
if word != "": if word != "":
if word in partition["name"].lower(): if word in unidecode(partition["name"]).lower():
score += 6 score += 6
elif word in partition["author"].lower(): elif word in unidecode(partition["author"]).lower():
score += 4 score += 4
elif word in partition["body"].lower(): elif word in unidecode(partition["body"]).lower():
score += 2 score += 2
else: else:
score -= 1 score -= 6
for word in partition["name"].split(" "): for word in unidecode(partition["name"]).split():
if word != "" and word.lower() not in query_words: if word != "" and word.lower() not in query_words:
score -= 1 score -= 1
return score return score

View File

@ -718,3 +718,56 @@ midi-player {
border-radius: 5px; border-radius: 5px;
background-color: var(--color-crust); background-color: var(--color-crust);
} }
/** Input[file] */
.file-area {
position: relative;
}
.file-area input[type=file] {
position: absolute;
height: 100%;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: 0;
cursor: pointer;
}
.file-area .inner-file-area {
padding: 30px;
background: var(--color-mantle);
border: 2px dashed var(--color-red);
text-align: center;
transition: background 0.3s ease-in-out;
}
.file-area .inner-file-area .success {
display: none;
}
.file-area:hover > .inner-file-area {
background: var(--color-surface0);
}
.file-area input[type=file]:valid + .inner-file-area {
border-color: var(--color-green);
}
.file-area input[type=file]:not(:required) + .inner-file-area {
border-color: var(--color-blue);
}
.file-area input[type=file]:valid + .inner-file-area .success {
display: inline-block;
}
.file-area input[type=file]:valid + .inner-file-area .default {
display: none;
}
.file-area input[type=file]:not(:required) + .inner-file-area .success {
display: none;
}
.file-area input[type=file]:not(:required) + .inner-file-area .default {
display: inline-block;
}

View File

@ -53,7 +53,7 @@
{% endif %} {% endif %}
{% if not_participant %} {% if not_participant %}
<a href="/albums/{{ album.uuid }}/join">{{ _("Join") }}</a> <a href="/albums/{{ album.uuid }}/join">{{ _("Join") }}</a>
{% elif album.users | length > 1 %} {% elif g.user and not not_participant %}
<a href="/albums/{{ album.uuid }}/quit">{{ _("Quit") }}</a> <a href="/albums/{{ album.uuid }}/quit">{{ _("Quit") }}</a>
{% endif %} {% endif %}
<a href="#share">{{ _("Share") }}</a> <a href="#share">{{ _("Share") }}</a>

View File

@ -7,7 +7,11 @@
{% if partition_uuid %} {% if partition_uuid %}
<input name="partition-uuid" value="{{ partition_uuid }}" type="hidden"> <input name="partition-uuid" value="{{ partition_uuid }}" type="hidden">
{% else %} {% else %}
<input name="file" type="file" accept=".pdf" required=""><br/> {% block input_file %}
{% set required=true %}
{% set filetype=".pdf" %}
{% include 'components/input_file.html' %}
{% endblock %}
{% endif %} {% endif %}
<input type="submit" value="{{ _('Add') }}" /> <input type="submit" value="{{ _('Add') }}" />
</form> </form>

View File

@ -0,0 +1,7 @@
<div class="file-area">
<input name="file" type="file" accept="{{ filetype }}" {% if required %}required=""{% endif %}>
<div class="inner-file-area">
<div class="success">{{ _("Your file is selected.") }}</div>
<div class="default">{{ _("Select or drag & drop your file") }} ({{ filetype }}).</div>
</div>
</div>

View File

@ -45,7 +45,7 @@
<div class="dropdown-content dp1"> <div class="dropdown-content dp1">
{% if not_participant %} {% if not_participant %}
<a href="/groupe/{{ groupe.uuid }}/join">{{ _("Join") }}</a> <a href="/groupe/{{ groupe.uuid }}/join">{{ _("Join") }}</a>
{% elif groupe.users | length > 1 and g.user %} {% elif g.user and not not_participant %}
<a href="/groupe/{{ groupe.uuid }}/quit">{{ _("Quit") }}</a> <a href="/groupe/{{ groupe.uuid }}/quit">{{ _("Quit") }}</a>
{% endif %} {% endif %}
<a href="#share">{{ _("Share") }}</a> <a href="#share">{{ _("Share") }}</a>

View File

@ -9,7 +9,11 @@
<h2>{{ _("Add an attachment to %(name)s", name=partition.name) }}</h2> <h2>{{ _("Add an attachment to %(name)s", name=partition.name) }}</h2>
<form action="/partition/{{ partition.uuid }}/add-attachment" method="post" enctype="multipart/form-data"> <form action="/partition/{{ partition.uuid }}/add-attachment" method="post" enctype="multipart/form-data">
<input type="text" name="name" id="name" placeholder="{{ _('Name') }}"><br/> <input type="text" name="name" id="name" placeholder="{{ _('Name') }}"><br/>
<input name="file" type="file" accept=".mp3,.mid" required=""><br/> {% block input_file %}
{% set required=true %}
{% set filetype=".mp3,.mid" %}
{% include 'components/input_file.html' %}
{% endblock %}
<input type="submit" value="{{ _('Add') }}"> <input type="submit" value="{{ _('Add') }}">
</form> </form>
<a href="#!" class="close-dialog">Close</a> <a href="#!" class="close-dialog">Close</a>
@ -55,7 +59,7 @@
{% endif %} {% endif %}
<br/> <br/>
{% if user %} {% if g.user %}
<div class="centered"> <div class="centered">
<a href="#create-attachment"><button>{{ _("Add an attachment") }}</button></a> <a href="#create-attachment"><button>{{ _("Add an attachment") }}</button></a>
</div> </div>

View File

@ -52,7 +52,13 @@
<td>{{ _("File") }}</td> <td>{{ _("File") }}</td>
<td><a href="/partition/{{ partition.uuid }}"> <td><a href="/partition/{{ partition.uuid }}">
<img class="partition-thumbnail" src="/thumbnails/{{ partition.uuid }}.jpg" loading="lazy"> <img class="partition-thumbnail" src="/thumbnails/{{ partition.uuid }}.jpg" loading="lazy">
</a></td> </a><br/>
{% block input_file %}
{% set required=false %}
{% set filetype=".pdf" %}
{% include 'components/input_file.html' %}
{% endblock %}
</td>
</tr> </tr>
<tr> <tr>
<td>{{ _("Title") }}</td> <td>{{ _("Title") }}</td>

View File

@ -13,7 +13,13 @@
<td>{{ _("File") }}</td> <td>{{ _("File") }}</td>
<td><a href="/partition/{{ partition.uuid }}"> <td><a href="/partition/{{ partition.uuid }}">
<img class="partition-thumbnail" src="/thumbnails/{{ partition.uuid }}.jpg" loading="lazy"> <img class="partition-thumbnail" src="/thumbnails/{{ partition.uuid }}.jpg" loading="lazy">
</a></td> </a><br/>
{% block input_file %}
{% set required=false %}
{% set filetype=".pdf" %}
{% include 'components/input_file.html' %}
{% endblock %}
</td>
</tr> </tr>
{% if partition.source != "unknown" and partition.source != "upload" %} {% if partition.source != "unknown" and partition.source != "upload" %}
<tr> <tr>

View File

@ -75,12 +75,12 @@ msgstr "You don't own this album."
msgid "Album deleted." msgid "Album deleted."
msgstr "Album deleted." msgstr "Album deleted."
#: partitioncloud/modules/albums.py:278 partitioncloud/modules/partition.py:153 #: partitioncloud/modules/albums.py:278 partitioncloud/modules/partition.py:154
#: partitioncloud/modules/partition.py:199 #: partitioncloud/modules/partition.py:211
msgid "Missing title" msgid "Missing title"
msgstr "Missing title" msgstr "Missing title"
#: partitioncloud/modules/albums.py:280 partitioncloud/modules/partition.py:63 #: partitioncloud/modules/albums.py:280 partitioncloud/modules/partition.py:64
msgid "Missing file" msgid "Missing file"
msgstr "Missing file" msgstr "Missing file"
@ -88,7 +88,7 @@ msgstr "Missing file"
msgid "Search results expired" msgid "Search results expired"
msgstr "Search results expired" msgstr "Search results expired"
#: partitioncloud/modules/albums.py:302 #: partitioncloud/modules/albums.py:302 partitioncloud/modules/partition.py:170
msgid "Invalid PDF file" msgid "Invalid PDF file"
msgstr "" msgstr ""
@ -183,43 +183,43 @@ msgstr "Group deleted."
msgid "You are not admin of this group." msgid "You are not admin of this group."
msgstr "You are not admin of this group." msgstr "You are not admin of this group."
#: partitioncloud/modules/partition.py:58 #: partitioncloud/modules/partition.py:59
msgid "You don't own this score." msgid "You don't own this score."
msgstr "You don't own this score." msgstr "You don't own this score."
#: partitioncloud/modules/partition.py:71 #: partitioncloud/modules/partition.py:72
msgid "Missing filename." msgid "Missing filename."
msgstr "Missing filename." msgstr "Missing filename."
#: partitioncloud/modules/partition.py:76 #: partitioncloud/modules/partition.py:77
msgid "Unsupported file type." msgid "Unsupported file type."
msgstr "Unsupported file type." msgstr "Unsupported file type."
#: partitioncloud/modules/partition.py:144 #: partitioncloud/modules/partition.py:145
msgid "You are not allowed to edit this file." msgid "You are not allowed to edit this file."
msgstr "You are not allowed to edit this file." msgstr "You are not allowed to edit this file."
#: partitioncloud/modules/partition.py:155 #: partitioncloud/modules/partition.py:156
#: partitioncloud/modules/partition.py:201 #: partitioncloud/modules/partition.py:213
msgid "Missing author in request body (can be null)." msgid "Missing author in request body (can be null)."
msgstr "Missing author in request body (can be null)." msgstr "Missing author in request body (can be null)."
#: partitioncloud/modules/partition.py:157 #: partitioncloud/modules/partition.py:158
#: partitioncloud/modules/partition.py:203 #: partitioncloud/modules/partition.py:215
msgid "Missing lyrics (can be null)." msgid "Missing lyrics (can be null)."
msgstr "Missing lyrics (can be null)." msgstr "Missing lyrics (can be null)."
#: partitioncloud/modules/partition.py:169 #: partitioncloud/modules/partition.py:181
#: partitioncloud/modules/partition.py:215 #: partitioncloud/modules/partition.py:227
#, python-format #, python-format
msgid "Successfully modified %(name)s" msgid "Successfully modified %(name)s"
msgstr "Successfully modified %(name)s" msgstr "Successfully modified %(name)s"
#: partitioncloud/modules/partition.py:230 #: partitioncloud/modules/partition.py:242
msgid "You are not allowed to delete this score." msgid "You are not allowed to delete this score."
msgstr "You are not allowed to delete this score." msgstr "You are not allowed to delete this score."
#: partitioncloud/modules/partition.py:238 #: partitioncloud/modules/partition.py:250
msgid "Score deleted." msgid "Score deleted."
msgstr "Score deleted." msgstr "Score deleted."
@ -411,8 +411,8 @@ msgstr "Do you really want to delete this album?"
#: partitioncloud/templates/groupe/index.html:20 #: partitioncloud/templates/groupe/index.html:20
#: partitioncloud/templates/groupe/index.html:57 #: partitioncloud/templates/groupe/index.html:57
#: partitioncloud/templates/partition/delete.html:10 #: partitioncloud/templates/partition/delete.html:10
#: partitioncloud/templates/partition/details.html:86 #: partitioncloud/templates/partition/details.html:92
#: partitioncloud/templates/partition/edit.html:57 #: partitioncloud/templates/partition/edit.html:63
#: partitioncloud/templates/settings/index.html:19 #: partitioncloud/templates/settings/index.html:19
msgid "Delete" msgid "Delete"
msgstr "Delete" msgstr "Delete"
@ -526,12 +526,20 @@ msgstr "author"
msgid "lyrics" msgid "lyrics"
msgstr "lyrics" msgstr "lyrics"
#: partitioncloud/templates/components/add_partition.html:12 #: partitioncloud/templates/components/add_partition.html:16
#: partitioncloud/templates/groupe/index.html:11 #: partitioncloud/templates/groupe/index.html:11
#: partitioncloud/templates/partition/attachments.html:13 #: partitioncloud/templates/partition/attachments.html:17
msgid "Add" msgid "Add"
msgstr "Add" msgstr "Add"
#: partitioncloud/templates/components/input_file.html:4
msgid "Your file is selected."
msgstr "Your file is selected."
#: partitioncloud/templates/components/input_file.html:5
msgid "Select or drag & drop your file"
msgstr "Select or drag & drop your file"
#: partitioncloud/templates/groupe/index.html:8 #: partitioncloud/templates/groupe/index.html:8
#, python-format #, python-format
msgid "Add an album to group %(name)s" msgid "Add an album to group %(name)s"
@ -568,7 +576,7 @@ msgstr "Attachments of %(name)s"
msgid "Add an attachment to %(name)s" msgid "Add an attachment to %(name)s"
msgstr "Add an attachment to %(name)s" msgstr "Add an attachment to %(name)s"
#: partitioncloud/templates/partition/attachments.html:22 #: partitioncloud/templates/partition/attachments.html:26
msgid "" msgid ""
"No pdf viewer available in this browser.\n" "No pdf viewer available in this browser.\n"
" You can use Firefox on Android." " You can use Firefox on Android."
@ -576,11 +584,11 @@ msgstr ""
"No pdf viewer available in this browser.\n" "No pdf viewer available in this browser.\n"
" You can use Firefox on Android." " You can use Firefox on Android."
#: partitioncloud/templates/partition/attachments.html:46 #: partitioncloud/templates/partition/attachments.html:50
msgid "JavaScript is mandatory to read MIDI files" msgid "JavaScript is mandatory to read MIDI files"
msgstr "JavaScript is mandatory to read MIDI files" msgstr "JavaScript is mandatory to read MIDI files"
#: partitioncloud/templates/partition/attachments.html:60 #: partitioncloud/templates/partition/attachments.html:64
msgid "Add an attachment" msgid "Add an attachment"
msgstr "Add an attachment" msgstr "Add an attachment"
@ -610,45 +618,45 @@ msgstr "Type"
msgid "File" msgid "File"
msgstr "File" msgstr "File"
#: partitioncloud/templates/partition/details.html:58 #: partitioncloud/templates/partition/details.html:64
#: partitioncloud/templates/partition/details.html:59 #: partitioncloud/templates/partition/details.html:65
#: partitioncloud/templates/partition/edit.html:29 #: partitioncloud/templates/partition/edit.html:35
#: partitioncloud/templates/partition/edit.html:30 #: partitioncloud/templates/partition/edit.html:36
msgid "Title" msgid "Title"
msgstr "Title" msgstr "Title"
#: partitioncloud/templates/partition/details.html:62 #: partitioncloud/templates/partition/details.html:68
#: partitioncloud/templates/partition/details.html:63 #: partitioncloud/templates/partition/details.html:69
#: partitioncloud/templates/partition/edit.html:33 #: partitioncloud/templates/partition/edit.html:39
#: partitioncloud/templates/partition/edit.html:34 #: partitioncloud/templates/partition/edit.html:40
msgid "Author" msgid "Author"
msgstr "Author" msgstr "Author"
#: partitioncloud/templates/partition/details.html:66 #: partitioncloud/templates/partition/details.html:72
#: partitioncloud/templates/partition/details.html:67 #: partitioncloud/templates/partition/details.html:73
#: partitioncloud/templates/partition/edit.html:37 #: partitioncloud/templates/partition/edit.html:43
#: partitioncloud/templates/partition/edit.html:38 #: partitioncloud/templates/partition/edit.html:44
msgid "Lyrics" msgid "Lyrics"
msgstr "Lyrics" msgstr "Lyrics"
#: partitioncloud/templates/partition/details.html:70 #: partitioncloud/templates/partition/details.html:76
#: partitioncloud/templates/partition/edit.html:41 #: partitioncloud/templates/partition/edit.html:47
msgid "Attachments" msgid "Attachments"
msgstr "Attachments" msgstr "Attachments"
#: partitioncloud/templates/partition/details.html:75 #: partitioncloud/templates/partition/details.html:81
#: partitioncloud/templates/partition/edit.html:46 #: partitioncloud/templates/partition/edit.html:52
#, python-format #, python-format
msgid "Yes, %(number)s" msgid "Yes, %(number)s"
msgstr "Yes, %(number)s" msgstr "Yes, %(number)s"
#: partitioncloud/templates/partition/details.html:77 #: partitioncloud/templates/partition/details.html:83
#: partitioncloud/templates/partition/edit.html:48 #: partitioncloud/templates/partition/edit.html:54
msgid "Add one" msgid "Add one"
msgstr "Add one" msgstr "Add one"
#: partitioncloud/templates/partition/details.html:83 #: partitioncloud/templates/partition/details.html:89
#: partitioncloud/templates/partition/edit.html:54 #: partitioncloud/templates/partition/edit.html:60
msgid "Update" msgid "Update"
msgstr "Update" msgstr "Update"
@ -657,7 +665,7 @@ msgstr "Update"
msgid "Modify \"%(name)s\"" msgid "Modify \"%(name)s\""
msgstr "Modify \"%(name)s\"" msgstr "Modify \"%(name)s\""
#: partitioncloud/templates/partition/edit.html:21 #: partitioncloud/templates/partition/edit.html:27
msgid "Source" msgid "Source"
msgstr "Source" msgstr "Source"

View File

@ -75,12 +75,12 @@ msgstr "Vous ne possédez pas cet album."
msgid "Album deleted." msgid "Album deleted."
msgstr "Album supprimé." msgstr "Album supprimé."
#: partitioncloud/modules/albums.py:278 partitioncloud/modules/partition.py:153 #: partitioncloud/modules/albums.py:278 partitioncloud/modules/partition.py:154
#: partitioncloud/modules/partition.py:199 #: partitioncloud/modules/partition.py:211
msgid "Missing title" msgid "Missing title"
msgstr "Un titre est requis." msgstr "Un titre est requis."
#: partitioncloud/modules/albums.py:280 partitioncloud/modules/partition.py:63 #: partitioncloud/modules/albums.py:280 partitioncloud/modules/partition.py:64
msgid "Missing file" msgid "Missing file"
msgstr "Aucun fichier n'a été fourni." msgstr "Aucun fichier n'a été fourni."
@ -88,7 +88,7 @@ msgstr "Aucun fichier n'a été fourni."
msgid "Search results expired" msgid "Search results expired"
msgstr "Les résultats de la recherche ont expiré." msgstr "Les résultats de la recherche ont expiré."
#: partitioncloud/modules/albums.py:302 #: partitioncloud/modules/albums.py:302 partitioncloud/modules/partition.py:170
msgid "Invalid PDF file" msgid "Invalid PDF file"
msgstr "Fichier PDF invalide" msgstr "Fichier PDF invalide"
@ -185,43 +185,43 @@ msgstr "Groupe supprimé."
msgid "You are not admin of this group." msgid "You are not admin of this group."
msgstr "Vous n'êtes pas administrateur de ce groupe" msgstr "Vous n'êtes pas administrateur de ce groupe"
#: partitioncloud/modules/partition.py:58 #: partitioncloud/modules/partition.py:59
msgid "You don't own this score." msgid "You don't own this score."
msgstr "Cette partition ne vous appartient pas" msgstr "Cette partition ne vous appartient pas"
#: partitioncloud/modules/partition.py:71 #: partitioncloud/modules/partition.py:72
msgid "Missing filename." msgid "Missing filename."
msgstr "Pas de nom de fichier" msgstr "Pas de nom de fichier"
#: partitioncloud/modules/partition.py:76 #: partitioncloud/modules/partition.py:77
msgid "Unsupported file type." msgid "Unsupported file type."
msgstr "Extension de fichier non supportée" msgstr "Extension de fichier non supportée"
#: partitioncloud/modules/partition.py:144 #: partitioncloud/modules/partition.py:145
msgid "You are not allowed to edit this file." msgid "You are not allowed to edit this file."
msgstr "Vous n'êtes pas autorisé à modifier cette partition." msgstr "Vous n'êtes pas autorisé à modifier cette partition."
#: partitioncloud/modules/partition.py:155 #: partitioncloud/modules/partition.py:156
#: partitioncloud/modules/partition.py:201 #: partitioncloud/modules/partition.py:213
msgid "Missing author in request body (can be null)." msgid "Missing author in request body (can be null)."
msgstr "Un nom d'auteur est requis (à minima nul)" msgstr "Un nom d'auteur est requis (à minima nul)"
#: partitioncloud/modules/partition.py:157 #: partitioncloud/modules/partition.py:158
#: partitioncloud/modules/partition.py:203 #: partitioncloud/modules/partition.py:215
msgid "Missing lyrics (can be null)." msgid "Missing lyrics (can be null)."
msgstr "Des paroles sont requises (à minima nulles)" msgstr "Des paroles sont requises (à minima nulles)"
#: partitioncloud/modules/partition.py:169 #: partitioncloud/modules/partition.py:181
#: partitioncloud/modules/partition.py:215 #: partitioncloud/modules/partition.py:227
#, python-format #, python-format
msgid "Successfully modified %(name)s" msgid "Successfully modified %(name)s"
msgstr "Partition %(name)s modifiée avec succès." msgstr "Partition %(name)s modifiée avec succès."
#: partitioncloud/modules/partition.py:230 #: partitioncloud/modules/partition.py:242
msgid "You are not allowed to delete this score." msgid "You are not allowed to delete this score."
msgstr "Vous n'êtes pas autorisé à supprimer cette partition." msgstr "Vous n'êtes pas autorisé à supprimer cette partition."
#: partitioncloud/modules/partition.py:238 #: partitioncloud/modules/partition.py:250
msgid "Score deleted." msgid "Score deleted."
msgstr "Partition supprimée." msgstr "Partition supprimée."
@ -417,8 +417,8 @@ msgstr "Êtes vous sûr de vouloir supprimer cet album ?"
#: partitioncloud/templates/groupe/index.html:20 #: partitioncloud/templates/groupe/index.html:20
#: partitioncloud/templates/groupe/index.html:57 #: partitioncloud/templates/groupe/index.html:57
#: partitioncloud/templates/partition/delete.html:10 #: partitioncloud/templates/partition/delete.html:10
#: partitioncloud/templates/partition/details.html:86 #: partitioncloud/templates/partition/details.html:92
#: partitioncloud/templates/partition/edit.html:57 #: partitioncloud/templates/partition/edit.html:63
#: partitioncloud/templates/settings/index.html:19 #: partitioncloud/templates/settings/index.html:19
msgid "Delete" msgid "Delete"
msgstr "Supprimer" msgstr "Supprimer"
@ -532,12 +532,20 @@ msgstr "auteur"
msgid "lyrics" msgid "lyrics"
msgstr "paroles" msgstr "paroles"
#: partitioncloud/templates/components/add_partition.html:12 #: partitioncloud/templates/components/add_partition.html:16
#: partitioncloud/templates/groupe/index.html:11 #: partitioncloud/templates/groupe/index.html:11
#: partitioncloud/templates/partition/attachments.html:13 #: partitioncloud/templates/partition/attachments.html:17
msgid "Add" msgid "Add"
msgstr "Ajouter" msgstr "Ajouter"
#: partitioncloud/templates/components/input_file.html:4
msgid "Your file is selected."
msgstr "Fichier sélectionné."
#: partitioncloud/templates/components/input_file.html:5
msgid "Select or drag & drop your file"
msgstr "Sélectionner ou déposer un fichier"
#: partitioncloud/templates/groupe/index.html:8 #: partitioncloud/templates/groupe/index.html:8
#, python-format #, python-format
msgid "Add an album to group %(name)s" msgid "Add an album to group %(name)s"
@ -577,7 +585,7 @@ msgstr "Attachments de %(name)s"
msgid "Add an attachment to %(name)s" msgid "Add an attachment to %(name)s"
msgstr "Ajouter un attachment à %(name)s" msgstr "Ajouter un attachment à %(name)s"
#: partitioncloud/templates/partition/attachments.html:22 #: partitioncloud/templates/partition/attachments.html:26
msgid "" msgid ""
"No pdf viewer available in this browser.\n" "No pdf viewer available in this browser.\n"
" You can use Firefox on Android." " You can use Firefox on Android."
@ -585,11 +593,11 @@ msgstr ""
"Impossible d'afficher le pdf dans ce navigateur.\n" "Impossible d'afficher le pdf dans ce navigateur.\n"
" Il est conseillé d'utiliser Firefox sur Android." " Il est conseillé d'utiliser Firefox sur Android."
#: partitioncloud/templates/partition/attachments.html:46 #: partitioncloud/templates/partition/attachments.html:50
msgid "JavaScript is mandatory to read MIDI files" msgid "JavaScript is mandatory to read MIDI files"
msgstr "JavaScript est nécessaire pour lire les fichiers MIDI" msgstr "JavaScript est nécessaire pour lire les fichiers MIDI"
#: partitioncloud/templates/partition/attachments.html:60 #: partitioncloud/templates/partition/attachments.html:64
msgid "Add an attachment" msgid "Add an attachment"
msgstr "Ajouter un attachment" msgstr "Ajouter un attachment"
@ -619,45 +627,45 @@ msgstr "Type d'ajout"
msgid "File" msgid "File"
msgstr "Fichier" msgstr "Fichier"
#: partitioncloud/templates/partition/details.html:58 #: partitioncloud/templates/partition/details.html:64
#: partitioncloud/templates/partition/details.html:59 #: partitioncloud/templates/partition/details.html:65
#: partitioncloud/templates/partition/edit.html:29 #: partitioncloud/templates/partition/edit.html:35
#: partitioncloud/templates/partition/edit.html:30 #: partitioncloud/templates/partition/edit.html:36
msgid "Title" msgid "Title"
msgstr "Titre" msgstr "Titre"
#: partitioncloud/templates/partition/details.html:62 #: partitioncloud/templates/partition/details.html:68
#: partitioncloud/templates/partition/details.html:63 #: partitioncloud/templates/partition/details.html:69
#: partitioncloud/templates/partition/edit.html:33 #: partitioncloud/templates/partition/edit.html:39
#: partitioncloud/templates/partition/edit.html:34 #: partitioncloud/templates/partition/edit.html:40
msgid "Author" msgid "Author"
msgstr "Auteur" msgstr "Auteur"
#: partitioncloud/templates/partition/details.html:66 #: partitioncloud/templates/partition/details.html:72
#: partitioncloud/templates/partition/details.html:67 #: partitioncloud/templates/partition/details.html:73
#: partitioncloud/templates/partition/edit.html:37 #: partitioncloud/templates/partition/edit.html:43
#: partitioncloud/templates/partition/edit.html:38 #: partitioncloud/templates/partition/edit.html:44
msgid "Lyrics" msgid "Lyrics"
msgstr "Paroles" msgstr "Paroles"
#: partitioncloud/templates/partition/details.html:70 #: partitioncloud/templates/partition/details.html:76
#: partitioncloud/templates/partition/edit.html:41 #: partitioncloud/templates/partition/edit.html:47
msgid "Attachments" msgid "Attachments"
msgstr "Pièces jointes" msgstr "Pièces jointes"
#: partitioncloud/templates/partition/details.html:75 #: partitioncloud/templates/partition/details.html:81
#: partitioncloud/templates/partition/edit.html:46 #: partitioncloud/templates/partition/edit.html:52
#, python-format #, python-format
msgid "Yes, %(number)s" msgid "Yes, %(number)s"
msgstr "Oui, %(number)s" msgstr "Oui, %(number)s"
#: partitioncloud/templates/partition/details.html:77 #: partitioncloud/templates/partition/details.html:83
#: partitioncloud/templates/partition/edit.html:48 #: partitioncloud/templates/partition/edit.html:54
msgid "Add one" msgid "Add one"
msgstr "En rajouter" msgstr "En rajouter"
#: partitioncloud/templates/partition/details.html:83 #: partitioncloud/templates/partition/details.html:89
#: partitioncloud/templates/partition/edit.html:54 #: partitioncloud/templates/partition/edit.html:60
msgid "Update" msgid "Update"
msgstr "Mettre à jour" msgstr "Mettre à jour"
@ -666,7 +674,7 @@ msgstr "Mettre à jour"
msgid "Modify \"%(name)s\"" msgid "Modify \"%(name)s\""
msgstr "Modifier \"%(name)s\"" msgstr "Modifier \"%(name)s\""
#: partitioncloud/templates/partition/edit.html:21 #: partitioncloud/templates/partition/edit.html:27
msgid "Source" msgid "Source"
msgstr "Source" msgstr "Source"

View File

@ -3,4 +3,5 @@ flask-babel
google google
colorama colorama
pypdf pypdf
qrcode qrcode
unidecode

View File

@ -187,4 +187,11 @@ def install_babel():
""" """
def install_pypdf(): def install_pypdf():
utils.install_package("pypdf") utils.install_package("pypdf")
"""
v1.10.*
"""
def install_unidecode():
utils.install_package("unidecode")

View File

@ -36,7 +36,8 @@ hooks = [
("v1.5.0", [("Move to instance directory", v1_hooks.move_instance)]), ("v1.5.0", [("Move to instance directory", v1_hooks.move_instance)]),
("v1.5.1", [("Move thumbnails", v1_hooks.move_thumbnails)]), ("v1.5.1", [("Move thumbnails", v1_hooks.move_thumbnails)]),
("v1.7.0", [("Install babel", v1_hooks.install_babel)]), ("v1.7.0", [("Install babel", v1_hooks.install_babel)]),
("v1.8.2", [("Install pypdf", v1_hooks.install_pypdf)]) ("v1.8.2", [("Install pypdf", v1_hooks.install_pypdf)]),
("v1.10.3", [("Install unidecode", v1_hooks.install_unidecode)]),
] ]