Add lost password recovery

This commit is contained in:
kitoy 2025-05-12 16:37:30 +02:00
parent c91fdad70b
commit 15c0f4fd79
38 changed files with 1299 additions and 397 deletions

3
.gitignore vendored
View File

@ -3,4 +3,5 @@ base.db
log.txt log.txt
config.py config.py
users/ users/
sys
*~

View File

@ -9,7 +9,7 @@ from flask_bcrypt import Bcrypt
from os import system from os import system
from views.blog import postit from views.blog import blog
from views.filesupload import filesupload from views.filesupload import filesupload
from views.inscription import inscription from views.inscription import inscription
from views.profil import profil from views.profil import profil
@ -31,7 +31,7 @@ if init_dir():
print ("Le repertoire des utilisateurs a été créer") print ("Le repertoire des utilisateurs a été créer")
#### Variables #################################################################################### #### Variables Globales #########################################################################
DOSSIER_PERSO= app.config['DOSSIER_APP'] DOSSIER_PERSO= app.config['DOSSIER_APP']
@ -39,7 +39,7 @@ DOSSIER_PERSO= app.config['DOSSIER_APP']
extensionimg = app.config['EXT_IMG'] extensionimg = app.config['EXT_IMG']
MAIL_SERVER = app.config['MAIL_SERVER'] MAIL_SERVER = app.config['MAIL_SERVER']
XMPP_SERVER = app.config['XMPP_SERVER'] XMPP_SERVER = app.config['XMPP_SERVER']
################################################################################################## #################################################################################################
xmpp_server_not_installed = system('whereis prosodyctl') xmpp_server_not_installed = system('whereis prosodyctl')
mail_server_not_installed = system('whereis set_mail_alias') + system('whereis set_mail_passwd') + \ mail_server_not_installed = system('whereis set_mail_alias') + system('whereis set_mail_passwd') + \
@ -59,7 +59,7 @@ if MAIL_SERVER and mail_server_not_installed :
app.register_blueprint(inscription) app.register_blueprint(inscription)
app.register_blueprint(postit) app.register_blueprint(blog)
app.register_blueprint(filesupload) app.register_blueprint(filesupload)
app.register_blueprint(profil) app.register_blueprint(profil)
app.register_blueprint(logs) app.register_blueprint(logs)
@ -113,4 +113,4 @@ def create_app():
return app return app
if __name__ == '__main__' : if __name__ == '__main__' :
app.run(host='127.0.0.1', port=8000, debug=False) app.run(host='127.0.0.1', port=8000, debug=True)

206
static/blog.css Normal file
View File

@ -0,0 +1,206 @@
/* kitoy <kitoy__at__kitoy.me> */
:root
{
--color-text: #fdfdfddd;
--color-background: #202020;
}
html{
height: 100%;
width: 80%;
margin-left: 10%;
}
body {
color: var(--color-text);
background-color: var(--color-background);
}
hr {
color: var(--color-text);
}
* {-moz-box-sizing: border-box; box-sizing: border-box;}
a {
color: var(--color-text);
text-decoration: none;
}
.date {
margin-bottom: 0;
}
.slug {
//margin-left: 1rem;
text-align: left;
margin-bottom: 2rem;
}
.readmore {
text-align: right;
}
.readmore a {
color: var(--color-text);
text-decoration: underline;
}
.copyleft {
display:inline-block;
transform: rotate(180deg);
padding-bottom: -15px;
}
pre {
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}
ul {
list-style-type: none;
margin: 0;
padding: 0;
}
.articles {
max-width: 80%;
margin-left: auto;
margin-right: auto;
margin-top: 5rem;
margin-bottom: 3rem;
padding:0.5em;
border: 7px double;
border-color: var(--text-color);
border-radius: 10px 10px 10px 10px;
line-height: 1.5;
letter-spacing: 0.1vw;
text-align: justify;
}
.articles ul {
list-style-type: disc;
margin: 5vw;
padding-top: 1vw;
padding-bottom: 1vw;
padding-left: 1.5vw;
}
.titre {
text-align: center;
}
.index {
text-align: left;
}
.articles img {
display: block;
margin-left: auto;
margin-right: auto;
width: 50%;
margin-bottom: 2vw;
}
.articles .description {
font-weight: 300;
font-style: italic;
font-size: 1.5vw;
padding-bottom: 30px;
color: ;
}
.pagination a {
border: 1px solid;
border-color: $color_title;
padding: 3px;
font-size: 13px;
}
.center {
margin: auto;
width: 50%;
padding: 10px;
}
.contact{
text-align:center;
}
footer {
position: relative;
bottom:0px;
width: 100%;
text-align:center;
}
@media only screen and (max-width: 980px)
{
.articles .description {
font-size: 4vw;
}
.articles {
margin-left: 0;
position: relative;
font-size: 3.5vw;
}
footer {
font-size: 3vw;
}
.articles img {
display: block;
margin-left: auto;
margin-right: auto;
width: 80%;
}
}
@media only screen and (max-width: 980px){
html{
height: 100%;
width: 100%;
margin: 0;
}
}
@media only screen and (max-width: 768px)
{
.h1 {
font-size: 5vw;
}
.icons img {
width: 7vw;
height: 7vw;
margin: 20px;
display:inline-block;
align-items:center;
}
}

View File

