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
  • +
+

+
+
+
+
+
+
+
+ +
+ {% endblock %} diff --git a/templates/articles.html b/templates/articles.html index 4c2f6c6..bcb1d6a 100644 --- a/templates/articles.html +++ b/templates/articles.html @@ -2,7 +2,7 @@ {% block title %}Articles{% endblock %} {% block main %}
-

Choose your article

+

Informations personnelles

Please select your article

diff --git a/templates/index.html b/templates/index.html index ece493a..fdbb867 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,19 +1,21 @@ - TetaWebApp - {% block title %}Accueil{% endblock %} + We Make THSF - {% block title %}Accueil{% endblock %} + + {% block bodyheader %} {% endblock %} -
{% block banner %}TetaWebApp{% endblock %}
+
{% block banner %}We Make THSF{% endblock %}
{% block nav %}