Compare commits

...

2 Commits

Author SHA1 Message Date
5abbf367ab Integrate htmx and dropzone for file Section 2026-01-13 01:53:13 +01:00
0e79fdcb36 Correction of spelling mistakes 2026-01-11 23:14:42 +01:00
24 changed files with 296 additions and 245 deletions

3
.gitignore vendored
View File

@@ -1,5 +1,5 @@
*__pycache__/
base.db
*.db
log.txt
config.py
users/
@@ -8,3 +8,4 @@ sys
*#*
.*
base.db.new
public/*

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
static/vendors/dropzone/dropzone.css vendored Normal file
View File

@@ -0,0 +1 @@
@keyframes passing-through{0%{opacity:0;transform:translateY(40px)}30%,70%{opacity:1;transform:translateY(0px)}100%{opacity:0;transform:translateY(-40px)}}@keyframes slide-in{0%{opacity:0;transform:translateY(40px)}30%{opacity:1;transform:translateY(0px)}}@keyframes pulse{0%{transform:scale(1)}10%{transform:scale(1.1)}20%{transform:scale(1)}}.dropzone,.dropzone *{box-sizing:border-box}.dropzone{min-height:150px;border:1px solid rgba(0,0,0,.8);border-radius:5px;padding:20px 20px}.dropzone.dz-clickable{cursor:pointer}.dropzone.dz-clickable *{cursor:default}.dropzone.dz-clickable .dz-message,.dropzone.dz-clickable .dz-message *{cursor:pointer}.dropzone.dz-started .dz-message{display:none}.dropzone.dz-drag-hover{border-style:solid}.dropzone.dz-drag-hover .dz-message{opacity:.5}.dropzone .dz-message{text-align:center;margin:3em 0}.dropzone .dz-message .dz-button{background:none;color:inherit;border:none;padding:0;font:inherit;cursor:pointer;outline:inherit}.dropzone .dz-preview{position:relative;display:inline-block;vertical-align:top;margin:16px;min-height:100px}.dropzone .dz-preview:hover{z-index:1000}.dropzone .dz-preview:hover .dz-details{opacity:1}.dropzone .dz-preview.dz-file-preview .dz-image{border-radius:20px;background:#999;background:linear-gradient(to bottom, #eee, #ddd)}.dropzone .dz-preview.dz-file-preview .dz-details{opacity:1}.dropzone .dz-preview.dz-image-preview{background:#fff}.dropzone .dz-preview.dz-image-preview .dz-details{transition:opacity .2s linear}.dropzone .dz-preview .dz-remove{font-size:14px;text-align:center;display:block;cursor:pointer;border:none}.dropzone .dz-preview .dz-remove:hover{text-decoration:underline}.dropzone .dz-preview:hover .dz-details{opacity:1}.dropzone .dz-preview .dz-details{z-index:20;position:absolute;top:0;left:0;opacity:0;font-size:13px;min-width:100%;max-width:100%;padding:2em 1em;text-align:center;color:rgba(0,0,0,.9);line-height:150%}.dropzone .dz-preview .dz-details .dz-size{margin-bottom:1em;font-size:16px}.dropzone .dz-preview .dz-details .dz-filename{white-space:nowrap}.dropzone .dz-preview .dz-details .dz-filename:hover span{border:1px solid rgba(200,200,200,.8);background-color:hsla(0,0%,100%,.8)}.dropzone .dz-preview .dz-details .dz-filename:not(:hover){overflow:hidden;text-overflow:ellipsis}.dropzone .dz-preview .dz-details .dz-filename:not(:hover) span{border:1px solid rgba(0,0,0,0)}.dropzone .dz-preview .dz-details .dz-filename span,.dropzone .dz-preview .dz-details .dz-size span{background-color:hsla(0,0%,100%,.4);padding:0 .4em;border-radius:3px}.dropzone .dz-preview:hover .dz-image img{transform:scale(1.05, 1.05);filter:blur(8px)}.dropzone .dz-preview .dz-image{border-radius:20px;overflow:hidden;width:120px;height:120px;position:relative;display:block;z-index:10}.dropzone .dz-preview .dz-image img{display:block}.dropzone .dz-preview.dz-success .dz-success-mark{animation:passing-through 3s cubic-bezier(0.77, 0, 0.175, 1)}.dropzone .dz-preview.dz-error .dz-error-mark{opacity:1;animation:slide-in 3s cubic-bezier(0.77, 0, 0.175, 1)}.dropzone .dz-preview .dz-success-mark,.dropzone .dz-preview .dz-error-mark{pointer-events:none;opacity:0;z-index:500;position:absolute;display:block;top:50%;left:50%;margin-left:-27px;margin-top:-27px;background:rgba(0,0,0,.8);border-radius:50%}.dropzone .dz-preview .dz-success-mark svg,.dropzone .dz-preview .dz-error-mark svg{display:block;width:54px;height:54px;fill:#fff}.dropzone .dz-preview.dz-processing .dz-progress{opacity:1;transition:all .2s linear}.dropzone .dz-preview.dz-complete .dz-progress{opacity:0;transition:opacity .4s ease-in}.dropzone .dz-preview:not(.dz-processing) .dz-progress{animation:pulse 6s ease infinite}.dropzone .dz-preview .dz-progress{opacity:1;z-index:1000;pointer-events:none;position:absolute;height:20px;top:50%;margin-top:-10px;left:15%;right:15%;border:3px solid rgba(0,0,0,.8);background:rgba(0,0,0,.8);border-radius:10px;overflow:hidden}.dropzone .dz-preview .dz-progress .dz-upload{background:#fff;display:block;position:relative;height:100%;width:0;transition:width 300ms ease-in-out;border-radius:17px}.dropzone .dz-preview.dz-error .dz-error-message{display:block}.dropzone .dz-preview.dz-error:hover .dz-error-message{opacity:1;pointer-events:auto}.dropzone .dz-preview .dz-error-message{pointer-events:none;z-index:1000;position:absolute;display:block;display:none;opacity:0;transition:opacity .3s ease;border-radius:8px;font-size:13px;top:130px;left:-10px;width:140px;background:#b10606;padding:.5em 1em;color:#fff}.dropzone .dz-preview .dz-error-message:after{content:"";position:absolute;top:-6px;left:64px;width:0;height:0;border-left:6px solid rgba(0,0,0,0);border-right:6px solid rgba(0,0,0,0);border-bottom:6px solid #b10606}/*# sourceMappingURL=dropzone.css.map */