@ -40,6 +40,10 @@ body {
text-shadow: 0 1px 3px rgba(0,0,0,.5); text-shadow: 0 1px 3px rgba(0,0,0,.5);
} }
.container {
margin-bottom: 5vw;
}
a { a {
color: #428bca; color: #428bca;
@ -158,6 +162,17 @@ a:focus, a:hover {
border-bottom-color: #fff; border-bottom-color: #fff;
} }
/* Footer */
footer {
position: bottom;
width: 100%;
bottom: 0;
text-align: center;
}
@media (min-width: 768px) { @media (min-width: 768px) {
.masthead-brand { .masthead-brand {
float: left; float: left;
@ -332,3 +347,4 @@ a:focus, a:hover {
.post-it h3 { .post-it h3 {
font-size: 0.9vw; font-size: 0.9vw;
} }

View File

@ -27,3 +27,7 @@ a:focus, a:hover {
text-decoration: underline; text-decoration: underline;
} }
footer {
margin-top: 3em;
}

View File

@ -48,13 +48,13 @@
} }
.succes p { .succes p {
background-color: #CDCBD0; background-color: #444;
color: #00A310; color: #00C613;
} }
.error p { .error p {
background-color: #CDCBD0; background-color: #444;
color: #B80000; color: #FF4A4A;
} }
#majuscule { #majuscule {

5
templates/#_footer.html# Normal file
View File

@ -0,0 +1,5 @@
<footer>
<
<p> Réalisé avec Flask et un thème bootstrap @mdo </p>
</footer>

View File

@ -0,0 +1,39 @@
{% extends 'up_squelette.html' %}
{% block main %}
<div class="col-sm-1"></div>
<div class="col-sm-10">
<br />
<div class="well"> 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é
si vous souhaitez le modifier plus-tard avant sa publication.
Par défaut il est laissé en privé pour éviter les publications
accidentelles.
</div>
</div>
<br />
<form method="POST" action="{{ url_for('blog.new_article') }}" id="postform">
<!--<input type="text" name="category" id="category" placeholder="Catégorie" class="form-control"><br />-->
<input type="text" name="title" id="title" placeholder="Titre" class="form-control"><br />
<input type="text" name="subtitle" id="subtitle" placeholder="Sous-titre" class="form-control"><br />
<hr>
<textarea id="editeurMarkdown" class="form-control" form="postform" name="content" id="content" placeholder="Contenu" style="height:10vw;"></textarea><br />
<div class="row">
<div class="col-sm-4"></div>
<div class="col-sm-1"><input type="radio" name="status" value="privé" checked> Privé </div>
<div class="col-sm-2"></div>
<div class="col-sm-1"><input type="radio" name="status" value="publique">Public<br></div>
<div class="col-sm-4"></div>
</div>
<br />
<button id="tada" class="btn btn-default btn-primary" type="submit"> Créer l'article </button>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,15 @@
<div class="msginfo">
{# on affiche les messages d'erreur puis les messages de succes #}
{% for categorie in ['error', 'succes'] %}
{% with msgs = get_flashed_messages(category_filter=[categorie]) %}
{% if msgs %}
<div class="flashed {{ categorie }}">
{% for m in msgs %}
<p>{{ m|safe }}</p>
{% endfor %}
</div>
{% endif %}
{% endwith %}
{% endfor %}
</div>

7
templates/_footer.html Normal file
View File

@ -0,0 +1,7 @@
<footer>
<p> Réalisé avec Flask et un thème bootstrap @mdo </p>
</footer>

View File

@ -15,5 +15,5 @@
<link href="{{ url_for('static', filename='up.css') }}" rel="stylesheet"> <link href="{{ url_for('static', filename='up.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='cover.css') }}" rel="stylesheet"> <link href="{{ url_for('static', filename='cover.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='simplemde.min.css') }}" rel="stylesheet"> <link href="{{ url_for('static', filename='simplemde.min.css') }}" rel="stylesheet">
<script type="text/javascript" src="{{ url_for('static', filename='divhider.js') }}"></script>
</head> </head>

5
templates/_js-core.html Normal file
View File

@ -0,0 +1,5 @@
<!-- Bootstrap core JavaScript -->
<!--================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="{{ url_for('static', filename='jquery.min.js') }}"></script>
<script src="{{ url_for('static', filename='bootstrap.min.js') }}"></script>

View File

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

View File

@ -3,6 +3,7 @@
<!-- Placed at the end of the document so the pages load faster --> <!-- Placed at the end of the document so the pages load faster -->
<script src="{{ url_for('static', filename='jquery.min.js') }}"></script> <script src="{{ url_for('static', filename='jquery.min.js') }}"></script>
<script src="{{ url_for('static', filename='bootstrap.min.js') }}"></script> <script src="{{ url_for('static', filename='bootstrap.min.js') }}"></script>
<script src="{{ url_for('static', filename='docs.min.js') }}"></script> <script src="{{ url_for('static', filename='docs.min.js') }}"></script>
<script src="{{ url_for('static', filename='simplemde.min.js') }}"></script> <script src="{{ url_for('static', filename='simplemde.min.js') }}"></script>

10
templates/_js_editor.html Normal file
View File

@ -0,0 +1,10 @@
<script src="{{ url_for('static', filename='simplemde.min.js') }}"></script>
<script>
new SimpleMDE({
element: document.getElementById("editeurMarkdown"),
spellChecker: true,
});
</script>

View File

@ -1,6 +1,5 @@
<!-- Fixed navbar --> <!-- Fixed navbar -->
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation"> <div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header"> <div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span> <span class="sr-only">Toggle navigation</span>
@ -8,22 +7,44 @@
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
</button> </button>
<!--<li class="active"><a href="/blog/">Blog</a></li>-->
</div> </div>
<div class="navbar-collapse collapse"> <div class="navbar-collapse collapse">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li {% if section == "Post-it" %} class="active" {% endif %}> <li{% if section == "Mon Blog" %} class="active" {% endif %}>
<a href="/post-it/"> <a href="#" class="dropdown-toggle" data-toggle="dropdown">
<span class="glyphicon glyphicon-globe" aria-hidden="true"></span> <span class="glyphicon glyphicon-globe" aria-hidden="true"></span>
Post-it ! Mon blog
</a> </a>
</li> <ul class="dropdown-menu" role="menu">
<li{% if section == "Files" %} class="active" {% endif %} > <li>
<a href="/myblog/new-article/"> <span class="glyphicon" aria-hidden="true"></span>
Ecrire un billet </a>
</li>
<li>
<a href="/myblog/list-articles/"><span class="glyphicon" aria-hidden="true">
</span>
Gérer mes billets
</a>
</li>
<li>
<a href="/myblog/personalize/"><span class="glyphicon glyphicon-cog" aria-hidden="true"></span>
Personnaliser mon blog
</a>
</li>
<li>
<a href="/myblog/view/"><span class="glyphicon" aria-hidden="true"></span>
Voir mon blog
</a>
</li>
</ul>
</li>
<li{% if section == "Files" %} class="active" {% endif %} >
<a href="#" class="dropdown-toggle" data-toggle="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown">
<span class="glyphicon glyphicon-folder-open" aria-hidden="true"></span> Mes Fichiers</a> <span class="glyphicon glyphicon-folder-open" aria-hidden="true"></span> Mes Fichiers</a>
<ul class="dropdown-menu" role="menu"> <ul class="dropdown-menu" role="menu">
<li><a href="/view/"> <span class="glyphicon glyphicon-cloud-download" aria-hidden="true"></span> <li><a href="/view/"> <span class="glyphicon glyphicon-cloud-download" aria-hidden="true"></span>
Fichiers envoyés </a></li> Fichiers envoyés </a></li>
<li><a href="/filesupload/"><span class="glyphicon glyphicon-cloud-upload" aria-hidden="true"></span> <li><a href="/filesupload/"><span class="glyphicon glyphicon-cloud-upload" aria-hidden="true"></span>
Envoyer des fichiers</a></li> Envoyer des fichiers</a></li>
<li><a href="/gallery/"><span class="glyphicon glyphicon-cog" aria-hidden="true"></span> <li><a href="/gallery/"><span class="glyphicon glyphicon-cog" aria-hidden="true"></span>
@ -31,43 +52,47 @@
</a></li> </a></li>
</ul> </ul>
</li>
<li{% if section == "mailbox" %} class="active" {% endif %} >
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<span class="glyphicon glyphicon-comment" aria-hidden="true"></span> Ma Messagerie </a>
<ul class="dropdown-menu" role="menu">
<li><a href="/mymailbox/alias"><span class="glyphicon glyphicon-sunglasses" aria-hidden="true"></span>
Gerer mes alias</a></li>
<li{% if section == "mailbox" %} class="active" {% endif %} > </ul>
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<span class="glyphicon glyphicon-comment" aria-hidden="true"></span> Ma Messagerie </a>
<ul class="dropdown-menu" role="menu">
<li><a href="/mymailbox/alias"><span class="glyphicon glyphicon-sunglasses" aria-hidden="true"></span>
Gerer mes alias</a></li>
</ul> </li>
<li{% if section == "Logs" %} class="active" {% endif %}>
<a href="/logs/"><span class="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span>
Logs
</a>
</li>
</li> <li {% if section == "Profil" %} class="dropdown active" {% else %} class="dropdown" {% endif %}>
<li{% if section == "Logs" %} class="active" {% endif %}> <a href="#" class="dropdown-toggle" data-toggle="dropdown">
<a href="/logs/"><span class="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span> <span class="glyphicon glyphicon-home" aria-hidden="true"></span>
Logs <span id="majuscule">{{ session['username'] }} </span>
</a> <span class="caret"></span></a>
</li> <ul class="dropdown-menu" role="menu">
<li {% if section == "Profil" %} class="dropdown active" {% else %} class="dropdown" {% endif %}> <li><a href="/profil/"><span class="glyphicon glyphicon-user" aria-hidden="true"></span> Profil</a></li>
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><span class="glyphicon glyphicon-home" aria-hidden="true"></span> <span id="majuscule">{{ session['username'] }} </span><span class="caret"></span></a> <li><a href="/profil/change-password/"> <span class="glyphicon glyphicon-lock" aria-hidden="true"></span>
<ul class="dropdown-menu" role="menu"> Changer mon mot de passe </a></li>
<li><a href="/profil/"><span class="glyphicon glyphicon-user" aria-hidden="true"></span> Profil</a></li> <li><a href="/invitation/"><span class="glyphicon glyphicon-cog" aria-hidden="true"></span> Inviter une personne</a></li>
<li><a href="/profil/change-password/"> <span class="glyphicon glyphicon-lock" aria-hidden="true"></span> <li><a href="/delete_me/"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span> Supprimer mon compte </a></li>
Changer mon mot de passe </a></li> </ul>
<li><a href="/invitation/"><span class="glyphicon glyphicon-cog" aria-hidden="true"></span> Inviter une personne</a></li> </li>
<li><a href="/delete_me/"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span> Supprimer mon compte </a></li> </ul>
</ul> <ul class="nav navbar-nav navbar-right">
</li> <li>
</ul> <a href="/logout/">
<ul class="nav navbar-nav navbar-right"> <span class="glyphicon glyphicon-off" aria-hidden="true"></span>
<li> Se déconnecter
<a href="/logout/"> </a>
<span class="glyphicon glyphicon-off" aria-hidden="true"></span> </li>
Se déconnecter </ul>
</a>
</li>
</ul>
</div><!--/.nav-collapse --> </div><!--/.nav-collapse -->
</div> </div>
</div>
<br/> <br/>
<br/> <br/>

View File

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html class="no-js" lang="fr"> <html lang="fr">
{% include '_head.html' %} {% include '_head.html' %}
<body> <body>
@ -35,6 +35,7 @@
<input type="text" name="user" id="user" placeholder="Utilisateur" class="form-control" width="200px"><br /> <input type="text" name="user" id="user" placeholder="Utilisateur" class="form-control" width="200px"><br />
<input type="password" name="passwd" id="passwd" placeholder="Mot de passe" class="form-control"><br /> <input type="password" name="passwd" id="passwd" placeholder="Mot de passe" class="form-control"><br />
<br> <br>
<p class="lead"><a href="{{ url_for('loginlogout.lost_password') }}"> Mouarf j'ai perdu mon mot de passe </a> </p>
<button id="tada" class="btn btn-default btn-primary" type="submit"> Login </button> <button id="tada" class="btn btn-default btn-primary" type="submit"> Login </button>
</form> </form>
</p> </p>
@ -49,6 +50,8 @@
{% endblock %} {% endblock %}
{% include '_footer.html' %}
{% include '_js.html' %} {% include '_js.html' %}
</body> </body>

View File

@ -1,100 +1,24 @@
{% extends 'up_squelette.html' %} <!doctype html>
<html>
<head>
<title> Blog de {{ user }} </title>
<link rel="stylesheet" href="/static/blog.css" type="text/css">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div class="articles">
<h2 class="titre"> {{ post_info.title }} </h2>
<h5 class="titre">Publié le {{ post_info.time }} </h5>
<h2 class="description titre"> {{ post_info.subtitle }} </h2>
<hr/>
{{ content|safe }}
{% block main %}
<div class="col-sm-1"></div>
<div class="col-sm-10">
<br />
<div class="well"> Hello <span id="majuscule">{{ session['username'] }} ! </span>
Bienvenue sur le tableau de post-it communautaire.
Il vous est possible de laisser des post-its en tout genre sur cette page.
Vous disposez pour cela d'un éditeur de type Markdown.
Une page <a href="/postit/board"><span class="glyphicon glyphicon-star" aria-hidden="true"></span>
est là pour consulter le tableau public du serveur</a>.
Celui-ci regroupe tout les post-it public des utilisateurs inscrits sur le serveur.<br>
Vous pouvez aussi écrire des post-its privé que vous seul pourrez consulter.
</div> </div>
</div> </body>
<br /> </html>
<form method="POST" action="{{ url_for('post-it.racine_blog') }}" id="postform">
<!--<input type="text" name="category" id="category" placeholder="Catégorie" class="form-control"><br />-->
<input type="text" name="title" id="title" placeholder="Titre" class="form-control"><br />
<textarea id="editeurMarkdown" class="form-control" form="postform" name="content" id="content" placeholder="Contenu" style="height:10vw;"></textarea><br />
<div class="row">
<div class="col-sm-4"></div>
<div class="col-sm-1"><input type="radio" name="status" value="prive" checked>Privé</div>
<div class="col-sm-2"></div>
<div class="col-sm-1"><input type="radio" name="status" value="public">Public<br></div>
<div class="col-sm-4"></div>
</div>
<br />
<button id="tada" class="btn btn-default btn-primary" type="submit">Publier</button>
</form>
<br>
<div class="row">
{% for post in posts %}
<div class="well col-sm-5 post-it" style="margin: 30px;">
<div class="col-sm-1">
{% if post.avatar != None %}
<img src="/profil/{{ post.author }}/{{ post.avatar }}" class="img-rounded" alt="" width="50" height="50"/>
{% endif %}
<br><br>
<p> {{ post.author }} </p>
</div>
<div class="col-sm-8 content" style="margin: 10px;">
<h6>{{ post.time }}</h6>
<h1>{{ post.title }}</h1>
{{ post.content[0:100]|safe }} ...
<br/>
<br/>
<button type="button" class="btn btn-default btn-primary" data-toggle="modal" data-target="#{{ post.id_postit }}"> Déplier </button>
</div>
<div class="col-sm-1">
{% if post.author == session['username'] %}
{% if post.status == 'prive' %}
<h4><span class="label label-danger">Privé</span></h4>
{% else %}
<h4><span class="label label-success">Public</span></h4>
{% endif %}
<br /><br>
<a href="{{ url_for('post-it.edit', title=post.title, time=post.time) }}"><button type="button" class="btn btn-sm btn-primary"><span class="glyphicon glyphicon-edit" aria-hidden="true"></span></button></a><br /><br>
<a href="{{ url_for('post-it.delete', title=post.title, time=post.time ) }}"><button type="button" class="btn btn-sm btn-danger"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span></button></a><br /><br>
{% endif %}
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="{{ post.id_postit }}" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">{{ post.title }}</h4>
</div>
<div class="modal-body">
{{ post.content|safe }}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% endblock %}

View File

@ -0,0 +1,40 @@
{% extends 'up_squelette.html' %}
{% block main %}
<div class="container">
<div class="row">
<div class="col-sm-1"></div>
<div class="col-sm-10">
<div class="well">Vous pouvez modifier votre article. Actuellement seule la date de première édition sera publiée. Prochainement : intégration de la date de mise à jour dans la base de donnée.</div>
</div>
</div>
<h2> {{ oldpost[0] }}</h2><br />
<form action="" method="POST" id="postform" style="height: 15vw;">
<input type="text" name="subtitle" id="subtitle" placeholder="Titre" class="form-control" value="{{ oldpost[1] }}"><br />
<textarea id="editeurMarkdown" class="form-control" form="postform" name="content" id="content" placeholder="Contenu" >{{ content }}</textarea><br />
<h3> Visibilité </h3>
<div class="row">
{% if oldpost[2] == 'public' %}
<div class="col-sm-4"></div>
<div class="col-sm-1"><input type="radio" name="status" value="prive"> <br/>Privé </div>
<div class="col-sm-2"></div>
<div class="col-sm-1"><input type="radio" name="status" value="public" checked> <br/> Publique </div>
<div class="col-sm-4"></div>
{% else %}
<div class="col-sm-4"></div>
<div class="col-sm-1"><input type="radio" name="status" value="Privé" checked> <br/> Privé </div>
<div class="col-sm-2"></div>
<div class="col-sm-1"><input type="radio" name="status" value="public"> <br/> Publique </div>
<div class="col-sm-4"></div>
{% endif %}
</div>
<br />
<button id="tada" class="btn btn-default btn-primary" type="submit"> Mettre à jour </button>
</form>
<br />
</div>
{% endblock %}
</div>

View File

@ -5,10 +5,6 @@
<div class="container theme-showcase" role="main"> <div class="container theme-showcase" role="main">
<!--<div class="page-header">
<h1>Images uploadées :</h1>
</div>-->
<br /> <br />
{% if fichiers %} {% if fichiers %}

27
templates/index_blog.html Normal file
View File

@ -0,0 +1,27 @@
<!doctype html>
<html>
<head>
<title> Blog de {{ user }} </title>
<link rel="stylesheet" href="/static/blog.css" type="text/css">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div class="articles">
{% for post in posts %}
<h2 class="index"><a href="/blog/{{user}}/{{post.title}}"> {{ post.title }}</a></h2>
<small>
<time datetime="{{ post.time }}">
Publié le {{ post.time }}
</time>
</small>
<div class="slug">
<p> {{ post.subtitle }} </p>
<p class="readmore"> <a style="margin-right:2rem;" href="/blog/{{user}}/{{post.title}}"> Lire la suite... </a></p>
</div>
{% endfor %}
</div>
</body>
</html>

View File

@ -0,0 +1,44 @@
{% extends 'up_squelette.html' %}
{% block main %}
<div class="row">
<div class="col-md-12">
<h2> Vos articles de blog </h2>
<table class="table" >
<thead>
<tr>
<th>Titre <span class="badge">{{ nb_articles }}</span></th>
<th> Créé le : </th>
<th> Dernière modification </th>
<th> status </th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody style="text-align: left;">
{% for article in list_posts %}
<tr>
<td>{{ article.title }}</td>
<td>{{ article.time }}</td>
<td>{{ article.last_updated }} </td>
<td>{{ article.status }}</td>
<td><a href="{{ url_for('blog.edit', title=article.title) }}"><button type="button" class="btn btn-sm btn-info"> Editer </button></a></td>
<td><a href="{{ url_for('blog.delete', title=article.title) }}"><button type="button" class="btn btn-sm btn-danger">Supprimer</button></a></td>
<td><a href="{{ url_for('blog.edit', title=article.title) }}"><button type="button" class="btn btn-sm btn-success"> Publier </button></a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html lang="fr">
{% include '_head.html' %}
<body>
{% block main %}
<div class="site-wrapper">
<div class="site-wrapper-inner">
<div class="cover-container">
<div class="masthead clearfix">
<div class="inner">
<h3 class="masthead-brand">Pywallter</h3>
</div>
</div>
<div class="inner cover">
<h1 class="cover-heading"> J'ai perdu mon mot de passe </h1>
<p class="lead">
Hé oui ca arrive à tout le monde... Il existe des gestionnaire des mots de passe pour éviter que t'arrives trop souvent. <a href="https://keepassxc.org/">Tiens en voilà un par exemple</a> et ca existe <a href="https://apps.apple.com/fr/app/keepass-password-manager/id6461546929?platform=iphone"> pour iphone </a> et <a href="https://www.keepassdx.com/"> pour android </a>
<br/>
</p>
<br>
{% include '_flash_msgs.html' %}
<p class="lead">
<form method="POST" action="{{ url_for('loginlogout.lost_password') }}">
<input type="text" name="user" id="user" placeholder="Utilisateur" class="form-control" width="200px"><br />
<button id="tada" class="btn btn-default btn-primary" type="submit"> Nom d'utilisateur </button>
</form>
</p>
</div>
</div>
</div>
</div>
{% endblock %}
{% include '_footer.html' %}
{% include '_js.html' %}
</body>
</html>

View File

@ -20,7 +20,7 @@
<div class="panel-body"> <div class="panel-body">
<form method="POST" action="" enctype="multipart/form-data"> <form method="POST" action="" enctype="multipart/form-data">
<p> Votre Adresse e-mail sur ce serveur : {{ address }} </p> <p> Votre Adresse e-mail sur ce serveur : {{ username }} </p>
<label> Mot de passe </label> <label> Mot de passe </label>
<input type="password" name="password" id="password" placeholder="Votre mot de passe" class="form-control"><br /> <input type="password" name="password" id="password" placeholder="Votre mot de passe" class="form-control"><br />

View File

@ -0,0 +1,40 @@
{% extends 'up_squelette.html' %}
{% block main %}
<div class="col-sm-1"></div>
<div class="col-sm-10">
<br />
<div class="well"> 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é
si vous souhaitez le modifier plus-tard avant sa publication.
Par défaut il est laissé en privé pour éviter les publications
accidentelles.
</div>
</div>
<br />
<form method="POST" action="{{ url_for('blog.new_article') }}" id="postform">
<!--<input type="text" name="category" id="category" placeholder="Catégorie" class="form-control"><br />-->
<input type="text" name="title" id="title" placeholder="Titre" class="form-control"><br />
<input type="text" name="subtitle" id="subtitle" placeholder="Sous-titre" class="form-control"><br />
<hr>
<textarea id="editeurMarkdown" class="form-control" form="postform" name="content" id="content" placeholder="Contenu" style="height:10vw;"></textarea><br />
<div class="row">
<div class="col-sm-4"></div>
<div class="col-sm-1"><input type="radio" name="status" value="private" checked>Privé</div>
<div class="col-sm-2"></div>
<div class="col-sm-1"><input type="radio" name="status" value="public">Public<br></div>
<div class="col-sm-4"></div>
</div>
<br />
<button id="tada" class="btn btn-default btn-primary" type="submit"> Créer l'article </button>
</form>
</div>
{% endblock %}

View File

@ -5,30 +5,31 @@
<div class="row"> <div class="row">
<div class="col-sm-1"></div> <div class="col-sm-1"></div>
<div class="col-sm-10"> <div class="col-sm-10">
<div class="well">Vous pouvez modifier votre article. Actuellement seule la date de première édition sera publiée. Prochainement : intégration des edit'time dans la base de donnée.</div> <div class="well">Vous pouvez modifier votre article. Actuellement seule la date de première édition sera publiée. Prochainement : intégration de la date de mise à jour dans la base de donnée.</div>
</div> </div>
</div> </div>
<form action="" method="POST" id="postform" style="height: 15vw;"> <form action="" method="POST" id="postform" style="height: 15vw;">
<input type="text" name="title" id="title" placeholder="Titre" class="form-control" value="{{ oldpost[0] }}"><br /> <input type="text" name="title" id="title" placeholder="Titre" class="form-control" value="{{ oldpost[0] }}"><br />
<textarea id="editeurMarkdown" class="form-control" form="postform" name="content" id="content" placeholder="Contenu" >{{ oldpost[1] }}</textarea><br /> <textarea id="editeurMarkdown" class="form-control" form="postform" name="content" id="content" placeholder="Contenu" >{{ oldpost[1] }}</textarea><br />
<h3> Visibilité </h3>
<div class="row"> <div class="row">
{% if oldpost[2] == 'public' %} {% if oldpost[2] == 'public' %}
<div class="col-sm-4"></div> <div class="col-sm-4"></div>
<div class="col-sm-1"><input type="radio" name="status" value="prive"> <br/>Privé </div> <div class="col-sm-1"><input type="radio" name="status" value="prive"> <br/>Privé </div>
<div class="col-sm-2"></div> <div class="col-sm-2"></div>
<div class="col-sm-1"><input type="radio" name="status" value="public" checked> <br/> Public</div> <div class="col-sm-1"><input type="radio" name="status" value="public" checked> <br/> Publique </div>
<div class="col-sm-4"></div> <div class="col-sm-4"></div>
{% else %} {% else %}
<div class="col-sm-4"></div> <div class="col-sm-4"></div>
<div class="col-sm-1"><input type="radio" name="status" value="prive" checked> <br/> Privé</div> <div class="col-sm-1"><input type="radio" name="status" value="prive" checked> <br/> Privé </div>
<div class="col-sm-2"></div> <div class="col-sm-2"></div>
<div class="col-sm-1"><input type="radio" name="status" value="public"> <br/>Public</div> <div class="col-sm-1"><input type="radio" name="status" value="public"> <br/> Public </div>
<div class="col-sm-4"></div> <div class="col-sm-4"></div>
{% endif %} {% endif %}
</div> </div>
<br /> <br />
<button id="tada" class="btn btn-default btn-primary" type="submit">Publier</button> <button id="tada" class="btn btn-default btn-primary" type="submit"> Mettre à jour </button>
</form> </form>
<br /> <br />

View File

@ -49,7 +49,7 @@
<label> Age </label> <label> Age </label>
<input type="text" name="age" value="{% if profil['age'] != None %}{{ profil['age'] }}{%endif%}" class="form-control"><br /> <input type="text" name="age" value="{% if profil['age'] != None %}{{ profil['age'] }}{%endif%}" class="form-control"><br />
<label> Mail de secours </label> <label> Mail de secours </label>
<input type="text" name="mail_rescue" id="mail_rescue" value="{% if profil['nom'] != None %}{{ profil['mail_rescue'] }}{%endif%}" class="form-control"><br /> <input type="text" name="mail_rescue" id="mail_rescue" value="{% if profil['mail_rescue'] != None %}{{ profil['mail_rescue'] }}{%endif%}" class="form-control"><br />
<button id="tada" class="btn btn-default btn-primary" type="submit">Envoyer</button> <button id="tada" class="btn btn-default btn-primary" type="submit">Envoyer</button>
</form> </form>
{# on affiche les messages d'erreur puis les messages de succes #} {# on affiche les messages d'erreur puis les messages de succes #}

View File

@ -1,34 +1,69 @@
{% extends 'up_squelette.html' %} {% extends 'up_squelette.html' %}
{% include '_nav_userlogin.html' %}
{% block main %} {% block main %}
<br> <br>
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<h2> Fichiers privés (Seul les personnes connectées et l'administrateur de l'ordinateur peuvent les voirs) </h2>
{% if listFilesPrivate %}
<table class="table"> <table class="table">
<thead> <thead>
<tr> <tr>
<th></th> <th></th>
<th>Fichier(s) <span class="badge">{{ i }}</span></th> <th>Fichier(s) <span class="badge">{{ nb_pv }}</span></th>
<th>Taille (en octets)</th> <th>Taille (en Megaoctect)</th>
<th></th> <th></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% if listeFichiers %}
{% for fichier in listeFichiers %} {% for file in listFilesPrivate %}
<tr> <tr>
<td>{{ fichier[0] }}</td> <td>{{ file[0] }}</td>
<td><a href="/myfiles/{{ fichier[1] }}">{{ fichier[1] }}</a></td> <td><a href="/myfiles/{{ username }}/{{ file[1] }}">{{ file[1] }}</a></td>
<td>{{ fichier[2] }}</td> <td>{{ file[2] }}</td>
<td><a href="{{ url_for('filesupload.remove', nom=fichier[1]) }}"><button type="button" class="btn btn-sm btn-danger">Supprimer</button></a></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>
</tr> <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 %} {% endfor %}
{% endif %} </tbody>
</tbody>
</table> </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 %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,8 +1,12 @@
<!DOCTYPE html> <!DOCTYPE html>
<html class="no-js" lang="fr"> <html lang="fr">
{% include '_head.html' %} {% include '_head.html' %}
<body role="document"> <body role="document">
{% include '_nav_userlogin.html'%} {% include '_nav_userlogin.html'%}
@ -11,7 +15,10 @@
{% block main %}{% endblock %} {% block main %}{% endblock %}
</div> </div>
{% include '_flash_msgs.html' %}
{% include '_footer.html' %}
{% include '_js.html' %} {% include '_js.html' %}
</body> </body>
</html> </html>

View File

@ -1,10 +1,10 @@
{% extends 'up_squelette.html' %}
{% include '_nav_userlogin.html' %} {% include '_nav_userlogin.html' %}
{% extends 'up_squelette.html' %}
{% block main %} {% block main %}
<div class="container">
<div class="row"> <div class="row">
<div class="col-sm-3"></div> <div class="col-sm-3"></div>
<div class="col-sm-6"> <div class="col-sm-6">
@ -46,7 +46,7 @@
</div> </div>
</div> </div>
</div>

View File

@ -29,7 +29,9 @@ def init_db():
prenom TEXT, prenom TEXT,
age TEXT, age TEXT,
website TEXT, website TEXT,
Token CHAR(30), blog_theme TEXT,
Token CHAR(64),
Lost_password_token CHAR(128),
invitations INTEGER DEFAULT (20), invitations INTEGER DEFAULT (20),
Mail_rescue TEXT ) Mail_rescue TEXT )
""") """)
@ -47,10 +49,27 @@ def init_db():
status TEXT status TEXT
) )
""") """)
cursor.execute("""
CREATE TABLE IF NOT EXISTS Blog_posts(
title TEXT NOT NULL,
subtitle TEXT,
comments TEXT,
filename TEXT,
time TEXT,
last_updated TEXT,
category TEXT,
author TEXT,
status TEXT,
PRIMARY KEY(title, author)
)
""")
conn.commit() conn.commit()
cursor.execute("""select * from users""") cursor.execute("""select * from users""")
accounts = cursor.fetchall() accounts = cursor.fetchall()
# Si aucun account n'est crée on créé l'utilisateur # Si aucun compte utilisateur existe on créé l'utilisateur
# pywallter qui permet la première inscription # pywallter qui permet la première inscription
if not(accounts) : if not(accounts) :
user = "pywallter" user = "pywallter"
@ -73,12 +92,50 @@ def db_migrate():
cursor.execute("""SELECT name FROM PRAGMA_TABLE_INFO('users');""") cursor.execute("""SELECT name FROM PRAGMA_TABLE_INFO('users');""")
db_columns = cursor.fetchall() db_columns = cursor.fetchall()
present = False invitations_col = False
blog_theme_col = False
updated_col = False
lost_password_token_col = False
for col in db_columns: for col in db_columns:
if "invitations" == col[0]: if "invitations" == col[0]:
present = True invitations_col = True
if "Lost_password_token" == col[0]:
lost_password_token_col = True
if not(present):
cursor.execute("""SELECT name FROM PRAGMA_TABLE_INFO('Blog_posts');""")
db_columns = cursor.fetchall()
for col in db_columns:
if "blog_theme" == col[0]:
blog_theme_col = True
if "last_updated" == col[0]:
updated_col = True
if not(invitations_col):
cursor.execute("""ALTER TABLE users ADD COLUMN invitations INTEGER DEFAULT (20);""") cursor.execute("""ALTER TABLE users ADD COLUMN invitations INTEGER DEFAULT (20);""")
conn.commit() conn.commit()
print ("Ajout du champ invitations") print ("Ajout du champ invitations dans la table users")
if not(lost_password_token_col):
cursor.execute("""ALTER TABLE Users ADD COLUMN Lost_password_token CHAR(64);""")
conn.commit()
print ("Ajout du champ Lost_password_token dans la table Users")
if not(blog_theme_col):
cursor.execute("""ALTER TABLE Blog_posts ADD COLUMN blog_theme TEXT;""")
conn.commit()
print ("Ajout du champ blog_theme dans la table Blog")
if not(updated_col):
cursor.execute("""ALTER TABLE Blog_posts ADD COLUMN last_updated TEXT;""")
conn.commit()
print ("Ajout du champ updated dans la table BLog")
conn.close()

