diff --git a/.gitignore b/.gitignore index 717ebd9..e86de7f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ base.db log.txt config.py users/ - +sys +*~ \ No newline at end of file diff --git a/pywallter.py b/pywallter.py index 7e93812..3e057d6 100755 --- a/pywallter.py +++ b/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) diff --git a/static/blog.css b/static/blog.css new file mode 100644 index 0000000..19bbf3f --- /dev/null +++ b/static/blog.css @@ -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; + } + + } diff --git a/static/cover.css b/static/cover.css index 01a7c20..80887ab 100644 --- a/static/cover.css +++ b/static/cover.css @@ -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; } + diff --git a/static/theme.css b/static/theme.css index f544f0d..381e2c6 100644 --- a/static/theme.css +++ b/static/theme.css @@ -27,3 +27,7 @@ a:focus, a:hover { text-decoration: underline; } + +footer { + margin-top: 3em; +} diff --git a/static/up.css b/static/up.css index 968a776..7495365 100644 --- a/static/up.css +++ b/static/up.css @@ -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 { diff --git a/templates/#_footer.html# b/templates/#_footer.html# new file mode 100644 index 0000000..7b2da95 --- /dev/null +++ b/templates/#_footer.html# @@ -0,0 +1,5 @@ + +<footer> + < + <p> Réalisé avec Flask et un thème bootstrap @mdo </p> +</footer> diff --git a/templates/#new_article_blog.html# b/templates/#new_article_blog.html# new file mode 100644 index 0000000..edae829 --- /dev/null +++ b/templates/#new_article_blog.html# @@ -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 %} diff --git a/templates/_flash_msgs.html b/templates/_flash_msgs.html new file mode 100644 index 0000000..e5b03d2 --- /dev/null +++ b/templates/_flash_msgs.html @@ -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> + diff --git a/templates/_footer.html b/templates/_footer.html new file mode 100644 index 0000000..89a19b5 --- /dev/null +++ b/templates/_footer.html @@ -0,0 +1,7 @@ + + +<footer> + + <p> Réalisé avec Flask et un thème bootstrap @mdo </p> + +</footer> diff --git a/templates/_head.html b/templates/_head.html index fbb681f..5113315 100644 --- a/templates/_head.html +++ b/templates/_head.html @@ -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> diff --git a/templates/_js-core.html b/templates/_js-core.html new file mode 100644 index 0000000..5d72254 --- /dev/null +++ b/templates/_js-core.html @@ -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> diff --git a/templates/_js-gallery.html b/templates/_js-gallery.html new file mode 100644 index 0000000..64270b8 --- /dev/null +++ b/templates/_js-gallery.html @@ -0,0 +1 @@ +<script src="{{ url_for('static', filename='docs.min.js') }}"></script> diff --git a/templates/_js.html b/templates/_js.html index 350d6de..8368800 100644 --- a/templates/_js.html +++ b/templates/_js.html @@ -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> diff --git a/templates/_js_editor.html b/templates/_js_editor.html new file mode 100644 index 0000000..38a52ac --- /dev/null +++ b/templates/_js_editor.html @@ -0,0 +1,10 @@ + +<script src="{{ url_for('static', filename='simplemde.min.js') }}"></script> + +<script> + new SimpleMDE({ + element: document.getElementById("editeurMarkdown"), + spellChecker: true, + }); + +</script> diff --git a/templates/_nav_userlogin.html b/templates/_nav_userlogin.html index b6bb41d..24167dc 100644 --- a/templates/_nav_userlogin.html +++ b/templates/_nav_userlogin.html @@ -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,22 +7,44 @@ <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/"> - <span class="glyphicon glyphicon-globe" aria-hidden="true"></span> - Post-it ! - </a> - </li> - <li{% if section == "Files" %} class="active" {% endif %} > + <ul class="nav navbar-nav"> + <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> + 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> <ul class="dropdown-menu" role="menu"> <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> Envoyer des fichiers</a></li> <li><a href="/gallery/"><span class="glyphicon glyphicon-cog" aria-hidden="true"></span> @@ -31,43 +52,47 @@ </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> + <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{% 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> + <li{% if section == "Logs" %} class="active" {% endif %}> + <a href="/logs/"><span class="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span> + Logs + </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 {% 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> - <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> - Changer mon mot de passe </a></li> - <li><a href="/invitation/"><span class="glyphicon glyphicon-cog" aria-hidden="true"></span> Inviter une personne</a></li> - <li><a href="/delete_me/"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span> Supprimer mon compte </a></li> - </ul> - </li> - </ul> - <ul class="nav navbar-nav navbar-right"> - <li> - <a href="/logout/"> - <span class="glyphicon glyphicon-off" aria-hidden="true"></span> - Se déconnecter - </a> - </li> - </ul> + <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> + <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> + Changer mon mot de passe </a></li> + <li><a href="/invitation/"><span class="glyphicon glyphicon-cog" aria-hidden="true"></span> Inviter une personne</a></li> + <li><a href="/delete_me/"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span> Supprimer mon compte </a></li> + </ul> + </li> + </ul> + <ul class="nav navbar-nav navbar-right"> + <li> + <a href="/logout/"> + <span class="glyphicon glyphicon-off" aria-hidden="true"></span> + Se déconnecter + </a> + </li> + </ul> </div><!--/.nav-collapse --> </div> -</div> + <br/> <br/> diff --git a/templates/accueil.html b/templates/accueil.html index 2464caf..caf0de5 100644 --- a/templates/accueil.html +++ b/templates/accueil.html @@ -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> diff --git a/templates/blog.html b/templates/blog.html index 16fd4c1..a3fa05a 100644 --- a/templates/blog.html +++ b/templates/blog.html @@ -1,100 +1,24 @@ -{% 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 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 /> - -<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;"> +<!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"> - <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 %} + </head> - <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> + <body> + <div class="articles"> + + <h2 class="titre"> {{ post_info.title }} </h2> + + <h5 class="titre">Publié le {{ post_info.time }} </h5> - </div> + <h2 class="description titre"> {{ post_info.subtitle }} </h2> + <hr/> + {{ content|safe }} + -<!-- 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> diff --git a/templates/edit_article.html b/templates/edit_article.html new file mode 100644 index 0000000..2752d76 --- /dev/null +++ b/templates/edit_article.html @@ -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> diff --git a/templates/gallery.html b/templates/gallery.html index 9c1ba0c..3a05ec3 100644 --- a/templates/gallery.html +++ b/templates/gallery.html @@ -5,10 +5,6 @@ <div class="container theme-showcase" role="main"> -<!--<div class="page-header"> -<h1>Images uploadées :</h1> -</div>--> - <br /> {% if fichiers %} diff --git a/templates/index_blog.html b/templates/index_blog.html new file mode 100644 index 0000000..7132a49 --- /dev/null +++ b/templates/index_blog.html @@ -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> diff --git a/templates/list_articles.html b/templates/list_articles.html new file mode 100644 index 0000000..a59721d --- /dev/null +++ b/templates/list_articles.html @@ -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 %} diff --git a/templates/lost_password.html b/templates/lost_password.html new file mode 100644 index 0000000..506aae2 --- /dev/null +++ b/templates/lost_password.html @@ -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> diff --git a/templates/mailbox.html b/templates/mailbox.html index f06b510..c586ecf 100644 --- a/templates/mailbox.html +++ b/templates/mailbox.html @@ -19,8 +19,8 @@ <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 /> diff --git a/templates/new_article_blog.html b/templates/new_article_blog.html new file mode 100644 index 0000000..91a879a --- /dev/null +++ b/templates/new_article_blog.html @@ -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 %} diff --git a/templates/postedit.html b/templates/postedit.html index 9212b88..cfcb258 100644 --- a/templates/postedit.html +++ b/templates/postedit.html @@ -5,30 +5,31 @@ <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> - <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-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> {% 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 /> diff --git a/templates/profil.html b/templates/profil.html index d1d48de..4808259 100644 --- a/templates/profil.html +++ b/templates/profil.html @@ -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 #} diff --git a/templates/up_list.html b/templates/up_list.html index a4c94a6..11d490d 100644 --- a/templates/up_list.html +++ b/templates/up_list.html @@ -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> - </tr> + <td>{{ file[0] }}</td> + <td><a href="/myfiles/{{ username }}/{{ file[1] }}">{{ file[1] }}</a></td> + <td>{{ file[2] }}</td> + <td><a href="{{ url_for('filesupload.remove_privateFile', filename=file[1]) }}"><button type="button" class="btn btn-sm btn-danger">Supprimer</button></a></td> + <td><a href="{{ url_for('filesupload.move_public', filename=file[1]) }}"><button type="button" class="btn btn-sm btn-success"> Rendre Publique </button></a></td> + </tr> {% endfor %} - {% endif %} - </tbody> + </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 %} diff --git a/templates/up_squelette.html b/templates/up_squelette.html index f61c8e6..7e38475 100644 --- a/templates/up_squelette.html +++ b/templates/up_squelette.html @@ -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> diff --git a/templates/up_up.html b/templates/up_up.html index daed71e..6d9db84 100644 --- a/templates/up_up.html +++ b/templates/up_up.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> diff --git a/tools/databaseinit.py b/tools/databaseinit.py index cd6c2b6..f622bba 100755 --- a/tools/databaseinit.py +++ b/tools/databaseinit.py @@ -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() diff --git a/tools/filesutils.py b/tools/filesutils.py new file mode 100644 index 0000000..53f3744 --- /dev/null +++ b/tools/filesutils.py @@ -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 diff --git a/tools/mailer.py b/tools/mailer.py new file mode 100644 index 0000000..e3f947f --- /dev/null +++ b/tools/mailer.py @@ -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()) diff --git a/tools/utils.py b/tools/utils.py index 87fa5ee..50ed7e5 100644 --- a/tools/utils.py +++ b/tools/utils.py @@ -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 diff --git a/views/blog.py b/views/blog.py index 3f9035b..ffd77e9 100644 --- a/views/blog.py +++ b/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'] + 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,)) - 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', section='Post-it', posts=posts) + 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() + 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: 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: - - return redirect(BASE_URL, code=401) - - - -@postit.route('/postit/board', methods=['GET']) -def viewsheet(): - 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: - 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) + 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) + +@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() + 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=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'); + diff --git a/views/filesupload.py b/views/filesupload.py index bc3a067..80aeb3f 100644 --- a/views/filesupload.py +++ b/views/filesupload.py @@ -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,69 +18,54 @@ 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') - else: - file, ext = os.path.splitext(nom) - if ext in extensionimg : - f.save(DOSSIER_PERSO + UTILISATEUR + '/images/' + nom) - image=DOSSIER_PERSO + UTILISATEUR + '/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): - 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=open(log_file, "a") - LOG.write (TIME + ' - ' + IP + ' - ' + UTILISATEUR + ' - ' + 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) : - 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')) + nom = secure_filename(f.filename) + 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 + user + '/images/' + nom) + image = DOSSIER_PERSO + user + '/images/' + nom + with Image.open(image) as img : + img.thumbnail((300,300)) + 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, user, "log.txt") + LOG=open(log_file, "a") + 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: + 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 + ' - ' + user + ' - ' + CLIENT_PLATFORM + '\n' + '---> ' + nom + '\n') # Écrit dans log + LOG.close() # Ferme log.txt + flash(u'Fichier envoyé avec succés', 'succes') + + 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.set_cookie('username', session['username']) return resp @@ -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 - 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')) + 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, + 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): - 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 - 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 +@filesupload.route('/make_public/<filename>') +def move_public(filename): + if 'username' in session: + 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: + 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')) diff --git a/views/loginlogout.py b/views/loginlogout.py index 50f3e38..9de4530 100644 --- a/views/loginlogout.py +++ b/views/loginlogout.py @@ -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 diff --git a/views/profil.py b/views/profil.py index d773116..1a35013 100644 --- a/views/profil.py +++ b/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()