1
static/vendors/htmx/htmx.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,26 @@
<script>
window.onload = function () {
var dropzoneOptions = {
dictDefaultMessage: 'Déposez vos fichiers ici!',
paramName: "file",
maxFilesize: 1024, // MB
url: "/upload-dropzone",
chunking: true,
forceChunking: true,
chunkSize: 1000000,
autoProcessQueue: true,
init: function () {
this.on("success", function (file) {
console.log("success > " + file.name);
setTimeout(() => { this.removeFile(file); }, 2000);
});
}
};
var uploader = document.querySelector('#uploader');
var myDropzone = new Dropzone(uploader, dropzoneOptions);
console.log("Loaded");
};
</script>

4
templates/_js_htmx.html Normal file
View File

@@ -0,0 +1,4 @@
<script src="{{ url_for('static', filename='vendors/htmx/htmx.min.js') }}"> </script>

View File

@@ -13,7 +13,7 @@
<li>
<a href="/myblog/new-article/" {% if request.path == "/myblog/new-article/" %} class="invert" {% endif %} > <span class="icons new-article-blog"></span>
Ecrire un billet </a>
Écrire un billet </a>
</li>
<li>
<a href="/myblog/list-articles/" {% if request.path == "/myblog/list-articles/" %} class="invert" {% endif %} ><span class="icons list-articles-blog"></span>
@@ -33,7 +33,7 @@
</li>
<li>
<a href="/private-blog/" {% if request.path == "/private-blog/" %} class="invert" {% endif %} ><span class="icons view-blog" aria-hidden="true"></span>
Voir le blog général
Blog du serveur
</a>
</li>
@@ -48,7 +48,7 @@
<a href="/view/" {% if request.path == "/view/" %} class="invert" {% endif %}> <span class="icons myfiles"></span> Mon dossier personnel </a>
</li>
<li>
<a href="/gallery/" {% if request.path == "/gallery/" %} class="invert" {% endif %} > <span class="icons mygallery"></span> Ma gallerie d'images </a>
<a href="/gallery/" {% if request.path == "/gallery/" %} class="invert" {% endif %} > <span class="icons mygallery"></span> Ma galerie d'images </a>
</li>
@@ -69,7 +69,7 @@
</li>
<li>
<a href="/mymailbox/alias" {% if request.path == "/mymailbox/alias" %} class="invert" {% endif %} ><span class="icons myalias"></span>
Gerer mes alias
Gérer mes alias
</a>
</li>
</ul>

View File

@@ -0,0 +1,3 @@
<script src="{{ url_for('static', filename='vendors/dropzone/dropzone-min.js') }}"></script>
<link href="{{ url_for('static', filename='vendors/dropzone/dropzone.css') }}" rel="stylesheet">

View File

@@ -4,7 +4,7 @@
<h1> Supprimer mon compte </h1>
<p class="center"> Vous voulez supprimer votre compte pas de problèmes toutes vos données seront effacées du serveur et après un periodes de {{ time_backup }} vos données seront complètements supprimées des sauvegardes, il n'y aura aucun retour en arrière possible.
<p class="center"> Vous voulez supprimer votre compte pas de problème, toutes vos données seront effacées du serveur et après un periodes de {{ time_backup }} vos données seront complètements supprimées des sauvegardes, il n'y aura aucun retour en arrière possible.
</p>
<h3> Entrez votre mot de passe pour confirmer la suppression de votre compte </h3>

View File

@@ -1,5 +1,9 @@
{% extends 'up_squelette.html' %}
{% block css %}
{% include 'css/simple_editor.html' %}
{% endblock %}
{% block main %}

View File

