Compare commits
83 Commits
3679b59a03
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 7ff1f6a2aa | |||
| 4fa225900f | |||
| a4fe70de60 | |||
| 5abbf367ab | |||
| 0e79fdcb36 | |||
| ff5870a33c | |||
| a6ce2c5647 | |||
| 3da7b70cd1 | |||
| 2b04397f7d | |||
| c669987ae7 | |||
| 1f92257ca8 | |||
| 3aa76bb320 | |||
| b1a1a521c2 | |||
| 81aabb21ab | |||
| 7befe1a430 | |||
| f2fea7a61f | |||
| 50db0fcce2 | |||
| d6b4d64390 | |||
| 9baeb3736b | |||
| 51b016aa69 | |||
| 5e54c1d383 | |||
| 7d4a374d3e | |||
| 9c36563ffd | |||
| 7bd8614359 | |||
| 6984d7b04f | |||
| c8633335f3 | |||
| eafb8cac70 | |||
| 496d0bd059 | |||
| 385fe8110e | |||
| af5c93a1df | |||
| 193fc29c82 | |||
| e3100e77b2 | |||
| 481e179833 | |||
| 3266b02be7 | |||
| 89400555ed | |||
| b47999b1b6 | |||
| 8b918dd2a7 | |||
| b4a49b406c | |||
| 4f89b16a1c | |||
| 0803124c14 | |||
|
|
685502aec7 | ||
|
|
6a4089243a | ||
|
|
9a7f22f9eb | ||
|
|
3e1c9439ec | ||
|
|
5c1dfb1fee | ||
|
|
c4b9eaf60a | ||
|
|
f9e873f1e7 | ||
|
|
0ef6950a69 | ||
|
|
c2068c1d45 | ||
|
|
6201e71d2c | ||
|
|
66eff29692 | ||
|
|
76e3cfb870 | ||
| 3bcfc43ed5 | |||
|
|
c3da453425 | ||
|
|
c4e1318722 | ||
|
|
899759a2fb | ||
|
|
e90834725c | ||
|
|
89bfce21b6 | ||
| f67ccfdd19 | |||
| 1e4d097425 | |||
| 417bbe021f | |||
| a985840bc8 | |||
| 2f61155040 | |||
| a5ba0b90bb | |||
| 68d798845d | |||
| 304a2f1a45 | |||
| ed7fd0d0f5 | |||
| d887acd164 | |||
| 603c19de26 | |||
| 57241b843b | |||
| 0e282894a6 | |||
| e14677e701 | |||
| 4288eca551 | |||
| 57c2fb4ce9 | |||
| 3e4fe7f831 | |||
| 7e6de90b32 | |||
| 3455795cdb | |||
| 42c610f37f | |||
| b19393562c | |||
| f9b092e456 | |||
| 12669a86fa | |||
| d0546db73f | |||
| 8a396b3147 |
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,8 +1,11 @@
|
|||||||
__pycache__/
|
*__pycache__/
|
||||||
base.db
|
*.db
|
||||||
log.txt
|
log.txt
|
||||||
config.py
|
config.py
|
||||||
users/
|
users/
|
||||||
sys
|
sys
|
||||||
*~
|
*~
|
||||||
#*
|
*#*
|
||||||
|
.*
|
||||||
|
base.db.new
|
||||||
|
public/*
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ on pourrait ajoute de la double authentification mais bon ...
|
|||||||
### Sur OpenBSD (7.1)
|
### Sur OpenBSD (7.1)
|
||||||
Il faut installer FLask et quelques dépendances
|
Il faut installer FLask et quelques dépendances
|
||||||
pkg_add py3-werkzeug py3-jinja2 py3-Pillow py3-wtforms py3-flask-wtf py3-flask \
|
pkg_add py3-werkzeug py3-jinja2 py3-Pillow py3-wtforms py3-flask-wtf py3-flask \
|
||||||
py3-bcrypt py3-markdown py3-gevent py3-zopeinterface
|
py3-bcrypt py3-markdown py3-gevent py3-zopeinterface py3-qrcode py3-pyotp
|
||||||
|
|
||||||
Il reste malheuresment une dépendances sur flask-bcrypt a installé avec pip:
|
Il reste malheuresment une dépendances sur flask-bcrypt a installé avec pip:
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ Il reste malheuresment une dépendances sur flask-bcrypt a installé avec pip:
|
|||||||
|
|
||||||
### Sur Debian
|
### Sur Debian
|
||||||
apt install python3-flask python3-flask-bcrypt python3-wtforms python3-pil python3-markdown \
|
apt install python3-flask python3-flask-bcrypt python3-wtforms python3-pil python3-markdown \
|
||||||
python3-gevent python3-zope.interface
|
python3-gevent python3-zope.interface python3-qrcode python3-pyotp
|
||||||
|
|
||||||
## Démarrer le programme :
|
## Démarrer le programme :
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,22 @@
|
|||||||
# L'adresse de base de votre site.
|
# L'Adresse de base de votre site.
|
||||||
# example BASE_URL="https://example.com"
|
# example BASE_URL="https://example.com"
|
||||||
# BASE_URL="http://localhost:8000/" # si vous lancez pywallter avec flask run"
|
# BASE_URL="http://localhost:8000/" # si vous lancez pywallter avec flask run"
|
||||||
|
# Addresse et port sur lequel doit ecouté le service pywallter
|
||||||
|
HOST="127.0.0.1" #ADDRESS IP DOMAIN
|
||||||
|
PORT="5001" # A Free port who
|
||||||
|
|
||||||
|
BASE_URL="https://your-domain.com/"
|
||||||
BASE_URL="https://example.com/"
|
# Essentiels pour authentifier les cookies changer cette valeur par une phrase de passe ou une longue suite de caractères
|
||||||
# Essentiels pour les cookies
|
|
||||||
SECRET_KEY="CHANGE-ME"
|
SECRET_KEY="CHANGE-ME"
|
||||||
|
|
||||||
# Dossier où seront stocker les fichiers
|
TITLE_SERVER="Un titre pour votre serveur "
|
||||||
|
DESC_SERVER="Une description"
|
||||||
|
|
||||||
|
BACKUP_TIME="15 jours"
|
||||||
|
|
||||||
|
# Dossier ou seront stocker les fichiers
|
||||||
DOSSIER_APP = "./users/"
|
DOSSIER_APP = "./users/"
|
||||||
|
DOSSIER_PUBLIC = "./public/"
|
||||||
|
|
||||||
# Fichiers sqlite
|
# Fichiers sqlite
|
||||||
DATABASE = "./base.db"
|
DATABASE = "./base.db"
|
||||||
@@ -20,12 +28,31 @@ EXT_IMG= {'.jpg', '.JPG', '.png', '.PNG', '.gif', '.GIF', '.bmp', '.BMP', '.jpeg
|
|||||||
# XMPP = True => Le service est installé et lancer
|
# XMPP = True => Le service est installé et lancer
|
||||||
# XMMP = False => Le service est désactivé
|
# XMMP = False => Le service est désactivé
|
||||||
XMPP_SERVER = False
|
XMPP_SERVER = False
|
||||||
|
# URL for XMPP Web client
|
||||||
|
XMPP_WEBSERVICE = None # Si vous vous voulez proposer une interface web client pour votre service XMPP
|
||||||
|
|
||||||
# Service Mail
|
# Service Mail
|
||||||
# MAIL_SERVER = True => Le service est installé et lancer
|
# MAIL_SERVER = True => Le service est installé et lancer
|
||||||
# MAIL_SERVER = False => Le service est désactivé
|
# MAIL_SERVER = False => Le service est désactivé
|
||||||
MAIL_SERVER = False
|
MAIL_SERVER = True
|
||||||
|
MAIL_DOMAIN="" # Votre domain mail . Si vous voulez fournir des adresse @votredomaine.net
|
||||||
|
IMAP_ADDRESS = "example.net" # Adresse du serveur IMAP
|
||||||
|
SMTP_ADDRESS = "example.net" # Adresse du serveur SMTP
|
||||||
|
|
||||||
|
# URL for email Web client
|
||||||
|
MAIL_WEBSERVICE = None # Si vous voulez proposer une interface web pour consulter les mails sur votre serveur
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Doas or sudo
|
# Doas or sudo
|
||||||
SETUID='doas'
|
SETUID='doas'
|
||||||
|
|
||||||
|
# Sending email for account recovery
|
||||||
|
SMTP_SERVER="mx.example.net"
|
||||||
|
SMTP_PORT="25"
|
||||||
|
SMTP_USER="smtp_user"
|
||||||
|
SMTP_PASSWD="your password"
|
||||||
|
SENDER_ADDRESS="" # l'adresse mail qui sera afficher lors d'un envoi de mail de votre application
|
||||||
|
#Activer les inscriptions
|
||||||
|
# cette option va disparaitre car elle n'a pas vraiment de sens ... si ce n'est de pouvoir fermer les inscriptions
|
||||||
|
SIGNIN_ENABLE=True
|
||||||
|
|||||||
33
pywallter.py
33
pywallter.py
@@ -12,12 +12,14 @@ from os import system
|
|||||||
from views.blog import blog
|
from views.blog import blog
|
||||||
from views.filesupload import filesupload
|
from views.filesupload import filesupload
|
||||||
from views.inscription import inscription
|
from views.inscription import inscription
|
||||||
|
from views.mymailbox import mymailbox
|
||||||
from views.profil import profil
|
from views.profil import profil
|
||||||
from views.logs import logs
|
from views.logs import logs
|
||||||
from views.loginlogout import loginlogout
|
from views.loginlogout import loginlogout
|
||||||
from views.gallery import mygallery
|
from views.gallery import mygallery
|
||||||
|
|
||||||
from tools.databaseinit import init_db, init_dir, db_migrate
|
from tools.databaseinit import init_db, check_directories, db_migrate, check_directories
|
||||||
|
|
||||||
|
|
||||||
import glob, os, sys, time
|
import glob, os, sys, time
|
||||||
|
|
||||||
@@ -25,22 +27,25 @@ app = Flask( 'pywallter' )
|
|||||||
app.config.from_pyfile('config.py')
|
app.config.from_pyfile('config.py')
|
||||||
bcrypt = Bcrypt(app)
|
bcrypt = Bcrypt(app)
|
||||||
|
|
||||||
init_db()
|
|
||||||
db_migrate()
|
|
||||||
if init_dir():
|
|
||||||
print ("Le repertoire des utilisateurs a été créer")
|
|
||||||
|
|
||||||
|
|
||||||
#### Variables Globales #########################################################################
|
#### Variables Globales #########################################################################
|
||||||
|
|
||||||
|
|
||||||
DOSSIER_PERSO= app.config['DOSSIER_APP']
|
DOSSIER_PERSO= app.config['DOSSIER_APP']
|
||||||
|
DATABASE= app.config['DATABASE']
|
||||||
extensionimg = app.config['EXT_IMG']
|
extensionimg = app.config['EXT_IMG']
|
||||||
MAIL_SERVER = app.config['MAIL_SERVER']
|
MAIL_SERVER = app.config['MAIL_SERVER']
|
||||||
XMPP_SERVER = app.config['XMPP_SERVER']
|
XMPP_SERVER = app.config['XMPP_SERVER']
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
|
init_db(DATABASE)
|
||||||
|
check_directories(DOSSIER_PERSO)
|
||||||
|
db_migrate(DATABASE)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
xmpp_server_not_installed = system('whereis prosodyctl')
|
xmpp_server_not_installed = system('whereis prosodyctl')
|
||||||
mail_server_not_installed = system('whereis set_mail_alias') + system('whereis set_mail_passwd') + \
|
mail_server_not_installed = system('whereis set_mail_alias') + system('whereis set_mail_passwd') + \
|
||||||
system('whereis dovecot') + system('whereis smtpd')
|
system('whereis dovecot') + system('whereis smtpd')
|
||||||
@@ -61,6 +66,7 @@ if MAIL_SERVER and mail_server_not_installed :
|
|||||||
app.register_blueprint(inscription)
|
app.register_blueprint(inscription)
|
||||||
app.register_blueprint(blog)
|
app.register_blueprint(blog)
|
||||||
app.register_blueprint(filesupload)
|
app.register_blueprint(filesupload)
|
||||||
|
app.register_blueprint(mymailbox)
|
||||||
app.register_blueprint(profil)
|
app.register_blueprint(profil)
|
||||||
app.register_blueprint(logs)
|
app.register_blueprint(logs)
|
||||||
app.register_blueprint(loginlogout)
|
app.register_blueprint(loginlogout)
|
||||||
@@ -75,8 +81,11 @@ def create_app():
|
|||||||
app = Flask( 'pywallter' )
|
app = Flask( 'pywallter' )
|
||||||
app.config.from_pyfile('config.py')
|
app.config.from_pyfile('config.py')
|
||||||
bcrypt = Bcrypt(app)
|
bcrypt = Bcrypt(app)
|
||||||
|
database= app.config['DATABASE']
|
||||||
xmpp_server_not_installed = system('whereis prosodyctl')
|
xmpp_server_not_installed = system('whereis prosodyctl')
|
||||||
mail_server_not_installed = system('whereis set_mail_alias') + system('whereis set_mail_passwd') + system('whereis smtpctl')
|
mail_server_not_installed = system('whereis set_mail_alias') + system('whereis set_mail_passwd') + system('whereis smtpctl')
|
||||||
|
folder_users= app.config['DOSSIER_APP']
|
||||||
|
extensionimg = app.config['EXT_IMG']
|
||||||
|
|
||||||
if XMPP_SERVER and xmpp_server_not_installed :
|
if XMPP_SERVER and xmpp_server_not_installed :
|
||||||
print ("Vous avez activé la prise en charge du protocole XMPP mais prosody n'est pas installé")
|
print ("Vous avez activé la prise en charge du protocole XMPP mais prosody n'est pas installé")
|
||||||
@@ -90,21 +99,19 @@ def create_app():
|
|||||||
print(" Pywallter ne peut démarrer en l'état désactivé la fonction Mail ou/et installé et confiurer les programme Dovecot et opensmtpd ainsi que les scripts pour gérer les comptes mails")
|
print(" Pywallter ne peut démarrer en l'état désactivé la fonction Mail ou/et installé et confiurer les programme Dovecot et opensmtpd ainsi que les scripts pour gérer les comptes mails")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
init_db()
|
init_db(database)
|
||||||
db_migrate()
|
db_migrate(database)
|
||||||
if init_dir():
|
if check_directories(folder_users):
|
||||||
print ("Le repertoire des utilisateurs a été créer")
|
print ("Le repertoire des utilisateurs a été créer")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
DOSSIER_PERSO= app.config['DOSSIER_APP']
|
|
||||||
|
|
||||||
extensionimg = app.config['EXT_IMG']
|
|
||||||
|
|
||||||
|
|
||||||
app.register_blueprint(inscription)
|
app.register_blueprint(inscription)
|
||||||
app.register_blueprint(blog)
|
app.register_blueprint(blog)
|
||||||
app.register_blueprint(filesupload)
|
app.register_blueprint(filesupload)
|
||||||
|
app.register_blueprint(mymailbox)
|
||||||
app.register_blueprint(profil)
|
app.register_blueprint(profil)
|
||||||
app.register_blueprint(logs)
|
app.register_blueprint(logs)
|
||||||
app.register_blueprint(loginlogout)
|
app.register_blueprint(loginlogout)
|
||||||
@@ -113,4 +120,4 @@ def create_app():
|
|||||||
return app
|
return app
|
||||||
|
|
||||||
if __name__ == '__main__' :
|
if __name__ == '__main__' :
|
||||||
app.run(host='192.168.7.146', port=8000, debug=True)
|
app.run(host=app.config['HOST'], port=app.config['PORT'], debug=True)
|
||||||
|
|||||||
237
static/blog-blanc&noir.css
Normal file
237
static/blog-blanc&noir.css
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
/* kitoy <kitoy__at__kitoy.me> */
|
||||||
|
|
||||||
|
:root
|
||||||
|
{
|
||||||
|
--color-background: #fefefe;
|
||||||
|
--color-title: #010101;
|
||||||
|
--color-text: #0f0f0f;
|
||||||
|
--color-link: #010101;
|
||||||
|
--font-emoji : "Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
|
||||||
|
--font-basic : system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
html{
|
||||||
|
height: 100%;
|
||||||
|
width: 80%;
|
||||||
|
margin-left: 10%;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
body {
|
||||||
|
color: var(--color-text);
|
||||||
|
background-color: var(--color-background);
|
||||||
|
font-family: var(--font-basic);
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5{
|
||||||
|
color: var(--color-title);
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
* {-moz-box-sizing: border-box; box-sizing: border-box;}
|
||||||
|
|
||||||
|
a {
|
||||||
|
box-shadow: inset 0 0 0 0 var(--color-text);
|
||||||
|
color: --color-link;
|
||||||
|
padding: 0 .25rem;
|
||||||
|
margin: 0 -.25rem;
|
||||||
|
transition: color .3s ease-in-out, box-shadow .3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: var(--color-background);
|
||||||
|
box-shadow: inset 300px 0 0 0 var(--color-text);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--color-link);
|
||||||
|
text-decoration: underline 2px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.date {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slug {
|
||||||
|
//margin-left: 1rem;
|
||||||
|
text-align: left;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.readmore {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.head-article {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.text-article p {
|
||||||
|
padding-left: 2vw;
|
||||||
|
padding-right:2vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-article h2, h3, h4, h5 {
|
||||||
|
padding-left: 1vw;
|
||||||
|
padding-right:1vw;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.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;
|
||||||
|
padding-bottom:1vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media only screen and (max-width: 980px)
|
||||||
|
{
|
||||||
|
|
||||||
|
.articles .description {
|
||||||
|
font-size: 4vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.articles {
|
||||||
|
max-width: 100%;
|
||||||
|
margin-left: auto;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
237
static/blog-noir&blanc.css
Normal file
237
static/blog-noir&blanc.css
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
/* kitoy <kitoy__at__kitoy.me> */
|
||||||
|
|
||||||
|
:root
|
||||||
|
{
|
||||||
|
--color-background: #010101;
|
||||||
|
--color-title: #fefefe;
|
||||||
|
--color-text: #fdfdfd;
|
||||||
|
--color-link: #fefefe;
|
||||||
|
--font-emoji : "Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
|
||||||
|
--font-basic : system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
html{
|
||||||
|
height: 100%;
|
||||||
|
width: 80%;
|
||||||
|
margin-left: 10%;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
body {
|
||||||
|
color: var(--color-text);
|
||||||
|
background-color: var(--color-background);
|
||||||
|
font-family: var(--font-basic);
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5{
|
||||||
|
color: var(--color-title);
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
* {-moz-box-sizing: border-box; box-sizing: border-box;}
|
||||||
|
|
||||||
|
a {
|
||||||
|
box-shadow: inset 0 0 0 0 var(--color-text);
|
||||||
|
color: --color-link;
|
||||||
|
padding: 0 .25rem;
|
||||||
|
margin: 0 -.25rem;
|
||||||
|
transition: color .3s ease-in-out, box-shadow .3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: var(--color-background);
|
||||||
|
box-shadow: inset 300px 0 0 0 var(--color-text);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--color-link);
|
||||||
|
text-decoration: underline 2px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.date {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slug {
|
||||||
|
//margin-left: 1rem;
|
||||||
|
text-align: left;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.readmore {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.head-article {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.text-article p {
|
||||||
|
padding-left: 2vw;
|
||||||
|
padding-right:2vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-article h2, h3, h4, h5 {
|
||||||
|
padding-left: 1vw;
|
||||||
|
padding-right:1vw;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.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;
|
||||||
|
padding-bottom:1vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media only screen and (max-width: 980px)
|
||||||
|
{
|
||||||
|
|
||||||
|
.articles .description {
|
||||||
|
font-size: 4vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.articles {
|
||||||
|
max-width: 100%;
|
||||||
|
margin-left: auto;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
238
static/blog-orange&noir.css
Normal file
238
static/blog-orange&noir.css
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
/* kitoy <kitoy__at__kitoy.me> */
|
||||||
|
|
||||||
|
:root
|
||||||
|
{
|
||||||
|
--color-background: #010101;
|
||||||
|
--color-title: #ee794c;
|
||||||
|
--color-text: #fdfdfd;
|
||||||
|
--color-link: #ee794c;
|
||||||
|
--font-emoji : "Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
|
||||||
|
--font-basic : system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
html{
|
||||||
|
height: 100%;
|
||||||
|
width: 80%;
|
||||||
|
margin-left: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
body {
|
||||||
|
color: var(--color-text);
|
||||||
|
background-color: var(--color-background);
|
||||||
|
font-family: var(--font-basic);
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5{
|
||||||
|
color: var(--color-title);
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
-moz-box-sizing: border-box; box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
box-shadow: inset 0 0 0 0 var(--color-text);
|
||||||
|
color: --color-link;
|
||||||
|
padding: 0 .25rem;
|
||||||
|
margin: 0 -.25rem;
|
||||||
|
transition: color .3s ease-in-out, box-shadow .3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: var(--color-background);
|
||||||
|
box-shadow: inset 300px 0 0 0 var(--color-text);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--color-link);
|
||||||
|
text-decoration: underline 2px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.date {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slug {
|
||||||
|
//margin-left: 1rem;
|
||||||
|
text-align: left;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.readmore {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.head-article {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.text-article p {
|
||||||
|
padding-left: 2vw;
|
||||||
|
padding-right:2vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-article h2, h3, h4, h5 {
|
||||||
|
padding-left: 1vw;
|
||||||
|
padding-right:1vw;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.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;
|
||||||
|
padding-bottom:1vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media only screen and (max-width: 980px)
|
||||||
|
{
|
||||||
|
|
||||||
|
.articles .description {
|
||||||
|
font-size: 4vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.articles {
|
||||||
|
max-width: 100%;
|
||||||
|
margin-left: auto;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
237
static/blog-orangina.css
Normal file
237
static/blog-orangina.css
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
/* kitoy <kitoy__at__kitoy.me> */
|
||||||
|
|
||||||
|
:root
|
||||||
|
{
|
||||||
|
--color-background: #1e227b;
|
||||||
|
--color-title: #e9d43a;
|
||||||
|
--color-text: #fdfdfd;
|
||||||
|
--color-link: #e9d43a;
|
||||||
|
--font-emoji : "Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
|
||||||
|
--font-basic : system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
html{
|
||||||
|
height: 100%;
|
||||||
|
width: 80%;
|
||||||
|
margin-left: 10%;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
body {
|
||||||
|
color: var(--color-text);
|
||||||
|
background-color: var(--color-background);
|
||||||
|
font-family: var(--font-basic);
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5{
|
||||||
|
color: var(--color-title);
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
* {-moz-box-sizing: border-box; box-sizing: border-box;}
|
||||||
|
|
||||||
|
a {
|
||||||
|
box-shadow: inset 0 0 0 0 var(--color-text);
|
||||||
|
color: --color-link;
|
||||||
|
padding: 0 .25rem;
|
||||||
|
margin: 0 -.25rem;
|
||||||
|
transition: color .3s ease-in-out, box-shadow .3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: var(--color-background);
|
||||||
|
box-shadow: inset 300px 0 0 0 var(--color-text);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--color-link);
|
||||||
|
text-decoration: underline 2px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.date {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slug {
|
||||||
|
//margin-left: 1rem;
|
||||||
|
text-align: left;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.readmore {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.head-article {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.text-article p {
|
||||||
|
padding-left: 2vw;
|
||||||
|
padding-right:2vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-article h2, h3, h4, h5 {
|
||||||
|
padding-left: 1vw;
|
||||||
|
padding-right:1vw;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.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;
|
||||||
|
padding-bottom:1vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media only screen and (max-width: 980px)
|
||||||
|
{
|
||||||
|
|
||||||
|
.articles .description {
|
||||||
|
font-size: 4vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.articles {
|
||||||
|
max-width: 100%;
|
||||||
|
margin-left: auto;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,8 +2,13 @@
|
|||||||
|
|
||||||
:root
|
:root
|
||||||
{
|
{
|
||||||
--color-text: #fdfdfddd;
|
|
||||||
--color-background: #202020;
|
--color-background: #202020;
|
||||||
|
--color-title: #fdfdfd;
|
||||||
|
--color-text: #dddddd;
|
||||||
|
--color-link: #fdfdfd;
|
||||||
|
--font-emoji : "Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
|
||||||
|
--font-basic : system-ui,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,Helvetica,Arial,"Helvetica Neue",sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
html{
|
html{
|
||||||
@@ -17,8 +22,13 @@
|
|||||||
body {
|
body {
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
background-color: var(--color-background);
|
background-color: var(--color-background);
|
||||||
|
font-family: var(--font-basic);
|
||||||
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5{
|
||||||
|
color: var(--color-title);
|
||||||
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
@@ -26,11 +36,28 @@
|
|||||||
|
|
||||||
* {-moz-box-sizing: border-box; box-sizing: border-box;}
|
* {-moz-box-sizing: border-box; box-sizing: border-box;}
|
||||||
|
|
||||||
|
a {
|
||||||
|
box-shadow: inset 0 0 0 0 var(--color-text);
|
||||||
|
color: --color-link;
|
||||||
|
padding: 0 .25rem;
|
||||||
|
margin: 0 -.25rem;
|
||||||
|
transition: color .3s ease-in-out, box-shadow .3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: var(--color-background);
|
||||||
|
box-shadow: inset 300px 0 0 0 var(--color-text);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--color-link);
|
||||||
|
text-decoration: underline 2px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
|
||||||
color: var(--color-text);
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.date {
|
.date {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
@@ -46,17 +73,6 @@
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.readmore a {
|
|
||||||
color: var(--color-text);
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.copyleft {
|
|
||||||
display:inline-block;
|
|
||||||
transform: rotate(180deg);
|
|
||||||
padding-bottom: -15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
white-space: pre-wrap; /* css-3 */
|
white-space: pre-wrap; /* css-3 */
|
||||||
@@ -98,16 +114,28 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.titre {
|
.head-article {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.text-article p {
|
||||||
|
padding-left: 2vw;
|
||||||
|
padding-right:2vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-article h2, h3, h4, h5 {
|
||||||
|
padding-left: 1vw;
|
||||||
|
padding-right:1vw;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.index {
|
.index {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.articles img {
|
.articles img {
|
||||||
display: block;
|
display: block;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
@@ -125,6 +153,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.pagination a {
|
.pagination a {
|
||||||
border: 1px solid;
|
border: 1px solid;
|
||||||
border-color: $color_title;
|
border-color: $color_title;
|
||||||
@@ -147,8 +176,10 @@
|
|||||||
bottom:0px;
|
bottom:0px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align:center;
|
text-align:center;
|
||||||
|
padding-bottom:1vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@media only screen and (max-width: 980px)
|
@media only screen and (max-width: 980px)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -157,7 +188,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.articles {
|
.articles {
|
||||||
margin-left: 0;
|
margin-left: auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
font-size: 3.5vw;
|
font-size: 3.5vw;
|
||||||
|
|
||||||
|
|||||||
82
static/bootstrap-theme.min.css
vendored
82
static/bootstrap-theme.min.css
vendored
File diff suppressed because one or more lines are too long
6757
static/bootstrap.css
vendored
6757
static/bootstrap.css
vendored
File diff suppressed because it is too large
Load Diff
6
static/bootstrap.min.css
vendored
6
static/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
6
static/bootstrap.min.js
vendored
6
static/bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
350
static/cover.css
350
static/cover.css
@@ -1,350 +0,0 @@
|
|||||||
/*
|
|
||||||
* Globals
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Links */
|
|
||||||
/*a,
|
|
||||||
a:focus,
|
|
||||||
a:hover {
|
|
||||||
color: #fff;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Custom default button */
|
|
||||||
.btn-default,
|
|
||||||
.btn-default:hover,
|
|
||||||
.btn-default:focus {
|
|
||||||
color: #fff;
|
|
||||||
text-shadow: none; /* Prevent inheritence from `body` */
|
|
||||||
/*background-color: #fff;*/
|
|
||||||
border: 1px solid #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Base structure
|
|
||||||
*/
|
|
||||||
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
height: 100%;
|
|
||||||
background-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
body {
|
|
||||||
color: #fff;
|
|
||||||
text-align: center;
|
|
||||||
text-shadow: 0 1px 3px rgba(0,0,0,.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
margin-bottom: 5vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #428bca;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
a:focus, a:hover {
|
|
||||||
|
|
||||||
color: #6cbdbd;
|
|
||||||
text-decoration: underline;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.content p {
|
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Extra markup and styles for table-esque vertical and horizontal centering */
|
|
||||||
.site-wrapper {
|
|
||||||
display: table;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%; /* For at least Firefox */
|
|
||||||
min-height: 100%;
|
|
||||||
-webkit-box-shadow: inset 0 0 100px rgba(0,0,0,.5);
|
|
||||||
box-shadow: inset 0 0 100px rgba(0,0,0,.5);
|
|
||||||
}
|
|
||||||
.site-wrapper-inner {
|
|
||||||
display: table-cell;
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
.cover-container {
|
|
||||||
margin-right: auto;
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Padding for spacing */
|
|
||||||
.inner {
|
|
||||||
padding: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel-body {
|
|
||||||
background-color: #444;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-header .close {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-header {
|
|
||||||
background-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-body{
|
|
||||||
background-color: #444;
|
|
||||||
text-align: justify;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-footer {
|
|
||||||
background-color: #444;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.row {
|
|
||||||
margin-top: 5vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container a {
|
|
||||||
color #00abff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel-body a {
|
|
||||||
color: #00abff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.well {
|
|
||||||
margin-top : 7em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.well a {
|
|
||||||
color: #00abff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Header
|
|
||||||
*/
|
|
||||||
.masthead-brand {
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.masthead-nav > li {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
.masthead-nav > li + li {
|
|
||||||
margin-left: 20px;
|
|
||||||
}
|
|
||||||
.masthead-nav > li > a {
|
|
||||||
padding-right: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #fff; /* IE8 proofing */
|
|
||||||
color: rgba(255,255,255,.75);
|
|
||||||
border-bottom: 2px solid transparent;
|
|
||||||
}
|
|
||||||
.masthead-nav > li > a:hover,
|
|
||||||
.masthead-nav > li > a:focus {
|
|
||||||
background-color: transparent;
|
|
||||||
border-bottom-color: #a9a9a9;
|
|
||||||
border-bottom-color: rgba(255,255,255,.25);
|
|
||||||
}
|
|
||||||
.masthead-nav > .active > a,
|
|
||||||
.masthead-nav > .active > a:hover,
|
|
||||||
.masthead-nav > .active > a:focus {
|
|
||||||
color: #fff;
|
|
||||||
border-bottom-color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Footer */
|
|
||||||
|
|
||||||
footer {
|
|
||||||
position: bottom;
|
|
||||||
width: 100%;
|
|
||||||
bottom: 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
.masthead-brand {
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
.masthead-nav {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Cover
|
|
||||||
*/
|
|
||||||
|
|
||||||
.cover {
|
|
||||||
padding: 0 20px;
|
|
||||||
}
|
|
||||||
.cover .btn-lg {
|
|
||||||
padding: 10px 20px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Footer
|
|
||||||
*/
|
|
||||||
|
|
||||||
.mastfoot {
|
|
||||||
color: #999; /* IE8 proofing */
|
|
||||||
color: rgba(255,255,255,.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Affix and center
|
|
||||||
*/
|
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
/* Pull out the header and footer */
|
|
||||||
.masthead {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
.mastfoot {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
/* Start the vertical centering */
|
|
||||||
.site-wrapper-inner {
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
/* Handle the widths */
|
|
||||||
.masthead,
|
|
||||||
.mastfoot,
|
|
||||||
.cover-container {
|
|
||||||
width: 100%; /* Must be percentage or pixels for horizontal alignment */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 992px) {
|
|
||||||
.masthead,
|
|
||||||
.mastfoot,
|
|
||||||
.cover-container {
|
|
||||||
width: 700px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#tada {
|
|
||||||
text-align: center;
|
|
||||||
text-decoration: none;
|
|
||||||
display: block;
|
|
||||||
position: relative;
|
|
||||||
margin: auto;
|
|
||||||
font-size: 16px;
|
|
||||||
-webkit-animation-name: tada;
|
|
||||||
-webkit-animation-duration: 1s;
|
|
||||||
-webkit-animation-iteration-count: 1;
|
|
||||||
animation-name: tada;
|
|
||||||
animation-duration: 1s;
|
|
||||||
animation-iteration-count: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ######### Animations ######### */
|
|
||||||
|
|
||||||
@-webkit-keyframes tada {
|
|
||||||
0% {
|
|
||||||
-webkit-transform: scale(1);
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
10%, 20% {
|
|
||||||
-webkit-transform: scale(0.9) rotate(-3deg);
|
|
||||||
transform: scale(0.9) rotate(-3deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
30%, 50%, 70%, 90% {
|
|
||||||
-webkit-transform: scale(1.1) rotate(3deg);
|
|
||||||
transform: scale(1.1) rotate(3deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
40%, 60%, 80% {
|
|
||||||
-webkit-transform: scale(1.1) rotate(-3deg);
|
|
||||||
transform: scale(1.1) rotate(-3deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
-webkit-transform: scale(1) rotate(0);
|
|
||||||
transform: scale(1) rotate(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes tada {
|
|
||||||
0% {
|
|
||||||
-webkit-transform: scale(1);
|
|
||||||
-ms-transform: scale(1);
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
10%, 20% {
|
|
||||||
-webkit-transform: scale(0.9) rotate(-3deg);
|
|
||||||
-ms-transform: scale(0.9) rotate(-3deg);
|
|
||||||
transform: scale(0.9) rotate(-3deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
30%, 50%, 70%, 90% {
|
|
||||||
-webkit-transform: scale(1.1) rotate(3deg);
|
|
||||||
-ms-transform: scale(1.1) rotate(3deg);
|
|
||||||
transform: scale(1.1) rotate(3deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
40%, 60%, 80% {
|
|
||||||
-webkit-transform: scale(1.1) rotate(-3deg);
|
|
||||||
-ms-transform: scale(1.1) rotate(-3deg);
|
|
||||||
transform: scale(1.1) rotate(-3deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
-webkit-transform: scale(1) rotate(0);
|
|
||||||
-ms-transform: scale(1) rotate(0);
|
|
||||||
transform: scale(1) rotate(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ######### Animations ######### */
|
|
||||||
/* ###### Simplemde editor ###### */
|
|
||||||
|
|
||||||
.editor-toolbar {
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
.editor-preview-side {
|
|
||||||
text-align: justify;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CodeMirror {
|
|
||||||
text-align: justify;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ### Style for post-it ### */
|
|
||||||
.post-it h1 {
|
|
||||||
font-size: 1.5vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-it h2 {
|
|
||||||
font-size: 1.2vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-it h3 {
|
|
||||||
font-size: 1vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-it h3 {
|
|
||||||
font-size: 0.9vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
@@ -296,6 +296,19 @@
|
|||||||
mask-size: 100% 100%;
|
mask-size: 100% 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.menu-icon {
|
||||||
|
display: inline-block;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='%23000' d='M48 56c0-13.3-10.7-24-24-24S0 42.7 0 56v344c0 44.2 35.8 80 80 80h408c13.3 0 24-10.7 24-24s-10.7-24-24-24H80c-17.7 0-32-14.3-32-32zm104 72h208c13.3 0 24-10.7 24-24s-10.7-24-24-24H152c-13.3 0-24 10.7-24 24s10.7 24 24 24m0 64c-13.3 0-24 10.7-24 24s10.7 24 24 24h144c13.3 0 24-10.7 24-24s-10.7-24-24-24zm0 112c-13.3 0-24 10.7-24 24s10.7 24 24 24h272c13.3 0 24-10.7 24-24s-10.7-24-24-24z'/%3E%3C/svg%3E");
|
||||||
|
background-color: currentColor;
|
||||||
|
-webkit-mask-image: var(--svg);
|
||||||
|
mask-image: var(--svg);
|
||||||
|
-webkit-mask-repeat: no-repeat;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
-webkit-mask-size: 100% 100%;
|
||||||
|
mask-size: 100% 100%;
|
||||||
|
}
|
||||||
|
|
||||||
/* Gallery */
|
/* Gallery */
|
||||||
|
|
||||||
@@ -317,3 +330,78 @@
|
|||||||
margin:5px;
|
margin:5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Share */
|
||||||
|
.social-share
|
||||||
|
{
|
||||||
|
display: inline-block;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cg fill='none' stroke='%23000' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Ccircle cx='18' cy='5' r='3'/%3E%3Ccircle cx='6' cy='12' r='3'/%3E%3Ccircle cx='18' cy='19' r='3'/%3E%3Cpath d='m8.59 13.51l6.83 3.98m-.01-10.98l-6.82 3.98'/%3E%3C/g%3E%3C/svg%3E");
|
||||||
|
background-color: currentColor;
|
||||||
|
-webkit-mask-image: var(--svg);
|
||||||
|
mask-image: var(--svg);
|
||||||
|
-webkit-mask-repeat: no-repeat;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
-webkit-mask-size: 100% 100%;
|
||||||
|
mask-size: 100% 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.share {
|
||||||
|
display: inline-block;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='M18.8 8.2H16V7h3.001c.55 0 .999.446.999.996v13.008a1 1 0 0 1-.996.996H4.996A1 1 0 0 1 4 21.004V7.996A1 1 0 0 1 4.999 7H8v1.2H5.2v12.6h13.6zm-6.2-3.938V13.5h-1.2V4.262L9.313 6.349L8.464 5.5l2.829-2.828a1 1 0 0 1 1.414 0L15.536 5.5l-.849.849z'/%3E%3C/svg%3E");
|
||||||
|
background-color: currentColor;
|
||||||
|
-webkit-mask-image: var(--svg);
|
||||||
|
mask-image: var(--svg);
|
||||||
|
-webkit-mask-repeat: no-repeat;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
-webkit-mask-size: 100% 100%;
|
||||||
|
mask-size: 100% 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unshare
|
||||||
|
{
|
||||||
|
display: inline-block;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cg fill='none' stroke='%23000' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Ccircle cx='18' cy='5' r='3'/%3E%3Ccircle cx='6' cy='12' r='3'/%3E%3Ccircle cx='18' cy='19' r='3'/%3E%3Cpath d='m8.59 13.51l6.83 3.98m-.01-10.98l-6.82 3.98'/%3E%3C/g%3E%3C/svg%3E");
|
||||||
|
background-color: currentColor;
|
||||||
|
-webkit-mask-image: var(--svg);
|
||||||
|
mask-image: var(--svg);
|
||||||
|
-webkit-mask-repeat: no-repeat;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
-webkit-mask-size: 100% 100%;
|
||||||
|
mask-size: 100% 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete
|
||||||
|
{
|
||||||
|
display: inline-block;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' fill-rule='evenodd' d='m6.774 6.4l.812 13.648a.8.8 0 0 0 .798.752h7.232a.8.8 0 0 0 .798-.752L17.226 6.4zm11.655 0l-.817 13.719A2 2 0 0 1 15.616 22H8.384a2 2 0 0 1-1.996-1.881L5.571 6.4H3.5v-.7a.5.5 0 0 1 .5-.5h16a.5.5 0 0 1 .5.5v.7zM14 3a.5.5 0 0 1 .5.5v.7h-5v-.7A.5.5 0 0 1 10 3zM9.5 9h1.2l.5 9H10zm3.8 0h1.2l-.5 9h-1.2z'/%3E%3C/svg%3E");
|
||||||
|
background-color: currentColor;
|
||||||
|
-webkit-mask-image: var(--svg);
|
||||||
|
mask-image: var(--svg);
|
||||||
|
-webkit-mask-repeat: no-repeat;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
-webkit-mask-size: 100% 100%;
|
||||||
|
mask-size: 100% 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-link{
|
||||||
|
display: inline-block;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cg fill='none' stroke='%23000' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Crect width='8' height='4' x='8' y='2' rx='1' ry='1'/%3E%3Cpath d='M8 4H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2M16 4h2a2 2 0 0 1 2 2v4m1 4H11'/%3E%3Cpath d='m15 10l-4 4l4 4'/%3E%3C/g%3E%3C/svg%3E");
|
||||||
|
background-color: currentColor;
|
||||||
|
-webkit-mask-image: var(--svg);
|
||||||
|
mask-image: var(--svg);
|
||||||
|
-webkit-mask-repeat: no-repeat;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
-webkit-mask-size: 100% 100%;
|
||||||
|
mask-size: 100% 100%;
|
||||||
|
}
|
||||||
|
|||||||
1
static/js/qrcode.min.js
vendored
Normal file
1
static/js/qrcode.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -9,20 +9,26 @@ body
|
|||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
"Menu Menu header header header header header"
|
"Menu Menu header header header header header"
|
||||||
"Menu Menu main main main main main"
|
"Menu Menu main main main main main"
|
||||||
"Menu Menu footer footer footer footer footer"
|
"Menu Menu footer footer footer footer footer";
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-button {
|
.menu-button {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
.hidden {
|
||||||
grid-area: header;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
main
|
header {
|
||||||
|
grid-area: header;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body > main
|
||||||
{
|
{
|
||||||
grid-area: main;
|
grid-area: main;
|
||||||
|
width: 80%;
|
||||||
text-align: justify;
|
text-align: justify;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
@@ -38,7 +44,45 @@ main > nav
|
|||||||
margin-bottom: 4vw;
|
margin-bottom: 4vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*Color text */
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
color: var(--pico-color-red-500);
|
||||||
|
}
|
||||||
|
|
||||||
|
.success {
|
||||||
|
color: var(--pico-color-green-500);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1200px)
|
||||||
|
{
|
||||||
|
.list-articles{
|
||||||
|
padding-top: 2vw;
|
||||||
|
display: inline-flex;
|
||||||
|
gap:30px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
article {
|
||||||
|
text-align: center;
|
||||||
|
width: 30vw;
|
||||||
|
height: 35vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
article > header {
|
||||||
|
height: 15vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
article > .subtitle {
|
||||||
|
height: 10vw;
|
||||||
|
}
|
||||||
|
article footer{
|
||||||
|
display: block;
|
||||||
|
height: 5vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 600px)
|
@media only screen and (max-width: 600px)
|
||||||
{
|
{
|
||||||
@@ -92,6 +136,7 @@ main > nav
|
|||||||
gap: 20px;
|
gap: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
nav li {
|
nav li {
|
||||||
word-break: keep-all;
|
word-break: keep-all;
|
||||||
width: 115px;
|
width: 115px;
|
||||||
@@ -166,6 +211,12 @@ footer
|
|||||||
color: var(--pico-color-red-50);
|
color: var(--pico-color-red-50);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-alert {
|
||||||
|
background-color: var(--pico-color-red-550);
|
||||||
|
color: var(--pico-color-red-50);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
article {
|
article {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
@@ -202,9 +253,9 @@ nav > ul:first-of-type, ul
|
|||||||
gap: 5px;
|
gap: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.picture{
|
.picture {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -218,3 +269,15 @@ nav > ul:first-of-type, ul
|
|||||||
color: var(--pico-color-grey-950);
|
color: var(--pico-color-grey-950);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.totp {
|
||||||
|
visibility: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type=checkbox]:checked~.totp {
|
||||||
|
visibility: visible;
|
||||||
|
|
||||||
|
pointer-events: unset;
|
||||||
|
}
|
||||||
|
|||||||
97
static/simplemde-custom.css
Normal file
97
static/simplemde-custom.css
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
.editor-toolbar a {
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none !important;
|
||||||
|
color: var(--pico-primary) !important;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
margin: 0;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.editor-toolbar.fullscreen {
|
||||||
|
width: 100%;
|
||||||
|
height: 50px;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: var(--pico-background-color);
|
||||||
|
border: 0;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
opacity: 1;
|
||||||
|
z-index: 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror {
|
||||||
|
background-color: var(--pico-background-color);
|
||||||
|
color: var(--pico-color) !important;
|
||||||
|
border-color: var(--pico-primary-hover) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-preview-active{
|
||||||
|
background: var(--pico-background-color);
|
||||||
|
color: var(--pico-color) !important;
|
||||||
|
padding-right : 4vw;
|
||||||
|
padding-left: 4vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-preview-active-side {
|
||||||
|
background: var(--pico-background-color);
|
||||||
|
color: var(--pico-color) !important;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
width: 50%;
|
||||||
|
top: 50px;
|
||||||
|
right: 0;
|
||||||
|
z-index: 9;
|
||||||
|
border: 2px solid;
|
||||||
|
border-color: var(--pico-primary-hover) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.CodeMirror .CodeMirror-code .cm-tag {
|
||||||
|
color: #63a35c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror .CodeMirror-code .cm-attribute {
|
||||||
|
color: #795da3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror .CodeMirror-code .cm-string {
|
||||||
|
color: #183691;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror .CodeMirror-selected {
|
||||||
|
background: var(--pico-text-selection-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.CodeMirror .CodeMirror-code .cm-link {
|
||||||
|
color: #7f8c8d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror .CodeMirror-code .cm-url {
|
||||||
|
color: #aab2b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.CodeMirror-cursor {
|
||||||
|
border-left-color: var(--pico-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-statusbar .lines:before {
|
||||||
|
color: var(--pico-primary) !important;
|
||||||
|
content: 'lignes: '
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-statusbar .words:before {
|
||||||
|
color: var(--pico-primary) !important;
|
||||||
|
content: 'Mots: '
|
||||||
|
}
|
||||||
214
static/up.css
214
static/up.css
@@ -1,214 +0,0 @@
|
|||||||
#tada {
|
|
||||||
text-align: center;
|
|
||||||
text-decoration: none;
|
|
||||||
display: block;
|
|
||||||
position: relative;
|
|
||||||
margin: auto;
|
|
||||||
font-size: 16px;
|
|
||||||
-webkit-animation-name: tada;
|
|
||||||
-webkit-animation-duration: 1s;
|
|
||||||
-webkit-animation-iteration-count: 1;
|
|
||||||
animation-name: tada;
|
|
||||||
animation-duration: 1s;
|
|
||||||
animation-iteration-count: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.msginfo {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
margin: auto;
|
|
||||||
top: 5%;
|
|
||||||
margin-left: 5%;
|
|
||||||
margin-bottom: 0%;
|
|
||||||
text-align: center;
|
|
||||||
width: 90%;
|
|
||||||
|
|
||||||
-webkit-animation-name: apparition;
|
|
||||||
-webkit-animation-duration: 0.2s;
|
|
||||||
-webkit-animation-iteration-count: 1;
|
|
||||||
animation-name: apparition;
|
|
||||||
animation-duration: 0.2s;
|
|
||||||
animation-iteration-count: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flashed {
|
|
||||||
list-style-type: none;
|
|
||||||
position: center;
|
|
||||||
margin: auto;
|
|
||||||
margin-top: 5%;
|
|
||||||
width: 50%;
|
|
||||||
background-color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flashed p {
|
|
||||||
font-family: sans-serif;
|
|
||||||
text-align: center;
|
|
||||||
border-radius: 3px;
|
|
||||||
/*box-shadow: 1px 1px 1px black; */
|
|
||||||
}
|
|
||||||
|
|
||||||
.success p {
|
|
||||||
background-color: #444;
|
|
||||||
color: #00C613;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error p {
|
|
||||||
background-color: #444;
|
|
||||||
color: #FF4A4A;
|
|
||||||
}
|
|
||||||
|
|
||||||
#majuscule {
|
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
|
||||||
|
|
||||||
#fic {
|
|
||||||
display: block;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ######### Animations ######### */
|
|
||||||
|
|
||||||
@-webkit-keyframes apparition {
|
|
||||||
0% {opacity: 0; }
|
|
||||||
100% {opacity: 1; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes apparition {
|
|
||||||
0% {opacity: 0; }
|
|
||||||
100% {opacity: 1; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@-webkit-keyframes disparition {
|
|
||||||
100% {opacity: 0; }
|
|
||||||
0% {opacity: 1; display: none; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes disparition {
|
|
||||||
100% {opacity: 0; }
|
|
||||||
0% {opacity: 1; display: none; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@-webkit-keyframes tada {
|
|
||||||
0% {
|
|
||||||
-webkit-transform: scale(1);
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
10%, 20% {
|
|
||||||
-webkit-transform: scale(0.9) rotate(-3deg);
|
|
||||||
transform: scale(0.9) rotate(-3deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
30%, 50%, 70%, 90% {
|
|
||||||
-webkit-transform: scale(1.1) rotate(3deg);
|
|
||||||
transform: scale(1.1) rotate(3deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
40%, 60%, 80% {
|
|
||||||
-webkit-transform: scale(1.1) rotate(-3deg);
|
|
||||||
transform: scale(1.1) rotate(-3deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
-webkit-transform: scale(1) rotate(0);
|
|
||||||
transform: scale(1) rotate(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes tada {
|
|
||||||
0% {
|
|
||||||
-webkit-transform: scale(1);
|
|
||||||
-ms-transform: scale(1);
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
10%, 20% {
|
|
||||||
-webkit-transform: scale(0.9) rotate(-3deg);
|
|
||||||
-ms-transform: scale(0.9) rotate(-3deg);
|
|
||||||
transform: scale(0.9) rotate(-3deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
30%, 50%, 70%, 90% {
|
|
||||||
-webkit-transform: scale(1.1) rotate(3deg);
|
|
||||||
-ms-transform: scale(1.1) rotate(3deg);
|
|
||||||
transform: scale(1.1) rotate(3deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
40%, 60%, 80% {
|
|
||||||
-webkit-transform: scale(1.1) rotate(-3deg);
|
|
||||||
-ms-transform: scale(1.1) rotate(-3deg);
|
|
||||||
transform: scale(1.1) rotate(-3deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
-webkit-transform: scale(1) rotate(0);
|
|
||||||
-ms-transform: scale(1) rotate(0);
|
|
||||||
transform: scale(1) rotate(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#gallery {
|
|
||||||
margin-top: 10vw;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item{
|
|
||||||
margin-bottom: 1vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item img{
|
|
||||||
width: 20vw;
|
|
||||||
display:flex;
|
|
||||||
flex-wrap:wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.row {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
padding: 0 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.control{
|
|
||||||
display:block;
|
|
||||||
position:inherit;
|
|
||||||
margin-top:5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create four equal columns that sits next to each other */
|
|
||||||
.column {
|
|
||||||
flex: 30%;
|
|
||||||
max-width: 30%;
|
|
||||||
padding: 0 4px;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.column img {
|
|
||||||
margin-top: 8px;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.column img:hover {
|
|
||||||
margin-top: 8px;
|
|
||||||
vertical-align: middle;
|
|
||||||
transform: scale(1, 1.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Responsive layout - makes a two column-layout instead of four columns */
|
|
||||||
@media (max-width: 850px) {
|
|
||||||
.column {
|
|
||||||
flex: 50%;
|
|
||||||
max-width: 50%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsive layout - makes the two columns stack on top of each other instead of next to each other */
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
.column {
|
|
||||||
flex: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
2
static/vendors/dropzone/dropzone-min.js
vendored
Normal file
2
static/vendors/dropzone/dropzone-min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
static/vendors/dropzone/dropzone-min.js.map
vendored
Normal file
1
static/vendors/dropzone/dropzone-min.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
1
static/vendors/dropzone/dropzone.css
vendored
Normal file
1
static/vendors/dropzone/dropzone.css
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
@keyframes passing-through{0%{opacity:0;transform:translateY(40px)}30%,70%{opacity:1;transform:translateY(0px)}100%{opacity:0;transform:translateY(-40px)}}@keyframes slide-in{0%{opacity:0;transform:translateY(40px)}30%{opacity:1;transform:translateY(0px)}}@keyframes pulse{0%{transform:scale(1)}10%{transform:scale(1.1)}20%{transform:scale(1)}}.dropzone,.dropzone *{box-sizing:border-box}.dropzone{min-height:150px;border:1px solid rgba(0,0,0,.8);border-radius:5px;padding:20px 20px}.dropzone.dz-clickable{cursor:pointer}.dropzone.dz-clickable *{cursor:default}.dropzone.dz-clickable .dz-message,.dropzone.dz-clickable .dz-message *{cursor:pointer}.dropzone.dz-started .dz-message{display:none}.dropzone.dz-drag-hover{border-style:solid}.dropzone.dz-drag-hover .dz-message{opacity:.5}.dropzone .dz-message{text-align:center;margin:3em 0}.dropzone .dz-message .dz-button{background:none;color:inherit;border:none;padding:0;font:inherit;cursor:pointer;outline:inherit}.dropzone .dz-preview{position:relative;display:inline-block;vertical-align:top;margin:16px;min-height:100px}.dropzone .dz-preview:hover{z-index:1000}.dropzone .dz-preview:hover .dz-details{opacity:1}.dropzone .dz-preview.dz-file-preview .dz-image{border-radius:20px;background:#999;background:linear-gradient(to bottom, #eee, #ddd)}.dropzone .dz-preview.dz-file-preview .dz-details{opacity:1}.dropzone .dz-preview.dz-image-preview{background:#fff}.dropzone .dz-preview.dz-image-preview .dz-details{transition:opacity .2s linear}.dropzone .dz-preview .dz-remove{font-size:14px;text-align:center;display:block;cursor:pointer;border:none}.dropzone .dz-preview .dz-remove:hover{text-decoration:underline}.dropzone .dz-preview:hover .dz-details{opacity:1}.dropzone .dz-preview .dz-details{z-index:20;position:absolute;top:0;left:0;opacity:0;font-size:13px;min-width:100%;max-width:100%;padding:2em 1em;text-align:center;color:rgba(0,0,0,.9);line-height:150%}.dropzone .dz-preview .dz-details .dz-size{margin-bottom:1em;font-size:16px}.dropzone .dz-preview .dz-details .dz-filename{white-space:nowrap}.dropzone .dz-preview .dz-details .dz-filename:hover span{border:1px solid rgba(200,200,200,.8);background-color:hsla(0,0%,100%,.8)}.dropzone .dz-preview .dz-details .dz-filename:not(:hover){overflow:hidden;text-overflow:ellipsis}.dropzone .dz-preview .dz-details .dz-filename:not(:hover) span{border:1px solid rgba(0,0,0,0)}.dropzone .dz-preview .dz-details .dz-filename span,.dropzone .dz-preview .dz-details .dz-size span{background-color:hsla(0,0%,100%,.4);padding:0 .4em;border-radius:3px}.dropzone .dz-preview:hover .dz-image img{transform:scale(1.05, 1.05);filter:blur(8px)}.dropzone .dz-preview .dz-image{border-radius:20px;overflow:hidden;width:120px;height:120px;position:relative;display:block;z-index:10}.dropzone .dz-preview .dz-image img{display:block}.dropzone .dz-preview.dz-success .dz-success-mark{animation:passing-through 3s cubic-bezier(0.77, 0, 0.175, 1)}.dropzone .dz-preview.dz-error .dz-error-mark{opacity:1;animation:slide-in 3s cubic-bezier(0.77, 0, 0.175, 1)}.dropzone .dz-preview .dz-success-mark,.dropzone .dz-preview .dz-error-mark{pointer-events:none;opacity:0;z-index:500;position:absolute;display:block;top:50%;left:50%;margin-left:-27px;margin-top:-27px;background:rgba(0,0,0,.8);border-radius:50%}.dropzone .dz-preview .dz-success-mark svg,.dropzone .dz-preview .dz-error-mark svg{display:block;width:54px;height:54px;fill:#fff}.dropzone .dz-preview.dz-processing .dz-progress{opacity:1;transition:all .2s linear}.dropzone .dz-preview.dz-complete .dz-progress{opacity:0;transition:opacity .4s ease-in}.dropzone .dz-preview:not(.dz-processing) .dz-progress{animation:pulse 6s ease infinite}.dropzone .dz-preview .dz-progress{opacity:1;z-index:1000;pointer-events:none;position:absolute;height:20px;top:50%;margin-top:-10px;left:15%;right:15%;border:3px solid rgba(0,0,0,.8);background:rgba(0,0,0,.8);border-radius:10px;overflow:hidden}.dropzone .dz-preview .dz-progress .dz-upload{background:#fff;display:block;position:relative;height:100%;width:0;transition:width 300ms ease-in-out;border-radius:17px}.dropzone .dz-preview.dz-error .dz-error-message{display:block}.dropzone .dz-preview.dz-error:hover .dz-error-message{opacity:1;pointer-events:auto}.dropzone .dz-preview .dz-error-message{pointer-events:none;z-index:1000;position:absolute;display:block;display:none;opacity:0;transition:opacity .3s ease;border-radius:8px;font-size:13px;top:130px;left:-10px;width:140px;background:#b10606;padding:.5em 1em;color:#fff}.dropzone .dz-preview .dz-error-message:after{content:"";position:absolute;top:-6px;left:64px;width:0;height:0;border-left:6px solid rgba(0,0,0,0);border-right:6px solid rgba(0,0,0,0);border-bottom:6px solid #b10606}/*# sourceMappingURL=dropzone.css.map */
|
||||||
1
static/vendors/htmx/htmx.min.js
vendored
Normal file
1
static/vendors/htmx/htmx.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
85
static/vendors/picocss/theme-switcher.js
vendored
85
static/vendors/picocss/theme-switcher.js
vendored
@@ -2,34 +2,83 @@
|
|||||||
* Minimal theme switcher
|
* Minimal theme switcher
|
||||||
*
|
*
|
||||||
* Pico.css - https://picocss.com
|
* Pico.css - https://picocss.com
|
||||||
* Copyright 2020 - Licensed under MIT
|
* Copyright 2019-2023 - Licensed under MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const themeSwitcher = {
|
const themeSwitcher = {
|
||||||
// Config
|
// Config
|
||||||
|
_scheme: "auto",
|
||||||
|
menuTarget: "details[role='list']",
|
||||||
buttonsTarget: "a[data-theme-switcher]",
|
buttonsTarget: "a[data-theme-switcher]",
|
||||||
buttonAttribute: "data-theme-switcher",
|
buttonAttribute: "data-theme-switcher",
|
||||||
rootAttribute: "data-theme",
|
rootAttribute: "data-theme",
|
||||||
|
localStorageKey: "picoPreferredColorScheme",
|
||||||
|
|
||||||
// Init
|
// Init
|
||||||
init() {
|
init() {
|
||||||
document.querySelectorAll(this.buttonsTarget).forEach(
|
this.scheme = this.schemeFromLocalStorage;
|
||||||
function (button) {
|
this.initSwitchers();
|
||||||
button.addEventListener(
|
},
|
||||||
"click",
|
|
||||||
function (event) {
|
// Get color scheme from local storage
|
||||||
event.preventDefault();
|
get schemeFromLocalStorage() {
|
||||||
document
|
if (typeof window.localStorage !== "undefined") {
|
||||||
.querySelector("html")
|
if (window.localStorage.getItem(this.localStorageKey) !== null) {
|
||||||
.setAttribute(
|
return window.localStorage.getItem(this.localStorageKey);
|
||||||
this.rootAttribute,
|
}
|
||||||
event.target.getAttribute(this.buttonAttribute)
|
}
|
||||||
);
|
return this._scheme;
|
||||||
}.bind(this),
|
},
|
||||||
false
|
|
||||||
);
|
// Preferred color scheme
|
||||||
}.bind(this)
|
get preferredColorScheme() {
|
||||||
);
|
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
||||||
|
},
|
||||||
|
|
||||||
|
// Init switchers
|
||||||
|
initSwitchers() {
|
||||||
|
const buttons = document.querySelectorAll(this.buttonsTarget);
|
||||||
|
buttons.forEach((button) => {
|
||||||
|
button.addEventListener(
|
||||||
|
"click",
|
||||||
|
(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
// Set scheme
|
||||||
|
this.scheme = button.getAttribute(this.buttonAttribute);
|
||||||
|
// Close dropdown
|
||||||
|
document.querySelector(this.menuTarget).removeAttribute("open");
|
||||||
|
},
|
||||||
|
false
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Set scheme
|
||||||
|
set scheme(scheme) {
|
||||||
|
if (scheme == "auto") {
|
||||||
|
this.preferredColorScheme == "dark" ? (this._scheme = "dark") : (this._scheme = "light");
|
||||||
|
} else if (scheme == "dark" || scheme == "light") {
|
||||||
|
this._scheme = scheme;
|
||||||
|
}
|
||||||
|
this.applyScheme();
|
||||||
|
this.schemeToLocalStorage();
|
||||||
|
},
|
||||||
|
|
||||||
|
// Get scheme
|
||||||
|
get scheme() {
|
||||||
|
return this._scheme;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Apply scheme
|
||||||
|
applyScheme() {
|
||||||
|
document.querySelector("html").setAttribute(this.rootAttribute, this.scheme);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Store scheme to local storage
|
||||||
|
schemeToLocalStorage() {
|
||||||
|
if (typeof window.localStorage !== "undefined") {
|
||||||
|
window.localStorage.setItem(this.localStorageKey, this.scheme);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
@@ -11,7 +11,5 @@
|
|||||||
<link href="{{ url_for('static', filename='vendors/picocss/pico.colors.min.css') }}" rel="stylesheet">
|
<link href="{{ url_for('static', filename='vendors/picocss/pico.colors.min.css') }}" rel="stylesheet">
|
||||||
<link href="{{ url_for('static', filename='pywallter.css') }}" rel="stylesheet">
|
<link href="{{ url_for('static', filename='pywallter.css') }}" rel="stylesheet">
|
||||||
<link href="{{ url_for('static', filename='icons.css') }}" rel="stylesheet">
|
<link href="{{ url_for('static', filename='icons.css') }}" rel="stylesheet">
|
||||||
<link href="{{ url_for('static', filename='simplemde.min.css') }}" rel="stylesheet">
|
<link href="{{ url_for('static', filename='vendors/glightbox/glightbox.min.css') }}" rel="stylesheet">
|
||||||
<link href="{{ url_for('static', filename='glightbox.min.css') }}" rel="stylesheet">
|
|
||||||
|
|
||||||
</head>
|
|
||||||
|
|||||||
@@ -5,6 +5,15 @@
|
|||||||
<span class="menu-icon"></span>
|
<span class="menu-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
|
||||||
|
<details class="dropdown">
|
||||||
|
<summary role="button" class="secondary menu-header">Theme</summary>
|
||||||
|
<ul>
|
||||||
|
<li><a href="#" data-theme-switcher="auto">Auto</a></li>
|
||||||
|
<li><a href="#" data-theme-switcher="light">Clair</a></li>
|
||||||
|
<li><a href="#" data-theme-switcher="dark">Sombre</a></li>
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
<a href="/logs/">
|
<a href="/logs/">
|
||||||
<button class="menu-header">
|
<button class="menu-header">
|
||||||
Mes logs <br/>
|
Mes logs <br/>
|
||||||
@@ -19,14 +28,6 @@
|
|||||||
</button>
|
</button>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<details class="dropdown">
|
|
||||||
<summary role="button" class="secondary menu-header">Theme</summary>
|
|
||||||
<ul>
|
|
||||||
<li><a href="#" data-theme-switcher="auto">Auto</a></li>
|
|
||||||
<li><a href="#" data-theme-switcher="light">Clair</a></li>
|
|
||||||
<li><a href="#" data-theme-switcher="dark">Sombre</a></li>
|
|
||||||
</ul>
|
|
||||||
</details>
|
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<!-- Bootstrap core JavaScript -->
|
|
||||||
<!--================================================== -->
|
<script src="{{ url_for('static', filename='vendors/jquery/jquery.min.js') }}"></script>
|
||||||
<!-- 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='vendors/picocss/theme-switcher.js') }}"></script>
|
<script src="{{ url_for('static', filename='vendors/picocss/theme-switcher.js') }}"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/qrcode.min.js') }}"></script>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<script src="{{ url_for('static', filename='glightbox.min.js') }}"></script>
|
<script src="{{ url_for('static', filename='vendors/glightbox/glightbox.min.js') }}"></script>
|
||||||
<script>
|
<script>
|
||||||
var lightbox = GLightbox();
|
var lightbox = GLightbox();
|
||||||
lightbox.on('open', (target) => {
|
lightbox.on('open', (target) => {
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
|
|
||||||
<!-- 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='docs.min.js') }}"></script>
|
|
||||||
<script src="{{ url_for('static', filename='simplemde.min.js') }}"></script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
new SimpleMDE({
|
|
||||||
element: document.getElementById("editeurMarkdown"),
|
|
||||||
spellChecker: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
26
templates/_js_dropzone.html
Normal file
26
templates/_js_dropzone.html
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
<script>
|
||||||
|
window.onload = function () {
|
||||||
|
|
||||||
|
var dropzoneOptions = {
|
||||||
|
dictDefaultMessage: 'Déposez vos fichiers ici!',
|
||||||
|
paramName: "file",
|
||||||
|
maxFilesize: 1024, // MB
|
||||||
|
url: "/upload-dropzone",
|
||||||
|
chunking: true,
|
||||||
|
forceChunking: true,
|
||||||
|
chunkSize: 1000000,
|
||||||
|
autoProcessQueue: true,
|
||||||
|
init: function () {
|
||||||
|
this.on("success", function (file) {
|
||||||
|
console.log("success > " + file.name);
|
||||||
|
setTimeout(() => { this.removeFile(file); }, 2000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var uploader = document.querySelector('#uploader');
|
||||||
|
var myDropzone = new Dropzone(uploader, dropzoneOptions);
|
||||||
|
console.log("Loaded");
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
<script src="{{ url_for('static', filename='simplemde.min.js') }}"></script>
|
<script src="{{ url_for('static', filename='vendors/simplemde/simplemde.min.js') }}"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
new SimpleMDE({
|
new SimpleMDE({
|
||||||
|
|||||||
4
templates/_js_htmx.html
Normal file
4
templates/_js_htmx.html
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
|
||||||
|
<script src="{{ url_for('static', filename='vendors/htmx/htmx.min.js') }}"> </script>
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
<li>
|
<li>
|
||||||
|
|
||||||
<a href="/myblog/new-article/" {% if request.path == "/myblog/new-article/" %} class="invert" {% endif %} > <span class="icons new-article-blog"></span>
|
<a href="/myblog/new-article/" {% if request.path == "/myblog/new-article/" %} class="invert" {% endif %} > <span class="icons new-article-blog"></span>
|
||||||
Ecrire un billet </a>
|
Écrire un billet </a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/myblog/list-articles/" {% if request.path == "/myblog/list-articles/" %} class="invert" {% endif %} ><span class="icons list-articles-blog"></span>
|
<a href="/myblog/list-articles/" {% if request.path == "/myblog/list-articles/" %} class="invert" {% endif %} ><span class="icons list-articles-blog"></span>
|
||||||
@@ -21,14 +21,21 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/myblog/personalize/" {% if request.path == "/myblog/personalize/" %} class="invert" {% endif %} ><span class="icons custom-blog"></span>
|
<a href="/myblog/personnalize/" {% if request.path == "/myblog/personnalize/" %} class="invert" {% endif %} ><span class="icons custom-blog"></span>
|
||||||
Configurer mon site web
|
Personnaliser mon blog
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/myblog/view/" {% if request.path == "/myblog/view/" %} class="invert" {% endif %} ><span class="icons view-blog" aria-hidden="true"></span>
|
<a href="/myblog/" {% if request.path == "/myblog/" %} class="invert" {% endif %} ><span class="icons view-blog" aria-hidden="true"></span>
|
||||||
Voir mon blog
|
Voir mon blog
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/private-blog/" {% if request.path == "/private-blog/" %} class="invert" {% endif %} ><span class="icons view-blog" aria-hidden="true"></span>
|
||||||
|
Blog du serveur
|
||||||
|
</a>
|
||||||
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
@@ -38,14 +45,13 @@
|
|||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a href="/view/" {% if request.path == "/view/" %} class="invert" {% endif %}> <span class="icons myfiles"></span> Mes dossiers personels </a>
|
<a href="/view/" {% if request.path == "/view/" %} class="invert" {% endif %}> <span class="icons myfiles"></span> Mon dossier personnel </a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/gallery/" {% if request.path == "/gallery/" %} class="invert" {% endif %} > <span class="icons mygallery"></span> Ma gallerie d'images </a>
|
<a href="/gallery/" {% if request.path == "/gallery/" %} class="invert" {% endif %} > <span class="icons mygallery"></span> Ma galerie d'images </a>
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="/filesupload/" {% if request.path == "/filesupload/" %} class="invert" {% endif %}> <span class="icons send-files"></span> Envoyer des fichiers </a>
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
@@ -57,16 +63,15 @@
|
|||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a href="/mymailbox/alias" {% if request.path == "/mymailbox/messagerie" %} class="invert" {% endif %}><span class="icons infos-messaging"></span>
|
<a href="/mymailbox/" {% if request.path == "/mymailbox/" %} class="invert" {% endif %}><span class="icons infos-messaging"></span>
|
||||||
Ma messagerie
|
Ma messagerie
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/mymailbox/alias" {% if request.path == "/mymailbox/alias" %} class="invert" {% endif %} ><span class="icons myalias"></span>
|
<a href="/mymailbox/alias" {% if request.path == "/mymailbox/alias" %} class="invert" {% endif %} ><span class="icons myalias"></span>
|
||||||
Gerer mes alias
|
Gérer mes alias
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
@@ -80,7 +85,7 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li ><a href="/profil/" {% if request.path == "/profil/" %} class="invert" {% endif %} ><span class="icons configure-profile"></span> Personnaliser mon profil</a></li>
|
<li ><a href="/profil/" {% if request.path == "/profil/" %} class="invert" {% endif %} ><span class="icons configure-profile"></span> Personnaliser mon profil</a></li>
|
||||||
<li><a href="/profil/change-password/" {% if request.path == "/profil/change-password/" %} class="invert" {% endif %} > <span class="icons mypassword" aria-hidden="true"></span>
|
<li><a href="/profil/change-password/" {% if request.path == "/profil/change-password/" %} class="invert" {% endif %} > <span class="icons mypassword" aria-hidden="true"></span>
|
||||||
Changer mon mot de passe </a></li>
|
Mot de passe & 2FA </a></li>
|
||||||
<li><a href="/delete_me/" {% if request.path == "/delete_me/" %} class="invert" {% endif %} ><span class="icons user-delete"></span> Supprimer mon compte </a></li>
|
<li><a href="/delete_me/" {% if request.path == "/delete_me/" %} class="invert" {% endif %} ><span class="icons user-delete"></span> Supprimer mon compte </a></li>
|
||||||
<li><a href="/invitation/" {% if request.path == "invitation/" %} class="invert" {% endif %} ><span class="icons invite"></span> Inviter une personne</a></li>
|
<li><a href="/invitation/" {% if request.path == "invitation/" %} class="invert" {% endif %} ><span class="icons invite"></span> Inviter une personne</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -9,38 +9,69 @@
|
|||||||
{% block main %}
|
{% block main %}
|
||||||
|
|
||||||
|
|
||||||
<h1> Bienvenue sur</h1>
|
<h1> Bienvenue sur : "{{ server_title }}" </h1>
|
||||||
|
<h3> {{ server_desc }} </h3>
|
||||||
|
|
||||||
<p>Bienvenue sur Olala, un portail utilisateur libre basé sur Flask à héberger sur un petit ordinateur.
|
<p>Bienvenue sur Pywallter, un application web basé sur Flask qui se veut être la plus légère possible.
|
||||||
Tu peux importer des fichiers et dans l'avenir les rendres disponibles pour ton site.
|
Tu peux importer des fichiers et dans l'avenir les rendres disponibles pour ton site ou ton blog.
|
||||||
Tu peux consulter ou participer au tableau des post-its pour communiquer avec les autres membres ou
|
Tu peux participer ou consulter le blog général du serveur pour te faire connaître par les autres membres ou
|
||||||
simplement savoir ce qu'il se passe sur le serveur.
|
simplement savoir ce qu'il se passe sur le serveur. Tu peux poster sur ton blog public et te faire connaître par les internets :p
|
||||||
</p>
|
</p>
|
||||||
|
{% if mail_server %}
|
||||||
<p>
|
<p>
|
||||||
Tu peux gérer ton compte MAIL et XMPP si les serveurs Mail(SMTP, IMAP) et XMPP sont actifs.
|
Si tu as la chance d'avoir un compte ici tu as accès à une messagerie e-mail et une messagerie instantanné avec ce même compte.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{% include '_flash_msgs.html' %}
|
{% endif %}
|
||||||
|
{% include '_flash_msgs.html' %}
|
||||||
|
|
||||||
|
|
||||||
<p class="lead">
|
<p class="lead">
|
||||||
<form method="POST" action="{{ url_for('loginlogout.login') }}">
|
<form method="POST" action="{{ url_for('loginlogout.login') }}">
|
||||||
<input type="text" name="user" id="user" placeholder="Utilisateur" class="form-control" width="200px"><br />
|
<input type="text" name="user" id="user" placeholder="Utilisateur" width="200px"><br />
|
||||||
<input type="password" name="passwd" id="passwd" placeholder="Mot de passe" class="form-control"><br />
|
<input type="password" name="passwd" id="passwd" placeholder="Mot de passe"><br />
|
||||||
<br>
|
<input type="checkbox" id="totpcheckbox">
|
||||||
|
<label for="totpcheckbox">J'ai un mot de passe à usage unique</label>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<label for="2FAInput" class="totp">Code TOTP:</label>
|
||||||
|
<input type="tel" name="code_totp" id="code_totp" class="totp" maxlength="6">
|
||||||
|
|
||||||
|
<br>
|
||||||
<p class="center"><a href="{{ url_for('loginlogout.lost_password') }}"> Mouarf j'ai perdu mon mot de passe </a> </p>
|
<p class="center"><a href="{{ url_for('loginlogout.lost_password') }}"> Mouarf j'ai perdu mon mot de passe </a> </p>
|
||||||
<button id="tada" class="btn btn-default btn-primary" type="submit"> Login </button>
|
<button class="btn btn-default btn-primary" type="submit"> Login </button>
|
||||||
</form>
|
</form>
|
||||||
|
{% if list_posts %}
|
||||||
|
<h2> Les derniers articles de blog sur le serveur </h2>
|
||||||
|
<div class="list-articles" >
|
||||||
|
|
||||||
|
{% for article in list_posts %}
|
||||||
|
<article>
|
||||||
|
<header>
|
||||||
|
<h3> {{ article.title }} </h3>
|
||||||
|
<h5> par <a href="/blog/{{ article.author }}/">{{ article.author }}</a> </h5>
|
||||||
|
<br/>
|
||||||
|
<small> Créé le : {{ article.creation_date }} </small> <br/>
|
||||||
|
{% if article.last_updated %}
|
||||||
|
<small> Modifié le : {{ article.last_updated }}</small><br/>
|
||||||
|
{% endif %}
|
||||||
|
</header>
|
||||||
|
<div class="subtitle">
|
||||||
|
<p> {{ article.subtitle }} </p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<footer><a href="/blog//public_unified/{{ article.author }}/{{ article.title }}"> <button> Lire la suite </button> </a></footer>
|
||||||
{% endblock %}
|
</article>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|
||||||
{% include '_footer.html' %}
|
{% include '_footer.html' %}
|
||||||
|
|
||||||
{% include '_js.html' %}
|
{% include '_js-core.html' %}
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,33 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title> Blog de {{ user }} </title>
|
<title> Article de {{ post_info.author }} </title>
|
||||||
<link rel="stylesheet" href="/static/blog.css" type="text/css">
|
|
||||||
|
<link rel="stylesheet" href="/{{ post_info.author }}/blog.css" type="text/css">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="articles">
|
<div class="articles">
|
||||||
|
<div class="head-article">
|
||||||
|
<h2> {{ post_info.title }} </h2>
|
||||||
|
|
||||||
<h2 class="titre"> {{ post_info.title }} </h2>
|
<h5>Publié le : {{ post_info.creation_date }} par <a href="{{ url_for('blog.view') }}/{{ post_info.author }}"> {{ post_info.author }} </h5></a>
|
||||||
|
{% if post_info.last_updated %}
|
||||||
|
<h5>Mis à jour le : {{ post_info.last_updated }} </h5>
|
||||||
|
{% endif %}
|
||||||
|
<h3 class="description"> {{ post_info.subtitle }} </h3>
|
||||||
|
</div>
|
||||||
|
<hr/>
|
||||||
|
|
||||||
<h5 class="titre">Publié le {{ post_info.time }} </h5>
|
<div class="text-article">
|
||||||
|
|
||||||
<h2 class="description titre"> {{ post_info.subtitle }} </h2>
|
|
||||||
<hr/>
|
|
||||||
{{ content|safe }}
|
{{ content|safe }}
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<footer> <p> pywallter </p> </footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
26
templates/blog_author.html
Normal file
26
templates/blog_author.html
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title> Article de {{ post_info.author }} </title>
|
||||||
|
<link rel="stylesheet" href="/{{ post_info.author}}/blog.css" type="text/css">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="articles">
|
||||||
|
|
||||||
|
<h2 class="titre"> {{ post_info.title }} </h2>
|
||||||
|
|
||||||
|
<h5 class="titre">Publié le : {{ post_info.creation_date }} </h5>
|
||||||
|
{% if post_info.last_updated %}
|
||||||
|
<h5 class="titre">Mis à jour le : {{ post_info.last_updated }} </h5>
|
||||||
|
{% endif %}
|
||||||
|
<h3 class="description titre"> {{ post_info.subtitle }} </h3>
|
||||||
|
<hr/>
|
||||||
|
{{ content|safe }}
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
35
templates/blog_rss.xml
Normal file
35
templates/blog_rss.xml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<rss version="2.0"
|
||||||
|
xmlns:content="http://purl.org/rss/1.0/modules/content/"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:atom="http://www.w3.org/2005/Atom"
|
||||||
|
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
|
||||||
|
xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
|
||||||
|
<channel>
|
||||||
|
<title> Le blog de {{ blog_name }} </title>
|
||||||
|
<link> {{ base_url }}/blog/ </link>
|
||||||
|
{% if blog_description %}
|
||||||
|
<description> {{ blog_description }} </description>
|
||||||
|
{% endif %}
|
||||||
|
<language>fr</language>
|
||||||
|
<lastBuildDate> {{ last_build_date }} </lastBuildDate>
|
||||||
|
|
||||||
|
{% for post in posts %}
|
||||||
|
<item>
|
||||||
|
<title> {{ post.title }} </title>
|
||||||
|
<pubdate> {% if post.last_updated %} {{ post.last_updated }} {% else %} {{ post.time }} {% endif %} </pubdate>
|
||||||
|
<link> {{base_url}}/blog/{{ post.status }}/{{post.author}}/{{post.title}}</link>
|
||||||
|
<guid> {{base_url}}/blog/{{ post.status }}/{{post.author}}/{{post.title}}</guid>
|
||||||
|
|
||||||
|
<description>
|
||||||
|
{{ post.subtitle }}
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<content:encoded>
|
||||||
|
{{ post.content | safe }}
|
||||||
|
</content:encoded>
|
||||||
|
</item>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
</channel>
|
||||||
|
</rss>
|
||||||
3
templates/css/dropzone.html
Normal file
3
templates/css/dropzone.html
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<script src="{{ url_for('static', filename='vendors/dropzone/dropzone-min.js') }}"></script>
|
||||||
|
<link href="{{ url_for('static', filename='vendors/dropzone/dropzone.css') }}" rel="stylesheet">
|
||||||
|
|
||||||
3
templates/css/simple_editor.html
Normal file
3
templates/css/simple_editor.html
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
<link href="{{ url_for('static', filename='vendors/simplemde/simplemde.min.css') }}" rel="stylesheet">
|
||||||
|
<link href="{{ url_for('static', filename='simplemde-custom.css') }}" rel="stylesheet">
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<h1> Supprimer mon compte </h1>
|
<h1> Supprimer mon compte </h1>
|
||||||
<p class="center"> Vous voulez supprimer votre compte pas de problèmes toutes vos données seront effacées du serveur et après un periodes de {{ time_backup }} vos données seront complètements supprimées des sauvegardes, il n'y aura aucun retour en arrière possible.
|
<p class="center"> Vous voulez supprimer votre compte pas de problème, toutes vos données seront effacées du serveur et après un periodes de {{ time_backup }} vos données seront complètements supprimées des sauvegardes, il n'y aura aucun retour en arrière possible.
|
||||||
</p>
|
</p>
|
||||||
<h3> Entrez votre mot de passe pour confirmer la suppression de votre compte </h3>
|
<h3> Entrez votre mot de passe pour confirmer la suppression de votre compte </h3>
|
||||||
|
|
||||||
|
|||||||
@@ -1,32 +1,63 @@
|
|||||||
{% extends 'up_squelette.html' %}
|
{% extends 'up_squelette.html' %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
{% include 'css/simple_editor.html' %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
|
|
||||||
<p>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.</p>
|
|
||||||
|
|
||||||
|
<form action="" method="POST" id="postform" >
|
||||||
|
<input type="text" name="title" id="title" placeholder="titre" class="form-control" value="{{ oldpost['title'] }}"><br />
|
||||||
|
<input type="text" name="subtitle" id="subtitle" placeholder="sous-titre" class="form-control" value="{{ oldpost['subtitle'] }}"><br />
|
||||||
|
<input type="text" name="category" id="category" placeholder="Catégorie" class="form-control" value="{{ oldpost['category'] }}"><br />
|
||||||
|
<textarea id="editeurMarkdown" class="form-control" form="postform" name="content" id="content" placeholder="Contenu" style="height:20vw;">{{ oldpost['content'] }}</textarea>
|
||||||
|
|
||||||
<h2> {{ oldpost[0] }}</h2><br />
|
<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" style="height:20vw;">{{ content }}</textarea><br />
|
|
||||||
<h3> Visibilité </h3>
|
<h3> Visibilité </h3>
|
||||||
|
|
||||||
<div class="center">
|
<div class="center">
|
||||||
{% if oldpost[2] == 'public' %}
|
<p> Les articles brouillons ne sont visibles que par vous même</p>
|
||||||
<input type="radio" name="status" value="prive"> Privé
|
<p> Les articles privés ne sont visibles que par les membres du serveur </p>
|
||||||
|
<p> Les articles public sont visibles par tout le monde </p>
|
||||||
|
|
||||||
|
|
||||||
|
{% if oldpost['status'] == 'public' or oldpost['status'] == "public_unified" %}
|
||||||
|
<input type="radio" name="status" value="draft"> Brouillon
|
||||||
|
<input type="radio" name="status" value="private"> Privé
|
||||||
<input type="radio" name="status" value="public" checked> Publique
|
<input type="radio" name="status" value="public" checked> Publique
|
||||||
|
|
||||||
|
{% elif oldpost['status'] == 'private' or oldpost['status'] == "private_unified" %}
|
||||||
|
<input type="radio" name="status" value="draft"> Brouillon
|
||||||
|
<input type="radio" name="status" value="private" checked> Privé
|
||||||
|
<input type="radio" name="status" value="public"> Publique
|
||||||
{% else %}
|
{% else %}
|
||||||
<input type="radio" name="status" value="Privé" checked> Privé
|
<input type="radio" name="status" value="draft" checked> Brouillon
|
||||||
|
<input type="radio" name="status" value="private"> Privé
|
||||||
<input type="radio" name="status" value="public"> Publique
|
<input type="radio" name="status" value="public"> Publique
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<br />
|
|
||||||
|
<fieldset>
|
||||||
|
<label>
|
||||||
|
|
||||||
|
{% if oldpost['status'] == "private_unified" or oldpost['status'] == "public_unified" %}
|
||||||
|
<input id="blog-unified" name="blog-unified" type="checkbox" role="switch" checked />
|
||||||
|
{% else %}
|
||||||
|
<input id="blog-unified" name="blog-unified" type="checkbox" role="switch" />
|
||||||
|
{% endif %}
|
||||||
|
Publier cet article dans le blog général
|
||||||
|
</label>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<button type="submit"> Mettre à jour </button>
|
<button type="submit"> Mettre à jour </button>
|
||||||
</form>
|
</form>
|
||||||
<br />
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
{% include '_js_editor.html' %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|||||||
50
templates/files.html
Normal file
50
templates/files.html
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
{% extends 'up_squelette.html' %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
{% include 'css/dropzone.html' %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
|
||||||
|
|
||||||
|
<p>Quand vous envoyez des images, elles se retrouveront directement dans <a href="/gallery/"> votre Galerie</a>. </p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Ayez bien conscience que ce site est une expérience et qu'il est indispensable d'avoir
|
||||||
|
une sauvegarde de tous les fichiers qui vous mettrez ici. Nous ne pourrons, en aucun cas, être tenu responsable de la
|
||||||
|
perte de vos données. Merci de votre compréhension.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
|
||||||
|
<form id="uploader" methods="POST" class="dropzone dz-clickable"></form>
|
||||||
|
|
||||||
|
<h2> Fichiers privés </h2>
|
||||||
|
<h5>
|
||||||
|
Vous pouvez partager des liens de vos fichiers avec les autres membres de ce serveur uniquement. Si vous partagez un lien avec une personne non inscrite elle ne pourra pas y avoir accès
|
||||||
|
</h5>
|
||||||
|
<div hx-get="/files/private/" hx-trigger="load, every 10s">
|
||||||
|
Chargement ...
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<h2> Fichiers publics </h2>
|
||||||
|
<h5> Vous pouvez partager les liens de ces fichiers avec n'importe qui sur Internet ils y auront accès </h5>
|
||||||
|
|
||||||
|
<div hx-get="/files/public/" hx-trigger="load, every 10s">
|
||||||
|
Chargement ...
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
{% include '_js_dropzone.html' %}
|
||||||
|
{% include '_js_htmx.html' %}
|
||||||
|
{% endblock %}
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
{% extends 'up_squelette.html' %}
|
{% extends 'up_squelette.html' %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
|
|
||||||
<h3> Ma gallerie </h3>
|
<h3> Ma gallerie </h3>
|
||||||
|
|
||||||
|
|
||||||
{% if fichiers %}
|
{% if fichiers %}
|
||||||
|
|
||||||
|
|
||||||
@@ -24,7 +27,7 @@
|
|||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
||||||
<h2> Il n'y a aucunes images dans votre gallerie </h2>
|
<h2> Il n'y a aucune image dans votre galerie </h2>
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|||||||
42
templates/index_author_blog.html
Normal file
42
templates/index_author_blog.html
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title> Le Blog du serveur </title>
|
||||||
|
<link rel="stylesheet" href="/{{ author }}/blog.css" type="text/css">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
{% if not(posts) %}
|
||||||
|
<h1 style="text-align: center;"> Désolé ce blog n'existe pas encore :/ </h1>
|
||||||
|
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<a href="/blog/{{ author }}/rss.xml"> S'abonner au flux RSS </a>
|
||||||
|
<div class="articles">
|
||||||
|
<a href="/blog/{{ author }}/rss.xml"> S'abonner au fl RSS </a>
|
||||||
|
{% for post in posts %}
|
||||||
|
|
||||||
|
<h2 class="index"> {{ post.title }} </h2>
|
||||||
|
<small>
|
||||||
|
<time datetime="{{ post.time }}">
|
||||||
|
Publié le {{ post.creation_date }}
|
||||||
|
</time>
|
||||||
|
<br/>
|
||||||
|
{% if post.last_updated %}
|
||||||
|
<time datetime="{{ post.last_updated }}">
|
||||||
|
Mis à jour le {{ post.last_updated }}
|
||||||
|
</time>
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
</small>
|
||||||
|
<div class="slug">
|
||||||
|
<p> {{ post.subtitle }} </p>
|
||||||
|
<p class="readmore"> <a style="margin-right:2rem;" href="/blog/{{ post.status }}/{{post.author}}/{{post.title}}"> Lire la suite... </a></p>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,27 +1,47 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title> Blog de {{ user }} </title>
|
<title> Le Blog du serveur </title>
|
||||||
|
{% if author %}
|
||||||
|
<link rel="stylesheet" href="/{{author}}/blog.css" type="text/css">
|
||||||
|
{% else %}
|
||||||
<link rel="stylesheet" href="/static/blog.css" type="text/css">
|
<link rel="stylesheet" href="/static/blog.css" type="text/css">
|
||||||
|
{% endif %}
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
{% if not(posts) %}
|
||||||
|
<h1 style="text-align: center;"> Désolé ce blog n'existe pas encore :/ </h1>
|
||||||
|
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
|
||||||
|
|
||||||
<div class="articles">
|
<div class="articles">
|
||||||
|
<a href="/blog/rss.xml"> Suivre ce blog par RSS </a>
|
||||||
{% for post in posts %}
|
{% for post in posts %}
|
||||||
|
|
||||||
<h2 class="index"><a href="/blog/{{user}}/{{post.title}}"> {{ post.title }}</a></h2>
|
<h2 class="index"> {{ post.title }}</h2>
|
||||||
<small>
|
<small>
|
||||||
<time datetime="{{ post.time }}">
|
<time datetime="{{ post.time }}">
|
||||||
Publié le {{ post.time }}
|
Publié le {{ post.creation_date }} par <a href="{{ url_for('blog.view') }}/{{ post.author }}"> {{ post.author }}</a>
|
||||||
</time>
|
</time>
|
||||||
</small>
|
<br/>
|
||||||
<div class="slug">
|
{% if post.last_updated %}
|
||||||
<p> {{ post.subtitle }} </p>
|
<time datetime="{{ post.last_updated }}">
|
||||||
<p class="readmore"> <a style="margin-right:2rem;" href="/blog/{{user}}/{{post.title}}"> Lire la suite... </a></p>
|
Mis à jour le {{ post.last_updated }}
|
||||||
</div>
|
</time>
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
</small>
|
||||||
|
<div class="slug">
|
||||||
|
<p> {{ post.subtitle }} </p>
|
||||||
|
<p class="readmore"> <a style="margin-right:2rem;" href="/blog/{{ post.status }}/{{post.author}}/{{post.title}}"> Lire la suite... </a></p>
|
||||||
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -2,69 +2,46 @@
|
|||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
|
|
||||||
<div class="site-wrapper">
|
|
||||||
|
|
||||||
<div class="site-wrapper-inner">
|
<h1>Inscription</h1>
|
||||||
|
<br>
|
||||||
|
{% include '_flash_msgs.html' %}
|
||||||
|
|
||||||
<div class="cover-container">
|
{% if signin_enable %}
|
||||||
|
<form method="POST" action="{{ url_inscription }}">
|
||||||
|
<h4> Choisissez votre nom d'utilisateur pour vous connecter sur le portail pywallter </h4>
|
||||||
|
|
||||||
<div class="masthead clearfix">
|
{% if MAIL_SERVER or XMPP_SERVER %}
|
||||||
<div class="inner">
|
<fieldset role="group">
|
||||||
<h3 class="masthead-brand">Pywallter</h3>
|
<input type="text" name="user" id="user" placeholder="Votre pseudo">
|
||||||
<ul class="nav masthead-nav">
|
<button class="outline">
|
||||||
<li><a href="/login/">Login</a></li>
|
@{{ hostname }}
|
||||||
|
</button>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
{% if signin_enable %}
|
<p>
|
||||||
<li class="active"><a href="/inscription/">Inscription</a></li>
|
Votre pseudo vous servira à vous connecter à votre compte Mail et de messagerie instantanné (XMPP)
|
||||||
{% endif %}
|
Par exemple vous souhaitez l'adresse toto@{{hostname}}; vous entrez le pseudo toto :)
|
||||||
<li><a href="#">Contact</a></li>
|
</p>
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="inner cover">
|
{% else %}
|
||||||
<h1 class="cover-heading">Inscription</h1>
|
<input type="text" name="user" id="user" placeholder="Votre Pseudo"><br />
|
||||||
<br>
|
{% endif %}
|
||||||
{% include '_flash_msgs.html' %}
|
|
||||||
|
|
||||||
{% if signin_enable %}
|
<input type="password" name="passwd" id="passwd" placeholder="Mot de passe"><br />
|
||||||
<form method="POST" class="form-horizontal" action="{{ url_inscription }}">
|
<input type="password" name="passwdconfirm" id="passwdconfirm" placeholder="Confirmation du mot de passe"><br />
|
||||||
<h4> Choisissez votre nom d'utilisateur pour vous connecter sur le portail pywallter </h4>
|
<br>
|
||||||
<input type="text" name="user" id="user" placeholder="Pseudo" class="form-control"><br />
|
<button type="submit">Créer mon compte</button>
|
||||||
{% if MAIL_SERVER or XMPP_SERVER %}
|
</form>
|
||||||
|
{% else %}
|
||||||
<p>
|
<p class="lead">
|
||||||
Votre nom d'utilisateur vous servira à vous connecter à votre compte Mail et de messagerie instantanné (XMPP)
|
Désolé les inscriptions ne sont pas activés sur le serveur
|
||||||
Par exemple vous souhaitez l'adresse toto@{{hostname}}; vous entrez le nom d'utilisateur toto :)
|
</p>
|
||||||
</p>
|
{%endif%}
|
||||||
{% endif %}
|
{% for i in users %}
|
||||||
|
<p>{{i}}</p>
|
||||||
<input type="password" name="passwd" id="passwd" placeholder="Mot de passe" class="form-control"><br />
|
{% endfor %}
|
||||||
<input type="password" name="passwdconfirm" id="passwdconfirm" placeholder="Confirmation du mot de passe" class="form-control"><br />
|
|
||||||
<br>
|
|
||||||
<button id="tada" class="btn btn-default btn-primary" type="submit">Créer mon compte</button>
|
|
||||||
</form>
|
|
||||||
{% else %}
|
|
||||||
<p class="lead">
|
|
||||||
Désolé les inscriptions ne sont pas activés sur le serveur
|
|
||||||
</p>
|
|
||||||
{%endif%}
|
|
||||||
{% for i in users %}
|
|
||||||
<p>{{i}}</p>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="mastfoot">
|
|
||||||
<div class="inner">
|
|
||||||
<p>Cover template for <a href="http://getbootstrap.com">Bootstrap</a>, by <a href="https://twitter.com/mdo">@mdo</a>.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -6,11 +6,10 @@
|
|||||||
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Si vous voulez vous pouvez inviter une personne à se crée un compte sur ce serveur
|
Si vous voulez vous pouvez inviter une personne à se créer un compte sur ce serveur.
|
||||||
pour cela vous devez crée un lien d'inscription. Ce lien restera valable tant
|
Pour cela vous devez créer un lien d'inscription. Ce lien restera valable tant que vous ne créez pas un autre lien.
|
||||||
que la personne ne s'est pas inscrite ou tant que vous ne créez pas un autre lien.
|
Les invitations sont limité à 20 personnes pour ne pas surcharger notre petit serveur :).
|
||||||
Les invitations se font une par une et sont limité à 20 personnes pour ne pas surcharger notre petit serveur :).
|
Une fois que la personne s'est inscrite votre nombre d'invitations sera mis à jour.
|
||||||
Une fois que la personne s'est incrite votre nombre d'invitations sera mis à jour
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
@@ -19,9 +18,16 @@
|
|||||||
{% if token %}
|
{% if token %}
|
||||||
<h3> Votre lien d'inscription en cours: </h3>
|
<h3> Votre lien d'inscription en cours: </h3>
|
||||||
|
|
||||||
<a href="{{ url_invitation }}">
|
<a href="{{ url_invitation }}" id="copy-link">
|
||||||
{{ url_invitation }}
|
{{ url_invitation }}
|
||||||
</a>
|
</a> <button class="ghost" onclick="copyLinkToClipboard()" > Copier le lien </button>
|
||||||
|
|
||||||
|
|
||||||
|
<p> ou faîtes scanner ce qrcode :)</p>
|
||||||
|
|
||||||
|
<img src="/invitation.png"/>
|
||||||
|
|
||||||
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<h3> Pas d'invitation en attente </h3>
|
<h3> Pas d'invitation en attente </h3>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -29,7 +35,22 @@
|
|||||||
<a href="/gen_token/">
|
<a href="/gen_token/">
|
||||||
<button type="submit" id="tada" class="btn btn btn-success"> Créer un nouveau lien </button></a>
|
<button type="submit" id="tada" class="btn btn btn-success"> Créer un nouveau lien </button></a>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
function copyLinkToClipboard(){
|
||||||
|
|
||||||
|
elt = document.getElementById("copy-link");
|
||||||
|
try {
|
||||||
|
console.log(elt.outerText);
|
||||||
|
navigator.clipboard.writeText(elt.outerText);
|
||||||
|
} catch (error)
|
||||||
|
{
|
||||||
|
console.error (error.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,10 @@
|
|||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
|
|
||||||
|
{% if nb_articles > 1 %}
|
||||||
<h2> Vos articles de blog </h2>
|
<h2> Vos {{ nb_articles }} articles de blog </h2>
|
||||||
|
{% endif %}
|
||||||
|
<br/>
|
||||||
|
|
||||||
{% for article in list_posts %}
|
{% for article in list_posts %}
|
||||||
<article>
|
<article>
|
||||||
@@ -14,13 +15,29 @@
|
|||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
<small> Créé le : {{ article.time }} </small> <br/>
|
<small> Créé le : {{ article.time }} </small> <br/>
|
||||||
<small> Modifié le : {{ article.last_updated }}</small>
|
|
||||||
<p> Status : {{ article.status }}</p>
|
{% if article.last_updated %}
|
||||||
<br/>
|
<small> Modifié le : {{ article.last_updated }}</small><br/>
|
||||||
|
{% endif %}
|
||||||
|
<br/>
|
||||||
|
{% if article.status == "private" %}
|
||||||
|
<small> Status : privé </small>
|
||||||
|
<small> (L'article n'est pas publié dans le blog général) </small>
|
||||||
|
{% elif article.status == "private_unified" %}
|
||||||
|
<small> Status : privé </small>
|
||||||
|
<small> (L'article est publié dans le blog général) </small>
|
||||||
|
{% elif article.status == "public" %}
|
||||||
|
<small> Status : public </small>
|
||||||
|
<small> (L'article n'est pas publié dans le blog général) </small>
|
||||||
|
{% elif article.status == "public_unified" %}
|
||||||
|
<small> Status : public </small>
|
||||||
|
<small> (L'article est publié dans le blog général) </small>
|
||||||
|
{% endif %}
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
|
||||||
<a href="{{ url_for('blog.edit', title=article.title) }}"><button type="button"> Editer </button></a>
|
<a href="{{ url_for('blog.edit', title=article.title) }}"><button type="button"> Editer </button></a>
|
||||||
<a href="{{ url_for('blog.delete', title=article.title) }}"><button type="button">Supprimer</button></a>
|
<a href="{{ url_for('blog.delete', title=article.title) }}"><button type="button">Supprimer</button></a>
|
||||||
<a href="{{ url_for('blog.edit', title=article.title) }}"><button type="button"> Publier </button></a>
|
|
||||||
</article>
|
</article>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
|||||||
54
templates/list_files.html
Normal file
54
templates/list_files.html
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
{% if listFiles %}
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th>Fichier(s) <span class="badge">{{ nb_files }}</span></th>
|
||||||
|
<th>Taille (en Megaoctect)</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
|
||||||
|
{% for file in listFiles %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ file[0] }}</td>
|
||||||
|
<td><a href="/myfiles/{{ username }}/{{ file[1] }}">{{ file[1] }}</a></td>
|
||||||
|
<td>{{ file[2] }}</td>
|
||||||
|
<td>
|
||||||
|
{% if status == "public" %}
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('filesupload.remove_publicFile', filename=file[1]) }}">
|
||||||
|
<button type="button" title="Supprimer"> <span class="icons delete"></span></button>
|
||||||
|
</a>
|
||||||
|
<a href="{{ url_for('filesupload.move_private', filename=file[1]) }}">
|
||||||
|
<button type="button" title="Passer ce fichier en status privé"><span class="icons share"></span></button>
|
||||||
|
</a>
|
||||||
|
<button type="button" onclick="navigator.clipboard.writeText('{{BASE_URL}}{{ url_for('filesupload.publicfiles', username=username, filename=file[1]) }}');" title="Copier le lien du fichier">
|
||||||
|
<span class="icons copy-link"> </span>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
{% else %}
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('filesupload.remove_privateFile', filename=file[1]) }}">
|
||||||
|
<button type="button" title="Supprimer"> <span class="icons delete"></span></button>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="{{ url_for('filesupload.move_public', filename=file[1]) }}">
|
||||||
|
<button type="button" title="Passer ce fichier en status public"><span class="icons share"></span></button>
|
||||||
|
</a>
|
||||||
|
<button type="button" onclick="navigator.clipboard.writeText('{{BASE_URL}}{{ url_for('filesupload.publicfiles', username=username, filename=file[1]) }}');" title="Copier le lien du fichier">
|
||||||
|
<span class="icons copy-link"> </span>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
<h5> Aucun fichier ici </h5>
|
||||||
|
{% endif %}
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
{% include '_footer.html' %}
|
{% include '_footer.html' %}
|
||||||
|
|
||||||
{% include '_js.html' %}
|
{% include '_js-core.html' %}
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
{% extends 'up_squelette.html' %}
|
|
||||||
|
|
||||||
{% block main %}
|
|
||||||
|
|
||||||
<!--<div class="page-header">
|
|
||||||
<h1>Profil</h1>
|
|
||||||
</div>-->
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
|
|
||||||
<div class="col-sm-3"></div>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
|
|
||||||
|
|
||||||
<div class="panel panel-primary">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<h3 class="panel-title"> Changer mon mot de passe </h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel-body">
|
|
||||||
<form method="POST" action="" enctype="multipart/form-data">
|
|
||||||
|
|
||||||
<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 />
|
|
||||||
<input type="password" name="passwd_confirm" id="passwd_confirm" placeholder="Confirmation du mot de passe" 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 #}
|
|
||||||
{% 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>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
@@ -5,85 +5,62 @@
|
|||||||
{% block main %}
|
{% block main %}
|
||||||
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
{% if MAIL_SERVER %}
|
{% if MAIL_SERVER %}
|
||||||
<div class="col-md-12">
|
<h3> Mes identités : A quoi ça sert les alias ? </h3>
|
||||||
|
<p> Les alias d'e-mail, c'est utile quand vous ne voulez pas donner votre vrai adresse e-mail.
|
||||||
<h3> A quoi ca sert les alias ? </h3>
|
Vous pouvez creer une adresse que vous pouvez supprimer rapidemment, cela permet de creer une adresse pour un destinataire particulier
|
||||||
<p> Les alias c'est utile quand vous ne voulez pas donner votre vrai adresse e-mail.
|
si vous n'avez pas confiance en lui par exemple ou de trier plus facilement les e-mails venant de ce destinataire.
|
||||||
Vous pouvez creer une adresse que vous pouvez supprimer rapidemment, cela permet personnaliser une adresse pour un destinataire
|
</p>
|
||||||
si vous n'avez pas confiance en lui ou de trier plus facilement les e-mails venant de ce destinataire.
|
<p> Vous n'avez pas besoin de configurer un autre compte mail sur vos applications mail, tous les e-mails
|
||||||
</p>
|
arriveront sur votre adresse e-mail principale déjà configurée. Faîtes attention de bien répondre avec votre mail d'alias cependant !
|
||||||
<p> Vous n'avez pas besoin de configurer un autre compte mail sur vos applications, tous les e-mails
|
</p>
|
||||||
arriveront sur votre adresse e-mail principale déjà configuré
|
<table class="table">
|
||||||
</p>
|
<thead>
|
||||||
<table class="table">
|
<tr>
|
||||||
<thead>
|
<th>Mes identités <span class="badge">{{ i }}</span></th>
|
||||||
<tr>
|
<th></th>
|
||||||
<th>Mes Alias <span class="badge">{{ i }}</span></th>
|
</tr>
|
||||||
<th></th>
|
</thead>
|
||||||
</tr>
|
<tbody>
|
||||||
</thead>
|
{% if aliases %}
|
||||||
<tbody>
|
{% for alias in aliases %}
|
||||||
{% if aliases %}
|
<tr>
|
||||||
{% for alias in aliases %}
|
<td>{{ alias }}</td>
|
||||||
<tr>
|
<td><a href="{{ url_for('mymailbox.remove_alias', aliasrm=alias) }}"><button type="button" class="btn btn-sm btn-danger">Supprimer</button></a></td>
|
||||||
<td>{{ alias }}</td>
|
</tr>
|
||||||
<td><a href="{{ url_for('profil.remove_alias', aliasrm=alias) }}"><button type="button" class="btn btn-sm btn-danger">Supprimer</button></a></td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="panel panel-primary">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<h3 class="panel-title"> Mes identités </h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="panel-body">
|
|
||||||
<form method="POST" action="" enctype="multipart/form-data">
|
|
||||||
|
|
||||||
<p> Votre Adresse e-mail sur ce serveur : {{ email }} </p>
|
|
||||||
|
|
||||||
<label> Nouvelle identité </label>
|
|
||||||
<br/>
|
|
||||||
<div class="col-sm-7">
|
|
||||||
<input type="text" name="alias" id="alias" placeholder="Nouvel_identité" class="form-control"><br />
|
|
||||||
</div>
|
|
||||||
<h4>@{{ hostname }}</h4>
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
<button id="tada" class="btn btn-default btn-primary" type="submit">Ajouter</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<div class="panel panel-primary">
|
|
||||||
<div class="panel-heading">
|
|
||||||
<h3 class="panel-title"> Mes identitées </h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="panel-body">
|
|
||||||
<h2> Le serveur de mail n'est pas activé cette fonctionnalité est désactivé <h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{# 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 %}
|
|
||||||
|
|
||||||
{% for m in msgs %}
|
|
||||||
<p>{{ m|safe }}</p>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{% endwith %}
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h3 class="panel-title"> Mes identités </h3>
|
||||||
|
|
||||||
|
|
||||||
|
<form method="POST" action="" enctype="multipart/form-data">
|
||||||
|
|
||||||
|
<h4> Votre addresse e-mail sur ce serveur : {{ email }} </h4>
|
||||||
|
|
||||||
|
<label> Nouvelle identité </label>
|
||||||
|
<br/>
|
||||||
|
<fieldset role="group">
|
||||||
|
<input type="text" name="alias" id="alias" placeholder="Nouvel_identité">
|
||||||
|
<button class="outline">
|
||||||
|
@{{ hostname }}
|
||||||
|
</button>
|
||||||
|
</fieldset>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<button class="btn btn-default btn-primary" type="submit">Créer cette identité</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<h3 class="panel-title"> Mes identitées </h3>
|
||||||
|
|
||||||
|
<h2> Le serveur de mail n'est pas activé cette fonctionnalitée est désactivé <h2>
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
89
templates/mymessaging.html
Normal file
89
templates/mymessaging.html
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
{% extends 'up_squelette.html' %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
|
||||||
|
{% if not(mail_server) or not(xmpp_server) %}
|
||||||
|
|
||||||
|
<h3 class="panel-title"> Ma Messagerie </h3>
|
||||||
|
|
||||||
|
<h2> Le service de messagerie n'est pas activé sur ce serveur <h2>
|
||||||
|
{% else %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h3> Bienvenue {{ username }} dans votre messagerie</h3>
|
||||||
|
|
||||||
|
<h4> Votre compte est : {{ myemail }} </h4>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Si vous êtes sur cette page, c'est que vous disposez d'un compte de messaegerie sur ce serveur.
|
||||||
|
Vous pouvez utiliser votre compte mail avec un client mail et votre compte XMPP avec un client XMPP.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p> Voici un exemple de <a href="https://www.thunderbird.net/fr/"> client mail </a>
|
||||||
|
et <a href="https://gajim.org/">client XMPP </a> pour un ordinateur </p>
|
||||||
|
|
||||||
|
<p> un exemple de <a href="https://k9mail.app/"> client mail </a> et <a href="https://play.google.com/store/apps/details?id=org.snikket.android&hl=ln&gl=US">client XMPP</a>
|
||||||
|
pour un téléphone sous Android </p>
|
||||||
|
|
||||||
|
<p> et un un exemple de<a href="https://support.apple.com/fr-fr/mail"> client mail</a> et <a href="https://itunes.apple.com/us/app/tigase-messenger/id1153516838"> client XMPP </a>sous iOS pour un iphone ou un Ipad </p>
|
||||||
|
|
||||||
|
{% if mail_webservice %}
|
||||||
|
<p>
|
||||||
|
Vous pouvez aussi accéder à vos mails avec votre navigateur web actuel en utilisant le webmail disponible à l'adresse <a href="{{ mail_webservice }}"> {{ mail_webservice }} </a>.
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if xmpp_webservice %}
|
||||||
|
<p>
|
||||||
|
Vous pouvez aussi accéder à votre messagerie instantanné avec votre Navgateur web actuel en utilisant le tchat disponible à l'adresse <a href="{{ xmpp_webservice }}"> {{ xmpp_webservice }} </a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
{% if xmpp_server %}
|
||||||
|
|
||||||
|
<h3> Vos informations pour configurer vos clients XMPP </h3>
|
||||||
|
<p> Si vous voulez configurer votre compte XMPP dans votre, voici les informations à rentrer dans votre client XMPP </p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li> identifiant : {{ myemail }} </li>
|
||||||
|
<li> mot de passse : <pre> Le même mot de passe que vous avez utilisé pour vous connecter ici </pre></li>
|
||||||
|
</ul>
|
||||||
|
{% else %}
|
||||||
|
<h4 class="alert" > La messagerie XMPP est désativé sur ce serveur </h4>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
{% if mail_server %}
|
||||||
|
<h3> Vos informations pour configurer vos clients Mail </h3>
|
||||||
|
<p> Si vous voulez configurer votre adresse e-mail, voici les informations à rentrer dans votre client mail </p>
|
||||||
|
<h4>Courrier entrant : </h4>
|
||||||
|
<ul>
|
||||||
|
<li> Protocol : IMAP </li>
|
||||||
|
<li> Addresse du serveur : {{ imap_address }} </li>
|
||||||
|
<li> Port : 993 SSL </li>
|
||||||
|
<li> identifiant : {{ myemail }} </li>
|
||||||
|
<li> mot de passse : <pre> Le même mot de passe que vous avez utilisé pour vous connecter ici </pre> </li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h4>Courrier sortant : </h4>
|
||||||
|
<ul>
|
||||||
|
<li> Protocol : SMTP </li>
|
||||||
|
<li> Addresse du serveur : {{ smtp_address }} </li>
|
||||||
|
<li> Port : 587 STARTTLS </li>
|
||||||
|
<li> identifiant : {{ myemail }} </li>
|
||||||
|
<li> mot de passse : <pre> Le même mot de passe que vous avez utilisé pour vous connecter ici </pre> </li>
|
||||||
|
</ul>
|
||||||
|
{% else %}
|
||||||
|
<h4 class="alert" > La messagerie e-mail est désativé sur ce serveur </h4>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
53
templates/mypassword.html
Normal file
53
templates/mypassword.html
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
{% extends 'up_squelette.html' %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
|
||||||
|
<h3> Changer mon mot de passe </h3>
|
||||||
|
|
||||||
|
<form method="POST" action="" >
|
||||||
|
|
||||||
|
<p> Votre compte sur ce serveur : {{ username }} </p>
|
||||||
|
|
||||||
|
<label> Mot de passe </label>
|
||||||
|
<input type="password" name="password" id="password" placeholder="Votre mot de passe"><br />
|
||||||
|
<input type="password" name="passwd_confirm" id="passwd_confirm" placeholder="Confirmation du mot de passe"><br />
|
||||||
|
<button class="btn btn-default btn-primary" type="submit">Envoyer</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
<h3> Mot de passe à usage unique </h3>
|
||||||
|
<p> Le mot de passe à usage unique utilisé par pywallter est un mot de passe composé de chiffres qui change périodiquement (ttes les 30 sec). Cela permet de lutter contres le phishing et le vol de votre de mot de passe. <br/> En cas de vol de votre mot de passe une application génère un code à usage unique et donc le voleur ne peut pas se connecter à votre compte juste en connaissant votre de passe </p>
|
||||||
|
|
||||||
|
<p> Voici quelques exemple de générateur de mot de passe à usage unique (OTP en anglais) pour <a href="https://getaegis.app/">Android</a>, <a href="https://apps.apple.com/us/app/totp-authenticator-fast-2fa/id1404230533">iOS</a> et <a href="https://keepassxc.org/">Linux/BSD et windows</a>
|
||||||
|
|
||||||
|
<p> Afin que le serveur et votre application génèrent le même code en même temps; il vous faut valider la clef sécrete partager par votre application et le serveur. Pour cela, si ce n'est pas déjà fait, scannez ou entrez la clef secrète dans votre application. Pour finir, cliquez sur valider la clef en entrant le code généré par votre application. Si le test réussi parfait c'est configuré ! </p>
|
||||||
|
|
||||||
|
<p> Pour changer votre clef secrète vous devez d'abord supprimer votre clef actuelle pour en regénérer une nouvelle. </p>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<p> Votre clef secrète : {{ totp_shared_key }} </p>
|
||||||
|
|
||||||
|
<img src="{{ url_for('profil.totp_qrcode') }}" width="300" height="300"/>
|
||||||
|
{% if shared_key_validate %}
|
||||||
|
<p class="success"> Votre clef secrète est valide et activé </p>
|
||||||
|
{% else %}
|
||||||
|
|
||||||
|
<p class="alert"> Votre clef secrète n'est pas validée et donc non active </p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form method="POST" action="{{ url_for('profil.set_totp') }}" >
|
||||||
|
<input type="disabled" class="hidden" name="shared_key" id="shared_key" value="{{ totp_shared_key }}"><br />
|
||||||
|
<fieldset role="group">
|
||||||
|
<input type="text" name="code_totp" id="code_totp" placeholder="Entrez votre code ici"><br />
|
||||||
|
<input type="submit" value="Valider ma clef secrète">
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{% if shared_key_validate %}
|
||||||
|
<p> Si vous voulez changer votre clef secrète vous devez d'abord supprimé votre clef actuelle</p>
|
||||||
|
<a href="{{ url_for('profil.del_totp') }}"> <button class="btn-alert"> Supprimer ma clef secrète </button></a>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<p> Hello <span id="majuscule">{{ session['username'] }} ! </span>
|
<p> Hello <span id="majuscule">{{ session['username'] }} ! </span>
|
||||||
Bienvenue sur la création d'un nouvel article de blog. Vous pouvez créer ou importer un article de blog ici,
|
Bienvenue sur la création d'un nouvel article de blog. Vous pouvez créer ou importer un article de blog ici,
|
||||||
vous avez le choix de le rendre publique dès sa création en cochant publique ou le laisser en privé
|
vous avez le choix de le rendre public dès sa création en cochant public ou le laisser en privé
|
||||||
si vous souhaitez le modifier plus-tard avant sa publication.
|
si vous souhaitez le modifier plus-tard avant sa publication.
|
||||||
Par défaut il est laissé en privé pour éviter les publications
|
Par défaut il est laissé en privé pour éviter les publications
|
||||||
accidentelles.
|
accidentelles.
|
||||||
@@ -15,20 +15,40 @@
|
|||||||
<br />
|
<br />
|
||||||
|
|
||||||
<form method="POST" action="{{ url_for('blog.new_article') }}" id="postform">
|
<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="title" id="title" placeholder="Titre" class="form-control"><br />
|
||||||
<input type="text" name="subtitle" id="subtitle" placeholder="Sous-titre" class="form-control"><br />
|
<input type="text" name="subtitle" id="subtitle" placeholder="Sous-titre" class="form-control"><br />
|
||||||
|
<input type="text" name="category" id="category" placeholder="Catégories #hashtag" class="form-control"><br />
|
||||||
<hr>
|
<hr>
|
||||||
<textarea id="editeurMarkdown" class="form-control" form="postform" name="content" id="content" placeholder="Contenu" style="height:20vw;"></textarea><br />
|
<textarea id="editeurMarkdown" class="form-control" form="postform" name="content" id="content" placeholder="Contenu" style="height:20vw; margin-right:2vw;"></textarea><br />
|
||||||
|
|
||||||
|
<h3> Visibilité </h3>
|
||||||
<div class="center">
|
<div class="center">
|
||||||
<input type="radio" name="status" value="private" checked> Privé
|
<p> Les articles brouillons ne sont visibles que par vous même</p>
|
||||||
|
<p> Les articles privés ne sont visibles que par les membres du serveur </p>
|
||||||
|
<p> Les articles public sont visibles par tout le monde </p>
|
||||||
|
<input type="radio" name="status" value="draft"> Brouillon
|
||||||
|
<input type="radio" name="status" value="private" checked> Privé
|
||||||
<input type="radio" name="status" value="public">Public<br>
|
<input type="radio" name="status" value="public">Public<br>
|
||||||
</div>
|
</div>
|
||||||
<br/>
|
<br/>
|
||||||
<button id="tada" type="submit"> Créer l'article </button>
|
|
||||||
|
<fieldset>
|
||||||
|
<label>
|
||||||
|
|
||||||
|
<input id="blog-unified" name="blog-unified" type="checkbox" role="switch" />
|
||||||
|
Publier cet article dans le blog général
|
||||||
|
</label>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<button type="submit"> Créer l'article </button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
{% include '_js_editor.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
{% extends 'up_squelette.html' %}
|
|
||||||
|
|
||||||
{% block main %}
|
|
||||||
|
|
||||||
<div class="page-header">
|
|
||||||
<p class="text-center"><h1>Paramètres</h1></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
34
templates/personnalize_blog.html
Normal file
34
templates/personnalize_blog.html
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{% extends 'up_squelette.html' %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
|
||||||
|
<h2> Personnalisation du blog </h2>
|
||||||
|
|
||||||
|
<form method="POST" action="" enctype="multipart/form-data">
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<label for="blog-theme">Theme</label>
|
||||||
|
<select id="blog-theme" name="blog-theme">
|
||||||
|
<option selected> Default </option>
|
||||||
|
<option>orangina</option>
|
||||||
|
<option>blanc&noir</option>
|
||||||
|
<option>noir&blanc</option>
|
||||||
|
<option>orange&noir</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div id="personnal-blog-theme" class="center">
|
||||||
|
<p> Vous pouvez faire le choix d'envoyer un fichier css pour personnaliser votre blog directement.
|
||||||
|
<label> Envoyer mon thème </label>
|
||||||
|
<input type="file" class="center" name="personnal-blog-theme" id="personnal-blog-theme"/>
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-default btn-primary" type="submit"> Enregistrer mes modifications </button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -6,8 +6,6 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h3> Mon profil </h3>
|
<h3> Mon profil </h3>
|
||||||
<p>
|
<p>
|
||||||
Votre profil vous sert à vous faire reconnaitre par les autres membres du serveur. Il n'y a pas besoin de mettre son vrai nom
|
Votre profil vous sert à vous faire reconnaitre par les autres membres du serveur. Il n'y a pas besoin de mettre son vrai nom
|
||||||
@@ -15,8 +13,10 @@
|
|||||||
cela vous convient.
|
cela vous convient.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p> Pour votre mail de secours il sert à vous envoyer un mail pour changer de mot de passe en cas de perte de ce dernier. Il n'y a
|
<p>
|
||||||
pas encore de validation de l'e-mail donc vérifiez bien ce que vous tapez. </p>
|
Pour votre mail de secours il sert à vous envoyer un mail pour changer de mot de passe en cas de perte de ce dernier. Il n'y a
|
||||||
|
pas encore de validation de l'e-mail donc vérifiez bien ce que vous tapez.
|
||||||
|
</p>
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
<br/>
|
<br/>
|
||||||
@@ -30,40 +30,40 @@
|
|||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<label for="theme">Theme</label>
|
<label for="theme">Thème</label>
|
||||||
<select id="theme" name="theme" required>
|
<select id="theme" name="theme">
|
||||||
<option value="" selected>Default</option>
|
<option value="" selected>Choisissez votre couleur</option>
|
||||||
<option>amber</option>
|
<option>amber</option>
|
||||||
<option>blue</option>
|
<option>blue</option>
|
||||||
<option>cyan</option>
|
<option>cyan</option>
|
||||||
<option>fuchsia</option>
|
<option>fuchsia</option>
|
||||||
<option>green</option>
|
<option>green</option>
|
||||||
<option>grey</option>
|
<option>grey</option>
|
||||||
<option>indigo</option>
|
<option>indigo</option>
|
||||||
<option>jade</option>
|
<option>jade</option>
|
||||||
<option>orange</option>
|
<option>orange</option>
|
||||||
<option>rose</option>
|
<option>rose</option>
|
||||||
<option>pumpkin</option>
|
<option>pumpkin</option>
|
||||||
<option>purple</option>
|
<option>purple</option>
|
||||||
<option>red</option>
|
<option>red</option>
|
||||||
<option>sand</option>
|
<option>sand</option>
|
||||||
<option>slate</option>
|
<option>slate</option>
|
||||||
<option>violet</option>
|
<option>violet</option>
|
||||||
<option>yellow</option>
|
<option>yellow</option>
|
||||||
<option>zinc</option>
|
<option>zinc</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<label>Nom </label>
|
<label>Nom </label>
|
||||||
<input type="text" name="nom" id="nom" value="{% if profil['nom'] != None %}{{ profil['nom'] }}{%endif%}"><br />
|
<input type="text" name="nom" id="nom" value="{% if profil['nom'] != None %}{{ profil['nom'] }}{%endif%}"><br />
|
||||||
<label>Prenom </label>
|
<label>Prenom </label>
|
||||||
<input type="text" name="prenom" id="prenom" value="{% if profil['nom'] != None %}{{ profil['prenom'] }}{%endif%}"><br />
|
<input type="text" name="prenom" id="prenom" value="{% if profil['nom'] != None %}{{ profil['prenom'] }}{%endif%}"><br />
|
||||||
<label> Age </label>
|
<label> Age </label>
|
||||||
<input type="text" name="age" value="{% if profil['age'] != None %}{{ profil['age'] }}{%endif%}"><br />
|
<input type="text" name="age" value="{% if profil['age'] != None %}{{ profil['age'] }}{%endif%}"><br />
|
||||||
<label> Mail de secours </label>
|
<label> Mail de secours </label>
|
||||||
<input type="text" name="mail_rescue" id="mail_rescue" value="{% if profil['mail_rescue'] != None %}{{ profil['mail_rescue'] }}{%endif%}"><br />
|
<input type="text" name="mail_rescue" id="mail_rescue" value="{% if profil['mail_rescue'] != None %}{{ profil['mail_rescue'] }}{%endif%}"><br />
|
||||||
<button id="tada" class="btn btn-default btn-primary" type="submit">Envoyer</button>
|
<button id="tada" class="btn btn-default btn-primary" type="submit">Envoyer</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
{% extends 'up_squelette.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">{{ nb_pv }}</span></th>
|
|
||||||
<th>Taille (en Megaoctect)</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
|
|
||||||
{% for file in listFilesPrivate %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ file[0] }}</td>
|
|
||||||
<td><a href="/myfiles/{{ username }}/{{ file[1] }}">{{ file[1] }}</a></td>
|
|
||||||
<td>{{ file[2] }}</td>
|
|
||||||
<td><a href="{{ url_for('filesupload.remove_privateFile', filename=file[1]) }}"><button type="button" class="btn btn-sm btn-danger">Supprimer</button></a></td>
|
|
||||||
<td><a href="{{ url_for('filesupload.move_public', filename=file[1]) }}"><button type="button" class="btn btn-sm btn-success"> Rendre Publique </button></a></td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{% else %}
|
|
||||||
<p> Vous n'avez aucun fichiers privés </p>
|
|
||||||
{% endif %}
|
|
||||||
<br />
|
|
||||||
<hr />
|
|
||||||
<br />
|
|
||||||
<h2> Fichiers publics (Tout le monde peut les voirs) </h2>
|
|
||||||
{% if listFilesPublic %}
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th></th>
|
|
||||||
<th>Fichier(s) <span class="badge">{{ nb_pu }}</span></th>
|
|
||||||
<th>Taille (en Megaoctets)</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
|
|
||||||
{% for file in listFilesPublic %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ file[0] }}</td>
|
|
||||||
<td><a href="/public/{{ username }}/{{ file[1] }}">{{ file[1] }}</a></td>
|
|
||||||
<td>{{ file[2] }}</td>
|
|
||||||
<td><a href="{{ url_for('filesupload.remove_publicFile', filename=file[1]) }}"><button type="button" class="btn btn-sm btn-danger">Supprimer</button></a></td>
|
|
||||||
<td><a href="{{ url_for('filesupload.move_private', filename=file[1]) }}"><button type="button" class="btn btn-sm btn-success"> Rendre Privée </button></a></td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{% else %}
|
|
||||||
<p> Vous n'avez aucun fichiers publics </p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="fr">
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
{% include '_head.html' %}
|
{% include '_head.html' %}
|
||||||
|
{% block css %} {% endblock %}
|
||||||
|
{% include 'css/simple_editor.html' %}
|
||||||
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
@@ -20,9 +22,13 @@
|
|||||||
|
|
||||||
|
|
||||||
{% include '_footer.html' %}
|
{% include '_footer.html' %}
|
||||||
{% include '_js-core.html' %}
|
|
||||||
{% include '_js-gallery.html' %}
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
{% include '_js-core.html' %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block js %} {% endblock %}
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
{% include '_nav_userlogin.html' %}
|
|
||||||
{% extends 'up_squelette.html' %}
|
|
||||||
{% block main %}
|
|
||||||
|
|
||||||
|
|
||||||
<br />
|
|
||||||
<p>Ici, vous pouvez envoyer des fichiers afin de les sauvegarder ou de les rendre accessibles à quelqu'un d'autre. Tu pourras ensuite
|
|
||||||
les consulter dans notre rubrique <a href="/view/"> Mes fichiers </a>. Les images envoyées, quand à elles se retrouveront directement
|
|
||||||
dans la <a href="/gallery/"> Gallerie</a>.<br>Ayez bien conscience que ce site est une expérience est qu'il est indispensable d'avoir
|
|
||||||
une sauvegarde de tous vos fichiers qui vous mettrez ici. Nous ne pourrons, en aucun cas, être tenu responsable de la perte de vos
|
|
||||||
données. Merci de votre compréhension.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
<h3>Choisissez un ou plusieurs fichiers à téléverser </h3>
|
|
||||||
|
|
||||||
|
|
||||||
<form action="" method="post" enctype="multipart/form-data">
|
|
||||||
<input type="file" class="center" name="fic"id="fic" multiple>
|
|
||||||
<br>
|
|
||||||
<button type="submit" id="tada" class="btn btn btn-success"> Téléverser !</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
@@ -5,17 +5,12 @@ from tools.utils import gen_token
|
|||||||
from flask_bcrypt import Bcrypt
|
from flask_bcrypt import Bcrypt
|
||||||
|
|
||||||
app = Flask( 'pywallter' )
|
app = Flask( 'pywallter' )
|
||||||
app.config.from_pyfile('config.py')
|
|
||||||
bcrypt = Bcrypt(app)
|
bcrypt = Bcrypt(app)
|
||||||
|
|
||||||
DATABASE = app.config['DATABASE']
|
|
||||||
DOSSIER_PERSO = app.config['DOSSIER_APP']
|
|
||||||
DATABASE = app.config['DATABASE']
|
|
||||||
|
|
||||||
|
|
||||||
|
def init_db(database):
|
||||||
def init_db():
|
conn = sqlite3.connect(database)
|
||||||
conn = sqlite3.connect(DATABASE)
|
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("""
|
cursor.execute("""
|
||||||
CREATE TABLE IF NOT EXISTS users(
|
CREATE TABLE IF NOT EXISTS users(
|
||||||
@@ -30,33 +25,22 @@ def init_db():
|
|||||||
age TEXT,
|
age TEXT,
|
||||||
website TEXT,
|
website TEXT,
|
||||||
blog_theme TEXT,
|
blog_theme TEXT,
|
||||||
|
totp CHAR(40),
|
||||||
Token CHAR(64),
|
Token CHAR(64),
|
||||||
Lost_password_token CHAR(128),
|
Lost_password_token CHAR(128),
|
||||||
invitations INTEGER DEFAULT (20),
|
invitations INTEGER DEFAULT (20),
|
||||||
Mail_rescue TEXT )
|
Mail_rescue TEXT )
|
||||||
""")
|
""")
|
||||||
conn.commit()
|
conn.commit()
|
||||||
print ('table users Ok')
|
print ("table users Ok")
|
||||||
|
|
||||||
cursor.execute("""
|
|
||||||
CREATE TABLE IF NOT EXISTS posts(
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
|
|
||||||
title TEXT,
|
|
||||||
content TEXT,
|
|
||||||
time TEXT,
|
|
||||||
category TEXT,
|
|
||||||
author TEXT,
|
|
||||||
status TEXT
|
|
||||||
)
|
|
||||||
""")
|
|
||||||
|
|
||||||
cursor.execute("""
|
cursor.execute("""
|
||||||
CREATE TABLE IF NOT EXISTS Blog_posts(
|
CREATE TABLE IF NOT EXISTS Blog_posts(
|
||||||
title TEXT NOT NULL,
|
title TEXT NOT NULL,
|
||||||
subtitle TEXT,
|
subtitle TEXT,
|
||||||
comments TEXT,
|
comments TEXT,
|
||||||
filename TEXT,
|
content TEXT,
|
||||||
time TEXT,
|
creation_date TEXT,
|
||||||
last_updated TEXT,
|
last_updated TEXT,
|
||||||
category TEXT,
|
category TEXT,
|
||||||
author TEXT,
|
author TEXT,
|
||||||
@@ -65,7 +49,7 @@ def init_db():
|
|||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
print ("Table Blog_posts Ok !")
|
||||||
|
|
||||||
cursor.execute("""select * from users""")
|
cursor.execute("""select * from users""")
|
||||||
accounts = cursor.fetchall()
|
accounts = cursor.fetchall()
|
||||||
@@ -73,45 +57,60 @@ def init_db():
|
|||||||
# pywallter qui permet la première inscription
|
# pywallter qui permet la première inscription
|
||||||
if not(accounts) :
|
if not(accounts) :
|
||||||
user = "pywallter"
|
user = "pywallter"
|
||||||
token = gen_token()
|
token = gen_token("Invitation")
|
||||||
passwd_bcrypt = bcrypt.generate_password_hash(token)
|
passwd_bcrypt = bcrypt.generate_password_hash(token)
|
||||||
cursor.execute("""INSERT INTO users(name, passwd, token) VALUES(?, ?, ?)""", (user, passwd_bcrypt, token))
|
cursor.execute("""INSERT INTO users(name, passwd, token) VALUES(?, ?, ?)""", (user, passwd_bcrypt, token))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
print ('table posts OK')
|
print ('table posts OK')
|
||||||
|
|
||||||
def init_dir():
|
def check_directories(users_folder):
|
||||||
if os.path.isdir('users'):
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
os.makedirs('./users/')
|
|
||||||
|
|
||||||
def db_migrate():
|
if os.path.isdir(users_folder):
|
||||||
conn = sqlite3.connect(DATABASE)
|
print("Le dossier {} existe".format(users_folder))
|
||||||
|
else:
|
||||||
|
os.makedirs(users_folder)
|
||||||
|
print("Le dossier {} a été créé".format(users_folder))
|
||||||
|
|
||||||
|
def db_migrate(database):
|
||||||
|
|
||||||
|
|
||||||
|
invitations_col = False
|
||||||
|
lost_password_token_col = False
|
||||||
|
totp_col = False
|
||||||
|
blog_unified_col = False
|
||||||
|
blog_theme_col = False
|
||||||
|
updated_col = False
|
||||||
|
creation_date_col = False
|
||||||
|
content_col = False
|
||||||
|
|
||||||
|
conn = sqlite3.connect(database)
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
|
|
||||||
cursor.execute("""SELECT name FROM PRAGMA_TABLE_INFO('users');""")
|
cursor.execute("""SELECT name FROM PRAGMA_TABLE_INFO('users');""")
|
||||||
db_columns = cursor.fetchall()
|
db_columns = cursor.fetchall()
|
||||||
invitations_col = False
|
|
||||||
blog_theme_col = False
|
|
||||||
updated_col = False
|
|
||||||
lost_password_token_col = False
|
|
||||||
|
|
||||||
for col in db_columns:
|
for col in db_columns:
|
||||||
if "invitations" == col[0]:
|
if "invitations" == col[0]:
|
||||||
invitations_col = True
|
invitations_col = True
|
||||||
if "Lost_password_token" == col[0]:
|
if "Lost_password_token" == col[0]:
|
||||||
lost_password_token_col = True
|
lost_password_token_col = True
|
||||||
|
if "totp" == col[0]:
|
||||||
|
totp_col = True
|
||||||
|
if "blog_theme" == col[0]:
|
||||||
|
blog_theme_col = True
|
||||||
|
|
||||||
|
|
||||||
cursor.execute("""SELECT name FROM PRAGMA_TABLE_INFO('Blog_posts');""")
|
cursor.execute("""SELECT name FROM PRAGMA_TABLE_INFO('Blog_posts');""")
|
||||||
db_columns = cursor.fetchall()
|
db_columns = cursor.fetchall()
|
||||||
for col in db_columns:
|
for col in db_columns:
|
||||||
if "blog_theme" == col[0]:
|
|
||||||
blog_theme_col = True
|
|
||||||
if "last_updated" == col[0]:
|
if "last_updated" == col[0]:
|
||||||
updated_col = True
|
updated_col = True
|
||||||
|
if "content" == col[0]:
|
||||||
|
content_col = True
|
||||||
|
if "creation_date" == col[0]:
|
||||||
|
creation_date_col= True
|
||||||
|
if "category" == col[0]:
|
||||||
|
category_col= True
|
||||||
|
|
||||||
|
|
||||||
if not(invitations_col):
|
if not(invitations_col):
|
||||||
@@ -121,13 +120,18 @@ def db_migrate():
|
|||||||
|
|
||||||
|
|
||||||
if not(lost_password_token_col):
|
if not(lost_password_token_col):
|
||||||
cursor.execute("""ALTER TABLE Users ADD COLUMN Lost_password_token CHAR(64);""")
|
cursor.execute("""ALTER TABLE users ADD COLUMN Lost_password_token CHAR(64);""")
|
||||||
conn.commit()
|
conn.commit()
|
||||||
print ("Ajout du champ Lost_password_token dans la table Users")
|
print ("Ajout du champ Lost_password_token dans la table Users")
|
||||||
|
|
||||||
|
if not(totp_col):
|
||||||
|
cursor.execute("""ALTER TABLE users ADD COLUMN totp CHAR(40);""")
|
||||||
|
conn.commit()
|
||||||
|
print ("Ajout du champ totp dans la table Users")
|
||||||
|
|
||||||
|
|
||||||
if not(blog_theme_col):
|
if not(blog_theme_col):
|
||||||
cursor.execute("""ALTER TABLE Blog_posts ADD COLUMN blog_theme TEXT;""")
|
cursor.execute("""ALTER TABLE users ADD COLUMN blog_theme TEXT;""")
|
||||||
conn.commit()
|
conn.commit()
|
||||||
print ("Ajout du champ blog_theme dans la table Blog")
|
print ("Ajout du champ blog_theme dans la table Blog")
|
||||||
|
|
||||||
@@ -137,5 +141,20 @@ def db_migrate():
|
|||||||
print ("Ajout du champ updated dans la table BLog")
|
print ("Ajout du champ updated dans la table BLog")
|
||||||
|
|
||||||
|
|
||||||
|
if not(content_col):
|
||||||
|
cursor.execute("""ALTER TABLE Blog_posts RENAME COLUMN filename TO content;""")
|
||||||
|
conn.commit()
|
||||||
|
print ("Filename renomé en content")
|
||||||
|
|
||||||
|
|
||||||
|
if not(creation_date_col):
|
||||||
|
cursor.execute("""ALTER TABLE Blog_posts RENAME COLUMN time TO creation_date;""")
|
||||||
|
conn.commit()
|
||||||
|
print ("Time renomé en creation_date")
|
||||||
|
|
||||||
|
if not(category_col):
|
||||||
|
cursor.execute("""ALTER TABLE Blog_posts ADD COLUMN category TEXT;""")
|
||||||
|
conn.commit()
|
||||||
|
print ("Ajout de la colono category")
|
||||||
|
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
from flask import Flask
|
from flask import Flask, url_for, session, redirect, request
|
||||||
|
from functools import wraps
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import os
|
import os
|
||||||
import string
|
import string
|
||||||
import random
|
import random
|
||||||
|
import time
|
||||||
|
import pyotp
|
||||||
|
|
||||||
app = Flask( 'pywallter' )
|
app = Flask( 'pywallter' )
|
||||||
app.config.from_pyfile('config.py')
|
app.config.from_pyfile('config.py')
|
||||||
@@ -12,17 +15,52 @@ DATABASE = app.config['DATABASE']
|
|||||||
DOSSIER_PERSO = app.config['DOSSIER_APP']
|
DOSSIER_PERSO = app.config['DOSSIER_APP']
|
||||||
DATABASE = app.config['DATABASE']
|
DATABASE = app.config['DATABASE']
|
||||||
|
|
||||||
|
|
||||||
|
def login_required(f):
|
||||||
|
@wraps(f)
|
||||||
|
def decorated_function(*args, **kwargs):
|
||||||
|
if 'username' not in session:
|
||||||
|
return redirect(url_for('loginlogout.login', next=request.url))
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
return decorated_function
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def append_to_log(log_line, user):
|
def append_to_log(log_line, user):
|
||||||
|
tmp=""
|
||||||
log_file=os.path.join(DOSSIER_PERSO, user, "log.txt")
|
log_file=os.path.join(DOSSIER_PERSO, user, "log.txt")
|
||||||
logs=open(log_file, "r")
|
if os.path.exists(log_file):
|
||||||
tmp=logs.read()
|
logs=open(log_file, "r")
|
||||||
logs.close()
|
tmp=logs.read()
|
||||||
|
logs.close()
|
||||||
log=open(log_file, "w")
|
log=open(log_file, "w")
|
||||||
log.write(log_line)
|
log.write(log_line)
|
||||||
log.write(tmp)
|
log.write(tmp)
|
||||||
log.close()
|
log.close()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def valid_email(mail):
|
||||||
|
valid=True
|
||||||
|
# Caractères non autorisés dans la RFC #822
|
||||||
|
invalid_char = { '(', ')', '<', '>', ',', ';', ':', '"', '[', ']', '|', 'ç', '%', '&', ' ' }
|
||||||
|
mail_cut = mail.split('@')
|
||||||
|
tld = mail_cut[-1].split('.')
|
||||||
|
|
||||||
|
for character in invalid_char:
|
||||||
|
if character in mail:
|
||||||
|
valid=False
|
||||||
|
|
||||||
|
print(tld)
|
||||||
|
if len(mail_cut) > 1 and len(tld) > 1 and valid:
|
||||||
|
if len(tld[0]) < 1 or len(tld[-1]) < 2 :
|
||||||
|
valid=False
|
||||||
|
else:
|
||||||
|
valid=False
|
||||||
|
|
||||||
|
return valid
|
||||||
|
|
||||||
|
|
||||||
def valid_username(username):
|
def valid_username(username):
|
||||||
valid=True
|
valid=True
|
||||||
# Caractères non autorisés dans la RFC #822
|
# Caractères non autorisés dans la RFC #822
|
||||||
@@ -32,8 +70,29 @@ def valid_username(username):
|
|||||||
if character in username:
|
if character in username:
|
||||||
valid=False
|
valid=False
|
||||||
|
|
||||||
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
|
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||||
|
|
||||||
|
cursor.execute("""SELECT name FROM users WHERE name=?""", (username,))
|
||||||
|
tmp = cursor.fetchone()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
if tmp:
|
||||||
|
valid = True
|
||||||
|
else:
|
||||||
|
valid = False
|
||||||
|
|
||||||
|
|
||||||
return valid
|
return valid
|
||||||
|
|
||||||
|
def set_mail_domain():
|
||||||
|
mail_domain = ""
|
||||||
|
if app.config['MAIL_SERVER']:
|
||||||
|
mail_domain = app.config['MAIL_DOMAIN']
|
||||||
|
else:
|
||||||
|
mail_domain = None
|
||||||
|
return mail_domain
|
||||||
|
|
||||||
|
|
||||||
def email_disp(email):
|
def email_disp(email):
|
||||||
disp = True
|
disp = True
|
||||||
@@ -85,7 +144,7 @@ def valid_token_register(token, token_type):
|
|||||||
cursor.execute("""SELECT name FROM users where token=?""", (token,))
|
cursor.execute("""SELECT name FROM users where token=?""", (token,))
|
||||||
tmp = cursor.fetchone()
|
tmp = cursor.fetchone()
|
||||||
conn.close()
|
conn.close()
|
||||||
print (tmp)
|
|
||||||
if tmp:
|
if tmp:
|
||||||
valid = True
|
valid = True
|
||||||
else:
|
else:
|
||||||
@@ -97,7 +156,6 @@ def valid_token_register(token, token_type):
|
|||||||
|
|
||||||
def get_user_by_token(token, token_type):
|
def get_user_by_token(token, token_type):
|
||||||
|
|
||||||
|
|
||||||
if len(token) != 30 and len(token) != 64:
|
if len(token) != 30 and len(token) != 64:
|
||||||
user = "Invalid Token"
|
user = "Invalid Token"
|
||||||
|
|
||||||
@@ -129,3 +187,13 @@ def gen_token(token_type):
|
|||||||
case "Lost password":
|
case "Lost password":
|
||||||
sample = ''.join(random.sample(digits + letters, 64))
|
sample = ''.join(random.sample(digits + letters, 64))
|
||||||
return sample
|
return sample
|
||||||
|
|
||||||
|
def totp_is_valid(code_key, code):
|
||||||
|
res = True
|
||||||
|
|
||||||
|
if code_key:
|
||||||
|
mytotp = pyotp.TOTP(code_key)
|
||||||
|
if not(code == mytotp.now() and res):
|
||||||
|
res = False
|
||||||
|
|
||||||
|
return res
|
||||||
|
|||||||
381
views/blog.py
381
views/blog.py
@@ -7,6 +7,8 @@ import sqlite3
|
|||||||
from markdown import markdown
|
from markdown import markdown
|
||||||
from tools.filesutils import getFileSizeKo
|
from tools.filesutils import getFileSizeKo
|
||||||
import string
|
import string
|
||||||
|
from shutil import copy
|
||||||
|
from tools.utils import login_required
|
||||||
|
|
||||||
blog = Blueprint('blog', __name__, template_folder='templates')
|
blog = Blueprint('blog', __name__, template_folder='templates')
|
||||||
|
|
||||||
@@ -15,163 +17,294 @@ app.config.from_pyfile('config.py')
|
|||||||
|
|
||||||
|
|
||||||
########################### Variables Globales #################################
|
########################### Variables Globales #################################
|
||||||
extensionimg = app.config['EXT_IMG']
|
extensionimg = app.config.get('EXT_IMG')
|
||||||
DATABASE = app.config['DATABASE']
|
DATABASE = app.config.get('DATABASE')
|
||||||
BASE_URL = app.config['BASE_URL']
|
BASE_URL = app.config.get('BASE_URL')
|
||||||
DOSSIER_PERSO= app.config['DOSSIER_APP']+'/'
|
DOSSIER_PERSO = app.config.get('DOSSIER_APP')+'/'
|
||||||
DOSSIER_PUBLIC= app.config['DOSSIER_PUBLIC']+'/'
|
DOSSIER_PUBLIC = app.config.get('DOSSIER_PUBLIC')+'/'
|
||||||
|
TITLE_SERVER = app.config.get('TITLE_SERVER')
|
||||||
|
DESC_SERVER = app.config.get('DESC_SERVER')
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
@blog.route('/myblog/new-article/', methods=['GET', 'POST'])
|
@blog.route('/myblog/new-article/', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
def new_article():
|
def new_article():
|
||||||
if 'username' in session:
|
user = '%s'% escape(session['username'])
|
||||||
user = '%s'% escape(session['username'])
|
if request.method == 'POST':
|
||||||
folder_blog = DOSSIER_PERSO + user + "/blog/articles/"
|
title = str(request.form['title'])
|
||||||
if request.method == 'POST':
|
subtitle = str(request.form['subtitle'])
|
||||||
title = request.form['title']
|
category = str(request.form['category'])
|
||||||
subtitle = request.form['subtitle']
|
content = str(request.form['content'])
|
||||||
content = request.form['content']
|
status = str(request.form['status'])
|
||||||
status = request.form['status']
|
post_date = time.strftime("%d/%m/%Y %H:%M:%S")
|
||||||
post_date = time.strftime("%d/%m/%Y %H:%M:%S")
|
if 'blog-unified' in request.form.keys():
|
||||||
filename = title.replace(" ", "_") + ".md"
|
status = status+'_unified'
|
||||||
|
|
||||||
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
|
||||||
cursor = conn.cursor() # Création de l'objet "curseur"
|
|
||||||
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
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
cursor = conn.cursor() # Création de l'objet "curseur"
|
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||||
cursor.execute("""SELECT title, subtitle, time, last_updated, status FROM Blog_posts WHERE author=? """, (user,) )
|
cursor.execute("""INSERT INTO Blog_posts(title, subtitle, category, content, creation_date, author, status) VALUES(?, ?, ?, ?, ?, ?, ?)""", (title, subtitle, category, content, post_date, user, status)) # Insérer des valeurs
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
@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 Blog_posts WHERE title=? AND author=?""", (title, user))
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
|
||||||
os.remove(folder_blog+filename+".md")
|
|
||||||
os.remove(folder_blog_public+filename+".html")
|
|
||||||
return redirect(url_for('blog.list_articles_blog'))
|
return redirect(url_for('blog.list_articles_blog'))
|
||||||
else:
|
else:
|
||||||
return redirect(BASE_URL, code=401) # sinon on redirige vers login
|
return render_template('new_article_blog.html')
|
||||||
|
|
||||||
@blog.route('/myblog/edit/<title>', methods=['GET', 'POST'])
|
@blog.route('/myblog/edit/<title>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
def edit(title):
|
def edit(title):
|
||||||
if 'username' in session :
|
user='%s'% escape(session['username'])
|
||||||
user='%s'% escape(session['username'])
|
folder_blog = DOSSIER_PERSO + user + "/blog/articles/"
|
||||||
filename = title.replace(" ", "_") + ".md"
|
if request.method == 'POST' :
|
||||||
folder_blog = DOSSIER_PERSO + user + "/blog/articles/"
|
newtitle = str(request.form['title'])
|
||||||
|
subtitle = str(request.form['subtitle'])
|
||||||
|
category = str(request.form['category'])
|
||||||
|
newcontent = str(request.form['content'])
|
||||||
|
newstatus = str(request.form['status'])
|
||||||
|
updated = time.strftime("%d/%m/%Y %H:%M:%S")
|
||||||
|
conn = sqlite3.connect(DATABASE)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
if 'blog-unified' in request.form.keys():
|
||||||
|
newstatus = newstatus+'_unified'
|
||||||
|
|
||||||
if request.method == 'POST' :
|
cursor.execute("""UPDATE Blog_posts SET title=?, subtitle=?, category=?, last_updated=?, status=?, content=? WHERE title=? AND author=?""", (newtitle, subtitle, category, updated, newstatus, newcontent, title, user))
|
||||||
subtitle = request.form['subtitle']
|
conn.commit()
|
||||||
newcontent = request.form['content']
|
conn.close()
|
||||||
newstatus = request.form['status']
|
return redirect(url_for('blog.list_articles_blog'))
|
||||||
updated = time.strftime("%d/%m/%Y %H:%M:%S")
|
|
||||||
conn = sqlite3.connect(DATABASE)
|
|
||||||
cursor = conn.cursor()
|
|
||||||
cursor.execute("""UPDATE Blog_posts SET subtitle=?, last_updated=?, status=? WHERE title=? AND author=?""", (subtitle, updated, newstatus, title, user))
|
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
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, subtitle, status FROM Blog_posts WHERE title=? AND author=?""", (title, user))
|
|
||||||
oldpost = cursor.fetchone()
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
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:
|
else:
|
||||||
return redirect(BASE_URL, code=401)
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
|
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||||
|
cursor.execute("""SELECT title, subtitle, category, content, status FROM Blog_posts WHERE title=? AND author=?""", (title, user))
|
||||||
|
oldpost = cursor.fetchone()
|
||||||
|
conn.close()
|
||||||
|
post = dict(title=oldpost[0], subtitle=oldpost[1], category=oldpost[2], content=oldpost[3], status=oldpost[4])
|
||||||
|
return render_template('edit_article.html',
|
||||||
|
section='Post-it',
|
||||||
|
oldpost=post)
|
||||||
|
|
||||||
@blog.route('/blog/<username>/', methods=['GET'])
|
|
||||||
def view(username):
|
@blog.route('/myblog/list-articles/', methods=['GET'])
|
||||||
user = username
|
@login_required
|
||||||
|
def list_articles_blog():
|
||||||
|
user = '%s'% escape(session['username'])
|
||||||
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
cursor = conn.cursor() # Création de l'objet "curseur"
|
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||||
cursor.execute("""SELECT title, subtitle, time, author FROM Blog_posts WHERE status='public' AND author=? """, (user,) )
|
cursor.execute("""SELECT title, subtitle, creation_date, last_updated, status FROM Blog_posts WHERE author=? """, (user,) )
|
||||||
|
list_posts=cursor.fetchall()
|
||||||
|
posts=list()
|
||||||
|
nb_articles=0
|
||||||
|
for post in list_posts:
|
||||||
|
posts = [dict(title=post[0],
|
||||||
|
subtitle=post[1],
|
||||||
|
time=post[2],
|
||||||
|
last_updated=post[3],
|
||||||
|
status=post[4])] + posts
|
||||||
|
nb_articles = nb_articles + 1
|
||||||
|
|
||||||
|
return render_template('list_articles.html',
|
||||||
|
section="Articles",
|
||||||
|
list_posts=posts,
|
||||||
|
nb_articles=nb_articles )
|
||||||
|
|
||||||
|
@blog.route('/myblog/delete/<title>')
|
||||||
|
@login_required
|
||||||
|
def delete(title):
|
||||||
|
title = escape(title)
|
||||||
|
user='%s'% escape(session['username'])
|
||||||
|
folder_blog = DOSSIER_PERSO + user + "/blog/articles/"
|
||||||
|
folder_blog_public = DOSSIER_PUBLIC + user + "/blog/articles/"
|
||||||
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
|
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||||
|
cursor.execute("""DELETE FROM Blog_posts WHERE title=? AND author=?""", (title, user))
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
return redirect(url_for('blog.list_articles_blog'))
|
||||||
|
|
||||||
|
|
||||||
|
@blog.route('/myblog/personnalize/', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def personnalize_blog():
|
||||||
|
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 blog_theme FROM users WHERE name=?""", (user,))
|
||||||
|
blog_info = cursor.fetchone()
|
||||||
|
conn.close()
|
||||||
|
blog_unified = blog_info[0]
|
||||||
|
if request.method == 'POST' :
|
||||||
|
|
||||||
|
f = request.files['personnal-blog-theme']
|
||||||
|
|
||||||
|
blog_theme = str(request.form['blog-theme'])
|
||||||
|
print(blog_theme)
|
||||||
|
|
||||||
|
if blog_theme != "Default":
|
||||||
|
copy( "static/blog-"+blog_theme+".css",
|
||||||
|
DOSSIER_PERSO+ user +'/blog.css' )
|
||||||
|
|
||||||
|
if f: # On vérifie qu'un fichier a bien été envoyé
|
||||||
|
nom = secure_filename(f.filename)
|
||||||
|
f.save(DOSSIER_PERSO + user + nom)
|
||||||
|
else:
|
||||||
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
|
cursor = conn.cursor() # Création de l\'objet "curseur"
|
||||||
|
cursor.execute("UPDATE users SET blog_theme=? WHERE name=?", (blog_theme, user))
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
return render_template('personnalize_blog.html', section='personnalize_blog', blog_theme=blog_info[0])
|
||||||
|
|
||||||
|
@blog.route('/myblog/', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
def viewmyblog():
|
||||||
|
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, creation_date, author, status FROM Blog_posts WHERE author=? AND status!='draft'""", (user,))
|
||||||
list_posts=cursor.fetchall()
|
list_posts=cursor.fetchall()
|
||||||
posts=list()
|
posts=list()
|
||||||
id=0
|
id=0
|
||||||
|
|
||||||
conn.close()
|
conn.close()
|
||||||
print (list_posts)
|
|
||||||
if list_posts != None:
|
if list_posts != None:
|
||||||
for post in list_posts:
|
for post in list_posts:
|
||||||
posts.append(dict(title=post[0], subtitle=post[1], time=post[2], author=post[3]))
|
posts=[dict(title=post[0], subtitle=post[1], creation_date=post[2], author=post[3], status=post[4])] + posts
|
||||||
|
|
||||||
|
return render_template('index_blog.html', section='Blog', posts=posts, author=user)
|
||||||
|
|
||||||
|
|
||||||
|
@blog.route('/private-blog/', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
def view_internal():
|
||||||
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
|
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||||
|
cursor.execute("""SELECT title, subtitle, content, creation_date, last_updated, author, status FROM Blog_posts WHERE status='private_unified' OR status='public_unified' """ )
|
||||||
|
list_posts=cursor.fetchall()
|
||||||
|
conn.close()
|
||||||
|
posts=list()
|
||||||
|
id=0
|
||||||
|
if list_posts != None:
|
||||||
|
for post in list_posts:
|
||||||
|
posts = [dict(title=post[0], subtitle=post[1], content=post[2], creation_date=post[3], last_updated=post[4], author=post[5], status=post[6] )] + posts
|
||||||
else:
|
else:
|
||||||
return redirect(BASE_URL, code=404)
|
return redirect(BASE_URL, code=404)
|
||||||
|
|
||||||
|
return render_template('index_blog.html', section='Blog', posts=posts)
|
||||||
|
|
||||||
return render_template('index_blog.html', section='Blog', posts=posts, user=user)
|
@blog.route('/blog/', methods=['GET'])
|
||||||
|
def view():
|
||||||
@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
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
cursor = conn.cursor() # Création de l'objet "curseur"
|
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||||
cursor.execute("""SELECT title, subtitle, time, author FROM Blog_posts WHERE author=? AND title=? """, (user, title) )
|
cursor.execute("""SELECT title, subtitle, creation_date, author, status FROM Blog_posts WHERE status='public_unified'""" )
|
||||||
|
list_posts=cursor.fetchall()
|
||||||
|
posts=list()
|
||||||
|
id=0
|
||||||
|
conn.close()
|
||||||
|
if list_posts != None:
|
||||||
|
for post in list_posts:
|
||||||
|
posts=[dict(title=post[0], subtitle=post[1], creation_date=post[2], author=post[3], status=post[4])] + posts
|
||||||
|
|
||||||
|
return render_template('index_blog.html', section='Blog', posts=posts)
|
||||||
|
|
||||||
|
|
||||||
|
@blog.route('/blog/<author>/', methods=['GET'])
|
||||||
|
def viewuser(author):
|
||||||
|
author = escape(author)
|
||||||
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
|
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||||
|
if 'username' in session :
|
||||||
|
cursor.execute("""SELECT title, subtitle, creation_date, last_updated, author, status FROM Blog_posts WHERE author=? AND status != 'draft' """, (author,))
|
||||||
|
else:
|
||||||
|
cursor.execute("""SELECT title, subtitle, creation_date, last_updated, author, status FROM Blog_posts WHERE author=? AND status='public' OR status='public_unified' """, (author,))
|
||||||
|
list_posts=cursor.fetchall()
|
||||||
|
posts=None
|
||||||
|
id=0
|
||||||
|
conn.close()
|
||||||
|
if list_posts != None:
|
||||||
|
posts=list()
|
||||||
|
for post in list_posts:
|
||||||
|
posts=[dict(title=post[0], subtitle=post[1], creation_date=post[2], last_updated=post[3], author=post[4], status=post[5])] + posts
|
||||||
|
|
||||||
|
|
||||||
|
return render_template('index_blog.html', section='Blog', posts=posts, author=author)
|
||||||
|
|
||||||
|
@blog.route('/blog/<author>/rss.xml', methods=['GET'])
|
||||||
|
def viewauthorrss(author):
|
||||||
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
|
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||||
|
cursor.execute("""SELECT MAX (creation_date) FROM Blog_posts WHERE author=? AND status='public_unified' OR status='public'""", (author, ) )
|
||||||
|
last_article_date = cursor.fetchone()
|
||||||
|
cursor.execute("""SELECT title, subtitle, content, creation_date, author, status FROM Blog_posts WHERE author=? AND status='public_unified' OR status='public'""", (author, ) )
|
||||||
|
list_posts=cursor.fetchall()
|
||||||
|
posts=list()
|
||||||
|
id=0
|
||||||
|
conn.close()
|
||||||
|
if list_posts != None:
|
||||||
|
last_build=last_article_date[0]
|
||||||
|
for post in list_posts:
|
||||||
|
posts=[dict(title=post[0], subtitle=post[1], content=markdown(post[2]), creation_date=post[3], author=post[4], status=post[5])] + posts
|
||||||
|
|
||||||
|
return render_template('blog_rss.xml',
|
||||||
|
base_url=BASE_URL,
|
||||||
|
blog_name=author,
|
||||||
|
last_build_date=last_build,
|
||||||
|
posts=posts)
|
||||||
|
|
||||||
|
|
||||||
|
@blog.route('/blog/private_unified/<username>/<title>', methods=['GET'])
|
||||||
|
@blog.route('/blog/private/<username>/<title>', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
def viewPrivateArticle(username, title):
|
||||||
|
user = escape(username)
|
||||||
|
title = escape(title)
|
||||||
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
|
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||||
|
cursor.execute("""SELECT title, subtitle, content, creation_date, last_updated, author, status FROM Blog_posts WHERE author=? AND title=? AND status!='draft' """, (user, title))
|
||||||
post = cursor.fetchone()
|
post = cursor.fetchone()
|
||||||
conn.close()
|
conn.close()
|
||||||
if post != None:
|
if post != None:
|
||||||
post_info = (dict(title=post[0], subtitle=post[1], time=post[2], author=post[3]))
|
post_info = (dict(title=post[0], subtitle=post[1], creation_date=post[3], last_updated=post[4],author=post[5]))
|
||||||
with open(folder_blog + filename, 'r') as f:
|
content = markdown(post[2])
|
||||||
content_md = f.read()
|
|
||||||
content = markdown(content_md)
|
|
||||||
return render_template('blog.html', post_info=post_info, content=content)
|
return render_template('blog.html', post_info=post_info, content=content)
|
||||||
else:
|
else:
|
||||||
flash(u"Cet article n'existe pas", 'error');
|
return redirect(url_for('blog'), code=404)
|
||||||
|
|
||||||
|
|
||||||
|
@blog.route('/blog/public_unified/<username>/<title>', methods=['GET'])
|
||||||
|
@blog.route('/blog/public/<username>/<title>', methods=['GET'])
|
||||||
|
def viewArticle(username, title):
|
||||||
|
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, content, creation_date, last_updated, author FROM Blog_posts WHERE author=? AND title=? AND status='public_unified' """, (user, title) )
|
||||||
|
post = cursor.fetchone()
|
||||||
|
conn.close()
|
||||||
|
if post != None:
|
||||||
|
post_info = (dict(title=post[0], subtitle=post[1], creation_date=post[3], last_updated=post[4],author=post[5]))
|
||||||
|
content= markdown(post[2])
|
||||||
|
|
||||||
|
return render_template('blog.html', post_info=post_info, content=content)
|
||||||
|
else:
|
||||||
|
return redirect(url_for('blog.view'), code=404)
|
||||||
|
|
||||||
|
@blog.route('/blog/rss.xml', methods=['GET'])
|
||||||
|
def viewrss():
|
||||||
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
|
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||||
|
cursor.execute("""SELECT MAX(creation_date) FROM Blog_posts WHERE status='public_unified'""")
|
||||||
|
last_article_date = cursor.fetchone()
|
||||||
|
print (last_article_date[0])
|
||||||
|
cursor.execute("""SELECT title, subtitle, content, creation_date, author, status FROM Blog_posts WHERE status='public_unified'""" )
|
||||||
|
list_posts=cursor.fetchall()
|
||||||
|
posts=list()
|
||||||
|
id=0
|
||||||
|
conn.close()
|
||||||
|
if list_posts != None:
|
||||||
|
last_build=last_article_date[0]
|
||||||
|
for post in list_posts:
|
||||||
|
posts=[dict(title=post[0], subtitle=post[1], content=markdown(post[2]), creation_date=post[3], author=post[4], status=post[5])] + posts
|
||||||
|
|
||||||
|
return render_template('blog_rss.xml',
|
||||||
|
base_url=BASE_URL,
|
||||||
|
blog_name= TITLE_SERVER,
|
||||||
|
last_build_date=last_build,
|
||||||
|
blog_description=DESC_SERVER,
|
||||||
|
posts=posts)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from flask import Blueprint, Flask, request, flash, render_template, url_for, session, redirect, abort, make_response, flash, abort, send_file, send_from_directory
|
from flask import Blueprint, Flask, request, flash, render_template, url_for, session, redirect, abort, make_response, flash, abort, send_file, send_from_directory
|
||||||
|
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
from markupsafe import escape
|
from markupsafe import escape
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
@@ -9,187 +10,245 @@ import sqlite3
|
|||||||
import os
|
import os
|
||||||
from shutil import move
|
from shutil import move
|
||||||
from tools.filesutils import getFileSizeMo, getFileSizeKo, check_and_create
|
from tools.filesutils import getFileSizeMo, getFileSizeKo, check_and_create
|
||||||
|
from tools.utils import login_required
|
||||||
|
|
||||||
filesupload = Blueprint('filesupload', __name__, template_folder='templates')
|
filesupload = Blueprint('filesupload', __name__, template_folder='templates')
|
||||||
|
|
||||||
app = Flask( 'pywallter' )
|
app = Flask( 'pywallter' )
|
||||||
app.config.from_pyfile('config.py')
|
app.config.from_pyfile('config.py')
|
||||||
|
|
||||||
|
#### Variables ##################################################################################
|
||||||
|
|
||||||
#### Variables ####################################################################################
|
DOSSIER_PERSO= app.config.get('DOSSIER_APP')
|
||||||
|
DOSSIER_PUBLIC= app.config.get('DOSSIER_PUBLIC')
|
||||||
|
|
||||||
DOSSIER_PERSO= app.config['DOSSIER_APP']+'/'
|
extensionimg = app.config.get('EXT_IMG')
|
||||||
DOSSIER_PUBLIC= app.config['DOSSIER_PUBLIC']+'/'
|
DATABASE = app.config.get('DATABASE')
|
||||||
|
BASE_URL= app.config.get('BASE_URL')
|
||||||
extensionimg = app.config['EXT_IMG']
|
|
||||||
DATABASE = app.config['DATABASE']
|
|
||||||
BASE_URL= app.config['BASE_URL']
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
|
||||||
@filesupload.route( '/filesupload/', methods=['GET', 'POST'])
|
@filesupload.route( '/filesupload/', methods=['POST'])
|
||||||
def uploadfiles():
|
@login_required
|
||||||
if 'username' in session :
|
def upload():
|
||||||
user = '%s'% escape(session['username'])
|
user = '%s'% escape(session['username'])
|
||||||
if request.method == 'POST' :
|
if 'fic' not in request.files:
|
||||||
files = request.files.getlist('fic')
|
flash(u'Mauvais format de ficher', 'error')
|
||||||
for f in files :
|
return redirect(request.url)
|
||||||
nom = secure_filename(f.filename)
|
file = request.files['fic']
|
||||||
check_and_create(DOSSIER_PERSO+ user + 'files')
|
|
||||||
check_and_create(DOSSIER_PERSO+ user + 'images')
|
|
||||||
if os.path.isfile(DOSSIER_PERSO + user + '/files/' + nom) or os.path.isfile(DOSSIER_PERSO + user + '/images/' + nom):
|
|
||||||
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:
|
# If the user does not select a file, the browser submits an
|
||||||
flash(u'Error : Vous avez oublié le fichier !', 'error')
|
# empty file without a filename.
|
||||||
return redirect(url_for('filesupload.uploadfiles'))
|
if file.filename == '':
|
||||||
resp = make_response(render_template('up_up.html', section="Upload"))
|
flash(u'Vous avez oubliez de selectionner un fichier', 'error' )
|
||||||
resp.set_cookie('username', session['username'])
|
else:
|
||||||
return resp
|
|
||||||
else :
|
files = request.files.getlist('fic')
|
||||||
return redirect(BASE_URL, code=401)
|
for f in files :
|
||||||
|
nom = secure_filename(f.filename)
|
||||||
|
check_and_create(os.path.join(DOSSIER_PERSO, user, 'files'))
|
||||||
|
check_and_create(os.path.join(DOSSIER_PERSO, user, 'images'))
|
||||||
|
if os.path.isfile(os.path.join(DOSSIER_PERSO,user, 'files', nom) or
|
||||||
|
os.path.isfile(DOSSIER_PERSO, user, 'images', nom)):
|
||||||
|
alert = "Le fichier "+str(f.filename)+" avec le même nom existe déjà, merci de spécifier un autre nom de fichier \n"
|
||||||
|
flash(alert, 'error')
|
||||||
|
else:
|
||||||
|
file, ext = os.path.splitext(nom)
|
||||||
|
if ext in extensionimg :
|
||||||
|
f.save(os.path.join(DOSSIER_PERSO, user, 'images', nom))
|
||||||
|
image = os.path.join(DOSSIER_PERSO, user, 'images', nom)
|
||||||
|
with Image.open(image) as img :
|
||||||
|
img.thumbnail((300,300))
|
||||||
|
img.save(os.path.join(DOSSIER_PERSO, user, 'images','thumbnails', nom ))
|
||||||
|
time_img_create=time.strftime("%A %d %B %Y %H:%M:%S")
|
||||||
|
IP=request.environ['REMOTE_ADDR']
|
||||||
|
client_platform=request.headers.get('User-Agent')
|
||||||
|
log_file=os.path.join(DOSSIER_PERSO, user, "log.txt")
|
||||||
|
log=open(log_file, "a")
|
||||||
|
log.write (time_img_create + ' - ' + IP + ' - ' + user + ' - ' + client_platform + '\n' + '---> ' + nom + '\n')
|
||||||
|
log.close()
|
||||||
|
|
||||||
|
else:
|
||||||
|
f.save(os.path.join(DOSSIER_PERSO, user, 'files', nom))
|
||||||
|
time_file_upload=time.strftime("%A %d %B %Y %H:%M:%S")
|
||||||
|
IP=request.environ.get('REMOTE_ADDR')
|
||||||
|
client_platform=request.headers.get('User-Agent')
|
||||||
|
log=open("log.txt", "a") # Ouvre fichier log.txt
|
||||||
|
log.write (time_file_upload + ' - ' + IP + ' - ' + user + ' - ' + client_platform + '\n' + '---> ' + nom + '\n') # Écrit dans log
|
||||||
|
log.close() # Ferme log.txt
|
||||||
|
flash(u'Les fichiers envoyées ont été traitée avec succés', 'succes')
|
||||||
|
|
||||||
|
|
||||||
@filesupload.route('/view/')
|
return redirect(url_for('filesupload.list'))
|
||||||
|
|
||||||
|
@filesupload.route( '/upload-dropzone', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def drop_upload():
|
||||||
|
user = '%s'% escape(session['username'])
|
||||||
|
file = request.files['file']
|
||||||
|
check_and_create(os.path.join(DOSSIER_PERSO, user, 'files'))
|
||||||
|
check_and_create(os.path.join(DOSSIER_PERSO, user, 'images' ))
|
||||||
|
filename = secure_filename(file.filename)
|
||||||
|
ext = os.path.splitext(filename)
|
||||||
|
is_image = False
|
||||||
|
print("nom du fichier :" +filename)
|
||||||
|
if ext in extensionimg :
|
||||||
|
save_path = os.path.join(DOSSIER_PERSO, user, 'images', filename )
|
||||||
|
is_image = True
|
||||||
|
else:
|
||||||
|
save_path = os.path.join(DOSSIER_PERSO, user, 'files', filename )
|
||||||
|
|
||||||
|
current_chunk = int(request.form['dzchunkindex'])
|
||||||
|
print (current_chunk)
|
||||||
|
|
||||||
|
if (os.path.isfile(save_path) or os.path.isfile( os.path.join(DOSSIER_PERSO, user, 'images', filename ))) and current_chunk == 0:
|
||||||
|
return make_response(('Un fichier avec le même nom existe déjà', 400))
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(save_path, 'ab') as f:
|
||||||
|
f.seek(int(request.form['dzchunkbyteoffset']))
|
||||||
|
f.write(file.stream.read())
|
||||||
|
except OSError:
|
||||||
|
return make_response(("Une erreur est survenue,"
|
||||||
|
" Impossible d'écrire le fichier sur le disque", 500))
|
||||||
|
total_chunks = int(request.form['dztotalchunkcount'])
|
||||||
|
if current_chunk + 1 == total_chunks:
|
||||||
|
# This was the last chunk, the file should be complete and the size we expect
|
||||||
|
if os.path.getsize(save_path) != int(request.form['dztotalfilesize']):
|
||||||
|
return make_response(('La taille du fichier source est différentes', 500))
|
||||||
|
else:
|
||||||
|
time_file_upload=time.strftime("%A %d %B %Y %H:%M:%S")
|
||||||
|
IP=request.environ['REMOTE_ADDR']
|
||||||
|
client_platform=request.headers.get('User-Agent')
|
||||||
|
log=open("log.txt", "a") # Ouvre fichier log.txt
|
||||||
|
log.write (time_file_upload + ' - ' + IP + ' - ' + user + ' - ' + client_platform + '\n' + '---> ' + filename + '\n') # Écrit dans log
|
||||||
|
log.close() # Ferme log.txt
|
||||||
|
|
||||||
|
if is_image :
|
||||||
|
with Image.open(save_path) as img :
|
||||||
|
img.thumbnail((300,300))
|
||||||
|
img.save(os.path.join(DOSSIER_PERSO, user, 'images', 'thumbnails', filename ) )
|
||||||
|
|
||||||
|
return make_response(('Chunk upload succesfull', 200))
|
||||||
|
|
||||||
|
@filesupload.route('/view/', methods=['GET'])
|
||||||
|
@login_required
|
||||||
def list():
|
def list():
|
||||||
if 'username' in session :
|
|
||||||
|
|
||||||
user = '%s'% escape(session['username'])
|
user = '%s'% escape(session['username'])
|
||||||
|
|
||||||
check_and_create(DOSSIER_PUBLIC + user + '/files/')
|
check_and_create(os.path.join(DOSSIER_PUBLIC, user, 'files'))
|
||||||
check_and_create(DOSSIER_PERSO + user + '/files/')
|
check_and_create(os.path.join(DOSSIER_PERSO, user, 'files'))
|
||||||
files_public = os.listdir(DOSSIER_PUBLIC + user + '/files/')
|
|
||||||
files_private = os.listdir(DOSSIER_PERSO + user + '/files/')
|
|
||||||
listFilesPublic = []
|
|
||||||
listFilesPrivate = []
|
|
||||||
nb_pv = 0
|
|
||||||
size=0
|
|
||||||
if files_private:
|
|
||||||
for fich in files_private:
|
|
||||||
nb_pv += 1
|
|
||||||
size = getFileSizeMo(DOSSIER_PERSO + user + '/files/' + fich) # size = taille des fichiers
|
|
||||||
listFilesPrivate.append([nb_pv, fich, size]) # On implémente la listeFichiers avec le num le ficier et sa taille
|
|
||||||
|
|
||||||
nb_pu = 0
|
return render_template('files.html',
|
||||||
if files_public:
|
section="Files",
|
||||||
for fich in files_public:
|
BASE_URL=BASE_URL,
|
||||||
nb_pu += 1
|
username=user)
|
||||||
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 :
|
@filesupload.route('/files/<status>/', methods=['GET'])
|
||||||
return redirect(BASE_URL, code=401)
|
@login_required
|
||||||
|
def list_files(status: str ):
|
||||||
|
user = '%s' % escape(session['username'])
|
||||||
|
listFiles = []
|
||||||
|
nb_files = 0
|
||||||
|
size=0
|
||||||
|
folder=""
|
||||||
|
|
||||||
|
if status == "public":
|
||||||
|
folder=DOSSIER_PUBLIC
|
||||||
|
else:
|
||||||
|
folder=DOSSIER_PERSO
|
||||||
|
|
||||||
|
files = os.listdir(os.path.join(folder, user, 'files'))
|
||||||
|
|
||||||
|
if files:
|
||||||
|
for fich in files:
|
||||||
|
nb_files += 1
|
||||||
|
size = getFileSizeMo(os.path.join(folder, user, 'files', fich)) # size = taille des fichiers
|
||||||
|
listFiles.append([nb_files, fich, size]) # On implémente la listeFichiers avec le num le ficier et sa taille
|
||||||
|
resp = "<h2> Bonjour " + user +" ça va bien putain ca marche ? </h2>"
|
||||||
|
|
||||||
|
return render_template('list_files.html',
|
||||||
|
BASE_URL=BASE_URL,
|
||||||
|
status=status,
|
||||||
|
size=size,
|
||||||
|
username=user,
|
||||||
|
nb_files=nb_files,
|
||||||
|
listFiles=listFiles)
|
||||||
|
|
||||||
|
|
||||||
@filesupload.route('/myfiles/<username>/<filename>')
|
@filesupload.route('/myfiles/<username>/<filename>')
|
||||||
|
@login_required
|
||||||
def myfiles(username, filename):
|
def myfiles(username, filename):
|
||||||
if 'username' in session :
|
user = '%s' % escape(session['username'])
|
||||||
user = '%s' % escape(session['username'])
|
return send_from_directory(
|
||||||
return send_from_directory(
|
os.path.join(DOSSIER_PERSO, username, 'files'), filename )
|
||||||
os.path.join(DOSSIER_PERSO, username, 'files'), filename )
|
|
||||||
else :
|
|
||||||
return redirect(BASE_URL, code=401)
|
|
||||||
|
|
||||||
@filesupload.route('/make_public/<filename>')
|
@filesupload.route('/make_public/<filename>')
|
||||||
|
@login_required
|
||||||
def move_public(filename):
|
def move_public(filename):
|
||||||
if 'username' in session:
|
user = '%s' % escape(session['username'])
|
||||||
|
|
||||||
user = '%s' % escape(session['username'])
|
src = os.path.join(DOSSIER_PERSO, user, 'files', filename)
|
||||||
check_and_create(DOSSIER_PUBLIC + user + '/files/')
|
dst = os.path.join(DOSSIER_PUBLIC, user, 'files')
|
||||||
check_and_create(DOSSIER_PERSO + user + '/files/')
|
move (src, dst)
|
||||||
|
return redirect(url_for('filesupload.list', _external=True))
|
||||||
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>')
|
@filesupload.route('/make_private/<filename>')
|
||||||
|
@login_required
|
||||||
def move_private(filename):
|
def move_private(filename):
|
||||||
if 'username' in session:
|
user = '%s' % escape(session['username'])
|
||||||
user = '%s' % escape(session['username'])
|
src = os.path.join(DOSSIER_PUBLIC, user, 'files', filename)
|
||||||
check_and_create(DOSSIER_PUBLIC + user + '/files/')
|
dst = os.path.join(DOSSIER_PERSO, user, 'files')
|
||||||
check_and_create(DOSSIER_PERSO + user + '/files/')
|
move (src, dst)
|
||||||
src = os.path.join(DOSSIER_PUBLIC, user, 'files', filename)
|
return redirect(url_for('filesupload.list', _external=True))
|
||||||
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>')
|
@filesupload.route('/remove_privateFile/<filename>')
|
||||||
|
@login_required
|
||||||
def remove_privateFile(filename):
|
def remove_privateFile(filename):
|
||||||
if 'username' in session :
|
user = '%s' % escape(session['username'])
|
||||||
user = '%s' % escape(session['username'])
|
filename = secure_filename(filename)
|
||||||
filename = secure_filename(filename)
|
try:
|
||||||
try:
|
os.remove( os.path.join(DOSSIER_PERSO, user, 'files', filename)) # on le supprime
|
||||||
os.remove(DOSSIER_PERSO + user + '/files/' + filename) # on le supprime
|
except FileNotFoundError:
|
||||||
except FileNotFoundError:
|
flash(u'Fichier {filename} inexistant.'.format(filename=filename), 'error')
|
||||||
flash(u'Fichier {filename} inexistant.'.format(filename=filename), 'error')
|
return redirect(url_for('filesupload.list', _external=True))
|
||||||
return redirect(url_for('filesupload.list', _external=True))
|
|
||||||
else :
|
|
||||||
return redirect(BASE_URL, code=401)
|
|
||||||
|
|
||||||
|
|
||||||
@filesupload.route('/remove_publicFile/<filename>')
|
@filesupload.route('/remove_publicFile/<filename>')
|
||||||
|
@login_required
|
||||||
def remove_publicFile(filename):
|
def remove_publicFile(filename):
|
||||||
if 'username' in session :
|
user = '%s' % escape(session['username'])
|
||||||
user = '%s' % escape(session['username'])
|
filename = secure_filename(filename)
|
||||||
filename = secure_filename(filename)
|
try:
|
||||||
try:
|
os.remove( os.path.join(DOSSIER_PUBLIC, user, 'files', filename)) # on le supprime
|
||||||
os.remove(DOSSIER_PUBLIC + user + '/files/' + filename) # on le supprime
|
except FileNotFoundError:
|
||||||
except FileNotFoundError:
|
flash(u'Fichier {filename} inexistant.'.format(filename=filename), 'error')
|
||||||
flash(u'Fichier {filename} inexistant.'.format(filename=filename), 'error')
|
return redirect(url_for('filesupload.list', _external=True))
|
||||||
return redirect(url_for('filesupload.list', _external=True))
|
|
||||||
else :
|
|
||||||
return redirect(BASE_URL, code=401)
|
@filesupload.route('/<author>/blog.css')
|
||||||
|
def blog_theme(author):
|
||||||
|
user = author
|
||||||
|
if os.path.isfile(os.path.join(DOSSIER_PERSO, user,'blog.css')):
|
||||||
|
return send_file(os.path.join(DOSSIER_PERSO, user, 'blog.css'), mimetype='text/css')
|
||||||
|
else:
|
||||||
|
return send_file("static/blog.css", mimetype='text/css')
|
||||||
|
|
||||||
@filesupload.route('/theme.min.css')
|
@filesupload.route('/theme.min.css')
|
||||||
def theme():
|
def theme():
|
||||||
if 'username' in session:
|
if 'username' in session:
|
||||||
user = '%s' % escape(session['username'])
|
user = '%s' % escape(session['username'])
|
||||||
if os.path.isfile(DOSSIER_PERSO+ user +'/theme.min.css'):
|
if os.path.isfile(os.path.join(DOSSIER_PERSO, user,'theme.min.css')):
|
||||||
return send_file(DOSSIER_PERSO+ user +'/theme.min.css', mimetype='text/css')
|
return send_file(os.path.join(DOSSIER_PERSO, user,'theme.min.css'), mimetype='text/css')
|
||||||
else:
|
|
||||||
return send_file("static/default.min.css", mimetype='text/css')
|
return send_file("static/default.min.css", mimetype='text/css')
|
||||||
|
|
||||||
|
@filesupload.route('/public/<username>/<filename>')
|
||||||
|
def publicfiles(username, filename):
|
||||||
|
return send_from_directory(
|
||||||
|
os.path.join(DOSSIER_PUBLIC, username, 'files'), filename )
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import time
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
import os
|
import os
|
||||||
from tools.filesutils import check_and_create
|
from tools.filesutils import check_and_create
|
||||||
|
from tools.utils import login_required
|
||||||
|
|
||||||
mygallery = Blueprint('mygallery', __name__, template_folder='templates')
|
mygallery = Blueprint('mygallery', __name__, template_folder='templates')
|
||||||
|
|
||||||
@@ -17,69 +18,66 @@ app.config.from_pyfile('config.py')
|
|||||||
|
|
||||||
#### Variables ##################################################################################
|
#### Variables ##################################################################################
|
||||||
|
|
||||||
DOSSIER_PERSO= app.config['DOSSIER_APP']
|
DOSSIER_PERSO = app.config.get("DOSSIER_APP")
|
||||||
DOSSIER_PUBLIC= app.config['DOSSIER_PUBLIC']+'/'
|
DOSSIER_PUBLIC = app.config.get("DOSSIER_PUBLIC")
|
||||||
extensionimg = app.config['EXT_IMG']
|
extensionimg = app.config.get("EXT_IMG")
|
||||||
|
|
||||||
DATABASE = app.config['DATABASE']
|
DATABASE = app.config.get("DATABASE")
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
@mygallery.route( '/gallery/')
|
@mygallery.route( '/gallery/')
|
||||||
|
@login_required
|
||||||
def gallery():
|
def gallery():
|
||||||
if 'username' in session :
|
user ='%s' % escape(session['username'])
|
||||||
user ='%s' % escape(session['username'])
|
print (DOSSIER_PUBLIC)
|
||||||
check_and_create(DOSSIER_PUBLIC + user + '/images/')
|
print (os.path.join(DOSSIER_PUBLIC, user , 'images'))
|
||||||
check_and_create(DOSSIER_PUBLIC + user + '/images/thumbnails/')
|
check_and_create(os.path.join(DOSSIER_PUBLIC, user , 'images'))
|
||||||
check_and_create(DOSSIER_PERSO + user + '/images/')
|
check_and_create(os.path.join(DOSSIER_PUBLIC, user , 'images', 'thumbnails'))
|
||||||
check_and_create(DOSSIER_PERSO + user + '/images/thumbnails/')
|
check_and_create(os.path.join(DOSSIER_PERSO , user , 'images'))
|
||||||
THUMBNAILS=DOSSIER_PERSO + user + '/images/thumbnails/'
|
check_and_create(os.path.join(DOSSIER_PERSO, user, 'images','thumbnails'))
|
||||||
fichiers = [fich for fich in os.listdir(THUMBNAILS)]
|
THUMBNAILS=os.path.join(DOSSIER_PERSO, user, 'images','thumbnails')
|
||||||
return render_template('gallery.html',
|
fichiers = [fich for fich in os.listdir(THUMBNAILS)]
|
||||||
section='Gallery',
|
return render_template('gallery.html',
|
||||||
THUMBNAILS=THUMBNAILS,
|
section='Gallery',
|
||||||
fichiers=fichiers)
|
THUMBNAILS=THUMBNAILS,
|
||||||
else :
|
fichiers=fichiers)
|
||||||
return redirect(url_for('loginlogout.login'), code=401)
|
|
||||||
|
|
||||||
@mygallery.route('/myfiles/images/<filename>')
|
@mygallery.route('/myfiles/images/<filename>')
|
||||||
|
@login_required
|
||||||
def myimg(filename):
|
def myimg(filename):
|
||||||
if 'username' in session :
|
user = '%s' % escape(session['username'])
|
||||||
UTILISATEUR='%s' % escape(session['username'])
|
return send_from_directory(
|
||||||
return send_from_directory(
|
os.path.join(DOSSIER_PERSO, user, 'images'), filename )
|
||||||
os.path.join(DOSSIER_PERSO, UTILISATEUR, 'images'), filename )
|
|
||||||
else :
|
|
||||||
return redirect(BASE_URL, code=401)
|
|
||||||
|
|
||||||
@mygallery.route('/myfiles/images/thumbnails/<filename>')
|
@mygallery.route('/myfiles/images/thumbnails/<filename>')
|
||||||
|
@login_required
|
||||||
def mythumbnails(filename):
|
def mythumbnails(filename):
|
||||||
if 'username' in session :
|
user='%s' % escape(session['username'])
|
||||||
UTILISATEUR='%s' % escape(session['username'])
|
return send_from_directory(
|
||||||
return send_from_directory(
|
os.path.join(DOSSIER_PERSO, user, 'images','thumbnails'), filename )
|
||||||
os.path.join(DOSSIER_PERSO, UTILISATEUR, 'images/thumbnails'), filename )
|
|
||||||
else :
|
|
||||||
return redirect(BASE_URL, code=401)
|
|
||||||
|
|
||||||
@mygallery.route('/remove_privateImage/<filename>')
|
@mygallery.route('/remove_privateImage/<filename>')
|
||||||
|
@login_required
|
||||||
def remove_privateImage(filename):
|
def remove_privateImage(filename):
|
||||||
if 'username' in session :
|
user = '%s' % escape(session['username'])
|
||||||
user = '%s' % escape(session['username'])
|
filename = secure_filename(filename)
|
||||||
filename = secure_filename(filename)
|
try:
|
||||||
try:
|
os.remove(os.path.join(DOSSIER_PERSO, user, 'images','thumbnails', filename)) # on le supprime
|
||||||
os.remove(DOSSIER_PERSO + user + '/images/thumbnails/' + filename) # on le supprime
|
os.remove(os.path.join(DOSSIER_PERSO, user, 'images', filename)) # on le supprime
|
||||||
os.remove(DOSSIER_PERSO + user + '/images/' + filename) # on le supprime
|
except FileNotFoundError:
|
||||||
except FileNotFoundError:
|
flash(u'Image {filename} inexistante.'.format(filename=filename), 'error')
|
||||||
flash(u'Image {filename} inexistante.'.format(filename=filename), 'error')
|
return redirect(url_for('mygallery.gallery'))
|
||||||
return redirect(url_for('mygallery.gallery'))
|
|
||||||
|
|
||||||
|
|
||||||
@mygallery.route('/remove_publicImage/<filename>')
|
@mygallery.route('/remove_publicImage/<filename>')
|
||||||
|
@login_required
|
||||||
def remove_publicImage(filename):
|
def remove_publicImage(filename):
|
||||||
if 'username' in session :
|
user = '%s' % escape(session['username'])
|
||||||
user = '%s' % escape(session['username'])
|
filename = secure_filename(filename)
|
||||||
filename = secure_filename(filename)
|
try:
|
||||||
try:
|
os.remove(os.path.join(DOSSIER_PUBLIC, user, 'images','thumbnails', filename)) # on le supprime
|
||||||
os.remove(DOSSIER_PUBLIC + user + '/images/thumbnails/' + filename) # on le supprime
|
os.remove(os.path.join(DOSSIER_PUBLIC, user, 'images', filename)) # on le supprime
|
||||||
os.remove(DOSSIER_PUBLIC + user + '/images/' + filename) # on le supprime
|
except FileNotFoundError:
|
||||||
except FileNotFoundError:
|
flash(u'Image {filename} inexistante.'.format(filename=filename), 'error')
|
||||||
flash(u'Image {filename} inexistante.'.format(filename=filename), 'error')
|
return redirect(url_for('mygallery.gallery'))
|
||||||
return redirect(url_for('mygallery.gallery'))
|
|
||||||
|
|||||||
@@ -1,25 +1,28 @@
|
|||||||
from flask import Blueprint, Flask, request, flash, render_template, url_for, session, redirect, abort, make_response, send_file
|
from flask import Blueprint, Flask, request, flash, render_template, url_for, session, redirect, abort, make_response, send_file
|
||||||
from flask_bcrypt import Bcrypt
|
from flask_bcrypt import Bcrypt
|
||||||
|
import time
|
||||||
|
from markupsafe import escape
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import glob, os, sys, time
|
import glob, os, sys, time
|
||||||
from tools.utils import email_disp, valid_token_register, valid_passwd, valid_username
|
from tools.utils import email_disp, valid_token_register, valid_passwd, valid_username
|
||||||
from socket import gethostname
|
from socket import gethostname
|
||||||
|
import subprocess
|
||||||
|
from tools.filesutils import check_and_create
|
||||||
|
from tools.utils import set_mail_domain
|
||||||
app = Flask( 'pywallter' )
|
app = Flask( 'pywallter' )
|
||||||
app.config.from_pyfile('config.py')
|
app.config.from_pyfile('config.py')
|
||||||
bcrypt = Bcrypt(app)
|
bcrypt = Bcrypt(app)
|
||||||
|
|
||||||
#### Variables ##################################################################################
|
#### Variables ##################################################################################
|
||||||
|
|
||||||
|
DATAS_USER = app.config.get('DOSSIER_APP')
|
||||||
DATAS_USER = app.config['DOSSIER_APP']
|
DOSSIER_PERSO = app.config.get('DOSSIER_APP')
|
||||||
extensionimg = app.config['EXT_IMG']
|
extensionimg = app.config.get('EXT_IMG')
|
||||||
DATABASE = app.config['DATABASE']
|
DATABASE = app.config.get('DATABASE')
|
||||||
MAIL_SERVER = app.config['MAIL_SERVER']
|
MAIL_SERVER = app.config.get('MAIL_SERVER')
|
||||||
XMPP_SERVER = app.config['XMPP_SERVER']
|
XMPP_SERVER = app.config.get('XMPP_SERVER')
|
||||||
SETUID = app.config['SETUID']
|
SETUID = app.config.get('SETUID')
|
||||||
BASE_URL = app.config['BASE_URL']
|
BASE_URL = app.config.get('BASE_URL')
|
||||||
|
|
||||||
#################################################################################################
|
#################################################################################################
|
||||||
|
|
||||||
@@ -29,24 +32,35 @@ inscription = Blueprint('inscription', __name__, template_folder='templates')
|
|||||||
|
|
||||||
@inscription.route( '/inscription/<token>', methods=['GET','POST'] )
|
@inscription.route( '/inscription/<token>', methods=['GET','POST'] )
|
||||||
def signin(token) :
|
def signin(token) :
|
||||||
hostname = gethostname()
|
|
||||||
url_inscription = url_for('loginlogout.index', _external=True)+'inscription/'+token
|
mail_domain = set_mail_domain()
|
||||||
|
|
||||||
|
url_inscription = BASE_URL +'/'+'inscription/'+token
|
||||||
resp = None
|
resp = None
|
||||||
if valid_token_register(token, "Invitation"):
|
if valid_token_register(token, "Invitation"):
|
||||||
if 'username' in session :
|
if 'username' in session :
|
||||||
resp = redirect(url_for('profil.profile', _external=True))
|
resp = redirect(url_for('profil.profile', _external=True))
|
||||||
else :
|
else :
|
||||||
|
|
||||||
# Réponse si la requete est de type GET ou si la requete POST echoue
|
# Réponse si la requete est de type GET ou si la requete POST echoue
|
||||||
resp = render_template('inscription.html',
|
resp = render_template('inscription.html',
|
||||||
signin_enable=app.config['SIGNIN_ENABLE'],
|
signin_enable=app.config['SIGNIN_ENABLE'],
|
||||||
token=token, hostname=hostname,
|
token=token, hostname=mail_domain,
|
||||||
url_inscription=url_inscription,
|
url_inscription=url_inscription,
|
||||||
MAIL_SERVER=MAIL_SERVER, XMPP_SERVER=XMPP_SERVER)
|
MAIL_SERVER=MAIL_SERVER, XMPP_SERVER=XMPP_SERVER)
|
||||||
|
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
|
|
||||||
|
# Une fois que tout c'est bien passé pour l'inscription on détruit le jeton.
|
||||||
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
|
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||||
|
|
||||||
|
cursor.execute("""SELECT name, invitations FROM users where Token=?""", (token,))
|
||||||
|
tmp = cursor.fetchone()
|
||||||
|
user_invite = tmp[0]
|
||||||
|
invitations_count=tmp[1]
|
||||||
|
conn.close()
|
||||||
|
|
||||||
#On test si aucun champs du formulaire n'est vide.
|
#On test si aucun champs du formulaire n'est vide.
|
||||||
if len(request.form['user']) == 0 or \
|
if len(request.form['user']) == 0 or \
|
||||||
len(request.form['passwd']) == 0 or \
|
len(request.form['passwd']) == 0 or \
|
||||||
@@ -55,38 +69,31 @@ def signin(token) :
|
|||||||
flash(u'Il faut remplir le formulaire en entier, les champs ne peuvent pas etre vide ', 'error')
|
flash(u'Il faut remplir le formulaire en entier, les champs ne peuvent pas etre vide ', 'error')
|
||||||
return render_template('inscription.html',
|
return render_template('inscription.html',
|
||||||
signin_enable=app.config['SIGNIN_ENABLE'],
|
signin_enable=app.config['SIGNIN_ENABLE'],
|
||||||
token=token, hostname=hostname,
|
token=token, hostname=mail_domain,
|
||||||
url_inscription=url_inscription,
|
url_inscription=url_inscription,
|
||||||
MAIL_SERVER=MAIL_SERVER, XMPP_SERVER=XMPP_SERVER)
|
MAIL_SERVER=MAIL_SERVER, XMPP_SERVER=XMPP_SERVER)
|
||||||
|
|
||||||
|
user = str(request.form['user'])
|
||||||
|
passwd = str(request.form['passwd'])
|
||||||
|
passwdconfirm = str(request.form['passwdconfirm'])
|
||||||
|
|
||||||
user = request.form['user']
|
bcrypt_passwd = bcrypt.generate_password_hash(passwd)
|
||||||
passwd = request.form['passwd']
|
|
||||||
passwdconfirm = request.form['passwdconfirm']
|
|
||||||
bcrypt_passwd = bcrypt.generate_password_hash(request.form['passwd'])
|
|
||||||
mail_passwd_change = 0
|
mail_passwd_change = 0
|
||||||
|
|
||||||
password_valid = valid_passwd(passwd)
|
|
||||||
not_error = True
|
not_error = True
|
||||||
|
|
||||||
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
if invitations_count < 1 :
|
||||||
cursor = conn.cursor() # Création de l'objet "curseur"
|
flash(u'Votre lien d\'inscription est invalide', 'error')
|
||||||
|
not_error=False
|
||||||
|
|
||||||
cursor.execute("""SELECT name FROM users WHERE name=?""", (user,))
|
if not_error and valid_username(user):
|
||||||
testuser = cursor.fetchone()
|
flash(u'Le Nom d\'utilisateur déjà utilisé ou contient des caractères invalides, merci d\'en choisir un autre', 'error')
|
||||||
conn.close()
|
|
||||||
|
|
||||||
if testuser and valid_username(user):
|
|
||||||
flash(u'Non d\'utilisateur déjà utilisé ou contient des caractères invalides, merci d\'en choisir un autre', 'error')
|
|
||||||
flash(u'Les caractères espaces et \' ( ) < > , ; : " [ ] | ç % & ne sont pas autorisés', 'error')
|
flash(u'Les caractères espaces et \' ( ) < > , ; : " [ ] | ç % & ne sont pas autorisés', 'error')
|
||||||
not_error = False
|
not_error = False
|
||||||
|
|
||||||
if not(password_valid):
|
|
||||||
flash (u'Les caractère & et " ne sont pas autorisé dans les mots de passe', 'error')
|
|
||||||
not_error = False
|
|
||||||
|
|
||||||
if MAIL_SERVER:
|
if MAIL_SERVER:
|
||||||
mail = user.lower()+'@'+hostname
|
mail = user.lower()+'@'+mail_domain
|
||||||
if not(email_disp(mail)) :
|
if not(email_disp(mail)) :
|
||||||
flash(u'Cette Adresse email est déjà utilisée , merci d\'en choisir une autre', 'error')
|
flash(u'Cette Adresse email est déjà utilisée , merci d\'en choisir une autre', 'error')
|
||||||
not_error = False
|
not_error = False
|
||||||
@@ -103,38 +110,53 @@ def signin(token) :
|
|||||||
|
|
||||||
# On change le mot de passe du compte mail
|
# On change le mot de passe du compte mail
|
||||||
if MAIL_SERVER:
|
if MAIL_SERVER:
|
||||||
cmd = SETUID + ' set_mail_passwd ' + '"'+mail+'" ' + '"'+passwd+'"'
|
cmd = subprocess.run([SETUID, 'set_mail_passwd', mail, passwd],stdout=subprocess.PIPE)
|
||||||
mail_passwd_change = os.system(cmd)
|
if cmd.returncode != 0:
|
||||||
if mail_passwd_change != 0:
|
|
||||||
flash(u'Il y a eu une problème lors du changement de mot passe pour le compte Mail', 'error')
|
flash(u'Il y a eu une problème lors du changement de mot passe pour le compte Mail', 'error')
|
||||||
else:
|
else:
|
||||||
cursor.execute("UPDATE users SET mail=? WHERE name=?", mail, user)
|
cursor.execute("UPDATE users SET mail=? WHERE name=?", (mail, user))
|
||||||
|
|
||||||
# On change le mot de passe du compte XMPP
|
# On change le mot de passe du compte XMPP
|
||||||
if XMPP_SERVER:
|
if XMPP_SERVER:
|
||||||
tmp = mail.split('@')
|
jid = mail.split('@')
|
||||||
cmd = SETUID+ ' prosodyctl register ' "'"+tmp[0]+"' " + "'"+tmp[1]+"' " + "'"+passwd+"'"
|
cmd = subprocess.run([SETUID,'prosodyctl', 'register', jid[0],jid[1],passwd],stdout=subprocess.PIPE)
|
||||||
res = os.system(cmd)
|
|
||||||
if res != 0:
|
if cmd.returncode != 0:
|
||||||
flash(u'Il y a eu un problème pour la création du compte XMPP !', 'error')
|
flash(u'Il y a eu un problème pour la création du compte XMPP !', 'error')
|
||||||
|
|
||||||
|
check_and_create(DOSSIER_PERSO + user)
|
||||||
|
time_create_user = time.strftime("%A %d %B %Y %H:%M:%S")
|
||||||
|
ip_address=request.environ['REMOTE_ADDR']
|
||||||
|
client_plateform=request.headers.get('User-Agent')
|
||||||
|
|
||||||
|
log=time_create_user + ' - ' + ip_address + ' - ' + user + ' - ' + client_plateform + '\n' + '---> ' + 'Création du compte \n'
|
||||||
|
log_file=os.path.join(DOSSIER_PERSO,user,"log.txt")
|
||||||
|
try:
|
||||||
|
with open(log_file, 'x') as file:
|
||||||
|
file.write(log)
|
||||||
|
except FileExistsError:
|
||||||
|
print('The file '+log_file +' already exists')
|
||||||
|
|
||||||
|
|
||||||
# Une fois que tout c'est bien passé pour l'inscription on détruit le jeton.
|
if user_invite == "pywallter":
|
||||||
cursor.execute("""SELECT name, invitations FROM users where Token=?""", (token,))
|
cursor.execute("""DELETE from users where name = ?""", (user_invite,))
|
||||||
tmp = cursor.fetchone()
|
elif invitations_count > 0:
|
||||||
username = tmp[0]
|
invitations_count= invitations_count - 1
|
||||||
invitations_count=tmp[1] - 1
|
cursor.execute("""UPDATE users set invitations=? where name=?""", (invitations_count, user_invite,))
|
||||||
if username == "pywallter":
|
|
||||||
cursor.execute("""DELETE from users where name = ?""", (username,))
|
|
||||||
else:
|
else:
|
||||||
cursor.execute("""UPDATE users set invitations=?, Token='' where name=?""", (invitations_count, username,))
|
cursor.execute("""UPDATE users set invitations=?, Token='' where name=?""", (invitations_count, user_invite,))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
flash(u'Inscription réalisée avec succés !', 'succes')
|
flash(u'Inscription réalisée avec succés !', 'success')
|
||||||
resp = redirect(url_for('loginlogout.login'))
|
resp = redirect(url_for('loginlogout.login'))
|
||||||
|
else:
|
||||||
|
return render_template('inscription.html',
|
||||||
|
signin_enable=app.config['SIGNIN_ENABLE'],
|
||||||
|
token=token, hostname=mail_domain,
|
||||||
|
url_inscription=url_inscription,
|
||||||
|
MAIL_SERVER=MAIL_SERVER, XMPP_SERVER=XMPP_SERVER)
|
||||||
else:
|
else:
|
||||||
resp = redirect(BASE_URL, code=401)
|
resp = redirect(BASE_URL, code=401)
|
||||||
|
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ from markupsafe import escape
|
|||||||
from flask_bcrypt import Bcrypt
|
from flask_bcrypt import Bcrypt
|
||||||
from socket import gethostname
|
from socket import gethostname
|
||||||
from os import remove, system
|
from os import remove, system
|
||||||
from tools.utils import email_disp, valid_token_register, valid_passwd, valid_username, gen_token
|
from tools.utils import email_disp, valid_token_register, valid_passwd, valid_username, gen_token, totp_is_valid
|
||||||
from tools.mailer import Mailer
|
from tools.mailer import Mailer
|
||||||
|
from tools.utils import login_required
|
||||||
|
|
||||||
app = Flask( 'pywallter' )
|
app = Flask( 'pywallter' )
|
||||||
app.config.from_pyfile('config.py')
|
app.config.from_pyfile('config.py')
|
||||||
@@ -14,42 +15,72 @@ bcrypt = Bcrypt(app)
|
|||||||
#### Variables ####################################################################################
|
#### Variables ####################################################################################
|
||||||
|
|
||||||
bcrypt = Bcrypt(app)
|
bcrypt = Bcrypt(app)
|
||||||
DATAS_USER = app.config['DOSSIER_APP']
|
DATAS_USER = app.config.get('DOSSIER_APP')
|
||||||
|
|
||||||
extensionimg = app.config['EXT_IMG']
|
extensionimg = app.config.get('EXT_IMG')
|
||||||
|
|
||||||
DATABASE = app.config['DATABASE']
|
DATABASE = app.config.get('DATABASE')
|
||||||
|
BASE_URL = app.config.get('BASE_URL')
|
||||||
BASE_URL = app.config['BASE_URL']
|
SETUID = app.config.get('SETUID')
|
||||||
SETUID = app.config['SETUID']
|
MAIL_SERVER = app.config.get('MAIL_SERVER')
|
||||||
MAIL_SERVER = app.config['MAIL_SERVER']
|
XMPP_SERVER = app.config.get('XMPP_SERVER')
|
||||||
XMPP_SERVER = app.config['XMPP_SERVER']
|
BACKUP_TIME = app.config.get('BACKUP_TIME')
|
||||||
BACKUP_TIME = app.config['BACKUP_TIME']
|
SERVER_TITLE = app.config.get('TITLE_SERVER')
|
||||||
|
SERVER_DESC = app.config.get('DESC_SERVER')
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
|
||||||
loginlogout = Blueprint('loginlogout', __name__, template_folder='templates')
|
loginlogout = Blueprint('loginlogout', __name__, template_folder='templates')
|
||||||
|
|
||||||
|
|
||||||
|
@loginlogout.route( '/' )
|
||||||
|
def index():
|
||||||
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
|
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||||
|
cursor.execute("""SELECT token passwd FROM users where name=? """, ("pywallter", ))
|
||||||
|
tmp = cursor.fetchone()
|
||||||
|
conn.close
|
||||||
|
if tmp:
|
||||||
|
token = tmp[0]
|
||||||
|
else:
|
||||||
|
token = None
|
||||||
|
|
||||||
|
if 'username' in session :
|
||||||
|
return redirect(url_for('profil.profile'))
|
||||||
|
else :
|
||||||
|
if token:
|
||||||
|
hostname = gethostname()
|
||||||
|
url_inscription = BASE_URL+'/inscription/'+token
|
||||||
|
return render_template('inscription.html', signin_enable=app.config['SIGNIN_ENABLE'],
|
||||||
|
token=token, hostname=hostname,
|
||||||
|
url_inscription=url_inscription,
|
||||||
|
server_title=SERVER_TITLE,
|
||||||
|
mail_server=MAIL_SERVER)
|
||||||
|
else:
|
||||||
|
return redirect(url_for('loginlogout.login', _external=True))
|
||||||
|
|
||||||
@loginlogout.route( '/login/', methods=['GET','POST'] )
|
@loginlogout.route( '/login/', methods=['GET','POST'] )
|
||||||
def login() :
|
def login():
|
||||||
if 'username' in session :
|
if 'username' in session :
|
||||||
resp = redirect(url_for('profil.profile', _external=True))
|
resp = redirect(url_for('profil.profile', _external=True))
|
||||||
else :
|
else :
|
||||||
resp = redirect(url_for('loginlogout.login', _external=True))
|
resp = redirect(url_for('loginlogout.login', _external=True))
|
||||||
if request.method == 'POST' :
|
if request.method == 'POST' :
|
||||||
user = request.form['user']
|
user = str(request.form['user'])
|
||||||
password = request.form['passwd']
|
password = str(request.form['passwd'])
|
||||||
|
totp = str(request.form['code_totp'])
|
||||||
|
|
||||||
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
cursor = conn.cursor() # Création de l'objet "curseur"
|
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||||
cursor.execute("""SELECT name, passwd FROM users WHERE name=?""", (user,))
|
cursor.execute("""SELECT name, passwd, totp FROM users WHERE name=?""", (user,))
|
||||||
user_exist = cursor.fetchone()
|
user_exist = cursor.fetchone()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
if user_exist:
|
if user_exist:
|
||||||
user = user_exist[0]
|
user = user_exist[0]
|
||||||
passwd_bcrypt = user_exist[1]
|
passwd_bcrypt = user_exist[1]
|
||||||
|
totp_key = user_exist[2]
|
||||||
if user == request.form['user'] and bcrypt.check_password_hash(passwd_bcrypt, password) is True:
|
if totp_is_valid(totp_key, totp) and user == request.form['user'] and bcrypt.check_password_hash(passwd_bcrypt, password) is True:
|
||||||
session['username'] = request.form['user']
|
session['username'] = request.form['user']
|
||||||
resp = redirect(url_for('profil.profile', _external=True))
|
resp = redirect(url_for('profil.profile', _external=True))
|
||||||
else:
|
else:
|
||||||
@@ -57,70 +88,87 @@ def login() :
|
|||||||
else:
|
else:
|
||||||
flash(u"L'utilisateur n'existe pas", 'error')
|
flash(u"L'utilisateur n'existe pas", 'error')
|
||||||
else:
|
else:
|
||||||
resp = render_template('accueil.html', signin_enable=app.config['SIGNIN_ENABLE'])
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
|
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||||
|
cursor.execute("""SELECT title, subtitle, creation_date, last_updated, author FROM Blog_posts WHERE status='public_unified'""" )
|
||||||
|
list_posts=cursor.fetchall()
|
||||||
|
posts=list()
|
||||||
|
id=0
|
||||||
|
conn.close()
|
||||||
|
if list_posts != None:
|
||||||
|
for post in list_posts:
|
||||||
|
posts=[dict(title=post[0], subtitle=post[1], creation_date=post[2], last_updated=post[3], author=post[4])] + posts
|
||||||
|
|
||||||
|
resp = render_template('accueil.html',
|
||||||
|
server_title=SERVER_TITLE,
|
||||||
|
server_desc=SERVER_DESC,
|
||||||
|
mail_server=MAIL_SERVER,
|
||||||
|
list_posts=posts,
|
||||||
|
signin_enable=app.config['SIGNIN_ENABLE'])
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
|
||||||
@loginlogout.route( '/logout/' )
|
@loginlogout.route( '/logout/' )
|
||||||
|
@login_required
|
||||||
def logout():
|
def logout():
|
||||||
session.pop('username', None) # Supprimer username de la session s'il s'y trouve
|
session.pop('username', None) # Supprimer username de la session s'il s'y trouve
|
||||||
return redirect(url_for('loginlogout.index'))
|
return redirect(url_for('loginlogout.index'))
|
||||||
|
|
||||||
|
|
||||||
@loginlogout.route( '/delete_me/', methods=['GET','POST'])
|
@loginlogout.route( '/delete_me/', methods=['GET','POST'])
|
||||||
|
@login_required
|
||||||
def delete_account():
|
def delete_account():
|
||||||
if 'username' in session :
|
user='%s'% escape(session['username'])
|
||||||
user='%s'% escape(session['username'])
|
resp = render_template('delete_account.html', time_backup=BACKUP_TIME)
|
||||||
resp = render_template('delete_account.html', time_backup=BACKUP_TIME)
|
if request.method == 'POST' :
|
||||||
if request.method == 'POST' :
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||||
cursor = conn.cursor() # Création de l'objet "curseur"
|
cursor.execute("""SELECT passwd FROM users WHERE name=?""", (user,))
|
||||||
cursor.execute("""SELECT passwd FROM users WHERE name=?""", (user,))
|
passwd = cursor.fetchone()[0].decode()
|
||||||
passwd = cursor.fetchone()[0].decode()
|
conn.close()
|
||||||
conn.close()
|
password = request.form['passwd']
|
||||||
password = request.form['passwd']
|
if bcrypt.check_password_hash(passwd, password) is True:
|
||||||
if bcrypt.check_password_hash(passwd, password) is True:
|
not_error = True
|
||||||
not_error = True
|
try:
|
||||||
|
cmd = 'rm -r ' + DATAS_USER + '/' + user
|
||||||
|
if system(cmd) != 0:
|
||||||
|
raise TypeError("Remove directory error")
|
||||||
|
except:
|
||||||
|
not_error = False
|
||||||
|
flash(u'Erreur lors de la suppression de votre dossier utilisateur.', 'error')
|
||||||
|
|
||||||
|
if MAIL_SERVER:
|
||||||
try:
|
try:
|
||||||
cmd = 'rm -r ' + DATAS_USER + '/' + user
|
cmd = SETUID + ' set_mail_passwd del' + '"'+mail+'"'
|
||||||
if system(cmd) != 0:
|
system(cmd)
|
||||||
raise TypeError("Remove directory error")
|
|
||||||
except:
|
except:
|
||||||
not_error = False
|
not_error = False
|
||||||
flash(u'Erreur lors de la suppression de votre dossier utilisateur.', 'error')
|
flash(u'Erreur lors de la suppression de votre compte Mail.', 'error')
|
||||||
|
|
||||||
if MAIL_SERVER:
|
if XMPP_SERVER:
|
||||||
try:
|
try:
|
||||||
cmd = SETUID + ' set_mail_passwd del' + '"'+mail+'"'
|
tmp = mail.split('@')
|
||||||
system(cmd)
|
cmd = SETUID+ ' prosodyctl deluser ' "'"+tmp[0]+"' " + "'"+tmp[1]+"'"
|
||||||
except:
|
system(cmd)
|
||||||
not_error = False
|
except:
|
||||||
flash(u'Erreur lors de la suppression de votre compte Mail.', 'error')
|
not_error = False
|
||||||
|
flash(u'Erreur lors de la suppression de votre compte XMPP.', 'error')
|
||||||
|
|
||||||
if XMPP_SERVER:
|
if not_error:
|
||||||
try:
|
try:
|
||||||
tmp = mail.split('@')
|
conn = sqlite3.connect(DATABASE)
|
||||||
cmd = SETUID+ ' prosodyctl deluser ' "'"+tmp[0]+"' " + "'"+tmp[1]+"'"
|
cursor = conn.cursor()
|
||||||
system(cmd)
|
cursor.execute("""DELETE FROM users WHERE name=?""", (user,))
|
||||||
except:
|
conn.commit()
|
||||||
not_error = False
|
conn.close()
|
||||||
flash(u'Erreur lors de la suppression de votre compte XMPP.', 'error')
|
except:
|
||||||
|
flash(u'Erreur lors de la suppression de votre compte.', 'error')
|
||||||
if not_error:
|
|
||||||
try:
|
|
||||||
conn = sqlite3.connect(DATABASE)
|
|
||||||
cursor = conn.cursor()
|
|
||||||
cursor.execute("""DELETE FROM users WHERE name=?""", (user,))
|
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
|
||||||
except:
|
|
||||||
flash(u'Erreur lors de la suppression de votre compte.', 'error')
|
|
||||||
else:
|
|
||||||
flash(u'Désinscription réalisé avec succés, y\'a plus rien !', 'succes')
|
|
||||||
resp = redirect(url_for('loginlogout.logout'))
|
|
||||||
else:
|
else:
|
||||||
flash(u'Mauvais mot de passe', 'error')
|
flash(u'Désinscription réalisé avec succés, y\'a plus rien !', 'succes')
|
||||||
return resp
|
resp = redirect(url_for('loginlogout.logout'))
|
||||||
|
else:
|
||||||
|
flash(u'Mauvais mot de passe', 'error')
|
||||||
|
return resp
|
||||||
|
|
||||||
|
|
||||||
@loginlogout.route( '/lost_password/', methods=['GET', 'POST'])
|
@loginlogout.route( '/lost_password/', methods=['GET', 'POST'])
|
||||||
@@ -159,27 +207,3 @@ def lost_password():
|
|||||||
|
|
||||||
return render_template('lost_password.html')
|
return render_template('lost_password.html')
|
||||||
|
|
||||||
@loginlogout.route( '/' )
|
|
||||||
def index():
|
|
||||||
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
|
||||||
cursor = conn.cursor() # Création de l'objet "curseur"
|
|
||||||
cursor.execute("""SELECT token passwd FROM users where name=? """, ("pywallter", ))
|
|
||||||
tmp = cursor.fetchone()
|
|
||||||
conn.close
|
|
||||||
if tmp:
|
|
||||||
token = tmp[0]
|
|
||||||
else:
|
|
||||||
token = None
|
|
||||||
|
|
||||||
if 'username' in session :
|
|
||||||
return redirect(url_for('profil.profile'))
|
|
||||||
else :
|
|
||||||
if token:
|
|
||||||
hostname = gethostname()
|
|
||||||
url_inscription = BASE_URL+'inscription/'+token
|
|
||||||
return render_template('inscription.html', signin_enable=app.config['SIGNIN_ENABLE'],
|
|
||||||
token=token, hostname=hostname,
|
|
||||||
url_inscription=url_inscription,
|
|
||||||
MAIL_SERVER=MAIL_SERVER)
|
|
||||||
else:
|
|
||||||
return redirect(url_for('loginlogout.login', _external=True))
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from flask import Blueprint, Flask, request, flash, render_template, url_for, session, redirect, abort, make_response, send_file
|
from flask import Blueprint, Flask, request, flash, render_template, url_for, session, redirect, abort, make_response, send_file
|
||||||
import glob, os, sys
|
import glob, os, sys
|
||||||
from markupsafe import escape
|
from markupsafe import escape
|
||||||
|
from tools.utils import login_required
|
||||||
|
|
||||||
logs = Blueprint('logs', __name__, template_folder='templates')
|
logs = Blueprint('logs', __name__, template_folder='templates')
|
||||||
|
|
||||||
@@ -10,23 +11,20 @@ app.config.from_pyfile('config.py')
|
|||||||
|
|
||||||
#### Variables ####################################################################################
|
#### Variables ####################################################################################
|
||||||
|
|
||||||
DOSSIER_PERSO= app.config['DOSSIER_APP']
|
DOSSIER_PERSO= app.config.get('DOSSIER_APP')
|
||||||
|
|
||||||
extensionimg = app.config['EXT_IMG']
|
extensionimg = app.config.get('EXT_IMG')
|
||||||
|
|
||||||
DATABASE = app.config['DATABASE']
|
DATABASE = app.config.get('DATABASE')
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
|
||||||
@logs.route('/logs/')
|
@logs.route('/logs/')
|
||||||
|
@login_required
|
||||||
def logfile():
|
def logfile():
|
||||||
if 'username' in session:
|
user='%s'% escape(session['username'])
|
||||||
UTILISATEUR='%s'% escape(session['username'])
|
log_file=os.path.join(DOSSIER_PERSO, user, "log.txt")
|
||||||
log_file=os.path.join(DOSSIER_PERSO, UTILISATEUR, "log.txt")
|
with open(log_file, 'r') as log:
|
||||||
with open(log_file, 'r') as log:
|
logs=log.readlines()
|
||||||
print("on passe ici")
|
log.close()
|
||||||
logs=log.readlines()
|
return render_template('logs.html', section="Logs", logs=logs)
|
||||||
log.close()
|
|
||||||
return render_template('logs.html', section="Logs", logs=logs)
|
|
||||||
else :
|
|
||||||
return redirect(url_for('loginlogout.login', _external=True), code=401)
|
|
||||||
|
|||||||
161
views/mymailbox.py
Normal file
161
views/mymailbox.py
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
from flask import Blueprint, Flask, request, flash, render_template, url_for, session, redirect, abort, make_response, send_file, flash, abort, send_from_directory
|
||||||
|
from werkzeug.utils import secure_filename
|
||||||
|
from PIL import Image
|
||||||
|
from markupsafe import escape
|
||||||
|
import time
|
||||||
|
import sqlite3
|
||||||
|
import os
|
||||||
|
from shutil import copy
|
||||||
|
from socket import gethostname
|
||||||
|
from tools.utils import email_disp, append_to_log, gen_token, valid_passwd, login_required, set_mail_domain
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
mymailbox = Blueprint('mymailbox', __name__, template_folder='templates')
|
||||||
|
|
||||||
|
app = Flask( 'pywallter' )
|
||||||
|
app.config.from_pyfile('config.py')
|
||||||
|
|
||||||
|
|
||||||
|
#### Variables ###################################################################################
|
||||||
|
DOSSIER_PERSO = app.config.get('DOSSIER_APP')
|
||||||
|
|
||||||
|
extensionimg = app.config.get('EXT_IMG')
|
||||||
|
|
||||||
|
DATABASE = app.config.get('DATABASE')
|
||||||
|
DATAS_USER = app.config.get('DOSSIER_APP')
|
||||||
|
MAIL_SERVER = app.config.get('MAIL_SERVER')
|
||||||
|
XMPP_SERVER = app.config.get('XMPP_SERVER')
|
||||||
|
SETUID = app.config.get('SETUID')
|
||||||
|
BASE_URL = app.config.get('BASE_URL')
|
||||||
|
MAIL_WEBSERVICE = app.config.get('MAIL_WEBSERVICE')
|
||||||
|
XMPP_WEBSERVICE = app.config.get('XMPP_WEBSERVICE')
|
||||||
|
|
||||||
|
##################################################################################################
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@mymailbox.route('/mymailbox/', methods=['GET'] )
|
||||||
|
@login_required
|
||||||
|
def mymessaging():
|
||||||
|
|
||||||
|
hostname=set_mail_domain()
|
||||||
|
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 Mail FROM users where name=?""", (user,))
|
||||||
|
tmp = cursor.fetchone()
|
||||||
|
myemail = tmp[0]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return render_template('mymessaging.html',
|
||||||
|
section="mymessaging",
|
||||||
|
hostname=hostname,
|
||||||
|
myemail=myemail,
|
||||||
|
imap_address=app.config.get('IMAP_ADDRESS'),
|
||||||
|
smtp_address=app.config.get('SMTP_ADDRESS'),
|
||||||
|
mail_server=MAIL_SERVER,
|
||||||
|
mail_webservice=MAIL_WEBSERVICE,
|
||||||
|
xmpp_server=XMPP_SERVER,
|
||||||
|
xmpp_webservice=XMPP_WEBSERVICE,
|
||||||
|
username=user )
|
||||||
|
|
||||||
|
|
||||||
|
@mymailbox.route('/mymailbox/alias', methods=['GET', 'POST'] )
|
||||||
|
@login_required
|
||||||
|
def myalias():
|
||||||
|
hostname = set_mail_domain()
|
||||||
|
user='%s' % escape(session['username'])
|
||||||
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
|
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||||
|
if request.method == 'POST' and MAIL_SERVER:
|
||||||
|
if request.form['alias']:
|
||||||
|
alias = request.form['alias'].lower()+'@'+hostname
|
||||||
|
else:
|
||||||
|
flash(u'Addresse invalide', 'error')
|
||||||
|
|
||||||
|
if email_disp(alias):
|
||||||
|
cursor.execute("""SELECT Mail, alias FROM users where name=?""", (user,))
|
||||||
|
tmp = cursor.fetchone()
|
||||||
|
mail = tmp[0]
|
||||||
|
if tmp[1]:
|
||||||
|
alias_list = tmp[1]
|
||||||
|
aliases = alias_list + "," +alias
|
||||||
|
else:
|
||||||
|
aliases = alias
|
||||||
|
cmd = SETUID+ " set_mail_alias " + "'"+mail+"'"+" add "+"'"+alias+"'"
|
||||||
|
res = os.system(cmd)
|
||||||
|
if res == 0:
|
||||||
|
cursor.execute("UPDATE users SET alias=? WHERE name=?",
|
||||||
|
(aliases, user))
|
||||||
|
conn.commit()
|
||||||
|
time_create_alias = time.strftime("%A %d %B %Y %H:%M:%S")
|
||||||
|
ip_address=request.environ['REMOTE_ADDR']
|
||||||
|
client_plateform=request.headers.get('User-Agent')
|
||||||
|
|
||||||
|
log=time_create_alias + ' - ' + ip_address + ' - ' + user + ' - ' + client_plateform + '\n' + '---> ' + "Ajout de l'alias "+ alias + '\n'
|
||||||
|
append_to_log(log, user)
|
||||||
|
flash(u'Votre alias a été ajouté', 'succes')
|
||||||
|
else:
|
||||||
|
flash(u'Adresse indisponible', 'error')
|
||||||
|
else:
|
||||||
|
flash(u'Adresse indisponible', 'error')
|
||||||
|
|
||||||
|
cursor.execute("""SELECT Mail, alias FROM users WHERE name=?""",
|
||||||
|
(user,))
|
||||||
|
tmp = cursor.fetchone()
|
||||||
|
mailbox = dict()
|
||||||
|
mailbox['Mail'] = tmp[0]
|
||||||
|
if tmp[1]:
|
||||||
|
mailbox['alias'] = tmp[1].split(',')
|
||||||
|
else:
|
||||||
|
mailbox['alias'] = list()
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
return render_template('myalias.html',
|
||||||
|
section="mailbox",
|
||||||
|
email=mailbox['Mail'],
|
||||||
|
aliases=mailbox['alias'],
|
||||||
|
hostname=hostname,
|
||||||
|
MAIL_SERVER=MAIL_SERVER,
|
||||||
|
username=user )
|
||||||
|
|
||||||
|
|
||||||
|
@mymailbox.route('/mymailbox/rmalias/<aliasrm>')
|
||||||
|
@login_required
|
||||||
|
def remove_alias(aliasrm):
|
||||||
|
aliasrm = escape(aliasrm)
|
||||||
|
if MAIL_SERVER:
|
||||||
|
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 Mail, alias FROM users WHERE name=?""", (user,))
|
||||||
|
tmp = cursor.fetchone()
|
||||||
|
mail = tmp[0]
|
||||||
|
alias_list = tmp[1].split(',')
|
||||||
|
aliases = ""
|
||||||
|
for alias in alias_list:
|
||||||
|
if alias != aliasrm:
|
||||||
|
if aliases:
|
||||||
|
aliases = aliases + "," + alias
|
||||||
|
else:
|
||||||
|
aliases = alias
|
||||||
|
cmd = SETUID + " set_mail_alias " + "'"+mail+"'"+" del "+"'"+alias+"'"
|
||||||
|
res = os.system(cmd)
|
||||||
|
if res == 0:
|
||||||
|
cursor.execute("UPDATE users SET alias=? WHERE name=?",
|
||||||
|
(aliases, user))
|
||||||
|
conn.commit()
|
||||||
|
time_del_alias=time.strftime("%A %d %B %Y %H:%M:%S")
|
||||||
|
ip_address=request.environ['REMOTE_ADDR']
|
||||||
|
client_platform=request.headers.get('User-Agent')
|
||||||
|
log = time_del_alias + ' - ' + ip_address + ' - ' + user + ' - ' + client_platform + '\n' + '---> ' + "Suppression de l'alias "+ alias + '\n'
|
||||||
|
append_to_log(log, user)
|
||||||
|
flash(u'Votre alias a été supprimé', 'succes')
|
||||||
|
else:
|
||||||
|
flash(u'Il y a eu une erreur', 'error')
|
||||||
|
|
||||||
|
return redirect(url_for('mymailbox.myalias'))
|
||||||
|
|
||||||
536
views/profil.py
536
views/profil.py
@@ -5,10 +5,15 @@ from markupsafe import escape
|
|||||||
import time
|
import time
|
||||||
import sqlite3
|
import sqlite3
|
||||||
import os
|
import os
|
||||||
|
import subprocess
|
||||||
from shutil import copy
|
from shutil import copy
|
||||||
from socket import gethostname
|
from socket import gethostname
|
||||||
from flask_bcrypt import Bcrypt
|
from flask_bcrypt import Bcrypt
|
||||||
from tools.utils import email_disp, append_to_log, gen_token, valid_passwd, valid_token_register, get_user_by_token
|
from tools.utils import email_disp, append_to_log, gen_token, valid_passwd, valid_token_register, get_user_by_token, totp_is_valid, login_required, valid_email
|
||||||
|
from pyotp import random_base32
|
||||||
|
from tools.filesutils import check_and_create
|
||||||
|
import qrcode
|
||||||
|
|
||||||
|
|
||||||
profil = Blueprint('profil', __name__, template_folder='templates')
|
profil = Blueprint('profil', __name__, template_folder='templates')
|
||||||
|
|
||||||
@@ -19,150 +24,148 @@ app.config.from_pyfile('config.py')
|
|||||||
#### Variables ####################################################################################
|
#### Variables ####################################################################################
|
||||||
|
|
||||||
bcrypt = Bcrypt(app)
|
bcrypt = Bcrypt(app)
|
||||||
DOSSIER_PERSO = app.config['DOSSIER_APP']
|
DOSSIER_PERSO = app.config.get('DOSSIER_APP')
|
||||||
|
|
||||||
extensionimg = app.config['EXT_IMG']
|
extensionimg = app.config.get('EXT_IMG')
|
||||||
|
|
||||||
DATABASE = app.config['DATABASE']
|
DATABASE = app.config.get('DATABASE')
|
||||||
DATAS_USER = app.config['DOSSIER_APP']
|
DATAS_USER = app.config.get('DOSSIER_APP')
|
||||||
MAIL_SERVER = app.config['MAIL_SERVER']
|
MAIL_SERVER = app.config.get('MAIL_SERVER')
|
||||||
XMPP_SERVER = app.config['XMPP_SERVER']
|
XMPP_SERVER = app.config.get('XMPP_SERVER')
|
||||||
SETUID = app.config['SETUID']
|
SETUID = app.config.get('SETUID')
|
||||||
BASE_URL = app.config['BASE_URL']
|
BASE_URL = app.config.get('BASE_URL')
|
||||||
BACKUP_TIME = app.config['BACKUP_TIME']
|
BACKUP_TIME = app.config.get('BACKUP_TIME')
|
||||||
|
|
||||||
##################################################################################################
|
##################################################################################################
|
||||||
|
|
||||||
|
|
||||||
@profil.route( '/profil/<user>/<img>', methods=['GET'] )
|
@profil.route( '/profil/<user>/<img>', methods=['GET'] )
|
||||||
|
@login_required
|
||||||
def profil_img(user, img) :
|
def profil_img(user, img) :
|
||||||
if 'username' in session :
|
return send_from_directory( os.path.join(DOSSIER_PERSO, user, 'profile'), img )
|
||||||
|
|
||||||
return send_from_directory( os.path.join(DOSSIER_PERSO, user, 'profile'), img )
|
|
||||||
else:
|
|
||||||
return redirect(BASE_URL, code=401)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@profil.route('/profil/', methods=['GET','POST'])
|
@profil.route('/profil/', methods=['GET','POST'])
|
||||||
|
@login_required
|
||||||
def profile() :
|
def profile() :
|
||||||
if 'username' in session :
|
user='%s' % escape(session['username'])
|
||||||
user='%s' % escape(session['username'])
|
check_and_create(os.path.join(DOSSIER_PERSO, user,'profile'))
|
||||||
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
cursor = conn.cursor() # Création de l'objet "curseur"
|
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||||
cursor.execute("""SELECT avatar, nom, prenom, age, Mail_rescue FROM users WHERE name=?""", (user,))
|
cursor.execute("""SELECT avatar, nom, prenom, age, Mail_rescue FROM users WHERE name=?""", (user,))
|
||||||
tmp = (cursor.fetchone())
|
tmp = (cursor.fetchone())
|
||||||
profil_user = dict()
|
profil_user = dict()
|
||||||
profil_user['avatar'] = tmp[0]
|
profil_user['avatar'] = tmp[0]
|
||||||
profil_user['nom'] = tmp[1]
|
profil_user['nom'] = tmp[1]
|
||||||
profil_user['prenom'] = tmp[2]
|
profil_user['prenom'] = tmp[2]
|
||||||
profil_user['age'] = tmp[3]
|
profil_user['age'] = tmp[3]
|
||||||
profil_user['mail_rescue'] = tmp[4]
|
profil_user['mail_rescue'] = tmp[4]
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
if request.method == 'POST' :
|
if request.method == 'POST' :
|
||||||
|
|
||||||
f = request.files['fic']
|
f = request.files['fic']
|
||||||
|
|
||||||
|
|
||||||
if request.form['theme'] != "Default":
|
if request.form['theme']:
|
||||||
copy( "static/vendors/picocss/pico.fluid.classless."+request.form['theme']+".min.css",
|
copy( "static/vendors/picocss/pico.fluid.classless."+request.form['theme']+".min.css",
|
||||||
DOSSIER_PERSO+ user +'/theme.min.css' )
|
os.path.join(DOSSIER_PERSO, user ,'theme.min.css') )
|
||||||
|
|
||||||
if request.form['nom']:
|
if request.form['nom']:
|
||||||
profil_user['nom'] = request.form['nom']
|
profil_user['nom'] = str(request.form['nom'])
|
||||||
if request.form['prenom']:
|
if request.form['prenom']:
|
||||||
profil_user['prenom'] = request.form['prenom']
|
profil_user['prenom'] = str(request.form['prenom'])
|
||||||
if request.form['age']:
|
if request.form['age']:
|
||||||
profil_user['age'] = request.form['age']
|
profil_user['age'] = str(request.form['age'])
|
||||||
if '@' in request.form['mail_rescue']:
|
|
||||||
if len(request.form['mail_rescue']) > 4:
|
if request.form['mail_rescue'] :
|
||||||
profil_user['mail_rescue'] = request.form['mail_rescue']
|
new_mail_rescue = str(request.form['mail_rescue'])
|
||||||
|
if valid_email(new_mail_rescue):
|
||||||
|
profil_user['mail_rescue']=new_mail_rescue
|
||||||
else:
|
else:
|
||||||
flash(u'Adresse de courriel invalide', 'error')
|
flash(u'Adresse de courriel invalide', 'error')
|
||||||
else:
|
|
||||||
flash(u'Adresse de courriel de secour invalide', 'error')
|
|
||||||
if f: # On vérifie qu'un fichier a bien été envoyé
|
if f: # On vérifie qu'un fichier a bien été envoyé
|
||||||
nom = secure_filename(f.filename)
|
nom = secure_filename(f.filename)
|
||||||
f.save(DOSSIER_PERSO + user + '/profile/' + nom)
|
f.save(os.path.join(DOSSIER_PERSO, user, 'profile', nom))
|
||||||
image = DOSSIER_PERSO + user + '/profile/' + nom
|
image = os.path.join(DOSSIER_PERSO, user, 'profile', nom)
|
||||||
with Image.open(image) as img:
|
with Image.open(image) as img:
|
||||||
img.thumbnail((300,200))
|
img.thumbnail((300,200))
|
||||||
img.save( DOSSIER_PERSO + user + '/profile/' + nom)
|
img.save(os.path.join(DOSSIER_PERSO, user, 'profile', nom))
|
||||||
filename = nom
|
filename = nom
|
||||||
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
cursor = conn.cursor() # Création de l'objet "curseur"
|
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||||
cursor.execute("UPDATE users SET avatar=? WHERE name=?",
|
cursor.execute("UPDATE users SET avatar=? WHERE name=?",
|
||||||
(filename, user))
|
(filename, user))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
cursor = conn.cursor() # Création de l'objet "curseur"
|
|
||||||
conn.close()
|
conn.close()
|
||||||
flash(u'Image de profil mise à jour', 'success')
|
flash(u'Image de profil mise à jour', 'success')
|
||||||
|
|
||||||
else:
|
else:
|
||||||
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
cursor = conn.cursor() # Création de l\'objet "curseur"
|
cursor = conn.cursor() # Création de l\'objet "curseur"
|
||||||
cursor.execute("UPDATE users SET nom=?, prenom=?, age=?, mail_rescue=? WHERE name=?",
|
cursor.execute("UPDATE users SET nom=?, prenom=?, age=?, mail_rescue=? WHERE name=?",
|
||||||
(profil_user['nom'], profil_user['prenom'], profil_user['age'], profil_user['mail_rescue'],
|
(profil_user['nom'], profil_user['prenom'], profil_user['age'], profil_user['mail_rescue'],
|
||||||
user))
|
user))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
flash(u'Le profil a été mis à jour', 'succes')
|
flash(u'Le profil a été mis à jour', 'success')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return render_template('profil.html',
|
return render_template('profil.html',
|
||||||
section="Profil",
|
section="Profil",
|
||||||
profil=profil_user,
|
profil=profil_user,
|
||||||
username=user)
|
username=user)
|
||||||
|
|
||||||
else :
|
|
||||||
return redirect(BASE_URL, code=401)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@profil.route('/profil/homepage', methods=['GET'] )
|
@profil.route('/profil/homepage', methods=['GET'] )
|
||||||
|
@login_required
|
||||||
def homepage():
|
def homepage():
|
||||||
if 'username' in session :
|
username='%s' % escape(session['username'])
|
||||||
username='%s' % escape(session['username'])
|
|
||||||
|
|
||||||
return render_template('homepage.html',
|
return render_template('homepage.html',
|
||||||
section="Profil",
|
section="Profil",
|
||||||
username=username)
|
username=username)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@profil.route('/profil/change-password/', methods=['GET','POST'] )
|
@profil.route('/profil/change-password/', methods=['GET','POST'] )
|
||||||
|
@login_required
|
||||||
def change_passwd() :
|
def change_passwd() :
|
||||||
if 'username' in session:
|
user='%s' % escape(session['username'])
|
||||||
UTILISATEUR='%s' % escape(session['username'])
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||||
cursor = conn.cursor() # Création de l'objet "curseur"
|
cursor.execute("""SELECT Mail, alias, xmpp, totp FROM users WHERE name=?""", (user,))
|
||||||
cursor.execute("""SELECT Mail, alias, xmpp FROM users WHERE name=?""", (UTILISATEUR,))
|
tmp = cursor.fetchone()
|
||||||
tmp = cursor.fetchone()
|
shared_key_validate=True
|
||||||
mailbox = dict()
|
account = dict()
|
||||||
mailbox['Mail'] = tmp[0]
|
account['Mail'] = tmp[0]
|
||||||
mailbox['alias'] = tmp[1]
|
account['alias'] = tmp[1]
|
||||||
mailbox['xmpp'] = tmp[2]
|
account['xmpp'] = tmp[2]
|
||||||
|
account['totp'] = tmp[3]
|
||||||
|
|
||||||
|
if request.method == 'POST' :
|
||||||
|
|
||||||
|
password = request.form['password']
|
||||||
|
password_confirm = request.form['passwd_confirm']
|
||||||
|
|
||||||
|
|
||||||
if request.method == 'POST' :
|
if not(password == "") and password == password_confirm and valid_passwd(password):
|
||||||
|
|
||||||
password = request.form['password']
|
|
||||||
password_confirm = request.form['passwd_confirm']
|
|
||||||
|
|
||||||
if password == password_confirm and valid_passwd(password):
|
|
||||||
mail_passwd_change = 0
|
mail_passwd_change = 0
|
||||||
xmpp_passwd_change = 0
|
xmpp_passwd_change = 0
|
||||||
passwd = request.form['password']
|
passwd = request.form['password']
|
||||||
|
|
||||||
if MAIL_SERVER:
|
if MAIL_SERVER:
|
||||||
cmd = SETUID+ ' set_mail_passwd ' + '"'+mailbox['Mail']+'" '+ '"'+passwd+'"'
|
cmd = SETUID+ ' set_mail_passwd ' + '"'+account['Mail']+'" '+ '"'+passwd+'"'
|
||||||
mail_passwd_change = os.system(cmd)
|
mail_passwd_change = os.system(cmd)
|
||||||
|
|
||||||
|
|
||||||
if XMPP_SERVER:
|
if XMPP_SERVER:
|
||||||
tmp = mailbox['Mail'].split('@')
|
tmp = account['Mail'].split('@')
|
||||||
cmd = SETUID+ " prosodyctl register '"+tmp[0]+"' " + "'"+tmp[1]+"' " + "'"+passwd+"'"
|
cmd = SETUID+ " prosodyctl register '"+tmp[0]+"' " + "'"+tmp[1]+"' " + "'"+passwd+"'"
|
||||||
xmpp_passwd_change = os.system(cmd)
|
xmpp_passwd_change = os.system(cmd)
|
||||||
if xmpp_passwd_change != 0:
|
if xmpp_passwd_change != 0:
|
||||||
@@ -172,29 +175,39 @@ def change_passwd() :
|
|||||||
if mail_passwd_change == 0:
|
if mail_passwd_change == 0:
|
||||||
passwd_bcrypt = bcrypt.generate_password_hash(passwd)
|
passwd_bcrypt = bcrypt.generate_password_hash(passwd)
|
||||||
cursor.execute("UPDATE users SET passwd=? WHERE name=?",
|
cursor.execute("UPDATE users SET passwd=? WHERE name=?",
|
||||||
(passwd_bcrypt, UTILISATEUR))
|
(passwd_bcrypt, user))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
TIME=time.strftime("%A %d %B %Y %H:%M:%S")
|
TIME=time.strftime("%A %d %B %Y %H:%M:%S")
|
||||||
IP=request.environ['REMOTE_ADDR']
|
IP=request.environ['REMOTE_ADDR']
|
||||||
CLIENT_PLATFORM=request.headers.get('User-Agent')
|
CLIENT_PLATFORM=request.headers.get('User-Agent')
|
||||||
log=TIME + ' - ' + IP + ' - ' + UTILISATEUR + ' - ' + CLIENT_PLATFORM + '\n' + '---> ' + "Changement du mot de passe" + '\n'
|
log=TIME + ' - ' + IP + ' - ' + user + ' - ' + CLIENT_PLATFORM + '\n' + '---> ' + "Changement du mot de passe" + '\n'
|
||||||
append_to_log(log, UTILISATEUR)
|
append_to_log(log, user)
|
||||||
flash(u'Votre mot de passe a été changé', 'succes')
|
flash(u'Votre mot de passe a été changé', 'success')
|
||||||
else:
|
else:
|
||||||
if not( valid_passwd(password) ):
|
if not( valid_passwd(password) ):
|
||||||
flash(u'Le mot de passe ne peut pas contenir les caractères " et &', 'error')
|
flash(u'Le mot de passe ne peut pas contenir les caractères " et &', 'error')
|
||||||
else:
|
elif password == "":
|
||||||
flash(u'Les mot de passes ne sont pas identique :/ ', 'error')
|
flash(u' Vous ne pouvez pas ne pas mettre de mot de passe ou un mot de passe vide', 'error')
|
||||||
|
else:
|
||||||
|
flash(u'Les mot de passes ne sont pas identiques :/ ', 'error')
|
||||||
|
|
||||||
conn.close()
|
conn.close()
|
||||||
return render_template('mailbox.html',
|
|
||||||
section="Profil",
|
if not(account['totp']):
|
||||||
address=mailbox['Mail'],
|
account['totp'] = random_base32()
|
||||||
alias=mailbox['alias'],
|
img = qrcode.make('otpauth://totp/'+BASE_URL+'?secret='+account['totp'])
|
||||||
username=UTILISATEUR)
|
img.save(os.path.join(DOSSIER_PERSO, user, "totp.png"))
|
||||||
|
shared_key_validate = False
|
||||||
|
|
||||||
|
return render_template('mypassword.html',
|
||||||
|
section="Profil",
|
||||||
|
address=account['Mail'],
|
||||||
|
alias=account['alias'],
|
||||||
|
totp_shared_key=account['totp'],
|
||||||
|
shared_key_validate=shared_key_validate,
|
||||||
|
username=user,
|
||||||
|
base_url=BASE_URL)
|
||||||
|
|
||||||
else :
|
|
||||||
return redirect(BASE_URL, code=401)
|
|
||||||
|
|
||||||
|
|
||||||
@profil.route('/change-password-lost/<token>', methods=['GET','POST'] )
|
@profil.route('/change-password-lost/<token>', methods=['GET','POST'] )
|
||||||
@@ -253,7 +266,8 @@ def change_passwd_lost(token) :
|
|||||||
log=TIME + ' - ' + IP + ' - ' + user + ' - ' + CLIENT_PLATFORM + '\n' + '---> ' + "Changement du mot de passe" + '\n'
|
log=TIME + ' - ' + IP + ' - ' + user + ' - ' + CLIENT_PLATFORM + '\n' + '---> ' + "Changement du mot de passe" + '\n'
|
||||||
append_to_log(log, user)
|
append_to_log(log, user)
|
||||||
flash(u'Votre mot de passe a été changé', 'succes')
|
flash(u'Votre mot de passe a été changé', 'succes')
|
||||||
cursor.execute("""UPDATE users set Lost_password_token='' where name=?""", (user,))
|
cursor.execute("""UPDATE users SET Lost_password_token='' where name=?""", (user,))
|
||||||
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
resp = redirect(url_for('loginlogout.login'))
|
resp = redirect(url_for('loginlogout.login'))
|
||||||
|
|
||||||
@@ -273,10 +287,53 @@ def change_passwd_lost(token) :
|
|||||||
|
|
||||||
return redirect(BASE_URL, code=401)
|
return redirect(BASE_URL, code=401)
|
||||||
|
|
||||||
|
@profil.route('/set_totp/', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def set_totp():
|
||||||
|
user='%s' % escape(session['username'])
|
||||||
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
|
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||||
|
|
||||||
@profil.route('/deltoken-password-lost/<token>', methods=['GET','POST'] )
|
shared_key = request.form['shared_key']
|
||||||
|
code_totp = request.form['code_totp']
|
||||||
|
|
||||||
|
|
||||||
|
if totp_is_valid(shared_key, code_totp) and code_totp !="" and shared_key != "":
|
||||||
|
print("shared_key: " +shared_key)
|
||||||
|
cursor.execute("""UPDATE users SET totp=? WHERE name=?""", (shared_key, user,))
|
||||||
|
conn.commit()
|
||||||
|
img = qrcode.make('otpauth://totp/'+BASE_URL+'?secret='+shared_key)
|
||||||
|
img.save(os.path.join(DOSSIER_PERSO, user, "totp.png"))
|
||||||
|
flash(u'Votre mot de passe à usage unique est configuré et actif.', 'success')
|
||||||
|
else:
|
||||||
|
flash(u'Le code de validation totp n\'est pas valide.', 'error')
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
return redirect(url_for('profil.change_passwd', _external=True))
|
||||||
|
|
||||||
|
@profil.route('/del_totp/', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
def del_totp():
|
||||||
|
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("""UPDATE users SET totp="" WHERE name=?""", (user,))
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
return redirect(url_for('profil.change_passwd', _external=True))
|
||||||
|
|
||||||
|
|
||||||
|
@profil.route('/totp.png', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
def totp_qrcode():
|
||||||
|
user='%s' % escape(session['username'])
|
||||||
|
return send_file(
|
||||||
|
os.path.join(DOSSIER_PERSO, user, "totp.png"), "totp.png")
|
||||||
|
|
||||||
|
|
||||||
|
@profil.route('/deltoken-password-lost/<token>', methods=['GET'] )
|
||||||
def deltoken_passwd_lost(token) :
|
def deltoken_passwd_lost(token) :
|
||||||
|
token = escape(token)
|
||||||
if valid_token_register(token, "Lost password"):
|
if valid_token_register(token, "Lost password"):
|
||||||
user = get_user_by_token(token, "Lost password")
|
user = get_user_by_token(token, "Lost password")
|
||||||
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
@@ -291,205 +348,112 @@ def deltoken_passwd_lost(token) :
|
|||||||
return redirect(url_for('loginlogout.login', _external=True))
|
return redirect(url_for('loginlogout.login', _external=True))
|
||||||
|
|
||||||
|
|
||||||
@profil.route('/mymailbox/alias', methods=['GET', 'POST'] )
|
|
||||||
def myalias():
|
|
||||||
hostname=gethostname()
|
|
||||||
if 'username' in session:
|
|
||||||
UTILISATEUR='%s' % escape(session['username'])
|
|
||||||
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
|
||||||
cursor = conn.cursor() # Création de l'objet "curseur"
|
|
||||||
if request.method == 'POST' and MAIL_SERVER:
|
|
||||||
if request.form['alias']:
|
|
||||||
alias = request.form['alias'].lower()+'@'+hostname
|
|
||||||
else:
|
|
||||||
flash(u'Addresse invalide')
|
|
||||||
|
|
||||||
if email_disp(alias):
|
|
||||||
cursor.execute("""SELECT Mail, alias FROM users where name=?""", (UTILISATEUR,))
|
|
||||||
tmp = cursor.fetchone()
|
|
||||||
mail = tmp[0]
|
|
||||||
if tmp[1]:
|
|
||||||
alias_list = tmp[1]
|
|
||||||
aliases = alias_list + "," +alias
|
|
||||||
else:
|
|
||||||
aliases = alias
|
|
||||||
cmd = SETUID+ " set_mail_alias " + "'"+mail+"'"+" add "+"'"+alias+"'"
|
|
||||||
res = os.system(cmd)
|
|
||||||
if res == 0:
|
|
||||||
cursor.execute("UPDATE users SET alias=? WHERE name=?",
|
|
||||||
(aliases, UTILISATEUR))
|
|
||||||
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 + ' - ' + UTILISATEUR + ' - ' + CLIENT_PLATFORM + '\n' + '---> ' + "Ajout de l'alias "+ alias + '\n'
|
|
||||||
append_to_log(log, UTILISATEUR)
|
|
||||||
flash(u'Votre alias a été ajouté', 'succes')
|
|
||||||
else:
|
|
||||||
flash(u'Adresse indisponible', 'error')
|
|
||||||
else:
|
|
||||||
flash(u'Adresse indisponible', 'error')
|
|
||||||
|
|
||||||
cursor.execute("""SELECT Mail, alias FROM users WHERE name=?""",
|
|
||||||
(UTILISATEUR,))
|
|
||||||
tmp = cursor.fetchone()
|
|
||||||
mailbox = dict()
|
|
||||||
mailbox['Mail'] = tmp[0]
|
|
||||||
if tmp[1]:
|
|
||||||
mailbox['alias'] = tmp[1].split(',')
|
|
||||||
else:
|
|
||||||
mailbox['alias'] = list()
|
|
||||||
conn.close()
|
|
||||||
return render_template('myalias.html',
|
|
||||||
section="mailbox",
|
|
||||||
email=mailbox['Mail'],
|
|
||||||
aliases=mailbox['alias'],
|
|
||||||
hostname=hostname,
|
|
||||||
MAIL_SERVER=MAIL_SERVER,
|
|
||||||
username=UTILISATEUR )
|
|
||||||
|
|
||||||
else:
|
|
||||||
return redirect(BASE_URL, code=401)
|
|
||||||
|
|
||||||
@profil.route('/mymailbox/rmalias/<aliasrm>')
|
|
||||||
def remove_alias(aliasrm):
|
|
||||||
if 'username' in session:
|
|
||||||
if MAIL_SERVER:
|
|
||||||
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 Mail, alias FROM users WHERE name=?""", (UTILISATEUR,))
|
|
||||||
tmp = cursor.fetchone()
|
|
||||||
mail = tmp[0]
|
|
||||||
alias_list = tmp[1].split(',')
|
|
||||||
aliases = ""
|
|
||||||
for alias in alias_list:
|
|
||||||
if alias != aliasrm:
|
|
||||||
if aliases:
|
|
||||||
aliases = aliases + "," + alias
|
|
||||||
else:
|
|
||||||
aliases = alias
|
|
||||||
cmd = SETUID + " set_mail_alias " + "'"+mail+"'"+" del "+"'"+alias+"'"
|
|
||||||
res = os.system(cmd)
|
|
||||||
if res == 0:
|
|
||||||
cursor.execute("UPDATE users SET alias=? WHERE name=?",
|
|
||||||
(aliases, UTILISATEUR))
|
|
||||||
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 + ' - ' + UTILISATEUR + ' - ' + CLIENT_PLATFORM + '\n' + '---> ' + "Suppression de l'alias "+ alias + '\n'
|
|
||||||
append_to_log(log, UTILISATEUR)
|
|
||||||
flash(u'Votre alias a été supprimé', 'succes')
|
|
||||||
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'])
|
@profil.route('/invitation/', methods=['GET'])
|
||||||
|
@login_required
|
||||||
def invitation():
|
def invitation():
|
||||||
if 'username' in session:
|
user='%s' % escape(session['username'])
|
||||||
UTILISATEUR='%s' % escape(session['username'])
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||||
cursor = conn.cursor() # Création de l'objet "curseur"
|
cursor.execute("""SELECT Token, invitations FROM users WHERE name=?""", (user,))
|
||||||
cursor.execute("""SELECT Token, invitations FROM users WHERE name=?""", (UTILISATEUR,))
|
tmp = cursor.fetchone()
|
||||||
tmp = cursor.fetchone()
|
token = tmp[0]
|
||||||
token = tmp[0]
|
nb_invitations = tmp[1]
|
||||||
if token:
|
if token and nb_invitations > 0:
|
||||||
url_invitation = BASE_URL + 'inscription/' + token
|
url_invitation = BASE_URL + '/inscription/' + token
|
||||||
else:
|
img = qrcode.make(url_invitation)
|
||||||
url_invitation = ""
|
img.save(os.path.join(DOSSIER_PERSO, user, "invitation.png"))
|
||||||
invitations_count = tmp[1]
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
return render_template('invitation.html',
|
|
||||||
section='Profil',
|
|
||||||
nb_invitation=invitations_count,
|
|
||||||
token=token,
|
|
||||||
url_invitation=url_invitation)
|
|
||||||
else:
|
else:
|
||||||
return redirect(BASE_URL, code=401)
|
url_invitation = ""
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return render_template('invitation.html',
|
||||||
|
section='Profil',
|
||||||
|
nb_invitation=nb_invitations,
|
||||||
|
token=token,
|
||||||
|
url_invitation=url_invitation)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@profil.route('/invitation.png', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
def invitation_qrcode():
|
||||||
|
user='%s' % escape(session['username'])
|
||||||
|
return send_file(
|
||||||
|
os.path.join(DOSSIER_PERSO, user, "invitation.png"))
|
||||||
|
|
||||||
@profil.route('/gen_token/', methods=['GET'])
|
@profil.route('/gen_token/', methods=['GET'])
|
||||||
|
@login_required
|
||||||
def generate_token():
|
def generate_token():
|
||||||
if 'username' in session:
|
user='%s' % escape(session['username'])
|
||||||
UTILISATEUR='%s' % escape(session['username'])
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||||
cursor = conn.cursor() # Création de l'objet "curseur"
|
cursor.execute("""SELECT invitations FROM users WHERE name=?""", (user,))
|
||||||
token = gen_token("Invitation")
|
tmp = cursor.fetchone()
|
||||||
cursor.execute("UPDATE users SET Token=? WHERE name=?",
|
token = tmp[0]
|
||||||
(token, UTILISATEUR))
|
token = gen_token("Invitation")
|
||||||
conn.commit()
|
cursor.execute("UPDATE users SET Token=? WHERE name=?",
|
||||||
conn.close()
|
(token, user))
|
||||||
return redirect(BASE_URL+'invitation/')
|
conn.commit()
|
||||||
else:
|
conn.close()
|
||||||
return redirect(BASE_URL, code=401)
|
return redirect(BASE_URL+'/invitation/')
|
||||||
|
|
||||||
|
|
||||||
@profil.route( '/delete_me/', methods=['GET','POST'])
|
@profil.route( '/delete_me/', methods=['GET','POST'])
|
||||||
|
@login_required
|
||||||
def delete_account():
|
def delete_account():
|
||||||
if 'username' in session :
|
user='%s'% escape(session['username'])
|
||||||
UTILISATEUR='%s'% escape(session['username'])
|
resp = render_template('delete_account.html', time_backup=BACKUP_TIME)
|
||||||
resp = render_template('delete_account.html', time_backup=BACKUP_TIME)
|
if request.method == 'POST' :
|
||||||
if request.method == 'POST' :
|
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
||||||
conn = sqlite3.connect(DATABASE) # Connexion à la base de donnée
|
cursor = conn.cursor() # Création de l'objet "curseur"
|
||||||
cursor = conn.cursor() # Création de l'objet "curseur"
|
cursor.execute("""SELECT passwd FROM users WHERE name=?""", (user,))
|
||||||
cursor.execute("""SELECT passwd FROM users WHERE name=?""", (UTILISATEUR,))
|
passwd = cursor.fetchone()[0]
|
||||||
passwd = cursor.fetchone()[0]
|
cursor.execute("""SELECT mail FROM users WHERE name=?""", (user,))
|
||||||
cursor.execute("""SELECT mail FROM users WHERE name=?""", (UTILISATEUR,))
|
mail = cursor.fetchone()[0]
|
||||||
mail = cursor.fetchone()[0]
|
conn.close()
|
||||||
conn.close()
|
password = str(request.form['passwd'])
|
||||||
password = request.form['passwd']
|
if bcrypt.check_password_hash(passwd, password) is True:
|
||||||
if bcrypt.check_password_hash(passwd, password) is True:
|
not_error = True
|
||||||
not_error = True
|
# Delete mail account
|
||||||
|
if MAIL_SERVER:
|
||||||
|
try:
|
||||||
|
cmd = subprocess.run([SETUID, 'set_mail_passwd','del',mail])
|
||||||
|
except:
|
||||||
|
not_error = False
|
||||||
|
flash(u'Erreur lors de la suppression de votre compte Mail.', 'error')
|
||||||
|
|
||||||
if MAIL_SERVER:
|
# Delete the XMPP account
|
||||||
try:
|
if XMPP_SERVER:
|
||||||
cmd = SETUID + ' set_mail_passwd del ' + '"'+mail+'"'
|
try:
|
||||||
print(cmd)
|
cmd = subprocess.run([SETUID,'prosodyctl', 'deluser', mail])
|
||||||
os.system(cmd)
|
print(str(cmd))
|
||||||
except:
|
except:
|
||||||
not_error = False
|
not_error = False
|
||||||
flash(u'Erreur lors de la suppression de votre compte Mail.', 'error')
|
flash(u'Erreur lors de la suppression de votre compte XMPP.', 'error')
|
||||||
|
|
||||||
|
# Delete files
|
||||||
|
try:
|
||||||
|
cmd = subprocess.run(['rm', '-r', DATAS_USER + '/' + user])
|
||||||
|
print(str(cmd))
|
||||||
|
if cmd.returncode != 0:
|
||||||
|
raise TypeError("Remove directory error")
|
||||||
|
except:
|
||||||
|
flash(u'Erreur lors de la suppression de votre dossier utilisateur.', 'error')
|
||||||
|
not_error = False
|
||||||
|
|
||||||
|
|
||||||
|
if not_error:
|
||||||
|
conn = sqlite3.connect(DATABASE)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("""DELETE FROM users WHERE name=?""", (user,))
|
||||||
|
cursor.execute("""DELETE FROM Blog_posts WHERE author=?""", (user,))
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
flash(u'Désinscription réalisé avec succés, y\'a plus rien !', 'success')
|
||||||
|
resp = redirect(url_for('loginlogout.logout'))
|
||||||
|
else:
|
||||||
|
flash(u'Mauvais mot de passe', 'error')
|
||||||
|
|
||||||
if XMPP_SERVER:
|
return resp
|
||||||
try:
|
|
||||||
tmp = mail.split('@')
|
|
||||||
cmd = SETUID+ ' prosodyctl deluser ' "'"+tmp[0]+"' " + "'"+tmp[1]+"'"
|
|
||||||
os.system(cmd)
|
|
||||||
except:
|
|
||||||
not_error = False
|
|
||||||
flash(u'Erreur lors de la suppression de votre compte XMPP.', 'error')
|
|
||||||
|
|
||||||
if not_error:
|
|
||||||
try:
|
|
||||||
cmd = 'rm -r ' + DATAS_USER + '/' + UTILISATEUR
|
|
||||||
if os.system(cmd) != 0:
|
|
||||||
raise TypeError("Remove directory error")
|
|
||||||
except:
|
|
||||||
flash(u'Erreur lors de la suppression de votre dossier utilisateur.', 'error')
|
|
||||||
|
|
||||||
|
|
||||||
try:
|
|
||||||
conn = sqlite3.connect(DATABASE)
|
|
||||||
cursor = conn.cursor()
|
|
||||||
cursor.execute("""DELETE FROM users WHERE name=?""", (UTILISATEUR,))
|
|
||||||
cursor.execute("""DELETE FROM posts WHERE author=?""", (UTILISATEUR,))
|
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
|
||||||
except:
|
|
||||||
flash(u'Erreur lors de la suppression de votre compte.', 'error')
|
|
||||||
else:
|
|
||||||
flash(u'Désinscription réalisé avec succés, y\'a plus rien !', 'succes')
|
|
||||||
resp = redirect(url_for('loginlogout.logout'))
|
|
||||||
else:
|
|
||||||
flash(u'Mauvais mot de passe', 'error')
|
|
||||||
return resp
|
|
||||||
|
|
||||||
|
|||||||
7
wsgi.py
7
wsgi.py
@@ -1,6 +1,11 @@
|
|||||||
from gevent.pywsgi import WSGIServer
|
from gevent.pywsgi import WSGIServer
|
||||||
from pywallter import create_app
|
from pywallter import create_app
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
app = create_app()
|
app = create_app()
|
||||||
http_server = WSGIServer(("127.0.0.1", 8000), app)
|
app.config.from_pyfile('config.py')
|
||||||
|
|
||||||
|
|
||||||
|
http_server = WSGIServer((app.config['HOST'], int(app.config['PORT']) ), app)
|
||||||
http_server.serve_forever()
|
http_server.serve_forever()
|
||||||
|
|||||||
Reference in New Issue
Block a user