12
tools/filesutils.py Normal file
View File

@ -0,0 +1,12 @@
import os
from math import floor
def getFileSizeMo(filename): # Prend un nom de fichier en arguments renvoie la taille en Mo
tmp = os.path.getsize(filename)
size = floor (tmp / 1024) / 1000
return size
def getFileSizeKo(filename): # Prend un nom de fichier en arguments renvoie la taille en Mo
tmp = os.path.getsize(filename)
size = floor(tmp / 1024)
return size

65
tools/mailer.py Normal file
View File

@ -0,0 +1,65 @@
from flask import Flask
import os, smtplib, ssl
from email.message import EmailMessage
app = Flask( 'pywallter' )
app.config.from_pyfile('config.py')
class Mailer:
def __init__(self):
self._smtp_server = app.config['SMTP_SERVER']
self._smtp_port = app.config['SMTP_PORT']
self._smtp_user = app.config['SMTP_USER']
self._smtp_passwd = app.config['SMTP_PASSWD']
self._sender_address = app.config['SENDER_ADDRESS']
def get_smtp_conf(self):
print ("Serveur SMTP: _smtp_server")
return self._smtp_server
def send_email(self, receiver_email, subject, message):
mail = EmailMessage()
mail['Subject'] = subject
mail['From'] = self._sender_address
mail['To'] = receiver_email
mail.set_content(message)
match self._smtp_port:
case "465":
self._send_ssl_mail(receiver_email, mail)
case "587":
self._send_starttls_mail(receiver_email, mail)
case "25":
with smtplib.SMTP(self._smtp_server, self._smtp_port) as server:
server.login(self._smtp_user, self._smtp_password)
server.sendmail(self._sender_address, receiver_email, mail.as_string())
case _:
print ("There are problem with mail port configuration ")
def _send_starttls_mail(self, receiver_email, mail):
context = ssl.create_default_context()
with smtplib.SMTP(self._smtp_server, self._smtp_port) as server:
server.ehlo() # Can be omitted
server.starttls(context=context)
server.ehlo() # Can be omitted
server.login(self._smtp_user, self._smtp_passwd)
server.sendmail(self._sender_address, receiver_email, mail.as_string())
def _send_ssl_mail(receiver_email, mail):
context = ssl.create_default_context()
with smtplib.SMTP(self._smtp_server, self._smtp_port) as server:
server.login(sender_email, password)
server.sendmail(self._sender_address, receiver_email, mail.as_string())