@@ -1,89 +1,50 @@
{% extends 'up_squelette.html' %}
{% block css %}
{% include 'css/dropzone.html' %}
{% endblock %}
{% block main %}
<p>Quand tu envoies des images, elles se retrouveront directement dans la <a href="/gallery/"> Gallerie</a>. </p>
<p>Ayez bien conscience que ce site est une expérience est qu'il est indispensable d'avoir
une sauvegarde de tous les fichiers qui vous mettrez ici. Nous ne pourrons, en aucun cas, être tenu responsable de la perte de vos
données. Merci de votre compréhension.
<p>Quand vous envoyez des images, elles se retrouveront directement dans <a href="/gallery/"> votre Galerie</a>. </p>
<p>
Ayez bien conscience que ce site est une expérience et qu'il est indispensable d'avoir
une sauvegarde de tous les fichiers qui vous mettrez ici. Nous ne pourrons, en aucun cas, être tenu responsable de la
perte de vos données. Merci de votre compréhension.
</p>
<br />
<h3>Choisissez un ou plusieurs fichiers à téléverser </h3>
<form action="{{ url_for('filesupload.upload') }}" method="post" enctype="multipart/form-data">
<input type="file" class="center" name="fic"id="fic" multiple>
<br>
<button type="submit" id="tada" class="btn btn btn-success"> Téléverser !</button>
</form>
<form id="uploader" methods="POST" class="dropzone dz-clickable"></form>
<h2> Fichiers privés </h2>
<h4> Vous pouvez partager des liens de vos fichiers avec les autres membres de ce serveur uniquement. Si vous partager un lien avec une personnes non inscrite elle ne pourra pas y avoir accès <h4>
{% if listFilesPrivate %}
<table class="table">
<thead>
<tr>
<th></th>
<th>Fichier(s) <span class="badge">{{ nb_pv }}</span></th>
<th>Taille (en Megaoctect)</th>
<th></th>
</tr>
</thead>
<tbody>
{% for file in listFilesPrivate %}
<tr>
<td>{{ file[0] }}</td>
<td><a href="/myfiles/{{ username }}/{{ file[1] }}">{{ file[1] }}</a></td>
<td>{{ file[2] }}</td>
<td><a href="{{ url_for('filesupload.remove_privateFile', filename=file[1]) }}"><button type="button" class="">Supprimer</button></a>
<a href="{{ url_for('filesupload.move_public', filename=file[1]) }}"><button type="button" class=""> Rendre public </button></a>
<button onclick="navigator.clipboard.writeText('/myfiles/{{ username }}/{{file[1]}}');" > Copier le lien </button></td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p> Vous n'avez aucun fichiers privés </p>
{% endif %}
<h5>
Vous pouvez partager des liens de vos fichiers avec les autres membres de ce serveur uniquement. Si vous partagez un lien avec une personne non inscrite elle ne pourra pas y avoir accès
</h5>
<div hx-get="/files/private/" hx-trigger="load, every 10s">
Chargement ...
</div>
<br />
<hr />
<br />
<h2> Fichiers publics </h2>
<h4> Vous pouvez partager les liens de ces fichiers avec n'importe qui sur Internet ils y auront accès <h4>
{% if listFilesPublic %}
<table class="table">
<thead>
<tr>
<th></th>
<th>Fichier(s) <span class="badge">{{ nb_pu }}</span></th>
<th>Taille (en Megaoctets)</th>
<th></th>
</tr>
</thead>
<tbody>
{% for file in listFilesPublic %}
<tr>
<td>{{ file[0] }}</td>
<td><a href="/public/{{ username }}/{{ file[1] }}">{{ file[1] }}</a></td>
<td>{{ file[2] }}</td>
<td><a href="{{ url_for('filesupload.remove_publicFile', filename=file[1]) }}"><button type="button" class="">Supprimer</button></a>
<a href="{{ url_for('filesupload.move_private', filename=file[1]) }}"><button type="button" class=""> Rendre Privée </button></a>
<button type="button" onclick="navigator.clipboard.writeText('{{BASE_URL}}{{ url_for('filesupload.publicfiles', username=username, filename=file[1]) }}');" class=""> Copier le lien </button></a></td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p> Vous n'avez aucun fichiers publics </p>
{% endif %}
<h5> Vous pouvez partager les liens de ces fichiers avec n'importe qui sur Internet ils y auront accès </h5>
<div hx-get="/files/public/" hx-trigger="load, every 10s">
Chargement ...
</div>
{% endblock %}
{% block js %}
{% include '_js_dropzone.html' %}
{% include '_js_htmx.html' %}
{% endblock %}

View File

@@ -27,7 +27,7 @@
{% else %}
<h2> Il n'y a aucunes images dans votre gallerie </h2>
<h2> Il n'y a aucune image dans votre galerie </h2>
{% endif %}

View File

@@ -6,10 +6,10 @@
<p>
Si vous voulez vous pouvez inviter une personne à se crée un compte sur ce serveur
pour cela vous devez crée un lien d'inscription. Ce lien restera valable tant que vous ne créez pas un autre lien.
Si vous voulez vous pouvez inviter une personne à se créer un compte sur ce serveur.
Pour cela vous devez créer un lien d'inscription. Ce lien restera valable tant que vous ne créez pas un autre lien.
Les invitations sont limité à 20 personnes pour ne pas surcharger notre petit serveur :).
Une fois que la personne s'est inscrite votre nombre d'invitations sera mis à jour
Une fois que la personne s'est inscrite votre nombre d'invitations sera mis à jour.
</p>

54
templates/list_files.html Normal file
View File

