diff --git a/config.local.py b/config.local.py
index 49c93c7..f97d146 100644
--- a/config.local.py
+++ b/config.local.py
@@ -1,3 +1,3 @@
SQLALCHEMY_TRACK_MODIFICATIONS = True
-SQLALCHEMY_DATABASE_URI = "postgresql://tetawebapp:tetawebapp@localhost/tetawebapp"
+SQLALCHEMY_DATABASE_URI = "postgresql://participer_thsf:participer_thsf@localhost/participer_thsf"
UPLOADED_FILES_DEST = "./upload"
diff --git a/tetawebapp.py b/participate.py
similarity index 76%
rename from tetawebapp.py
rename to participate.py
index 6f97cbc..74e11a9 100755
--- a/tetawebapp.py
+++ b/participate.py
@@ -25,7 +25,7 @@ app.debug = True
# Various configuration settings belong here (optionnal)
app.config.from_pyfile('config.local.py')
# Generate a new key: head -n 40 /dev/urandom | md5sum | cut -d' ' -f1
-app.secret_key = 'ce1d1c9ff0ff388a838b3a1e3207dd27'
+app.secret_key = '9ac80548e3a8d8dfd1aefcd9a3a73473'
# Feel free to use SQLAlchemy (or not)
db = SQLAlchemy(app)
@@ -34,12 +34,19 @@ db = SQLAlchemy(app)
# Sample user database
########################################################################
class Tetawebapp_users(db.Model):
- __tablename__ = 'tetawebapp_users'
+ __tablename__ = 'participer_thsf_users'
id = db.Column(db.Integer, primary_key=True)
mail = db.Column(db.Text, nullable=False)
password = db.Column(db.Text, nullable=False)
- name = db.Column(db.Text, nullable=False)
+ name = db.Column(db.Text, nullable=True)
+ phone = db.Column(db.Text, nullable=True)
+ diet = db.Column(db.Text, nullable=True)
+class Tetawebapp_roles(db.Model):
+ __tablename__ = 'participer_thsf_roles'
+ id = db.Column(db.Integer, primary_key=True)
+ role = db.Column(db.Text, nullable=False)
+ description = db.Column(db.Text, nullable=False)
########################################################################
# Menu and navigation management
@@ -54,9 +61,11 @@ def get_menu(page):
- One of the routes MUST match the route called by request
- The int 0 is used to determine which menu entry is actally called.
The value MUST be 0."""
- menu = [[u'Home', {u'/': [u'/']}, 0],
- [u'Articles', {u'/articles': [u'/articles', u'/articles/']}, 0],
- [u'Basics', {u'/basics': [u'/basics']}, 0],
+ menu = [[u'Accueil', {u'/': [u'/']}, 0],
+ [u'Mon compte', {u'/account': [u'/account', u'/account/']}, 0],
+ [u'Mes quarts', {u'/turn': [u'/turn']}, 0],
+ [u'Feuille de staff', {u'/staff_sheet': [u'/staff_sheet']}, 0],
+ [u'Déconnexion', {u'/logout': [u'/logout']}, 0],
[u'Inputs', {u'/inputs': [u'/inputs']}, 0],
[u'Ajax', {u'/ajax': [u'/ajax']}, 0],
[u'Database', {u'/database': [u'/database']}, 0],
@@ -116,11 +125,11 @@ def check_session(func):
return func(*args, **kwargs)
else:
session['token'] = ''
- response = app.make_response(render_template('login.html', message=''))
+ response = app.make_response(render_template('login_or_register.html', message=''))
sync_cookies(response, session)
return response
except KeyError:
- return render_template('login.html', message='')
+ return render_template('login_or_register.html', message='')
return check
def check_login(login, password):
@@ -133,6 +142,11 @@ def check_login(login, password):
return True
return False
+def check_user_info():
+ """ Check user info and send appropriate message if info are not complete"""
+ message = "Vos informations personnelles ne sont pas totalement renseignées. N'oubliez pas de remplir votre fiche située dans la section 'Mon compte'"
+ return message.decode('utf-8')
+
def gen_token():
""" Generate a random token to be stored in session and cookie """
token = binascii.hexlify(os.urandom(42))
@@ -151,6 +165,27 @@ def page_not_found(e):
@app.route("/login", methods=['GET', 'POST'])
def login():
+ login = request.form.get('login')
+ password = request.form.get('password')
+ if check_login(login, password):
+ # Generate and store a token in session
+ session['token'] = gen_token()
+ # Return user to index page
+ page = '/'
+ menu = get_menu(page)
+ message = check_user_info()
+ response = app.make_response(render_template('index.html', menu=menu, message=message))
+ # Push token to cookie
+ sync_cookies(response, session)
+ return response
+ # Credentials are not valid
+ response = app.make_response(render_template('login_or_register.html', message='Invalid user or password'))
+ session['token'] = ''
+ sync_cookies(response, session)
+ return response
+
+@app.route("/register", methods=['GET', 'POST'])
+def register():
login = request.form.get('login')
password = request.form.get('password')
if check_login(login, password):
@@ -164,7 +199,7 @@ def login():
sync_cookies(response, session)
return response
# Credentials are not valid
- response = app.make_response(render_template('login.html', message='Invalid user or password'))
+ response = app.make_response(render_template('login_or_register.html', message='Invalid user or password'))
session['token'] = ''
sync_cookies(response, session)
return response
@@ -175,26 +210,50 @@ def index():
""" Index page """
page = str(request.url_rule)
menu = get_menu(page)
- return render_template('index.html', menu=menu)
+ message = check_user_info()
+ return render_template('index.html', menu=menu, message=message)
-@app.route("/articles", methods=['GET', 'POST'])
+@app.route("/account", methods=['GET', 'POST'])
@check_session
-def articles():
+def account():
""" Arcticles page """
page = str(request.url_rule)
menu = get_menu(page)
- navbar = get_navbar(page, '')
- return render_template('articles.html', menu=menu, navbar=navbar)
+ return render_template('account.html', menu=menu)
-@app.route("/articles/", methods=['GET', 'POST'])
+@app.route("/account/", methods=['GET', 'POST'])
@check_session
-def articles_by_id(ID):
+def account_by_id(ID):
""" Arcticles page """
page = str(request.url_rule)
menu = get_menu(page)
selected = page.replace('', ID)
navbar = get_navbar(page, selected)
- return render_template('articles_by_id.html', menu=menu, navbar=navbar, ID=ID)
+ return render_template('account_by_id.html', menu=menu, navbar=navbar, ID=ID)
+
+@app.route("/logout", methods=['GET', 'POST'])
+@check_session
+def logout():
+ """ Logout user """
+ # Remove session token
+ session['token'] = None
+ # Return user to index page
+ response = app.make_response(render_template('login_or_register.html', message=''))
+ # Push token to cookie
+ sync_cookies(response, session)
+ return response
+
+
+
+
+
+
+
+
+
+
+
+
@app.route("/basics", methods=['GET', 'POST'])
@check_session
diff --git a/participer.redatomik.org.conf b/participer.redatomik.org.conf
new file mode 100644
index 0000000..59257b8
--- /dev/null
+++ b/participer.redatomik.org.conf
@@ -0,0 +1,26 @@
+Define IP_ADDR 0.0.0.0
+Define TCP_PORT 80
+Define SRV_NAME participer.redatomik.org
+Define ROOT_DIR /var/www/participer.redatomik.org
+Define WSGI_ALIAS /var/www/participer.redatomik.org/participer.redatomik.org.wsgi
+Define DAEMON_PROCESS participer.redatomik.org
+Define APACHE_USER apache
+Define APACHE_GROUP apache
+Define THREAD_NUMBER 5
+
+ ServerName ${SRV_NAME}
+ ServerAdmin bofh@tetalab.org
+ DocumentRoot ${ROOT_DIR}
+ WSGIDaemonProcess ${DAEMON_PROCESS} user=${APACHE_USER} group=${APACHE_GROUP} threads=${THREAD_NUMBER}
+ WSGIScriptAlias / ${WSGI_ALIAS}
+
+ WSGIProcessGroup ${DAEMON_PROCESS}
+ WSGIApplicationGroup %{GLOBAL}
+ Order deny,allow
+ Allow from all
+ AllowOverride All
+ Require all granted
+
+ ErrorLog /var/log/httpd/${SRV_NAME}.error.log
+ CustomLog /var/log/httpd/${SRV_NAME}.access.log combined
+
diff --git a/participer.thsf.net.conf b/participer.thsf.net.conf
new file mode 100644
index 0000000..53f050d
--- /dev/null
+++ b/participer.thsf.net.conf
@@ -0,0 +1,26 @@
+Define IP_ADDR 0.0.0.0
+Define TCP_PORT 80
+Define SRV_NAME participer.thsf.net
+Define ROOT_DIR /var/www/participer.thsf.net
+Define WSGI_ALIAS /var/www/participer.thsf.net/participer.thsf.net.wsgi
+Define DAEMON_PROCESS participer.thsf.net
+Define APACHE_USER apache
+Define APACHE_GROUP apache
+Define THREAD_NUMBER 5
+
+ ServerName ${SRV_NAME}
+ ServerAdmin bofh@tetalab.org
+ DocumentRoot ${ROOT_DIR}
+ WSGIDaemonProcess ${DAEMON_PROCESS} user=${APACHE_USER} group=${APACHE_GROUP} threads=${THREAD_NUMBER}
+ WSGIScriptAlias / ${WSGI_ALIAS}
+
+ WSGIProcessGroup ${DAEMON_PROCESS}
+ WSGIApplicationGroup %{GLOBAL}
+ Order deny,allow
+ Allow from all
+ AllowOverride All
+ Require all granted
+
+ ErrorLog /var/log/httpd/${SRV_NAME}.error.log
+ CustomLog /var/log/httpd/${SRV_NAME}.access.log combined
+
diff --git a/static/scripts/participate.js b/static/scripts/participate.js
new file mode 100644
index 0000000..36ac4b3
--- /dev/null
+++ b/static/scripts/participate.js
@@ -0,0 +1,19 @@
+function register() {
+ var mail = document.getElementById('reg_mail').value;
+ var password = document.getElementById('reg_password').value;
+ var confirm = document.getElementById('reg_confirm').value;
+ var regEmail = new RegExp('^[0-9a-z._-]+@{1}[0-9a-z.-]{2,}[.]{1}[a-z]{2,5}$','i');
+ if (password.length < 8){
+ alert("Le mot de passe doit avoir une longueur d'au moins 8 caractères");
+ return false;
+ }
+ if (password != confirm){
+ alert("Confirmation mot de passe incohérente");
+ return false;
+ }
+ if (! regEmail.test(mail)){
+ alert("Adresse email invalide");
+ return false;
+ }
+ return true;
+}
diff --git a/static/styles/colors.css b/static/styles/colors.css
index 59daf9d..65fb9af 100644
--- a/static/styles/colors.css
+++ b/static/styles/colors.css
@@ -14,8 +14,6 @@
--text-color: #555555;
--white: #FFFFFF;
--black: #000000;
- --font-normal: url("/static/fonts/RobotoCondensed-Regular.ttf") format("truetype");
- --font-bold: url("/static/fonts/RobotoCondensed-Bold.ttf") format("truetype");
--banner-logo: url(/static/images/logo.png);
--add_icon: url(/static/images/add.png);
--edit_icon: url(/static/images/edit.png);
diff --git a/static/styles/fonts.css b/static/styles/fonts.css
index 425ce76..e517edd 100644
--- a/static/styles/fonts.css
+++ b/static/styles/fonts.css
@@ -6,15 +6,15 @@
*/
@font-face {
- font-family: "Roboto Condensed";
- font-style: normal;
- font-weight: 400;
- src: var(--font-normal);
+ font-family: 'Roboto Condensed';
+ font-style: normal;
+ font-weight: 400;
+ src: url(/static/fonts/RobotoCondensed-Regular.ttf) format('truetype');
}
@font-face {
- font-family: "Roboto Condensed";
- font-style: normal;
- font-weight: 700;
- src: var(--font-bold);
+ font-family: 'Roboto Condensed';
+ font-style: normal;
+ font-weight: 700;
+ src: url(/static/fonts/RobotoCondensed-Bold.ttf) format('truetype');
}
diff --git a/static/styles/participate.css b/static/styles/participate.css
new file mode 100644
index 0000000..c3b2616
--- /dev/null
+++ b/static/styles/participate.css
@@ -0,0 +1,5 @@
+main > article > p.note {
+ color: var(--text-color);
+ display: block;
+ font-size: 12px;
+}
diff --git a/static/styles/tetawebapp.css b/static/styles/tetawebapp.css
index 2f57c9b..2c0fd18 100644
--- a/static/styles/tetawebapp.css
+++ b/static/styles/tetawebapp.css
@@ -7,13 +7,13 @@
*/
* {
- box-sizing: border-box;
+ box-sizing: border-box;
}
body {
margin: 10px;
- font-family: "Roboto Condensed";
- background-color: var(--dark-bg);
+ font-family: 'Roboto Condensed';
+ background-color: var(--dark-bg);
}
div.content {
@@ -224,8 +224,8 @@ footer {
color: var(--white);
}
-input[type="text"],
-input[type="password"],
+input[type='text'],
+input[type='password'],
textarea,
select,
pre {
@@ -235,7 +235,7 @@ pre {
background-color: var(--white);
color: var(--text-color);
padding: 5px;
- font-family: "Roboto Condensed";
+ font-family: 'Roboto Condensed';
margin: 5px;
}
@@ -244,8 +244,8 @@ pre {
}
button,
-input[type="button"],
-input[type="submit"] {
+input[type='button'],
+input[type='submit'] {
border-color: var(--dark-border);
border-style: solid;
border-width: 1px;
@@ -253,15 +253,15 @@ input[type="submit"] {
color: var(--white);
font-weight: bold;
padding: 5px;
- font-family: "Roboto Condensed";
+ font-family: 'Roboto Condensed';
margin: 5px;
border-radius: 4px;
}
button:hover,
-input[type="button"]:hover,
-input[type="submit"]:hover,
-input[type="file"]:hover {
+input[type='button']:hover,
+input[type='submit']:hover,
+input[type='file']:hover {
background-color: var(--light-coloured-bg);
color: var(--text-color);
cursor: pointer;
@@ -280,7 +280,7 @@ div.file_upload {
border-color: var(--clear-bg);
}
-input[type="file"] {
+input[type='file'] {
position: absolute;
width: 18px;
height: 18px;
@@ -368,3 +368,17 @@ input.upload {
background-repeat: no-repeat;
background-position: center center;
}
+
+form {
+ width: 350px;
+ text-align: center;
+ line-height: 40px;
+}
+
+form > label {
+ float: left;
+}
+
+form > input[type='text'], form > input[type='password'] {
+ float: right;
+}
diff --git a/templates/account.html b/templates/account.html
new file mode 100644
index 0000000..ab78428
--- /dev/null
+++ b/templates/account.html
@@ -0,0 +1,28 @@
+{% extends "index.html" %}
+{% block title %}Articles{% endblock %}
+ {% block main %}
+
+ Informations personnelles
+
+ Merci de bien vouloir remplir vos informations personnelles afin que l'équipe d'organisation
+ du THSF puisse rester en contact avec vous avant et tout au long de l'évènement.
+
+
+ Notez que:
+
+ - Votre adresse mail doit être valide et consultée régulièrement si vous ne voulez pas manquez des informations importantes telels que les dates de réunions de staff
+ - Votre numéro de téléphone nous permettra de vous contacter pendant l'évènement
+ - Si vous avez un régime alimentaire particulier (intolérences, veganisme, religieux), merci de le préciser dans le champs prévu à cet effet
+ - Aucune des données que vous nous transmettrez ne sera fournie à un tiers
+
+
+