View File

@ -55,9 +55,6 @@ def email_disp(email):
if alias: if alias:
if email in alias: if email in alias:
disp=False disp=False
else: else:
disp = False disp = False
@ -72,16 +69,20 @@ def valid_passwd(password):
def valid_token_register(token): def valid_token_register(token, token_type):
valid = True valid = True
print(token) print(token)
if len(token) != 30: if len(token) != 30 and len(token) != 64 :
valid = False valid = False
if valid: if valid:
conn = sqlite3.connect(DATABASE) conn = sqlite3.connect(DATABASE)
cursor = conn.cursor() cursor = conn.cursor()
cursor.execute("""SELECT name, invitations FROM users where Token=?""", (token,)) match token_type:
case "Lost password":
cursor.execute("""SELECT name FROM users where Lost_password_token=?""", (token,))
case "Invitation":
cursor.execute("""SELECT name FROM users where token=?""", (token,))
tmp = cursor.fetchone() tmp = cursor.fetchone()
conn.close() conn.close()
print (tmp) print (tmp)
@ -89,13 +90,42 @@ def valid_token_register(token):
valid = True valid = True
else: else:
valid = False valid = False
print(valid)
return valid return valid
#Génère un token de 30 caratères aléatoires
def gen_token():
letters = random.choices(string.ascii_letters, k=20)
digits = random.choices(string.digits, k=10)
sample = ''.join(random.sample(digits + letters, 30))
def get_user_by_token(token, token_type):
if len(token) != 30 and len(token) != 64:
user = "Invalid Token"
conn = sqlite3.connect(DATABASE)
cursor = conn.cursor()
match token_type:
case "Lost password":
cursor.execute("""SELECT name FROM users where Lost_password_token=?""", (token,))
case "Invitation":
cursor.execute("""SELECT name FROM users where token=?""", (token,))
user = cursor.fetchone()[0]
conn.close()
print ("User: " + user)
if not(user):
user = "Invalid Token"
return user
#Génère un token de 30 ou 64 caratères aléatoires
def gen_token(token_type):
letters = random.choices(string.ascii_letters, k=128)
digits = random.choices(string.digits, k=30)
match token_type:
case "Invitation":
sample = ''.join(random.sample(digits + letters, 30))
case "Lost password":
sample = ''.join(random.sample(digits + letters, 64))
return sample return sample