@@ -0,0 +1,54 @@
{% if listFiles %}
<table class="table">
<thead>
<tr>
<th></th>
<th>Fichier(s) <span class="badge">{{ nb_files }}</span></th>
<th>Taille (en Megaoctect)</th>
<th></th>
</tr>
</thead>
<tbody>
{% for file in listFiles %}
<tr>
<td>{{ file[0] }}</td>
<td><a href="/myfiles/{{ username }}/{{ file[1] }}">{{ file[1] }}</a></td>
<td>{{ file[2] }}</td>
<td>
{% if status == "public" %}
<td>
<a href="{{ url_for('filesupload.remove_publicFile', filename=file[1]) }}">
<button type="button" title="Supprimer"> <span class="icons delete"></span></button>
</a>
<a href="{{ url_for('filesupload.move_private', filename=file[1]) }}">
<button type="button" title="Passer ce fichier en status privé"><span class="icons share"></span></button>
</a>
<button type="button" onclick="navigator.clipboard.writeText('{{BASE_URL}}{{ url_for('filesupload.publicfiles', username=username, filename=file[1]) }}');" title="Copier le lien du fichier">
<span class="icons copy-link"> </span>
</button>
</td>
{% else %}
<td>
<a href="{{ url_for('filesupload.remove_privateFile', filename=file[1]) }}">
<button type="button" title="Supprimer"> <span class="icons delete"></span></button>
</a>
<a href="{{ url_for('filesupload.move_public', filename=file[1]) }}">
<button type="button" title="Passer ce fichier en status public"><span class="icons share"></span></button>
</a>
<button type="button" onclick="navigator.clipboard.writeText('{{BASE_URL}}{{ url_for('filesupload.publicfiles', username=username, filename=file[1]) }}');" title="Copier le lien du fichier">
<span class="icons copy-link"> </span>
</button>
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<h5> Aucun fichier ici </h5>
{% endif %}

View File

@@ -6,18 +6,18 @@
{% if MAIL_SERVER %}
<h3> Mes identités : A quoi ca sert les alias ? </h3>
<p> Les alias d'e-mail c'est utile quand vous ne voulez pas donner votre vrai addresse e-mail.
<h3> Mes identités : A quoi ça sert les alias ? </h3>
<p> Les alias d'e-mail, c'est utile quand vous ne voulez pas donner votre vrai adresse e-mail.
Vous pouvez creer une adresse que vous pouvez supprimer rapidemment, cela permet de creer une adresse pour un destinataire particulier
si vous n'avez pas confiance en lui par exemple ou de trier plus facilement les e-mails venant de ce destinataire.
</p>
<p> Vous n'avez pas besoin de configurer un autre compte mail sur vos applications mail, tous les e-mails
arriveront sur votre adresse e-mail principale déjà configuré. Faîtes attention de bien répondre avec votre mail d'alias cependant !
arriveront sur votre adresse e-mail principale déjà configurée. Faîtes attention de bien répondre avec votre mail d'alias cependant !
</p>
<table class="table">
<thead>
<tr>
<th>Mes identités <span class="badge">{{ i }}</span></th>
<th>Mes identités <span class="badge">{{ i }}</span></th>
<th></th>
</tr>
</thead>

View File

@@ -1,86 +0,0 @@
{% extends 'up_squelette.html' %}
{% block main %}
<p>Quand tu envoies des images, elles se retrouveront directement dans la <a href="/gallery/"> Gallerie</a>. </p>
<p>Ayez bien conscience que ce site est une expérience est qu'il est indispensable d'avoir
une sauvegarde de tous les fichiers qui vous mettrez ici. Nous ne pourrons, en aucun cas, être tenu responsable de la perte de vos
données. Merci de votre compréhension.
</p>
<br />
<h3>Choisissez un ou plusieurs fichiers à téléverser </h3>
<form action="" method="post" enctype="multipart/form-data">
<input type="file" class="center" name="fic"id="fic" multiple>
<br>
<button type="submit" id="tada" class="btn btn btn-success"> Téléverser !</button>
</form>
<h2> Fichiers privés (Seul les personnes connectées et l'administrateur de l'ordinateur peuvent les voirs) </h2>
{% if listFilesPrivate %}
<table class="table">
<thead>
<tr>
<th></th>
<th>Fichier(s) <span class="badge">{{ nb_pv }}</span></th>
<th>Taille (en Megaoctect)</th>
<th></th>
</tr>
</thead>
<tbody>
{% for file in listFilesPrivate %}
<tr>
<td>{{ file[0] }}</td>
<td><a href="/myfiles/{{ username }}/{{ file[1] }}">{{ file[1] }}</a></td>
<td>{{ file[2] }}</td>
<td><a href="{{ url_for('filesupload.remove_privateFile', filename=file[1]) }}"><button type="button" class="btn btn-sm btn-danger">Supprimer</button></a></td>
<td><a href="{{ url_for('filesupload.move_public', filename=file[1]) }}"><button type="button" class="btn btn-sm btn-success"> Rendre Publique </button></a></td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p> Vous n'avez aucun fichiers privés </p>
{% endif %}
<br />
<hr />
<br />
<h2> Fichiers publics (Tout le monde peut les voirs) </h2>
{% if listFilesPublic %}
<table class="table">
<thead>
<tr>
<th></th>
<th>Fichier(s) <span class="badge">{{ nb_pu }}</span></th>
<th>Taille (en Megaoctets)</th>
<th></th>
</tr>
</thead>
<tbody>
{% for file in listFilesPublic %}
<tr>
<td>{{ file[0] }}</td>
<td><a href="/public/{{ username }}/{{ file[1] }}">{{ file[1] }}</a></td>
<td>{{ file[2] }}</td>
<td><a href="{{ url_for('filesupload.remove_publicFile', filename=file[1]) }}"><button type="button" class="btn btn-sm btn-danger">Supprimer</button></a></td>
<td><a href="{{ url_for('filesupload.move_private', filename=file[1]) }}"><button type="button" class="btn btn-sm btn-success"> Rendre Privée </button></a></td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p> Vous n'avez aucun fichiers publics </p>
{% endif %}
{% endblock %}

