Add lost password recovery
This commit is contained in:
parent
c91fdad70b
commit
15c0f4fd79
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,4 +3,5 @@ base.db
|
||||
log.txt
|
||||
config.py
|
||||
users/
|
||||
|
||||
sys
|
||||
*~
|
10
pywallter.py
10
pywallter.py
@ -9,7 +9,7 @@ from flask_bcrypt import Bcrypt
|
||||
from os import system
|
||||
|
||||
|
||||
from views.blog import postit
|
||||
from views.blog import blog
|
||||
from views.filesupload import filesupload
|
||||
from views.inscription import inscription
|
||||
from views.profil import profil
|
||||
@ -31,7 +31,7 @@ if init_dir():
|
||||
print ("Le repertoire des utilisateurs a été créer")
|
||||
|
||||
|
||||
#### Variables ####################################################################################
|
||||
#### Variables Globales #########################################################################
|
||||
|
||||
|
||||
DOSSIER_PERSO= app.config['DOSSIER_APP']
|
||||
@ -39,7 +39,7 @@ DOSSIER_PERSO= app.config['DOSSIER_APP']
|
||||
extensionimg = app.config['EXT_IMG']
|
||||
MAIL_SERVER = app.config['MAIL_SERVER']
|
||||
XMPP_SERVER = app.config['XMPP_SERVER']
|
||||
##################################################################################################
|
||||
#################################################################################################
|
||||
|
||||
xmpp_server_not_installed = system('whereis prosodyctl')
|
||||
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(postit)
|
||||
app.register_blueprint(blog)
|
||||
app.register_blueprint(filesupload)
|
||||
app.register_blueprint(profil)
|
||||
app.register_blueprint(logs)
|
||||
@ -113,4 +113,4 @@ def create_app():
|
||||
return app
|
||||
|
||||
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
206
static/blog.css
Normal 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;
|
||||
}
|
||||
|
||||
}
|
@ -40,6 +40,10 @@ body {
|
||||
text-shadow: 0 1px 3px rgba(0,0,0,.5);
|
||||
}
|
||||
|
||||
.container {
|
||||
margin-bottom: 5vw;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #428bca;
|
||||
|
||||
@ -158,6 +162,17 @@ a:focus, a:hover {
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
|
||||
|
||||
/* Footer */
|
||||
|
||||
footer {
|
||||
position: bottom;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.masthead-brand {
|
||||
float: left;
|
||||
@ -332,3 +347,4 @@ a:focus, a:hover {
|
||||
.post-it h3 {
|
||||
font-size: 0.9vw;
|
||||
}
|
||||
|
||||
|
@ -27,3 +27,7 @@ a:focus, a:hover {
|
||||
text-decoration: underline;
|
||||
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 3em;
|
||||
}
|
||||
|
@ -48,13 +48,13 @@
|
||||
}
|
||||
|
||||
.succes p {
|
||||
background-color: #CDCBD0;
|
||||
color: #00A310;
|
||||
background-color: #444;
|
||||
color: #00C613;
|
||||
}
|
||||
|
||||
.error p {
|
||||
background-color: #CDCBD0;
|
||||
color: #B80000;
|
||||
background-color: #444;
|
||||
color: #FF4A4A;
|
||||
}
|
||||
|
||||
#majuscule {
|
||||
|
5
templates/#_footer.html#
Normal file
5
templates/#_footer.html#
Normal file
@ -0,0 +1,5 @@
|
||||
|
||||
<footer>
|
||||
<
|
||||
<p> Réalisé avec Flask et un thème bootstrap @mdo </p>
|
||||
</footer>
|
39
templates/#new_article_blog.html#
Normal file
39
templates/#new_article_blog.html#
Normal 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 %}
|
15
templates/_flash_msgs.html
Normal file
15
templates/_flash_msgs.html
Normal 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
7
templates/_footer.html
Normal file
@ -0,0 +1,7 @@
|
||||
|
||||
|
||||
<footer>
|
||||
|
||||
<p> Réalisé avec Flask et un thème bootstrap @mdo </p>
|
||||
|
||||
</footer>
|
@ -15,5 +15,5 @@
|
||||
<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='simplemde.min.css') }}" rel="stylesheet">
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='divhider.js') }}"></script>
|
||||
|
||||
</head>
|
||||
|
5
templates/_js-core.html
Normal file
5
templates/_js-core.html
Normal 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>
|
1
templates/_js-gallery.html
Normal file
1
templates/_js-gallery.html
Normal file
@ -0,0 +1 @@
|
||||
<script src="{{ url_for('static', filename='docs.min.js') }}"></script>
|
@ -3,6 +3,7 @@
|
||||
<!-- 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>
|
||||
|
||||
<script src="{{ url_for('static', filename='docs.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='simplemde.min.js') }}"></script>
|
||||
|
||||
|
10
templates/_js_editor.html
Normal file
10
templates/_js_editor.html
Normal 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>
|
@ -1,6 +1,5 @@
|
||||
<!-- Fixed navbar -->
|
||||
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
@ -8,16 +7,38 @@
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<!--<li class="active"><a href="/blog/">Blog</a></li>-->
|
||||
</div>
|
||||
<div class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li {% if section == "Post-it" %} class="active" {% endif %}>
|
||||
<a href="/post-it/">
|
||||
<li{% if section == "Mon Blog" %} class="active" {% endif %}>
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<span class="glyphicon glyphicon-globe" aria-hidden="true"></span>
|
||||
Post-it !
|
||||
Mon blog
|
||||
</a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<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">
|
||||
<span class="glyphicon glyphicon-folder-open" aria-hidden="true"></span> Mes Fichiers</a>
|
||||
@ -31,7 +52,7 @@
|
||||
</a></li>
|
||||
|
||||
</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>
|
||||
@ -47,8 +68,12 @@
|
||||
Logs
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li {% if section == "Profil" %} class="dropdown active" {% else %} class="dropdown" {% endif %}>
|
||||
<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>
|
||||
<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>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li><a href="/profil/"><span class="glyphicon glyphicon-user" aria-hidden="true"></span> Profil</a></li>
|
||||
<li><a href="/profil/change-password/"> <span class="glyphicon glyphicon-lock" aria-hidden="true"></span>
|
||||
@ -68,6 +93,6 @@
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="no-js" lang="fr">
|
||||
<html lang="fr">
|
||||
|
||||
{% include '_head.html' %}
|
||||
<body>
|
||||
@ -35,6 +35,7 @@
|
||||
<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 />
|
||||
<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>
|
||||
</form>
|
||||
</p>
|
||||
@ -49,6 +50,8 @@
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% include '_footer.html' %}
|
||||
|
||||
{% include '_js.html' %}
|
||||
|
||||
</body>
|
||||
|
@ -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>
|
||||
|
||||
{% block main %}
|
||||
<body>
|
||||
<div class="articles">
|
||||
|
||||
<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>
|
||||
<br />
|
||||
<h2 class="titre"> {{ post_info.title }} </h2>
|
||||
|
||||
<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>
|
||||
<h5 class="titre">Publié le {{ post_info.time }} </h5>
|
||||
|
||||
<br>
|
||||
<h2 class="description titre"> {{ post_info.subtitle }} </h2>
|
||||
<hr/>
|
||||
{{ content|safe }}
|
||||
|
||||
<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">×</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 %}
|
||||
</body>
|
||||
</html>
|
||||
|
40
templates/edit_article.html
Normal file
40
templates/edit_article.html
Normal 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>
|
@ -5,10 +5,6 @@
|
||||
|
||||
<div class="container theme-showcase" role="main">
|
||||
|
||||
<!--<div class="page-header">
|
||||
<h1>Images uploadées :</h1>
|
||||
</div>-->
|
||||
|
||||
<br />
|
||||
|
||||
{% if fichiers %}
|
||||
|
27
templates/index_blog.html
Normal file
27
templates/index_blog.html
Normal 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>
|
44
templates/list_articles.html
Normal file
44
templates/list_articles.html
Normal 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 %}
|
53
templates/lost_password.html
Normal file
53
templates/lost_password.html
Normal 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>
|
@ -20,7 +20,7 @@
|
||||
<div class="panel-body">
|
||||
<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>
|
||||
<input type="password" name="password" id="password" placeholder="Votre mot de passe" class="form-control"><br />
|
||||
|
40
templates/new_article_blog.html
Normal file
40
templates/new_article_blog.html
Normal 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 %}
|
@ -5,19 +5,20 @@
|
||||
<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 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>
|
||||
|
||||
<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 />
|
||||
<textarea id="editeurMarkdown" class="form-control" form="postform" name="content" id="content" placeholder="Contenu" >{{ oldpost[1] }}</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/> Public</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>
|
||||
@ -28,7 +29,7 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
<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>
|
||||
<br />
|
||||
|
||||
|
@ -49,7 +49,7 @@
|
||||
<label> Age </label>
|
||||
<input type="text" name="age" value="{% if profil['age'] != None %}{{ profil['age'] }}{%endif%}" class="form-control"><br />
|
||||
<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>
|
||||
</form>
|
||||
{# on affiche les messages d'erreur puis les messages de succes #}
|
||||
|
@ -1,34 +1,69 @@
|
||||
{% extends 'up_squelette.html' %}
|
||||
|
||||
{% include '_nav_userlogin.html' %}
|
||||
|
||||
{% block main %}
|
||||
|
||||
<br>
|
||||
<div class="row">
|
||||
<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">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Fichier(s) <span class="badge">{{ i }}</span></th>
|
||||
<th>Taille (en octets)</th>
|
||||
<th>Fichier(s) <span class="badge">{{ nb_pv }}</span></th>
|
||||
<th>Taille (en Megaoctect)</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if listeFichiers %}
|
||||
{% for fichier in listeFichiers %}
|
||||
|
||||
{% for file in listFilesPrivate %}
|
||||
<tr>
|
||||
<td>{{ fichier[0] }}</td>
|
||||
<td><a href="/myfiles/{{ fichier[1] }}">{{ fichier[1] }}</a></td>
|
||||
<td>{{ fichier[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>{{ 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 %}
|
||||
{% endif %}
|
||||
</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 %}
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
@ -1,8 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="no-js" lang="fr">
|
||||
<html lang="fr">
|
||||
|
||||
{% include '_head.html' %}
|
||||
|
||||
|
||||
<body role="document">
|
||||
|
||||
|
||||
{% include '_nav_userlogin.html'%}
|
||||
|
||||
|
||||
@ -11,7 +15,10 @@
|
||||
{% block main %}{% endblock %}
|
||||
</div>
|
||||
|
||||
{% include '_flash_msgs.html' %}
|
||||
{% include '_footer.html' %}
|
||||
{% include '_js.html' %}
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
@ -1,10 +1,10 @@
|
||||
{% extends 'up_squelette.html' %}
|
||||
|
||||
|
||||
{% include '_nav_userlogin.html' %}
|
||||
|
||||
{% extends 'up_squelette.html' %}
|
||||
{% block main %}
|
||||
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-3"></div>
|
||||
<div class="col-sm-6">
|
||||
@ -46,7 +46,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
@ -29,7 +29,9 @@ def init_db():
|
||||
prenom TEXT,
|
||||
age TEXT,
|
||||
website TEXT,
|
||||
Token CHAR(30),
|
||||
blog_theme TEXT,
|
||||
Token CHAR(64),
|
||||
Lost_password_token CHAR(128),
|
||||
invitations INTEGER DEFAULT (20),
|
||||
Mail_rescue TEXT )
|
||||
""")
|
||||
@ -47,10 +49,27 @@ def init_db():
|
||||
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()
|
||||
|
||||
|
||||
cursor.execute("""select * from users""")
|
||||
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
|
||||
if not(accounts) :
|
||||
user = "pywallter"
|
||||
@ -73,12 +92,50 @@ def db_migrate():
|
||||
|
||||
cursor.execute("""SELECT name FROM PRAGMA_TABLE_INFO('users');""")
|
||||
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:
|
||||
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);""")
|
||||
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
12
tools/filesutils.py
Normal 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
65
tools/mailer.py
Normal 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())
|
@ -55,9 +55,6 @@ def email_disp(email):
|
||||
if alias:
|
||||
if email in alias:
|
||||
disp=False
|
||||
|
||||
|
||||
|
||||
else:
|
||||
disp = False
|
||||
|
||||
@ -72,16 +69,20 @@ def valid_passwd(password):
|
||||
|
||||
|
||||
|
||||
def valid_token_register(token):
|
||||
def valid_token_register(token, token_type):
|
||||
valid = True
|
||||
print(token)
|
||||
if len(token) != 30:
|
||||
if len(token) != 30 and len(token) != 64 :
|
||||
valid = False
|
||||
|
||||
if valid:
|
||||
conn = sqlite3.connect(DATABASE)
|
||||
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()
|
||||
conn.close()
|
||||
print (tmp)
|
||||
@ -89,13 +90,42 @@ def valid_token_register(token):
|
||||
valid = True
|
||||
else:
|
||||
valid = False
|
||||
print(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
|
||||
|
191
views/blog.py
191
views/blog.py
@ -1,140 +1,177 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from flask import Blueprint, render_template, session, redirect, url_for, request, flash, abort, Flask
|
||||
import time
|
||||
from markupsafe import escape
|
||||
import sqlite3
|
||||
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.config.from_pyfile('config.py')
|
||||
|
||||
|
||||
#### Variables ####################################################################################
|
||||
|
||||
DOSSIER_PERSO= app.config['DOSSIER_APP']
|
||||
|
||||
########################### Variables Globales #################################
|
||||
extensionimg = app.config['EXT_IMG']
|
||||
|
||||
DATABASE = app.config['DATABASE']
|
||||
|
||||
BASE_URL = app.config['BASE_URL']
|
||||
##################################################################################################
|
||||
DOSSIER_PERSO= app.config['DOSSIER_APP']+'/'
|
||||
DOSSIER_PUBLIC= app.config['DOSSIER_PUBLIC']+'/'
|
||||
|
||||
################################################################################
|
||||
|
||||
|
||||
|
||||
|
||||
@postit.route('/post-it/', methods=['GET', 'POST'])
|
||||
def racine_blog():
|
||||
@blog.route('/myblog/new-article/', methods=['GET', 'POST'])
|
||||
def new_article():
|
||||
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':
|
||||
title = request.form['title']
|
||||
subtitle = request.form['subtitle']
|
||||
content = request.form['content']
|
||||
#category = request.form['category']
|
||||
status = request.form['status']
|
||||
post_date = time.strftime("%A %d %B %Y %H:%M:%S")
|
||||
conn = sqlite3.connect(DATABASE) # Connexion la base de donne
|
||||
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:
|
||||
post_date = time.strftime("%d/%m/%Y %H:%M:%S")
|
||||
filename = title.replace(" ", "_") + ".md"
|
||||
|
||||
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||
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,))
|
||||
cursor.execute("""INSERT INTO Blog_posts(title, subtitle, filename, time, author, status) VALUES(?, ?, ?, ?, ?, ?)""", (title, subtitle, filename, post_date, user, status)) # Insérer des valeurs
|
||||
conn.commit()
|
||||
## On génère le fichiers markdown
|
||||
with open(folder_blog + filename, 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
return redirect(url_for('blog.list_articles_blog'))
|
||||
else:
|
||||
return render_template('new_article_blog.html')
|
||||
else:
|
||||
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()
|
||||
conn.close()
|
||||
posts=list()
|
||||
id=0
|
||||
nb_articles=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', section='Post-it', posts=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:
|
||||
return redirect(BASE_URL, code=401)
|
||||
|
||||
|
||||
|
||||
@postit.route('/delete/<title>/<time>')
|
||||
def delete(title, time):
|
||||
@blog.route('/myblog/delete/<title>')
|
||||
def delete(title):
|
||||
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
|
||||
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.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:
|
||||
return redirect(BASE_URL, code=401) # sinon on redirige vers login
|
||||
|
||||
@postit.route('/edit/<title>/<time>', methods=['GET', 'POST'])
|
||||
def edit(title, time):
|
||||
@blog.route('/myblog/edit/<title>', methods=['GET', 'POST'])
|
||||
def edit(title):
|
||||
if 'username' in session :
|
||||
user='%s'% escape(session['username'])
|
||||
filename = title.replace(" ", "_") + ".md"
|
||||
folder_blog = DOSSIER_PERSO + user + "/blog/articles/"
|
||||
|
||||
if request.method == 'POST' :
|
||||
newtitle = request.form['title']
|
||||
subtitle = request.form['subtitle']
|
||||
newcontent = request.form['content']
|
||||
newstatus = request.form['status']
|
||||
updated = time.strftime("%d/%m/%Y %H:%M:%S")
|
||||
conn = sqlite3.connect(DATABASE)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("""UPDATE posts SET title=?, content=?, status=? WHERE title=? AND time=?""",
|
||||
(newtitle, newcontent, newstatus, title, time))
|
||||
cursor.execute("""UPDATE Blog_posts SET subtitle=?, last_updated=?, status=? WHERE title=? AND author=?""", (subtitle, updated, newstatus, title, user))
|
||||
conn.commit()
|
||||
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:
|
||||
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||
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()
|
||||
conn.close()
|
||||
return render_template('postedit.html',
|
||||
section='Post-it',
|
||||
oldpost=oldpost)
|
||||
else:
|
||||
|
||||
with open(folder_blog + filename, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
return render_template('edit_article.html',
|
||||
section='Post-it',
|
||||
oldpost=oldpost,
|
||||
content=content)
|
||||
else:
|
||||
return redirect(BASE_URL, code=401)
|
||||
|
||||
|
||||
|
||||
@postit.route('/postit/board', methods=['GET'])
|
||||
def viewsheet():
|
||||
if 'username' in session:
|
||||
@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, content, time, author, status FROM posts WHERE status='public' """)
|
||||
cursor.execute("""SELECT title, subtitle, time, author FROM Blog_posts WHERE status='public' AND author=? """, (user,) )
|
||||
list_posts=cursor.fetchall()
|
||||
posts=list()
|
||||
id=0
|
||||
for post in list_posts:
|
||||
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)
|
||||
print (list_posts)
|
||||
if list_posts != None:
|
||||
for post in list_posts:
|
||||
posts.append(dict(title=post[0], subtitle=post[1], time=post[2], author=post[3]))
|
||||
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');
|
||||
|
||||
|
@ -7,7 +7,8 @@ from PIL import Image
|
||||
import time
|
||||
import sqlite3
|
||||
import os
|
||||
|
||||
from shutil import move
|
||||
from tools.filesutils import getFileSizeMo
|
||||
|
||||
filesupload = Blueprint('filesupload', __name__, template_folder='templates')
|
||||
|
||||
@ -17,66 +18,51 @@ app.config.from_pyfile('config.py')
|
||||
|
||||
#### Variables ####################################################################################
|
||||
|
||||
DOSSIER_PERSO= app.config['DOSSIER_APP']
|
||||
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']
|
||||
##################################################################################################
|
||||
|
||||
|
||||
@filesupload.route( '/filesupload/', methods=['GET', 'POST'])
|
||||
def uploadfiles():
|
||||
if 'username' in session :
|
||||
UTILISATEUR='%s'% escape(session['username'])
|
||||
user = '%s'% escape(session['username'])
|
||||
if request.method == 'POST' :
|
||||
files = request.files.getlist('fic')
|
||||
for f in files :
|
||||
if f: # On vérifie qu'un fichier a bien été envoyé
|
||||
nom = secure_filename(f.filename)
|
||||
if os.path.isfile(DOSSIER_PERSO + UTILISATEUR + '/files/' + nom):
|
||||
flash(u'Fichier déjà existant, merci de spécifier un autre nom de fichier', 'error')
|
||||
else:
|
||||
if os.path.isfile(DOSSIER_PERSO + UTILISATEUR + '/images/' + nom):
|
||||
flash(u'Image déjà existante, merci de spécifier un autre nom de fichier', 'error')
|
||||
if os.path.isfile(DOSSIER_PERSO + user + '/files/' + nom) or os.path.isfile(DOSSIER_PERSO + user + '/images/' + nom):
|
||||
flash(u'Un fichier avec le même nom existe déjà, merci de spécifier un autre nom de fichier', 'error')
|
||||
else:
|
||||
file, ext = os.path.splitext(nom)
|
||||
if ext in extensionimg :
|
||||
f.save(DOSSIER_PERSO + UTILISATEUR + '/images/' + nom)
|
||||
image=DOSSIER_PERSO + UTILISATEUR + '/images/' + nom
|
||||
f.save(DOSSIER_PERSO + user + '/images/' + nom)
|
||||
image = DOSSIER_PERSO + user + '/images/' + nom
|
||||
with Image.open(image) as img :
|
||||
img.thumbnail((300,300))
|
||||
img.save( DOSSIER_PERSO + UTILISATEUR + '/images/thumbnails/' + nom )
|
||||
if os.path.isfile(DOSSIER_PERSO + UTILISATEUR + '/images/' + nom) :
|
||||
if os.path.isfile(DOSSIER_PERSO + UTILISATEUR + '/images/thumbnails/' + nom):
|
||||
img.save( DOSSIER_PERSO + user + '/images/thumbnails/' + nom )
|
||||
TIME=time.strftime("%A %d %B %Y %H:%M:%S")
|
||||
IP=request.environ['REMOTE_ADDR']
|
||||
CLIENT_PLATFORM=request.headers.get('User-Agent')
|
||||
log_file=os.path.join(DOSSIER_PERSO, UTILISATEUR, "log.txt")
|
||||
log_file=os.path.join(DOSSIER_PERSO, user, "log.txt")
|
||||
LOG=open(log_file, "a")
|
||||
LOG.write (TIME + ' - ' + IP + ' - ' + UTILISATEUR + ' - ' + CLIENT_PLATFORM + '\n' + '---> ' + nom + '\n')
|
||||
LOG.write (TIME + ' - ' + IP + ' - ' + user + ' - ' + CLIENT_PLATFORM + '\n' + '---> ' + nom + '\n')
|
||||
LOG.close()
|
||||
flash(u'Image envoyée et traitée avec succés', 'succes')
|
||||
else:
|
||||
flash(u'Échec lors du traitement de l\'image', 'error')
|
||||
return redirect(url_for('filesupload.uploadfiles'))
|
||||
else:
|
||||
flash(u'Éches lors de l\'envoi de l\'image', 'error')
|
||||
return redirect(url_for('filesupload.uploadfiles'))
|
||||
else:
|
||||
f.save(DOSSIER_PERSO + UTILISATEUR + '/files/' + nom)
|
||||
if os.path.isfile(DOSSIER_PERSO + UTILISATEUR + '/files/' + nom) :
|
||||
f.save(DOSSIER_PERSO + user + '/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.write (TIME + ' - ' + IP + ' - ' + user + ' - ' + 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'))
|
||||
@ -90,51 +76,122 @@ def uploadfiles():
|
||||
@filesupload.route('/view/')
|
||||
def list():
|
||||
if 'username' in session :
|
||||
UTILISATEUR='%s'% escape(session['username'])
|
||||
i = 0
|
||||
fichiers = os.listdir(DOSSIER_PERSO + UTILISATEUR + '/files/')
|
||||
listeFichiers = []
|
||||
if fichiers:
|
||||
for fich in fichiers:
|
||||
i += 1
|
||||
size = os.path.getsize(DOSSIER_PERSO + UTILISATEUR + '/files/' + fich) # size = taille des fichiers
|
||||
listeFichiers.append([i, fich, size]) # On implémente la listeFichiers avec le num le ficier et sa taille
|
||||
user = '%s'% escape(session['username'])
|
||||
files_public = os.listdir(DOSSIER_PUBLIC + user + '/files')
|
||||
files_private = os.listdir(DOSSIER_PERSO + user + '/files/')
|
||||
listFilesPublic = []
|
||||
listFilesPrivate = []
|
||||
nb_pv = 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])
|
||||
|
||||
return render_template('up_list.html',
|
||||
section="Files",
|
||||
size=size,
|
||||
i=i,
|
||||
listeFichiers=listeFichiers)
|
||||
else :
|
||||
flash(u'Aucun fichier uploadé ! Redirection vers Upload', 'error')
|
||||
return redirect(url_for('filesupload.uploadfiles'))
|
||||
username=user,
|
||||
nb_pv=nb_pv,
|
||||
nb_pu=nb_pu,
|
||||
listFilesPrivate=listFilesPrivate,
|
||||
listFilesPublic=listFilesPublic)
|
||||
|
||||
else :
|
||||
return redirect(BASE_URL, code=401)
|
||||
|
||||
@filesupload.route('/myfiles/<filename>')
|
||||
def myfiles(filename):
|
||||
@filesupload.route('/myfiles/<username>/<filename>')
|
||||
def myfiles(username, filename):
|
||||
if 'username' in session :
|
||||
UTILISATEUR='%s' % escape(session['username'])
|
||||
user = '%s' % escape(session['username'])
|
||||
return send_from_directory(
|
||||
os.path.join(DOSSIER_PERSO, UTILISATEUR, 'files'), filename )
|
||||
os.path.join(DOSSIER_PERSO, username, 'files'), filename )
|
||||
else :
|
||||
return redirect(BASE_URL, code=401)
|
||||
|
||||
@filesupload.route('/remove/<nom>')
|
||||
def remove(nom):
|
||||
@filesupload.route('/make_public/<filename>')
|
||||
def move_public(filename):
|
||||
if 'username' in session:
|
||||
UTILISATEUR='%s' % escape(session['username'])
|
||||
nom = secure_filename(nom)
|
||||
if os.path.isfile(DOSSIER_PERSO + UTILISATEUR + '/files/' + nom): # si le fichier existe
|
||||
os.remove(DOSSIER_PERSO + UTILISATEUR + '/files/' + nom) # on le supprime
|
||||
user = '%s' % escape(session['username'])
|
||||
src = os.path.join(DOSSIER_PERSO, user, 'files', filename)
|
||||
dst = os.path.join(DOSSIER_PUBLIC, user, 'files/')
|
||||
move (src, dst)
|
||||
return redirect(url_for('filesupload.list', _external=True))
|
||||
else:
|
||||
if os.path.isfile(DOSSIER_PERSO + UTILISATEUR + '/images/thumbnails/' + nom): # si le fichier existe
|
||||
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
|
||||
return redirect(BASE_URL, code=401)
|
||||
|
||||
@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 :
|
||||
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'))
|
||||
|
@ -4,6 +4,8 @@ from markupsafe import escape
|
||||
from flask_bcrypt import Bcrypt
|
||||
from socket import gethostname
|
||||
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.config.from_pyfile('config.py')
|
||||
@ -35,19 +37,25 @@ def login() :
|
||||
else :
|
||||
resp = redirect(url_for('loginlogout.login', _external=True))
|
||||
if request.method == 'POST' :
|
||||
user = request.form['user']
|
||||
password = request.form['passwd']
|
||||
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||
cursor.execute("""SELECT name, passwd FROM users""")
|
||||
users = cursor.fetchall()
|
||||
cursor.execute("""SELECT name, passwd FROM users WHERE name=?""", (user,))
|
||||
user_exist = cursor.fetchone()
|
||||
conn.close()
|
||||
password = request.form['passwd']
|
||||
for user in users:
|
||||
passwd = str(user[1] )
|
||||
if user[0] == request.form['user'] and bcrypt.check_password_hash(user[1], password) is True:
|
||||
|
||||
if user_exist:
|
||||
user = user_exist[0]
|
||||
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']
|
||||
resp = redirect(url_for('profil.profile', _external=True))
|
||||
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:
|
||||
resp = render_template('accueil.html', signin_enable=app.config['SIGNIN_ENABLE'])
|
||||
return resp
|
||||
@ -58,22 +66,23 @@ def logout():
|
||||
session.pop('username', None) # Supprimer username de la session s'il s'y trouve
|
||||
return redirect(url_for('loginlogout.index'))
|
||||
|
||||
|
||||
@loginlogout.route( '/delete_me/', methods=['GET','POST'])
|
||||
def delete_account():
|
||||
if 'username' in session :
|
||||
UTILISATEUR='%s'% escape(session['username'])
|
||||
user='%s'% escape(session['username'])
|
||||
resp = render_template('delete_account.html', time_backup=BACKUP_TIME)
|
||||
if request.method == 'POST' :
|
||||
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||
cursor.execute("""SELECT passwd FROM users WHERE name=?""", (UTILISATEUR,))
|
||||
passwd = cursor.fetchone()[0]
|
||||
cursor.execute("""SELECT passwd FROM users WHERE name=?""", (user,))
|
||||
passwd = cursor.fetchone()[0].decode()
|
||||
conn.close()
|
||||
password = request.form['passwd']
|
||||
if bcrypt.check_password_hash(passwd, password) is True:
|
||||
not_error = True
|
||||
try:
|
||||
cmd = 'rm -r ' + DATAS_USER + '/' + UTILISATEUR
|
||||
cmd = 'rm -r ' + DATAS_USER + '/' + user
|
||||
if system(cmd) != 0:
|
||||
raise TypeError("Remove directory error")
|
||||
except:
|
||||
@ -101,7 +110,7 @@ def delete_account():
|
||||
try:
|
||||
conn = sqlite3.connect(DATABASE)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("""DELETE FROM users WHERE name=?""", (UTILISATEUR,))
|
||||
cursor.execute("""DELETE FROM users WHERE name=?""", (user,))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
except:
|
||||
@ -114,6 +123,42 @@ def delete_account():
|
||||
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( '/' )
|
||||
def index():
|
||||
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||
|
110
views/profil.py
110
views/profil.py
@ -7,7 +7,7 @@ import sqlite3
|
||||
import os
|
||||
from socket import gethostname
|
||||
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')
|
||||
|
||||
@ -49,7 +49,7 @@ def profile() :
|
||||
UTILISATEUR='%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 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())
|
||||
profil_user = dict()
|
||||
profil_user['avatar'] = tmp[0]
|
||||
@ -133,7 +133,7 @@ def change_passwd() :
|
||||
|
||||
if password == password_confirm and valid_passwd(password):
|
||||
mail_passwd_change = 0
|
||||
xmmp_passwd_change = 0
|
||||
xmpp_passwd_change = 0
|
||||
passwd = request.form['password']
|
||||
|
||||
if MAIL_SERVER:
|
||||
@ -144,8 +144,8 @@ def change_passwd() :
|
||||
if XMPP_SERVER:
|
||||
tmp = mailbox['Mail'].split('@')
|
||||
cmd = SETUID+ " prosodyctl register '"+tmp[0]+"' " + "'"+tmp[1]+"' " + "'"+passwd+"'"
|
||||
res = os.system(cmd)
|
||||
if res != 0:
|
||||
xmpp_passwd_change = 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')
|
||||
|
||||
|
||||
@ -177,6 +177,100 @@ def change_passwd() :
|
||||
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'] )
|
||||
def myalias():
|
||||
hostname=gethostname()
|
||||
@ -271,12 +365,12 @@ def remove_alias(aliasrm):
|
||||
else:
|
||||
flash(u'Il y a eu une erreur', 'error')
|
||||
|
||||
|
||||
|
||||
return redirect(url_for('profil.myalias', _external=True))
|
||||
else:
|
||||
return redirect(BASE_URL, code=401)
|
||||
|
||||
|
||||
|
||||
@profil.route('/invitation/', methods=['GET'])
|
||||
def invitation():
|
||||
if 'username' in session:
|
||||
@ -307,7 +401,7 @@ def generate_token():
|
||||
UTILISATEUR='%s' % escape(session['username'])
|
||||
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||
token = gen_token()
|
||||
token = gen_token("Invitation")
|
||||
cursor.execute("UPDATE users SET Token=? WHERE name=?",
|
||||
(token, UTILISATEUR))
|
||||
conn.commit()
|
||||
|
Loading…
Reference in New Issue
Block a user