View File

@ -1,140 +1,177 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from flask import Blueprint, render_template, session, redirect, url_for, request, flash, abort, Flask from flask import Blueprint, render_template, session, redirect, url_for, request, flash, abort, Flask
import time import time
from markupsafe import escape from markupsafe import escape
import sqlite3 import sqlite3
from markdown import markdown from markdown import markdown
postit = Blueprint('post-it', __name__, template_folder='templates') from tools.filesutils import getFileSizeKo
import string
blog = Blueprint('blog', __name__, template_folder='templates')
app = Flask( 'pywallter' ) app = Flask( 'pywallter' )
app.config.from_pyfile('config.py') app.config.from_pyfile('config.py')
#### Variables #################################################################################### ########################### Variables Globales #################################
DOSSIER_PERSO= app.config['DOSSIER_APP']
extensionimg = app.config['EXT_IMG'] extensionimg = app.config['EXT_IMG']
DATABASE = app.config['DATABASE'] DATABASE = app.config['DATABASE']
BASE_URL = app.config['BASE_URL'] BASE_URL = app.config['BASE_URL']
################################################################################################## DOSSIER_PERSO= app.config['DOSSIER_APP']+'/'
DOSSIER_PUBLIC= app.config['DOSSIER_PUBLIC']+'/'
################################################################################
@blog.route('/myblog/new-article/', methods=['GET', 'POST'])
def new_article():
@postit.route('/post-it/', methods=['GET', 'POST'])
def racine_blog():
if 'username' in session: if 'username' in session:
UTILISATEUR='%s'% escape(session['username']) user = '%s'% escape(session['username'])
folder_blog = DOSSIER_PERSO + user + "/blog/articles/"
if request.method == 'POST': if request.method == 'POST':
title= request.form['title'] title = request.form['title']
subtitle = request.form['subtitle']
content = request.form['content'] content = request.form['content']
#category = request.form['category']
status = request.form['status'] status = request.form['status']
post_date = time.strftime("%A %d %B %Y %H:%M:%S") post_date = time.strftime("%d/%m/%Y %H:%M:%S")
conn = sqlite3.connect(DATABASE) # Connexion la base de donne filename = title.replace(" ", "_") + ".md"
cursor = conn.cursor() # Création de l'objet "curseur"
cursor.execute("""INSERT INTO posts(title, content, time, author, status) VALUES(?, ?, ?, ?, ?)""", (title, content, post_date, UTILISATEUR, status)) # Insérer des valeurs
conn.commit()
cursor.execute("""SELECT avatar FROM users WHERE name=? """, (UTILISATEUR,))
user_info = cursor.fetchone()
cursor.execute("""SELECT title, content, time, author, status FROM posts where author=?""" , (UTILISATEUR,))
list_posts = cursor.fetchall()
conn.close()
posts=list()
id=0
for post in list_posts:
posts.append(dict(title=post[0], id_postit=id ,content=markdown(post[1]), time=post[2], author=post[3],status=post[4], avatar=user_info[0]))
id=id+1
return render_template('blog.html', posts=posts)
else:
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
cursor = conn.cursor() # Création de l'objet "curseur" cursor = conn.cursor() # Création de l'objet "curseur"
cursor.execute("""SELECT avatar FROM users WHERE name=?""", (UTILISATEUR,)) cursor.execute("""INSERT INTO Blog_posts(title, subtitle, filename, time, author, status) VALUES(?, ?, ?, ?, ?, ?)""", (title, subtitle, filename, post_date, user, status)) # Insérer des valeurs
user_info = cursor.fetchone() conn.commit()
cursor.execute("""SELECT title, content, time, author, status FROM posts WHERE author=?""" , (UTILISATEUR,)) ## On génère le fichiers markdown
list_posts = cursor.fetchall() with open(folder_blog + filename, 'w') as f:
conn.close() f.write(content)
posts=list()
id=0 return redirect(url_for('blog.list_articles_blog'))
for post in list_posts: else:
posts.append(dict(title=post[0], id_postit=id, content=markdown(post[1]), time=post[2], author=post[3],status=post[4], avatar=user_info[0])) return render_template('new_article_blog.html')
id=id+1 else:
return render_template('blog.html', section='Post-it', posts=posts) return redirect(BASE_URL, code=401)
@blog.route('/myblog/list-articles/', methods=['GET'])
def list_articles_blog():
if 'username' in session:
user = '%s'% escape(session['username'])
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
cursor = conn.cursor() # Création de l'objet "curseur"
cursor.execute("""SELECT title, subtitle, time, last_updated, status FROM Blog_posts WHERE author=? """, (user,) )
list_posts=cursor.fetchall()
posts=list()
nb_articles=0
for post in list_posts:
posts.append(dict(title=post[0],
subtitle=post[1],
time=post[2],
last_updated=post[3],
status=post[4]))
nb_articles =+ 1
return render_template('list_articles.html',
section="Articles",
list_posts=posts,
nb_articles=nb_articles
)
else: else:
return redirect(BASE_URL, code=401) return redirect(BASE_URL, code=401)
@blog.route('/myblog/delete/<title>')
@postit.route('/delete/<title>/<time>') def delete(title):
def delete(title, time):
if 'username' in session : if 'username' in session :
user='%s'% escape(session['username'])
folder_blog = DOSSIER_PERSO + user + "/blog/articles/"
folder_blog_public = DOSSIER_PUBLIC + user + "/blog/articles/"
filename = title.replace(" ", "_")
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
cursor = conn.cursor() # Création de l'objet "curseur" cursor = conn.cursor() # Création de l'objet "curseur"
cursor.execute("""DELETE FROM posts WHERE title=? AND time=?""", (title, time)) cursor.execute("""DELETE FROM Blog_posts WHERE title=? AND author=?""", (title, user))
conn.commit() conn.commit()
conn.close() conn.close()
return redirect(url_for('post-it.racine_blog')) os.remove(folder_blog+filename+".md")
os.remove(folder_blog_public+filename+".html")
return redirect(url_for('blog.list_articles_blog'))
else: else:
return redirect(BASE_URL, code=401) # sinon on redirige vers login return redirect(BASE_URL, code=401) # sinon on redirige vers login
@postit.route('/edit/<title>/<time>', methods=['GET', 'POST']) @blog.route('/myblog/edit/<title>', methods=['GET', 'POST'])
def edit(title, time): def edit(title):
if 'username' in session : if 'username' in session :
user='%s'% escape(session['username'])
filename = title.replace(" ", "_") + ".md"
folder_blog = DOSSIER_PERSO + user + "/blog/articles/"
if request.method == 'POST' : if request.method == 'POST' :
newtitle = request.form['title'] subtitle = request.form['subtitle']
newcontent = request.form['content'] newcontent = request.form['content']
newstatus = request.form['status'] newstatus = request.form['status']
updated = time.strftime("%d/%m/%Y %H:%M:%S")
conn = sqlite3.connect(DATABASE) conn = sqlite3.connect(DATABASE)
cursor = conn.cursor() cursor = conn.cursor()
cursor.execute("""UPDATE posts SET title=?, content=?, status=? WHERE title=? AND time=?""", cursor.execute("""UPDATE Blog_posts SET subtitle=?, last_updated=?, status=? WHERE title=? AND author=?""", (subtitle, updated, newstatus, title, user))
(newtitle, newcontent, newstatus, title, time))
conn.commit() conn.commit()
conn.close() conn.close()
return redirect(url_for('post-it.racine_blog'))
with open(folder_blog + filename, 'w') as f:
f.write(newcontent)
return redirect(url_for('blog.list_articles_blog'))
else: else:
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
cursor = conn.cursor() # Création de l'objet "curseur" cursor = conn.cursor() # Création de l'objet "curseur"
cursor.execute("""SELECT title, content, status FROM posts WHERE title=? AND time =?""", (title, time)) cursor.execute("""SELECT title, subtitle, status FROM Blog_posts WHERE title=? AND author=?""", (title, user))
oldpost = cursor.fetchone() oldpost = cursor.fetchone()
conn.close() conn.close()
return render_template('postedit.html',
with open(folder_blog + filename, 'r') as f:
content = f.read()
return render_template('edit_article.html',
section='Post-it', section='Post-it',
oldpost=oldpost) oldpost=oldpost,
content=content)
else: else:
return redirect(BASE_URL, code=401) return redirect(BASE_URL, code=401)
@blog.route('/blog/<username>/', methods=['GET'])
def view(username):
user = username
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
cursor = conn.cursor() # Création de l'objet "curseur"
cursor.execute("""SELECT title, subtitle, time, author FROM Blog_posts WHERE status='public' AND author=? """, (user,) )
list_posts=cursor.fetchall()
posts=list()
id=0
conn.close()
@postit.route('/postit/board', methods=['GET']) print (list_posts)
def viewsheet(): if list_posts != None:
if 'username' in session:
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
cursor = conn.cursor() # Création de l'objet "curseur"
cursor.execute("""SELECT title, content, time, author, status FROM posts WHERE status='public' """)
list_posts=cursor.fetchall()
posts=list()
id=0
for post in list_posts: for post in list_posts:
author = post[3] posts.append(dict(title=post[0], subtitle=post[1], time=post[2], author=post[3]))
cursor.execute("""SELECT avatar FROM users WHERE name=?""", (author,))
tmp = cursor.fetchone()
if tmp != None :
author_avatar = tmp[0]
else:
author_avatar = tmp
posts.append(dict(title=post[0], id_postit=id, content=markdown(post[1]), time=post[2], author=post[3],status=post[4], avatar=author_avatar))
id=id+1
conn.close()
return render_template('board.html', section='Post-it', posts=posts)
else: else:
return redirect(BASE_URL, code=401) return redirect(BASE_URL, code=404)
return render_template('index_blog.html', section='Blog', posts=posts, user=user)
@blog.route('/blog/<username>/<title>', methods=['GET'])
def viewArticle(username, title):
folder_blog = DOSSIER_PERSO + username + "/blog/articles/"
filename = title.replace(" ", "_") + ".md"
user = username
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
cursor = conn.cursor() # Création de l'objet "curseur"
cursor.execute("""SELECT title, subtitle, time, author FROM Blog_posts WHERE author=? AND title=? """, (user, title) )
post = cursor.fetchone()
conn.close()
if post != None:
post_info = (dict(title=post[0], subtitle=post[1], time=post[2], author=post[3]))
with open(folder_blog + filename, 'r') as f:
content_md = f.read()
content = markdown(content_md)
return render_template('blog.html', post_info=post_info, content=content)
else:
flash(u"Cet article n'existe pas", 'error');