View File

@@ -19,7 +19,7 @@
<h4> Votre compte est : {{ myemail }} </h4>
<p>
Si vous êtes sur cette page, c'est que vous diposez d'un compte de messaegerie sur le serveur.
Si vous êtes sur cette page, c'est que vous disposez d'un compte de messaegerie sur ce serveur.
Vous pouvez utiliser votre compte mail avec un client mail et votre compte XMPP avec un client XMPP.
</p>
@@ -33,19 +33,21 @@
{% if mail_webservice %}
<p>
Vous pouvez aussi y accéder avec votre navigateur web actuel en utilisant le webmail disponible à l'adresse <a href="{{ mail_webservice }}"> {{ mail_webservice }} </a>.
Vous pouvez aussi accéder à vos mails avec votre navigateur web actuel en utilisant le webmail disponible à l'adresse <a href="{{ mail_webservice }}"> {{ mail_webservice }} </a>.
</p>
{% endif %}
{% if xmpp_webservice %}
<p>
Vous pouvez aussi y accéder avec votre Navgateur web actuel en utilisant le webmail disponible à l'adresse <a href="{{ xmpp_webservice }}"> {{ xmpp_webservice }} </a>.
Vous pouvez aussi accéder à votre messagerie instantanné avec votre Navgateur web actuel en utilisant le tchat disponible à l'adresse <a href="{{ xmpp_webservice }}"> {{ xmpp_webservice }} </a>.
</p>
{% endif %}
{% if xmpp_server %}
<h3> Vos informations pour configurer vos client Mail </h3>
<h3> Vos informations pour configurer vos clients XMPP </h3>
<p> Si vous voulez configurer votre compte XMPP dans votre, voici les informations à rentrer dans votre client XMPP </p>
<ul>
@@ -58,7 +60,7 @@
{% if mail_server %}
<h3> Vos informations pour configurer vos client Mail </h3>
<h3> Vos informations pour configurer vos clients Mail </h3>
<p> Si vous voulez configurer votre adresse e-mail, voici les informations à rentrer dans votre client mail </p>
<h4>Courrier entrant : </h4>
<ul>
@@ -82,8 +84,6 @@
{% endif %}
{% endif %}
{% endblock %}

View File

@@ -20,7 +20,7 @@
<p> Voici quelques exemple de générateur de mot de passe à usage unique (OTP en anglais) pour <a href="https://getaegis.app/">Android</a>, <a href="https://apps.apple.com/us/app/totp-authenticator-fast-2fa/id1404230533">iOS</a> et <a href="https://keepassxc.org/">Linux/BSD et windows</a>
<p> Afin que le serveur et votre application génère le même code en même temps; il vous faut valider la clef sécrete partager par votre application et le serveur. Pour cela, si ce n'est pas déjà fait, scannez ou entrez la clef secrète dans votre application. Pour finir, cliquez sur valider la clef en entrant le code générer par votre application. Si le test réussi parfait c'est configuré ! </p>
<p> Afin que le serveur et votre application génèrent le même code en même temps; il vous faut valider la clef sécrete partager par votre application et le serveur. Pour cela, si ce n'est pas déjà fait, scannez ou entrez la clef secrète dans votre application. Pour finir, cliquez sur valider la clef en entrant le code généré par votre application. Si le test réussi parfait c'est configuré ! </p>
<p> Pour changer votre clef secrète vous devez d'abord supprimer votre clef actuelle pour en regénérer une nouvelle. </p>
@@ -32,7 +32,7 @@
<p class="success"> Votre clef secrète est valide et activé </p>
{% else %}
<p class="alert"> Votre clef secrète n'est pas validé et donc non active </p>
<p class="alert"> Votre clef secrète n'est pas validée et donc non active </p>
{% endif %}
<form method="POST" action="{{ url_for('profil.set_totp') }}" >

View File

@@ -6,7 +6,7 @@
<p> Hello <span id="majuscule">{{ session['username'] }} ! </span>
Bienvenue sur la création d'un nouvel article de blog. Vous pouvez créer ou importer un article de blog ici,
vous avez le choix de le rendre publique dès sa création en cochant publique ou le laisser en privé
vous avez le choix de le rendre public dès sa création en cochant public ou le laisser en privé
si vous souhaitez le modifier plus-tard avant sa publication.
Par défaut il est laissé en privé pour éviter les publications
accidentelles.

View File

