commit fa9a66c1a991b79f78cb8252609fedf6f93fe85a
parent c49e794a8114f25d21eb713e4522afa79761fd28
Author: Friedel Schon <[email protected]>
Date: Fri, 20 May 2022 01:02:01 +0200
merge gerco's layout and master + merge-fix
Diffstat:
8 files changed, 252 insertions(+), 190 deletions(-)
diff --git a/server/forms.py b/server/forms.py
@@ -2,7 +2,7 @@ import re
from tokenize import String
from flask_wtf import FlaskForm
from flask_wtf.file import FileAllowed, FileField
-from wtforms import BooleanField, HiddenField, PasswordField, StringField, SubmitField, IntegerField
+from wtforms import BooleanField, HiddenField, PasswordField, StringField, SubmitField, IntegerField, SelectField
from wtforms.validators import DataRequired, Email, EqualTo, Length, ValidationError
from .models import User
@@ -76,3 +76,16 @@ class UpdateTrapForm(FlaskForm):
class ConnectTrapForm(FlaskForm):
mac = StringField('MAC', validators=[ Length(min=16, max=16) ])
submit = SubmitField('Verbinden')
+
+
+
+
+""" search form for admin.html """
+class SearchForm(FlaskForm):
+ username = StringField('Naam', validators=[ DataRequired(), Length(min=2, max=20)])
+ submit = SubmitField('Zoeken')
+
+""" account-settings form for admin_user.html """
+class AdminForm(FlaskForm):
+ type = SelectField('Type', choices=[('client', 'Klant'), ('admin', 'Administrator')])
+ submit = SubmitField('Bewerken')
diff --git a/server/routes.py b/server/routes.py
@@ -9,7 +9,7 @@ from flask_login import current_user, login_required, login_user, logout_user
from PIL import Image
from .app import app, bcrypt, db, socket
-from .forms import ConnectTrapForm, LoginForm, RegistrationForm, UpdateAccountForm, UpdateTrapForm
+from .forms import AdminForm, ConnectTrapForm, LoginForm, RegistrationForm, SearchForm, UpdateAccountForm, UpdateTrapForm
from .models import Trap, User, UserType
def clean_traps():
@@ -227,6 +227,75 @@ def trap_delete(trap_id):
return redirect(url_for('traps'))
[email protected]('/contact')
+@login_required
+def contact():
+ return render_template('contact.html')
+
+
+""" admin.html route """
[email protected]("/users", methods=['GET','POST'])
+@login_required
+def admin():
+ if current_user.type != UserType.ADMIN:
+ flash('U mag deze website niet bereiken', 'error')
+ return redirect('/')
+ form = SearchForm()
+ if form.validate_on_submit():
+ user = User.query.filter_by(name=form.username.data).first()
+ if user == None:
+ flash(f'Geen gebrukers gevonden met de gebruikersnaam: {form.username.data}!', 'danger')
+ else:
+ flash(f'Gebruiker gevonden met gebruikersnaam: {form.username.data}!', 'success')
+ return redirect(url_for('admin_user', user_id= user.id))
+ return render_template('admin.html', form=form)
+
+""" account-admin route """
[email protected]("/user/<int:user_id>", methods=['GET','POST'])
+@login_required
+def admin_user(user_id):
+ if current_user.type != UserType.ADMIN:
+ flash('U mag deze website niet bereiken', 'error')
+ return redirect('/')
+ form = AdminForm()
+ user = User.query.filter_by(id=user_id).first()
+ image_file = url_for('static', filename='profile_pics/' + user.image_file)
+ if form.validate_on_submit():
+ user.type = form.type.data
+ db.session.commit()
+ flash(f'De gebruiker {user.username} is nu een {user.type}', 'success')
+ return redirect(url_for('admin'))
+ elif request.method == 'GET':
+ form.type.data = user.type
+ return render_template('admin_user.html', form=form, user=user, image_file=image_file)
+
+""" delete-user route """
[email protected]("/user/<int:user_id>/delete", methods=['GET','POST'])
+@login_required
+def delete_user(user_id):
+ if current_user.type != UserType.ADMIN:
+ flash('U mag deze website niet bereiken', 'danger')
+ return redirect('/')
+ user = User.query.get_or_404(user_id)
+ db.session.delete(user)
+ db.session.commit()
+ flash(f'De gebruiker {user.username} werd verwijdert', 'success')
+ return redirect(url_for('admin'))
+
+""" reset user's password route """
[email protected]("/user/<int:user_id>/reset", methods=['GET','POST'])
+@login_required
+def reset_user(user_id):
+ if current_user.type != UserType.ADMIN:
+ flash('U mag deze website niet bereiken', 'danger')
+ return redirect('/')
+ user = User.query.get_or_404(user_id)
+ user.password = bcrypt.generate_password_hash(user.email).decode('utf-8')
+ db.session.commit()
+ flash(f'{user.name}\'s wachtwoord is nu zijn/haar e-mail', 'success')
+ return redirect(url_for('admin'))
+
+
""" 404 not found handler """
@app.errorhandler(404)
def not_found(error):
diff --git a/server/site.db b/server/site.db
Binary files differ.
diff --git a/server/templates/admin.html b/server/templates/admin.html
@@ -1,91 +1,27 @@
{% extends "layout.html" %}
{% block content %}
-<script type="text/javascript">
- socket.on('trap-change', function (data) {
- if (data['user'] == current_user)
- location.reload();
- });
-</script>
-<article class="media content-section">
- <div class="media-body">
- <h3>These are traps who are waiting to be connected</h3>
- </div>
-</article>
-
-{% for trap in connect_traps %}
-<article class="media content-section">
- <div class="media-body">
- <p>
- <a class="btn btn-secondary btn-sm mt-1 mb-1"
- href="{{ url_for('trap_update', trap_id=trap.mac) }}">Bewerken</a>
- <a class="btn btn-secondary btn-sm mt-1 mb-1"
- href="{{ url_for('trap_delete', trap_id=trap.mac) }}">Verwijderen</a>
- </p>
- <h3><a class="article-title" href="#">
- <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="{{ trap.status_color() }}"
- class="bi bi-circle-fill" viewBox="0 0 20 20">
- <circle cx="10" cy="10" r="10" />
- </svg>
- -
- {% if trap.name %}
- {{ trap.name }}
+<h1>Rechten bewerken!</h1>
+<div class="content-section">
+ <form method="POST" action="">
+ {{ form.hidden_tag() }}
+ <fieldset class="form-group">
+ <legend class="border-bottom mb-4">Zoeken</legend>
+ <div class="form-group">
+ {{ form.username.label(class="form-control-label") }}
+ {% if form.username.errors %}
+ {{ form.username(class="form-control form-control-lg is-invalid") }}
+ <div class="invalid-feedback">
+ {% for error in form.username.errors %}
+ <span>{{ error }}</span>
+ {% endfor %}
+ </div>
{% else %}
- <code>[{{ trap.pretty_mac() }}]</code>
+ {{ form.username(class="form-control form-control-lg") }}
{% endif %}
- </a>
- </h3>
- {% if trap.name %}
- <p>
- <code>[{{ trap.pretty_mac() }}]</code>
- </p>
- {% endif %}
- </div>
-</article>
-
-<article class="media content-section">
- <div class="media-body">
- <h3>These are all other traps!</h3>
- </div>
-</article>
-
-{% for trap in traps %}
-<article class="media content-section">
- <div class="media-body">
- <p>
- <a class="btn btn-secondary btn-sm mt-1 mb-1"
- href="{{ url_for('trap_update', trap_id=trap.mac) }}">Bewerken</a>
- <a class="btn btn-secondary btn-sm mt-1 mb-1"
- href="{{ url_for('trap_delete', trap_id=trap.mac) }}">Verwijderen</a>
- </p>
- <h3><a class="article-title" href="#">
- <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="{{ trap.status_color() }}"
- class="bi bi-circle-fill" viewBox="0 0 20 20">
- <circle cx="10" cy="10" r="10" />
- </svg>
- -
- {% if trap.name %}
- {{ trap.name }}
- {% else %}
- <code>[{{ trap.pretty_mac() }}]</code>
- {% endif %}
- </a>
- </h3>
- {% if trap.name %}
- <p>
- <code>[{{ trap.pretty_mac() }}]</code>
- </p>
- {% endif %}
- </div>
-
- {#} <div class="media-body">
- <h3>Naam: {{ trap.name }}</h3>
- <p> Mac adres: {{ trap.mac }} </p>
- {% if trap.caught %}
- <p> Status: Gevangen! </p>
- {% else %}
- <p>Status: Leeg!</p>
- {% endif %}
- </div>{#}
-</article>
-{% endfor %}
+ </div>
+ </fieldset>
+ <div class="form-group">
+ {{ form.submit(class="btn btn-outline-info") }}
+ </div>
+</div>
{% endblock content %}
\ No newline at end of file
diff --git a/server/templates/admin_user.html b/server/templates/admin_user.html
@@ -0,0 +1,56 @@
+{% extends "layout.html" %}
+{% block content %}
+<div class="media">
+ <img class="rounded-circle account-img" src="{{ image_file }}">
+ <div class="media-body">
+ <h2 class="account-heading">{{ user.username }}</h2>
+ <p class="text-secondary">{{ user.email }}</p>
+ </div>
+</div>
+<form method="POST" action="">
+ {{ form.hidden_tag() }}
+ <fieldset class="form-group">
+ <legend class="border-bottom mb-4">Rechten bewerken!</legend>
+ <div class="form-group">
+ {{ form.type.label(class="form-control-label") }}
+ {% if form.type.errors %}
+ {{ form.type(class="form-control form-control-lg is-invalid") }}
+ <div class="invalid-feedback">
+ {% for error in form.type.errors %}
+ <span>{{ error }}</span>
+ {% endfor %}
+ </div>
+ {% else %}
+ {{ form.type(class="form-control form-control-lg") }}
+ {% endif %}
+ </div>
+ </fieldset>
+ <div class="form-group">
+ <button type="button" class="btn btn-danger" data-toggle="modal" data-target="#deleteModal">Verwijderen</button>
+ <a class="btn btn-danger" href="{{ url_for('reset_user', user_id=user.id) }}">Wachtwoord
+ terugzetten</a>
+ <br><br>
+ {{ form.submit(class="btn btn-outline-info") }}
+ </div>
+</form>
+<!-- Modal -->
+<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog" aria-labelledby="deleteModalLabel"
+ aria-hidden="true">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h5 class="modal-title" id="deleteModalLabel">Profiel verwijderen?</h5>
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+ <span aria-hidden="true">×</span>
+ </button>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-secondary" data-dismiss="modal">Sluiten</button>
+ <form action="{{ url_for('delete_user', user_id=user.id) }}" method="POST">
+ <input class="btn btn-danger" type="submit" value="Delete">
+ </form>
+ </div>
+ </div>
+ </div>
+</div>
+{% endblock content %}
+\ No newline at end of file
diff --git a/server/templates/contact.html b/server/templates/contact.html
@@ -0,0 +1,22 @@
+{% extends "layout.html" %}
+{% block content %}
+{% with contact = current_user.contact_class() %}
+<article class="media content-section">
+ <div class="media-body">
+ <h2>Uw contactgegevens</h2>
+ <p>
+ <b>{{ contact.name }}</b>
+ </p>
+ <p>
+ {{ contact.email }}
+ </p>
+ <p>
+ {{ contact.phone }}
+ </p>
+ <p>
+ {{ contact.address.replace('\n', '<br>') | safe }}
+ </p>
+ </div>
+</article>
+{% endwith %}
+{% endblock content %}
+\ No newline at end of file
diff --git a/server/templates/dashboard.html b/server/templates/dashboard.html
@@ -1,45 +0,0 @@
-{% extends "layout.html" %}
-{% block content %}
-<article class="media content-section">
- <div class="media-body">
- <h2><b>Dit zijn uw muizenvallen</b></h2>
- <p>
- Klik op de titel van een muizenval om de instellingen in te zien!
- </p>
- </div>
-</article>
-{% for trap in traps %}
-<article class="media content-section">
- <div class="media-body">
- <h3><a class="article-title" href="#">
- <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="{{ trap.status_color() }}"
- class="bi bi-circle-fill" viewBox="0 0 20 20">
- <circle cx="10" cy="10" r="10" />
- </svg>
- -
- {% if trap.name %}
- {{ trap.name }}
- {% else %}
- <code>[{{ trap.pretty_mac() }}]</code>
- {% endif %}
- <small>van {{ home.owner_class().name }}</small>
- </a>
- </h3>
- {% if trap.name %}
- <p>
- <code>[{{ trap.pretty_mac() }}]</code>
- </p>
- {% endif %}
- {#} <p>
- onderhouden door <b>{{ home.contact_class().name }}</b>
- </p>{#}
- <p>
- <a class="article-title" href="#">
- {{ home.street }} {{ home.number }}<br>
- {{ home.zipcode }} {{ home.place }}
- </a>
- </p>
- </div>
-</article>
-{% endfor %}
-{% endblock content %}
-\ No newline at end of file
diff --git a/server/templates/layout.html b/server/templates/layout.html
@@ -5,6 +5,16 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+ <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
+ integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
+ crossorigin="anonymous"></script>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
+ integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
+ crossorigin="anonymous"></script>
+ <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
+ integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
+ crossorigin="anonymous"></script>
+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
@@ -40,89 +50,89 @@
<title>muizenval.tk</title>
{% endif %}
</head>
-<body>
+
+<body>
<nav class="navbar fixed-top bg-light">
<div class="container">
<a class="navbar-brand" href="{{ url_for('index') }}">
- <img src="/static/logo.svg" alt="" width="50%" height="50%">
- Home
+ <img src="/static/logo.svg" alt="" width="50%" height="50%">
</a>
<ul class="nav nav-pills">
{% if not current_user.is_authenticated %}
<li class="nav-item">
- <a class="nav-link active" href="{{ url_for('login') }}">Login</a>
+ <a class="nav-link active" href="{{ url_for('login') }}">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('register') }}">Registeren</a>
- </li>
+ </li>
{% else %}
<li class="nav-item">
- <a class="nav-link active" href="{{ url_for('logout') }}">Logout</a>
+ <a class="nav-link active" href="{{ url_for('logout') }}">Logout</a>
</li>
{% endif %}
- </ul>
+ </ul>
</div>
- </nav>
-
+ </nav>
+
<div class="container" style="padding-top:20px;">
<div class="row">
- <!-- sidebar -->
+ <!-- sidebar -->
<div class="col-3">
- {% if current_user.is_authenticated %}
- <ul class="nav nav-pills flex-column nav-justified">
- <li class="nav-item">
- <a class="nav-link" href="{{ url_for('traps') }}">Dashboard</a>
- </li>
- <li class="nav-item">
- <a class="nav-link" href="{{ url_for('trap_connect') }}">Koppel een val</a>
- </li>
- <li class="nav-item">
- <a class="nav-link" href="{{ url_for('about') }}">about</a>
- </li>
- {% if current_user.is_authenticated %}
- <a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#" role="button" aria-expanded="false">{{current_user.name}}
-
- </a>
-
- <ul class="dropdown-menu text-small shadow" aria-labelledby="dropdownUser2">
- <li><a class="dropdown-item" href="{{ url_for('account') }}">Instellingen</a></li>
-
- <li><hr class="dropdown-divider"></li>
- <li><a class="dropdown-item" href="{{ url_for('logout') }}">Uitloggen</a></li>
- </ul>
+ <ul class="nav nav-pills flex-column nav-justified">
+ {% if current_user.is_authenticated %}
+ <li class="nav-item">
+ <a class="nav-link" href="{{ url_for('account') }}"><b>{{current_user.name}}</b></a>
+ </li>
+ <li class="nav-item">
+ <a class="nav-link" href="{{ url_for('traps') }}">Dashboard</a>
+ </li>
+ <li class="nav-item">
+ <a class="nav-link" href="{{ url_for('trap_connect') }}">Koppel een val</a>
+ </li>
+ {% if current_user.contact %}
+ <li class="nav-item">
+ <a class="nav-link" href="{{ url_for('contact') }}">Contact opnemen</a>
+ </li>
+ {% endif %}
+ {% if current_user.type.name == 'ADMIN' %}
+ <li class="nav-item">
+ <a class="nav-link" href="{{ url_for('admin') }}">Gebruikers beheerden</a>
+ </li>
+ {% endif %}
+ {% endif %}
+ <li class="nav-item">
+ <a class="nav-link" href="{{ url_for('about') }}">Over ons</a>
+ </li>
- <ul class="dropdown-menu">
- <li><a class="dropdown-item" href="{{ url_for('account') }}">Instellingen</a></li>
- <li><a class="dropdown-item" href="{{ url_for('logout') }}">Uitloggen</a></li>
+ <!--
+ <div class="dropdown">
+ <li class="nav-item">
+ <a class="nav-link dropdown-toggle" href="#" role="button" id="account-dropdown"
+ data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+ <b>{{current_user.name}}</b>
+ </a>
+
+ <div class="dropdown-menu" aria-labelledby="account-dropdown">
+ <a class="dropdown-item" href="{{ url_for('account') }}">Instellingen</a>
+ <a class="dropdown-item" href="{{ url_for('logout') }}">Uitloggen</a>
+ </div>
+ </li>
+ </div>-->
</ul>
- {% endif %}
- </ul>
- {% endif %}
</div>
-
+
<!-- content-->
<div class="col-7">
- {% block content %}{% endblock %}
+ {% for category, message in get_flashed_messages(with_categories=true) %}
+ <div class="alert alert-{{ category }}">
+ {{ message }}
+ </div>
+ {% endfor %}
+ {% block content %}{% endblock %}
</div>
</div>
</div>
-
-
- <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
- integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
- crossorigin="anonymous"></script>
- <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
- integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
- crossorigin="anonymous"></script>
- <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
- integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
- crossorigin="anonymous"></script>
- <script>
-
- const dropdownElementList = document.querySelectorAll('.dropdown-toggle')
- const dropdownList = [...dropdownElementList].map(dropdownToggleEl => new bootstrap.Dropdown(dropdownToggleEl))
- </script>
</body>
</html>
\ No newline at end of file