View File

@ -7,7 +7,8 @@ from PIL import Image
import time import time
import sqlite3 import sqlite3
import os import os
from shutil import move
from tools.filesutils import getFileSizeMo
filesupload = Blueprint('filesupload', __name__, template_folder='templates') filesupload = Blueprint('filesupload', __name__, template_folder='templates')
@ -17,69 +18,54 @@ app.config.from_pyfile('config.py')
#### Variables #################################################################################### #### Variables ####################################################################################
DOSSIER_PERSO= app.config['DOSSIER_APP'] DOSSIER_PERSO= app.config['DOSSIER_APP']+'/'
DOSSIER_PUBLIC= app.config['DOSSIER_PUBLIC']+'/'
extensionimg = app.config['EXT_IMG'] extensionimg = app.config['EXT_IMG']
DATABASE = app.config['DATABASE'] DATABASE = app.config['DATABASE']
BASE_URL= app.config['BASE_URL'] BASE_URL= app.config['BASE_URL']
################################################################################################## ##################################################################################################
@filesupload.route( '/filesupload/', methods=['GET', 'POST']) @filesupload.route( '/filesupload/', methods=['GET', 'POST'])
def uploadfiles(): def uploadfiles():
if 'username' in session : if 'username' in session :
UTILISATEUR='%s'% escape(session['username']) user = '%s'% escape(session['username'])
if request.method == 'POST' : if request.method == 'POST' :
files = request.files.getlist('fic') files = request.files.getlist('fic')
for f in files : for f in files :
if f: # On vérifie qu'un fichier a bien été envoyé nom = secure_filename(f.filename)
nom = secure_filename(f.filename) if os.path.isfile(DOSSIER_PERSO + user + '/files/' + nom) or os.path.isfile(DOSSIER_PERSO + user + '/images/' + nom):
if os.path.isfile(DOSSIER_PERSO + UTILISATEUR + '/files/' + nom): flash(u'Un fichier avec le même nom existe déjà, merci de spécifier un autre nom de fichier', 'error')
flash(u'Fichier déjà existant, merci de spécifier un autre nom de fichier', 'error') else:
else: file, ext = os.path.splitext(nom)
if os.path.isfile(DOSSIER_PERSO + UTILISATEUR + '/images/' + nom): if ext in extensionimg :
flash(u'Image déjà existante, merci de spécifier un autre nom de fichier', 'error') f.save(DOSSIER_PERSO + user + '/images/' + nom)
else: image = DOSSIER_PERSO + user + '/images/' + nom
file, ext = os.path.splitext(nom) with Image.open(image) as img :
if ext in extensionimg : img.thumbnail((300,300))
f.save(DOSSIER_PERSO + UTILISATEUR + '/images/' + nom) img.save( DOSSIER_PERSO + user + '/images/thumbnails/' + nom )
image=DOSSIER_PERSO + UTILISATEUR + '/images/' + nom TIME=time.strftime("%A %d %B %Y %H:%M:%S")
with Image.open(image) as img : IP=request.environ['REMOTE_ADDR']
img.thumbnail((300,300)) CLIENT_PLATFORM=request.headers.get('User-Agent')
img.save( DOSSIER_PERSO + UTILISATEUR + '/images/thumbnails/' + nom ) log_file=os.path.join(DOSSIER_PERSO, user, "log.txt")
if os.path.isfile(DOSSIER_PERSO + UTILISATEUR + '/images/' + nom) : LOG=open(log_file, "a")
if os.path.isfile(DOSSIER_PERSO + UTILISATEUR + '/images/thumbnails/' + nom): LOG.write (TIME + ' - ' + IP + ' - ' + user + ' - ' + CLIENT_PLATFORM + '\n' + '---> ' + nom + '\n')
TIME=time.strftime("%A %d %B %Y %H:%M:%S") LOG.close()
IP=request.environ['REMOTE_ADDR'] flash(u'Image envoyée et traitée avec succés', 'succes')
CLIENT_PLATFORM=request.headers.get('User-Agent') else:
log_file=os.path.join(DOSSIER_PERSO, UTILISATEUR, "log.txt") f.save(DOSSIER_PERSO + user + '/files/' + nom)
LOG=open(log_file, "a") TIME=time.strftime("%A %d %B %Y %H:%M:%S")
LOG.write (TIME + ' - ' + IP + ' - ' + UTILISATEUR + ' - ' + CLIENT_PLATFORM + '\n' + '---> ' + nom + '\n') IP=request.environ['REMOTE_ADDR']
LOG.close() CLIENT_PLATFORM=request.headers.get('User-Agent')
flash(u'Image envoyée et traitée avec succés', 'succes') LOG=open("log.txt", "a") # Ouvre fichier log.txt
else: LOG.write (TIME + ' - ' + IP + ' - ' + user + ' - ' + CLIENT_PLATFORM + '\n' + '---> ' + nom + '\n') # Écrit dans log
flash(u'Échec lors du traitement de l\'image', 'error') LOG.close() # Ferme log.txt
return redirect(url_for('filesupload.uploadfiles')) flash(u'Fichier envoyé avec succés', 'succes')
else:
flash(u'Éches lors de l\'envoi de l\'image', 'error') else:
return redirect(url_for('filesupload.uploadfiles')) flash(u'Error : Vous avez oublié le fichier !', 'error')
else: return redirect(url_for('filesupload.uploadfiles'))
f.save(DOSSIER_PERSO + UTILISATEUR + '/files/' + nom)
if os.path.isfile(DOSSIER_PERSO + UTILISATEUR + '/files/' + nom) :
TIME=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 + ' - ' + IP + ' - ' + UTILISATEUR + ' - ' + CLIENT_PLATFORM + '\n' + '---> ' + nom + '\n') # Écrit dans log
LOG.close() # Ferme log.txt
flash(u'Fichier envoyé avec succés', 'succes')
#return redirect(url_for('filesupload.upload'))
else:
return redirect(url_for('filesupload.uploadfiles'))
else:
flash(u'Error : Vous avez oublié le fichier !', 'error')
return redirect(url_for('filesupload.uploadfiles'))
resp = make_response(render_template('up_up.html', section="Upload")) resp = make_response(render_template('up_up.html', section="Upload"))
resp.set_cookie('username', session['username']) resp.set_cookie('username', session['username'])
return resp return resp
@ -90,51 +76,122 @@ def uploadfiles():
@filesupload.route('/view/') @filesupload.route('/view/')
def list(): def list():
if 'username' in session : if 'username' in session :
UTILISATEUR='%s'% escape(session['username']) user = '%s'% escape(session['username'])
i = 0 files_public = os.listdir(DOSSIER_PUBLIC + user + '/files')
fichiers = os.listdir(DOSSIER_PERSO + UTILISATEUR + '/files/') files_private = os.listdir(DOSSIER_PERSO + user + '/files/')
listeFichiers = [] listFilesPublic = []
if fichiers: listFilesPrivate = []
for fich in fichiers: nb_pv = 0
i += 1 if files_private:
size = os.path.getsize(DOSSIER_PERSO + UTILISATEUR + '/files/' + fich) # size = taille des fichiers for fich in files_private:
listeFichiers.append([i, fich, size]) # On implémente la listeFichiers avec le num le ficier et sa taille nb_pv += 1
return render_template('up_list.html', size = getFileSizeMo(DOSSIER_PERSO + user + '/files/' + fich) # size = taille des fichiers
section="Files", listFilesPrivate.append([nb_pv, fich, size]) # On implémente la listeFichiers avec le num le ficier et sa taille
size=size,
i=i, nb_pu = 0
listeFichiers=listeFichiers) if files_public:
else : for fich in files_public:
flash(u'Aucun fichier uploadé ! Redirection vers Upload', 'error') nb_pu += 1
return redirect(url_for('filesupload.uploadfiles')) size = getFileSizeMo(DOSSIER_PUBLIC + user + '/files/' + fich) # size = taille des fichiers
listFilesPublic.append([nb_pu, fich, size])
return render_template('up_list.html',
section="Files",
size=size,
username=user,
nb_pv=nb_pv,
nb_pu=nb_pu,
listFilesPrivate=listFilesPrivate,
listFilesPublic=listFilesPublic)
else : else :
return redirect(BASE_URL, code=401) return redirect(BASE_URL, code=401)
@filesupload.route('/myfiles/<filename>') @filesupload.route('/myfiles/<username>/<filename>')
def myfiles(filename): def myfiles(username, filename):
if 'username' in session : if 'username' in session :
UTILISATEUR='%s' % escape(session['username']) user = '%s' % escape(session['username'])
return send_from_directory( return send_from_directory(
os.path.join(DOSSIER_PERSO, UTILISATEUR, 'files'), filename ) os.path.join(DOSSIER_PERSO, username, 'files'), filename )
else : else :
return redirect(BASE_URL, code=401) return redirect(BASE_URL, code=401)
@filesupload.route('/remove/<nom>') @filesupload.route('/make_public/<filename>')
def remove(nom): def move_public(filename):
if 'username' in session : if 'username' in session:
UTILISATEUR='%s' % escape(session['username']) user = '%s' % escape(session['username'])
nom = secure_filename(nom) src = os.path.join(DOSSIER_PERSO, user, 'files', filename)
if os.path.isfile(DOSSIER_PERSO + UTILISATEUR + '/files/' + nom): # si le fichier existe dst = os.path.join(DOSSIER_PUBLIC, user, 'files/')
os.remove(DOSSIER_PERSO + UTILISATEUR + '/files/' + nom) # on le supprime move (src, dst)
return redirect(url_for('filesupload.list', _external=True)) return redirect(url_for('filesupload.list', _external=True))
else: else:
if os.path.isfile(DOSSIER_PERSO + UTILISATEUR + '/images/thumbnails/' + nom): # si le fichier existe return redirect(BASE_URL, code=401)
os.remove(DOSSIER_PERSO + UTILISATEUR + '/images/thumbnails/' + nom) # on le supprime
os.remove(DOSSIER_PERSO + UTILISATEUR + '/images/' + nom) # on le supprime
return redirect(url_for('gallery'))
else:
flash(u'Fichier {nom} inexistant.'.format(nom=nom), 'error')
return redirect(url_for('filesupload.list', _external=True)) # sinon on redirige vers la liste, avec un message d'erreur
@filesupload.route('/make_private/<filename>')
def move_private(filename):
if 'username' in session:
user = '%s' % escape(session['username'])
src = os.path.join(DOSSIER_PUBLIC, user, 'files', filename)
dst = os.path.join(DOSSIER_PERSO, user, 'files/')
move (src, dst)
return redirect(url_for('filesupload.list', _external=True))
else:
return redirect(BASE_URL, code=401)
@filesupload.route('/public/<username>/<filename>')
def publicfiles(username, filename):
return send_from_directory(
os.path.join(DOSSIER_PUBLIC, username, 'files'), filename )
@filesupload.route('/remove_privateFile/<filename>')
def remove_privateFile(filename):
if 'username' in session :
user = '%s' % escape(session['username'])
filename = secure_filename(filename)
try:
os.remove(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))
else : else :
return redirect(BASE_URL, code=401) return redirect(BASE_URL, code=401)
@filesupload.route('/remove_privateImage/<filename>')
def remove_privateImage(filename):
if 'username' in session :
user = '%s' % escape(session['username'])
filename = secure_filename(filename)
try:
os.remove(DOSSIER_PERSO + user + '/images/thumbnails/' + filename) # on le supprime
os.remove(DOSSIER_PERSO + user + '/images/' + filename) # on le supprime
except FileNotFoundError:
flash(u'Image {filename} inexistante.'.format(filename=filename), 'error')
return redirect(url_for('gallery'))
@filesupload.route('/remove_publicFile/<filename>')
def remove_publicFile(filename):
if 'username' in session :
user = '%s' % escape(session['username'])
filename = secure_filename(filename)
try:
os.remove(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))
else :
return redirect(BASE_URL, code=401)
@filesupload.route('/remove_publicImage/<filename>')
def remove_publicImage(filename):
if 'username' in session :
user = '%s' % escape(session['username'])
filename = secure_filename(filename)
try:
os.remove(DOSSIER_PUBLIC + user + '/images/thumbnails/' + filename) # on le supprime
os.remove(DOSSIER_PUBLIC + user + '/images/' + filename) # on le supprime
except FileNotFoundError:
flash(u'Image {filename} inexistante.'.format(filename=filename), 'error')
return redirect(url_for('gallery'))