@@ -2,6 +2,7 @@
<html lang="fr">
<head>
{% include '_head.html' %}
{% block css %} {% endblock %}
{% include 'css/simple_editor.html' %}
</head>

View File

@@ -39,6 +39,28 @@ def append_to_log(log_line, user):
log.close()
def valid_email(mail):
valid=True
# Caractères non autorisés dans la RFC #822
invalid_char = { '(', ')', '<', '>', ',', ';', ':', '"', '[', ']', '|', 'ç', '%', '&', ' ' }
mail_cut = mail.split('@')
tld = mail_cut[-1].split('.')
for character in invalid_char:
if character in mail:
valid=False
print(tld)
if len(mail_cut) > 1 and len(tld) > 1 and valid:
if len(tld[0]) < 1 or len(tld[-1]) < 2 :
valid=False
else:
valid=False
return valid
def valid_username(username):
valid=True
# Caractères non autorisés dans la RFC #822

View File

@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from flask import Blueprint, Flask, request, flash, render_template, url_for, session, redirect, abort, make_response, flash, abort, send_file, send_from_directory
from werkzeug.utils import secure_filename
from markupsafe import escape
from PIL import Image
@@ -16,15 +17,14 @@ filesupload = Blueprint('filesupload', __name__, template_folder='templates')
app = Flask( 'pywallter' )
app.config.from_pyfile('config.py')
#### Variables ##################################################################################
#### Variables ####################################################################################
DOSSIER_PERSO= app.config.get('DOSSIER_APP')
DOSSIER_PUBLIC= app.config.get('DOSSIER_PUBLIC')
DOSSIER_PERSO= app.config['DOSSIER_APP']+'/'
DOSSIER_PUBLIC= app.config['DOSSIER_PUBLIC']+'/'
extensionimg = app.config['EXT_IMG']
DATABASE = app.config['DATABASE']
BASE_URL= app.config['BASE_URL']
extensionimg = app.config.get('EXT_IMG')
DATABASE = app.config.get('DATABASE')
BASE_URL= app.config.get('BASE_URL')
##################################################################################################
@@ -46,19 +46,20 @@ def upload():
files = request.files.getlist('fic')
for f in files :
nom = secure_filename(f.filename)
check_and_create(DOSSIER_PERSO+ user + '/files')
check_and_create(DOSSIER_PERSO+ user + '/images')
if os.path.isfile(DOSSIER_PERSO + user + '/files/' + nom) or os.path.isfile(DOSSIER_PERSO + user + '/images/' + nom):
check_and_create(os.path.join(DOSSIER_PERSO, user, 'files'))
check_and_create(os.path.join(DOSSIER_PERSO, user, 'images'))
if os.path.isfile(os.path.join(DOSSIER_PERSO,user, 'files', nom) or
os.path.isfile(DOSSIER_PERSO, user, 'images', nom)):
alert = "Le fichier "+str(f.filename)+" avec le même nom existe déjà, merci de spécifier un autre nom de fichier \n"
flash(alert, 'error')
else:
file, ext = os.path.splitext(nom)
if ext in extensionimg :
f.save(DOSSIER_PERSO + user + '/images/' + nom)
image = DOSSIER_PERSO + user + '/images/' + nom
f.save(os.path.join(DOSSIER_PERSO, user, 'images', nom))
image = os.path.join(DOSSIER_PERSO, user, 'images', nom)
with Image.open(image) as img :
img.thumbnail((300,300))
img.save( DOSSIER_PERSO + user + '/images/thumbnails/' + nom )
img.save(os.path.join(DOSSIER_PERSO, user, 'images','thumbnails', nom ))
time_img_create=time.strftime("%A %d %B %Y %H:%M:%S")
IP=request.environ['REMOTE_ADDR']
client_platform=request.headers.get('User-Agent')
@@ -68,9 +69,9 @@ def upload():
log.close()
else:
f.save(DOSSIER_PERSO + user + '/files/' + nom)
f.save(os.path.join(DOSSIER_PERSO, user, 'files', nom))
time_file_upload=time.strftime("%A %d %B %Y %H:%M:%S")
IP=request.environ['REMOTE_ADDR']
IP=request.environ.get('REMOTE_ADDR')
client_platform=request.headers.get('User-Agent')
log=open("log.txt", "a") # Ouvre fichier log.txt
log.write (time_file_upload + ' - ' + IP + ' - ' + user + ' - ' + client_platform + '\n' + '---> ' + nom + '\n') # Écrit dans log
@@ -80,43 +81,101 @@ def upload():
return redirect(url_for('filesupload.list'))
@filesupload.route( '/upload-dropzone', methods=['POST'])
@login_required
def drop_upload():
user = '%s'% escape(session['username'])
file = request.files['file']
check_and_create(os.path.join(DOSSIER_PERSO, user, 'files'))
check_and_create(os.path.join(DOSSIER_PERSO, user, 'images' ))
filename = secure_filename(file.filename)
ext = os.path.splitext(filename)
is_image = False
print("nom du fichier :" +filename)
if ext in extensionimg :
save_path = os.path.join(DOSSIER_PERSO, user, 'images', filename )
is_image = True
else:
save_path = os.path.join(DOSSIER_PERSO, user, 'files', filename )
@filesupload.route('/view/')
current_chunk = int(request.form['dzchunkindex'])
print (current_chunk)
if (os.path.isfile(save_path) or os.path.isfile( os.path.join(DOSSIER_PERSO, user, 'images', filename ))) and current_chunk == 0:
return make_response(('Un fichier avec le même nom existe déjà', 400))
try:
with open(save_path, 'ab') as f:
f.seek(int(request.form['dzchunkbyteoffset']))
f.write(file.stream.read())
except OSError:
return make_response(("Une erreur est survenue,"
" Impossible d'écrire le fichier sur le disque", 500))
total_chunks = int(request.form['dztotalchunkcount'])
if current_chunk + 1 == total_chunks:
# This was the last chunk, the file should be complete and the size we expect
if os.path.getsize(save_path) != int(request.form['dztotalfilesize']):
return make_response(('La taille du fichier source est différentes', 500))
else:
time_file_upload=time.strftime("%A %d %B %Y %H:%M:%S")
IP=request.environ['REMOTE_ADDR']
client_platform=request.headers.get('User-Agent')
log=open("log.txt", "a") # Ouvre fichier log.txt
log.write (time_file_upload + ' - ' + IP + ' - ' + user + ' - ' + client_platform + '\n' + '---> ' + filename + '\n') # Écrit dans log
log.close() # Ferme log.txt
if is_image :
with Image.open(save_path) as img :
img.thumbnail((300,300))
img.save(os.path.join(DOSSIER_PERSO, user, 'images', 'thumbnails', filename ) )
return make_response(('Chunk upload succesfull', 200))
@filesupload.route('/view/', methods=['GET'])
@login_required
def list():
user = '%s'% escape(session['username'])
check_and_create(DOSSIER_PUBLIC + user + '/files/')
check_and_create(DOSSIER_PERSO + user + '/files/')
files_public = os.listdir(DOSSIER_PUBLIC + user + '/files/')
files_private = os.listdir(DOSSIER_PERSO + user + '/files/')
listFilesPublic = []
listFilesPrivate = []
nb_pv = 0
size=0
if files_private:
for fich in files_private:
nb_pv += 1
size = getFileSizeMo(DOSSIER_PERSO + user + '/files/' + fich) # size = taille des fichiers
listFilesPrivate.append([nb_pv, fich, size]) # On implémente la listeFichiers avec le num le ficier et sa taille
nb_pu = 0
if files_public:
for fich in files_public:
nb_pu += 1
size = getFileSizeMo(DOSSIER_PUBLIC + user + '/files/' + fich) # size = taille des fichiers
listFilesPublic.append([nb_pu, fich, size])
check_and_create(os.path.join(DOSSIER_PUBLIC, user, 'files'))
check_and_create(os.path.join(DOSSIER_PERSO, user, 'files'))
return render_template('files.html',
section="Files",
BASE_URL=BASE_URL,
username=user)
@filesupload.route('/files/<status>/', methods=['GET'])
@login_required
def list_files(status: str ):
user = '%s' % escape(session['username'])
listFiles = []
nb_files = 0
size=0
folder=""
if status == "public":
folder=DOSSIER_PUBLIC
else:
folder=DOSSIER_PERSO
files = os.listdir(os.path.join(folder, user, 'files'))
if files:
for fich in files:
nb_files += 1
size = getFileSizeMo(os.path.join(folder, user, 'files', fich)) # size = taille des fichiers
listFiles.append([nb_files, fich, size]) # On implémente la listeFichiers avec le num le ficier et sa taille
resp = "<h2> Bonjour " + user +" ça va bien putain ca marche ? </h2>"
return render_template('list_files.html',
BASE_URL=BASE_URL,
status=status,
size=size,
username=user,
nb_pv=nb_pv,
nb_pu=nb_pu,
listFilesPrivate=listFilesPrivate,
listFilesPublic=listFilesPublic)
nb_files=nb_files,
listFiles=listFiles)
@filesupload.route('/myfiles/<username>/<filename>')
@@ -130,11 +189,9 @@ def myfiles(username, filename):
@login_required
def move_public(filename):
user = '%s' % escape(session['username'])
check_and_create(DOSSIER_PUBLIC + user + '/files/')
check_and_create(DOSSIER_PERSO + user + '/files/')
src = os.path.join(DOSSIER_PERSO, user, 'files', filename)
dst = os.path.join(DOSSIER_PUBLIC, user, 'files/')
dst = os.path.join(DOSSIER_PUBLIC, user, 'files')
move (src, dst)
return redirect(url_for('filesupload.list', _external=True))
@@ -142,10 +199,8 @@ def move_public(filename):
@login_required
def move_private(filename):
user = '%s' % escape(session['username'])
check_and_create(DOSSIER_PUBLIC + user + '/files/')
check_and_create(DOSSIER_PERSO + user + '/files/')
src = os.path.join(DOSSIER_PUBLIC, user, 'files', filename)
dst = os.path.join(DOSSIER_PERSO, user, 'files/')
dst = os.path.join(DOSSIER_PERSO, user, 'files')
move (src, dst)
return redirect(url_for('filesupload.list', _external=True))
@@ -158,7 +213,7 @@ def remove_privateFile(filename):
user = '%s' % escape(session['username'])
filename = secure_filename(filename)
try:
os.remove(DOSSIER_PERSO + user + '/files/' + filename) # on le supprime
os.remove( os.path.join(DOSSIER_PERSO, user, 'files', filename)) # on le supprime
except FileNotFoundError:
flash(u'Fichier {filename} inexistant.'.format(filename=filename), 'error')
return redirect(url_for('filesupload.list', _external=True))
@@ -170,7 +225,7 @@ def remove_publicFile(filename):
user = '%s' % escape(session['username'])
filename = secure_filename(filename)
try:
os.remove(DOSSIER_PUBLIC + user + '/files/' + filename) # on le supprime
os.remove( os.path.join(DOSSIER_PUBLIC, user, 'files', filename)) # on le supprime
except FileNotFoundError:
flash(u'Fichier {filename} inexistant.'.format(filename=filename), 'error')
return redirect(url_for('filesupload.list', _external=True))
@@ -179,8 +234,8 @@ def remove_publicFile(filename):
@filesupload.route('/<author>/blog.css')
def blog_theme(author):
user = author
if os.path.isfile(DOSSIER_PERSO+ user +'/blog.css'):
return send_file(DOSSIER_PERSO+ user +'/blog.css', mimetype='text/css')
if os.path.isfile(os.path.join(DOSSIER_PERSO, user,'blog.css')):
return send_file(os.path.join(DOSSIER_PERSO, user, 'blog.css'), mimetype='text/css')
else:
return send_file("/static/blog.css", mimetype='text/css')
@@ -188,8 +243,8 @@ def blog_theme(author):
def theme():
if 'username' in session:
user = '%s' % escape(session['username'])
if os.path.isfile(DOSSIER_PERSO+ user +'/theme.min.css'):
return send_file(DOSSIER_PERSO+ user +'/theme.min.css', mimetype='text/css')
if os.path.isfile(os.path.join(DOSSIER_PERSO, user,'theme.min.css')):
return send_file(os.path.join(DOSSIER_PERSO, user,'theme.min.css'), mimetype='text/css')
return send_file("static/default.min.css", mimetype='text/css')

View File

@@ -9,7 +9,7 @@ import subprocess
from shutil import copy
from socket import gethostname
from flask_bcrypt import Bcrypt
from tools.utils import email_disp, append_to_log, gen_token, valid_passwd, valid_token_register, get_user_by_token, totp_is_valid, login_required
from tools.utils import email_disp, append_to_log, gen_token, valid_passwd, valid_token_register, get_user_by_token, totp_is_valid, login_required, valid_email
from pyotp import random_base32
from tools.filesutils import check_and_create
import qrcode
@@ -73,18 +73,20 @@ def profile() :
os.path.join(DOSSIER_PERSO, user ,'theme.min.css') )
if request.form['nom']:
profil_user['nom'] = request.form['nom']
profil_user['nom'] = str(request.form['nom'])
if request.form['prenom']:
profil_user['prenom'] = request.form['prenom']
profil_user['prenom'] = str(request.form['prenom'])
if request.form['age']:
profil_user['age'] = request.form['age']
if '@' in request.form['mail_rescue']:
if len(request.form['mail_rescue']) > 4:
profil_user['mail_rescue'] = request.form['mail_rescue']
profil_user['age'] = str(request.form['age'])
if request.form['mail_rescue'] :
new_mail_rescue = str(request.form['mail_rescue'])
if valid_email(new_mail_rescue):
profil_user['mail_rescue']=new_mail_rescue
else:
flash(u'Adresse de courriel invalide', 'error')
else:
flash(u'Adresse de courriel de secour invalide', 'error')
if f: # On vérifie qu'un fichier a bien été envoyé
nom = secure_filename(f.filename)
f.save(os.path.join(DOSSIER_PERSO, user, 'profile', nom))
@@ -98,7 +100,6 @@ def profile() :
cursor.execute("UPDATE users SET avatar=? WHERE name=?",
(filename, user))
conn.commit()
cursor = conn.cursor() # Création de l'objet "curseur"
conn.close()
flash(u'Image de profil mise à jour', 'success')
@@ -109,7 +110,7 @@ def profile() :
(profil_user['nom'], profil_user['prenom'], profil_user['age'], profil_user['mail_rescue'],
user))
conn.commit()
flash(u'Le profil a été mis à jour', 'succes')
flash(u'Le profil a été mis à jour', 'success')
@@ -195,7 +196,7 @@ def change_passwd() :
if not(account['totp']):
account['totp'] = random_base32()
img = qrcode.make('otpauth://totp/'+BASE_URL+'?secret='+account['totp'])
img.save(DOSSIER_PERSO + user + "/totp.png")
img.save(os.path.join(DOSSIER_PERSO, user, "totp.png"))
shared_key_validate = False
return render_template('mypassword.html',
@@ -302,7 +303,7 @@ def set_totp():
cursor.execute("""UPDATE users SET totp=? WHERE name=?""", (shared_key, user,))
conn.commit()
img = qrcode.make('otpauth://totp/'+BASE_URL+'?secret='+shared_key)
img.save(DOSSIER_PERSO + user + "/totp.png")
img.save(os.path.join(DOSSIER_PERSO, user, "totp.png"))
flash(u'Votre mot de passe à usage unique est configuré et actif.', 'success')
else:
flash(u'Le code de validation totp n\'est pas valide.', 'error')