View File

@ -4,6 +4,8 @@ from markupsafe import escape
from flask_bcrypt import Bcrypt from flask_bcrypt import Bcrypt
from socket import gethostname from socket import gethostname
from os import remove, system from os import remove, system
from tools.utils import email_disp, valid_token_register, valid_passwd, valid_username, gen_token
from tools.mailer import Mailer
app = Flask( 'pywallter' ) app = Flask( 'pywallter' )
app.config.from_pyfile('config.py') app.config.from_pyfile('config.py')
@ -35,19 +37,25 @@ def login() :
else : else :
resp = redirect(url_for('loginlogout.login', _external=True)) resp = redirect(url_for('loginlogout.login', _external=True))
if request.method == 'POST' : if request.method == 'POST' :
user = request.form['user']
password = request.form['passwd']
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
cursor = conn.cursor() # Création de l'objet "curseur" cursor = conn.cursor() # Création de l'objet "curseur"
cursor.execute("""SELECT name, passwd FROM users""") cursor.execute("""SELECT name, passwd FROM users WHERE name=?""", (user,))
users = cursor.fetchall() user_exist = cursor.fetchone()
conn.close() conn.close()
password = request.form['passwd']
for user in users: if user_exist:
passwd = str(user[1] ) user = user_exist[0]
if user[0] == request.form['user'] and bcrypt.check_password_hash(user[1], password) is True: passwd_bcrypt = user_exist[1].decode()
if user == request.form['user'] and bcrypt.check_password_hash(passwd_bcrypt, password) is True:
session['username'] = request.form['user'] session['username'] = request.form['user']
resp = redirect(url_for('profil.profile', _external=True)) resp = redirect(url_for('profil.profile', _external=True))
else: else:
flash(u'Mauvais nom d\'utilisateur ou mot de passe', 'error') flash(u'Mauvais mot de passe', 'error')
else:
flash(u"L'utilisateur n'existe pas", 'error')
else: else:
resp = render_template('accueil.html', signin_enable=app.config['SIGNIN_ENABLE']) resp = render_template('accueil.html', signin_enable=app.config['SIGNIN_ENABLE'])
return resp return resp
@ -58,22 +66,23 @@ def logout():
session.pop('username', None) # Supprimer username de la session s'il s'y trouve session.pop('username', None) # Supprimer username de la session s'il s'y trouve
return redirect(url_for('loginlogout.index')) return redirect(url_for('loginlogout.index'))
@loginlogout.route( '/delete_me/', methods=['GET','POST']) @loginlogout.route( '/delete_me/', methods=['GET','POST'])
def delete_account(): def delete_account():
if 'username' in session : if 'username' in session :
UTILISATEUR='%s'% escape(session['username']) user='%s'% escape(session['username'])
resp = render_template('delete_account.html', time_backup=BACKUP_TIME) resp = render_template('delete_account.html', time_backup=BACKUP_TIME)
if request.method == 'POST' : if request.method == 'POST' :
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
cursor = conn.cursor() # Création de l'objet "curseur" cursor = conn.cursor() # Création de l'objet "curseur"
cursor.execute("""SELECT passwd FROM users WHERE name=?""", (UTILISATEUR,)) cursor.execute("""SELECT passwd FROM users WHERE name=?""", (user,))
passwd = cursor.fetchone()[0] passwd = cursor.fetchone()[0].decode()
conn.close() conn.close()
password = request.form['passwd'] password = request.form['passwd']
if bcrypt.check_password_hash(passwd, password) is True: if bcrypt.check_password_hash(passwd, password) is True:
not_error = True not_error = True
try: try:
cmd = 'rm -r ' + DATAS_USER + '/' + UTILISATEUR cmd = 'rm -r ' + DATAS_USER + '/' + user
if system(cmd) != 0: if system(cmd) != 0:
raise TypeError("Remove directory error") raise TypeError("Remove directory error")
except: except:
@ -101,7 +110,7 @@ def delete_account():
try: try:
conn = sqlite3.connect(DATABASE) conn = sqlite3.connect(DATABASE)
cursor = conn.cursor() cursor = conn.cursor()
cursor.execute("""DELETE FROM users WHERE name=?""", (UTILISATEUR,)) cursor.execute("""DELETE FROM users WHERE name=?""", (user,))
conn.commit() conn.commit()
conn.close() conn.close()
except: except:
@ -114,6 +123,42 @@ def delete_account():
return resp return resp
@loginlogout.route( '/lost_password/', methods=['GET', 'POST'])
def lost_password():
if request.method == 'POST' :
user = request.form['user']
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
cursor = conn.cursor() # Création de l'objet "curseur"
cursor.execute("""SELECT name, Mail_rescue FROM users WHERE name=?""", (user,))
find_user = cursor.fetchone()
if find_user:
token = gen_token("Lost password")
cursor.execute("UPDATE users SET Lost_password_token=? WHERE name=?",
(token, user))
conn.commit()
mail_lost_password=Mailer()
message = """
"Vous avez fait une demande pour changer votre mot de passe, cliquez sur le liens en
dessous pour changer votre mot de passe :
"""+ BASE_URL + url_for('profil.change_passwd_lost', token=token) + """
Si ce n'est pas vous qui avez fait cette demande vous pouvez détruire le lien de changement
de mot de passe en cliquant sur le lien en dessous \n
"""+ BASE_URL + url_for('profil.deltoken_passwd_lost', token=token) + """
Au plaisir de vous revoir sur pywallter """
if find_user[1]:
flash(u"Un lien pour changer votre mot de passe a été envoyer à votre adresse email de secour ", 'succes')
mail_lost_password.send_email(find_user[1], "Récupération de votre mot de passe", message )
else:
flash(u"L'utilisateur "+ user + " n'existe pas.", 'error')
return render_template('lost_password.html')
@loginlogout.route( '/' ) @loginlogout.route( '/' )
def index(): def index():
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée

View File

@ -7,7 +7,7 @@ import sqlite3
import os import os
from socket import gethostname from socket import gethostname
from flask_bcrypt import Bcrypt from flask_bcrypt import Bcrypt
from tools.utils import email_disp, append_to_log, gen_token, valid_passwd from tools.utils import email_disp, append_to_log, gen_token, valid_passwd, valid_token_register, get_user_by_token
profil = Blueprint('profil', __name__, template_folder='templates') profil = Blueprint('profil', __name__, template_folder='templates')
@ -49,7 +49,7 @@ def profile() :
UTILISATEUR='%s' % escape(session['username']) UTILISATEUR='%s' % escape(session['username'])
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
cursor = conn.cursor() # Création de l'objet "curseur" cursor = conn.cursor() # Création de l'objet "curseur"
cursor.execute("""SELECT avatar, nom, prenom, age, mail_rescue FROM users WHERE name=?""", (UTILISATEUR,)) cursor.execute("""SELECT avatar, nom, prenom, age, Mail_rescue FROM users WHERE name=?""", (UTILISATEUR,))
tmp = (cursor.fetchone()) tmp = (cursor.fetchone())
profil_user = dict() profil_user = dict()
profil_user['avatar'] = tmp[0] profil_user['avatar'] = tmp[0]
@ -133,7 +133,7 @@ def change_passwd() :
if password == password_confirm and valid_passwd(password): if password == password_confirm and valid_passwd(password):
mail_passwd_change = 0 mail_passwd_change = 0
xmmp_passwd_change = 0 xmpp_passwd_change = 0
passwd = request.form['password'] passwd = request.form['password']
if MAIL_SERVER: if MAIL_SERVER:
@ -144,8 +144,8 @@ def change_passwd() :
if XMPP_SERVER: if XMPP_SERVER:
tmp = mailbox['Mail'].split('@') tmp = mailbox['Mail'].split('@')
cmd = SETUID+ " prosodyctl register '"+tmp[0]+"' " + "'"+tmp[1]+"' " + "'"+passwd+"'" cmd = SETUID+ " prosodyctl register '"+tmp[0]+"' " + "'"+tmp[1]+"' " + "'"+passwd+"'"
res = os.system(cmd) xmpp_passwd_change = os.system(cmd)
if res != 0: if xmpp_passwd_change != 0:
flash(u'Il y a eu un problème pour le changement du mot de passe du compte XMPP !', 'error') flash(u'Il y a eu un problème pour le changement du mot de passe du compte XMPP !', 'error')
@ -177,6 +177,100 @@ def change_passwd() :
return redirect(BASE_URL, code=401) return redirect(BASE_URL, code=401)
@profil.route('/change-password-lost/<token>', methods=['GET','POST'] )
def change_passwd_lost(token) :
if valid_token_register(token, "Lost password"):
user = get_user_by_token(token, "Lost password")
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
cursor = conn.cursor() # Création de l'objet "curseur"
cursor.execute("""SELECT Mail, alias, xmpp FROM users WHERE name=?""", (user,))
tmp = cursor.fetchone()
mailbox = dict()
mailbox['Mail'] = tmp[0]
mailbox['alias'] = tmp[1]
mailbox['xmpp'] = tmp[2]
if request.method == 'GET' :
return render_template('mailbox.html',
section="Profil",
address=mailbox['Mail'],
username=user)
else:
password = request.form['password']
password_confirm = request.form['passwd_confirm']
if password == password_confirm and valid_passwd(password):
mail_passwd_change = 0
xmpp_passwd_change = 0
if MAIL_SERVER:
cmd = SETUID+ ' set_mail_passwd ' + '"'+mailbox['Mail']+'" '+ '"'+password+'"'
mail_passwd_change = os.system(cmd)
if XMPP_SERVER:
tmp = mailbox['Mail'].split('@')
cmd = SETUID+ " prosodyctl register '"+tmp[0]+"' " + "'"+tmp[1]+"' " + "'"+password+"'"
xmpp_change_passwd = os.system(cmd)
if xmpp_passwd_change != 0:
flash(u'Il y a eu un problème pour le changement du mot de passe du compte XMPP !', 'error')
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
cursor = conn.cursor() # Création de l'objet "curseur"
if mail_passwd_change == 0:
passwd_bcrypt = bcrypt.generate_password_hash(password)
cursor.execute("UPDATE users SET passwd=? WHERE name=?",
(passwd_bcrypt, user))
conn.commit()
TIME=time.strftime("%A %d %B %Y %H:%M:%S")
IP=request.environ['REMOTE_ADDR']
CLIENT_PLATFORM=request.headers.get('User-Agent')
log=TIME + ' - ' + IP + ' - ' + user + ' - ' + CLIENT_PLATFORM + '\n' + '---> ' + "Changement du mot de passe" + '\n'
append_to_log(log, user)
flash(u'Votre mot de passe a été changé', 'succes')
cursor.execute("""UPDATE users set Lost_password_token='' where name=?""", (user,))
conn.close()
resp = redirect(url_for('loginlogout.login'))
else:
if not( valid_passwd(password) ):
flash(u'Le mot de passe ne peut pas contenir les caractères " et &', 'error')
else:
flash(u'Les mot de passes ne sont pas identique :/ ', 'error')
resp = render_template('mailbox.html',
section="Profil",
address=mailbox['Mail'],
username=user)
return resp
else:
return redirect(BASE_URL, code=401)
@profil.route('/deltoken-password-lost/<token>', methods=['GET','POST'] )
def deltoken_passwd_lost(token) :
if valid_token_register(token, "Lost password"):
user = get_user_by_token(token, "Lost password")
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
cursor = conn.cursor() # Création de l'objet "curseur"
cursor.execute("""UPDATE users set Lost_password_token='' where name=?""", (user,))
conn.commit()
conn.close()
flash(u'Votre jeton pour changer votre mot de passe a été supprimé', 'succes')
else:
flash(u'Votre jeton est invalide', 'succes')
return redirect(url_for('loginlogout.login', _external=True))
@profil.route('/mymailbox/alias', methods=['GET', 'POST'] ) @profil.route('/mymailbox/alias', methods=['GET', 'POST'] )
def myalias(): def myalias():
hostname=gethostname() hostname=gethostname()
@ -271,12 +365,12 @@ def remove_alias(aliasrm):
else: else:
flash(u'Il y a eu une erreur', 'error') flash(u'Il y a eu une erreur', 'error')
return redirect(url_for('profil.myalias', _external=True)) return redirect(url_for('profil.myalias', _external=True))
else: else:
return redirect(BASE_URL, code=401) return redirect(BASE_URL, code=401)
@profil.route('/invitation/', methods=['GET']) @profil.route('/invitation/', methods=['GET'])
def invitation(): def invitation():
if 'username' in session: if 'username' in session:
@ -307,7 +401,7 @@ def generate_token():
UTILISATEUR='%s' % escape(session['username']) UTILISATEUR='%s' % escape(session['username'])
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
cursor = conn.cursor() # Création de l'objet "curseur" cursor = conn.cursor() # Création de l'objet "curseur"
token = gen_token() token = gen_token("Invitation")
cursor.execute("UPDATE users SET Token=? WHERE name=?", cursor.execute("UPDATE users SET Token=? WHERE name=?",
(token, UTILISATEUR)) (token, UTILISATEUR))
conn.commit() conn.commit()