Merge pull request #294 from bornhack/profile_page_makeover

Profile page makeover
This commit is contained in:
Víðir Valberg Guðmundsson 2019-03-26 22:33:01 +01:00 committed by GitHub
commit 5fdf0e70e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 611 additions and 467 deletions

View file

@ -19,10 +19,9 @@ from villages.views import *
admin.site.login = login_required(admin.site.login) admin.site.login = login_required(admin.site.login)
urlpatterns = [ urlpatterns = [
path( path('profile/', include('allauth.urls')),
'profile/', path('profile/', include('allauth_2fa.urls')),
include('profiles.urls', namespace='profiles') path('profile/', include('profiles.urls', namespace='profiles')),
),
path( path(
'tickets/', 'tickets/',
include('tickets.urls', namespace='tickets') include('tickets.urls', namespace='tickets')
@ -65,8 +64,6 @@ urlpatterns = [
TemplateView.as_view(template_name='legal/general_terms_and_conditions.html'), TemplateView.as_view(template_name='legal/general_terms_and_conditions.html'),
name='general-terms' name='general-terms'
), ),
path('accounts/', include('allauth.urls')),
path('accounts/', include('allauth_2fa.urls')),
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
# We don't need CSRF checks for the API # We don't need CSRF checks for the API

View file

@ -1,12 +1,7 @@
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db import models from django.db import models
from django.conf import settings
from django.utils import timezone
from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from datetime import timedelta
from utils.models import UUIDModel, CreatedUpdatedModel from utils.models import UUIDModel, CreatedUpdatedModel

View file

@ -1,76 +1,105 @@
{% extends 'profile_base.html' %} {% extends 'profile_base.html' %}
{% load i18n %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% block head_title %}{% trans "Account" %}{% endblock %}
{% block profile_content %} {% block profile_content %}
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<h1>{% trans "E-mail Addresses" %}</h1> <div class="panel panel-default">
{% if user.emailaddress_set.all %} <div class="panel-heading">
<p>{% trans 'The following e-mail addresses are associated with your account:' %}</p> <h4>E-mail Addresses</h4>
</div>
<div class="panel-body">
<form action="{% url 'account_email' %}" class="email_list" method="post"> {% if user.emailaddress_set.all %}
{% csrf_token %} <p>The following e-mail addresses are associated with your account:</p>
<fieldset class="blockLabels"> <form action="{% url 'account_email' %}" class="email_list" method="post">
{% csrf_token %}
<fieldset class="blockLabels">
{% for emailaddress in user.emailaddress_set.all %} <table class="table">
<div class="ctrlHolder"> <thead>
<label for="email_radio_{{forloop.counter}}" class="{% if emailaddress.primary %}primary_email{%endif%}"> <tr>
<th></th>
<th>Address</th>
<th>Status</th>
<th>Primary?</th>
</tr>
</thead>
<tbody>
<input {% for emailaddress in user.emailaddress_set.all %}
id="email_radio_{{forloop.counter}}" <tr class="ctrlHolder">
type="radio" <label for="email_radio_{{forloop.counter}}" class="{% if emailaddress.primary %}primary_email{%endif%}">
name="email" <td>
value="{{emailaddress.email}}" <input
{% if emailaddress.primary or user.emailaddress_set.count == 1 %} id="email_radio_{{forloop.counter}}"
checked="checked" type="radio"
{%endif %} name="email"
/> value="{{emailaddress.email}}"
{% if emailaddress.primary or user.emailaddress_set.count == 1 %}
checked="checked"
{%endif %}
/>
</td>
<td>
{{ emailaddress.email }}
</td>
<td>
{% if emailaddress.verified %}
<span class="label label-success">Verified</span>
{% else %}
<span class="label label-danger">Unverified</span>
{% endif %}
</td>
<td>
{% if emailaddress.primary %}<span class="label label-primary">Primary</span>{% endif %}
</td>
</label>
</tr>
{% endfor %}
{{ emailaddress.email }} </tbody>
{% if emailaddress.verified %} </table>
<span class="badge">{% trans "Verified" %}</span>
{% else %}
<span class="badge">{% trans "Unverified" %}</span>
{% endif %}
{% if emailaddress.primary %}<span class="badge">{% trans "Primary" %}</span>{% endif %}
</label>
</div>
{% endfor %}
<div class="buttonHolder"> <div class="buttonHolder">
<button class="btn btn-black" type="submit" name="action_primary" >{% trans 'Make Primary' %}</button> <button class="btn btn-success" type="submit" name="action_primary" >Make Primary</button>
<button class="btn btn-black" type="submit" name="action_send" >{% trans 'Re-send Verification' %}</button> <button class="btn btn-primary" type="submit" name="action_send" >Re-send Verification</button>
<button class="btn btn-black" type="submit" name="action_remove" >{% trans 'Remove' %}</button> <button class="btn btn-danger" type="submit" name="action_remove" >Remove</button>
</div> </div>
</fieldset> </fieldset>
</form> </form>
{% else %}
<p>
<strong>Warning:</strong> You currently do not have any e-mail address set up. You should really add an e-mail address so you can receive notifications, reset your password, etc.
</p>
{% endif %}
</div>
</div>
{% else %} <div class="panel panel-default">
<p><strong>{% trans 'Warning:'%}</strong> {% trans "You currently do not have any e-mail address set up. You should really add an e-mail address so you can receive notifications, reset your password, etc." %}</p> <div class="panel-heading">
<h4>Add E-mail Address</h4>
</div>
<div class="panel-body">
<form method="post" action="{% url 'account_email' %}" class="add_email">
{% csrf_token %}
{% bootstrap_form form %}
<button name="action_add" class="btn btn-success" type="submit">Add E-mail</button>
</form>
</div>
</div>
{% endif %}
<h2>{% trans "Add E-mail Address" %}</h2>
<form method="post" action="{% url 'account_email' %}" class="add_email">
{% csrf_token %}
{% bootstrap_form form %}
<button name="action_add" class="btn btn-black" type="submit">{% trans "Add E-mail" %}</button>
</form>
</div> </div>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
(function() { (function() {
var message = "{% trans 'Do you really want to remove the selected e-mail address?' %}"; var message = "Do you really want to remove the selected e-mail address?";
var actions = document.getElementsByName('action_remove'); var actions = document.getElementsByName('action_remove');
if (actions.length) { if (actions.length) {
actions[0].addEventListener("click", function(e) { actions[0].addEventListener("click", function(e) {

View file

@ -16,7 +16,7 @@
<form class="login" method="POST"> <form class="login" method="POST">
{% csrf_token %} {% csrf_token %}
{% bootstrap_form form %} {% bootstrap_form form %}
<button class="form-control btn btn-black" type="submit">{% trans "Login" %}</button> <button class="form-control btn btn-primary" type="submit">{% trans "Login" %}</button>
{% if redirect_field_value %} {% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" /> <input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
{% endif %} {% endif %}

View file

@ -10,7 +10,7 @@
<form method="post"> <form method="post">
{% csrf_token %} {% csrf_token %}
<button type="submit" <button type="submit"
class="btn btn-black form-control"> class="btn btn-danger form-control">
Logout Logout
</button> </button>
</form> </form>

View file

@ -7,13 +7,18 @@
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<h1>Change password</h1> <div class="panel panel-default">
<div class="panel-heading">
<h4>Change Password</h4>
</div>
<div class="panel-body">
<form class="login" method="POST"> <form class="login" method="POST">
{% csrf_token %} {% csrf_token %}
{% bootstrap_form form %} {% bootstrap_form form %}
<button class="form-control btn btn-black" type="submit">Save</button> <button class="form-control btn btn-primary" type="submit">Save</button>
</form> </form>
</div>
</div> </div>
</div> </div>

View file

@ -16,7 +16,7 @@
<form class="login" method="POST"> <form class="login" method="POST">
{% csrf_token %} {% csrf_token %}
{% bootstrap_form form %} {% bootstrap_form form %}
<button class="form-control btn btn-black" type="submit">Send e-mail</button> <button class="form-control btn btn-primary" type="submit">Send e-mail</button>
</form> </form>
</div> </div>
{% endblock %} {% endblock %}

View file

@ -13,7 +13,7 @@
<form class="login" method="POST"> <form class="login" method="POST">
{% csrf_token %} {% csrf_token %}
{% bootstrap_form form %} {% bootstrap_form form %}
<button class="form-control btn btn-black" type="submit">{% trans "Sign Up" %}</button> <button class="form-control btn btn-success" type="submit">{% trans "Sign Up" %}</button>
</form> </form>
</div> </div>
</div> </div>

View file

@ -15,7 +15,7 @@
{% bootstrap_form form %} {% bootstrap_form form %}
<button type="submit" <button type="submit"
class="btn btn-black form-control"> class="btn btn-primary form-control">
{% trans 'Authenticate' %} {% trans 'Authenticate' %}
</button> </button>
</form> </form>

View file

@ -2,34 +2,60 @@
{% load account %} {% load account %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load i18n %}
{% block profile_content %} {% block profile_content %}
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<h1>
{% trans "Two-Factor Authentication Backup Tokens" %}
</h1>
<ul> <div class="alert alert-danger">
{% for token in backup_tokens %} <h4><strong>Attention!</strong></h4>
<li>{{ token.token }}</li> <p >
Backup Tokens are important if you for some reason <strong>lose access</strong> to your two-factor authentication device (ie. misplace your phone).<br />
If this happens you will <strong>not be able to login to BornHack.dk</strong> without using a backup token.<br />
So generate some tokens and <strong>keep them safe</strong> for a rainy day ;)
</p>
</div>
{% empty %} <div class="panel panel-default">
{% trans 'No tokens. Press the button below to generate some.' %} <div class="panel-heading">
<h4>Two-Factor Authentication Backup Tokens</h4>
{% endfor %}
</ul>
<form method="post">
{% csrf_token %}
<div class="buttonHolder">
<button class="btn btn-black" type="submit">
{% trans 'Generate backup tokens' %}
</button>
<a class="btn btn-warning" href="{% url 'two-factor-remove' %}">Disable Two-Factor</a>
</div> </div>
</form> <div class="panel-body">
{% if backup_tokens %}
<ul>
{% for token in backup_tokens %}
<li><h4>{{ token.token }}</h4></li>
{% endfor %}
</ul>
{% else %}
<p>No tokens. Press the button below to generate some.</p>
{% endif %}
</div>
<div class="panel-footer">
<form method="post">
{% csrf_token %}
<div class="buttonHolder">
<button class="btn btn-success" type="submit">
{% if backup_tokens %}
Generate new backup tokens
{% else %}
Generate backup tokens
{% endif %}
</button>
</div>
</form>
</div>
</div>
<div class="panel panel-danger">
<div class="panel-heading">
<h4>Disable Two-Factor Authenticatoin</h4>
</div>
<div class="panel-body">
<a class="btn btn-danger" href="{% url 'two-factor-remove' %}">Disable Two-Factor</a>
</div>
</div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View file

@ -2,23 +2,26 @@
{% load account %} {% load account %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load i18n %}
{% block profile_content %} {% block profile_content %}
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<h1> <div class="panel panel-danger">
{% trans "Disable Two-Factor Authentication" %} <div class="panel-heading">
</h1> <h4>Disable Two-Factor Authentication</h4>
</div>
<div class="panel-body">
<p>Are you sure you want to disable two-factor authentication?</p>
<p>{% trans "Are you sure?" %}</p> <form method="post">
{% csrf_token %}
<form method="post"> <button class="btn btn-danger" type="submit">
{% csrf_token %} Yes, I'm sure
<button class="btn btn-danger" type="submit"> </button>
{% trans 'Disable Two-Factor' %} <a href="{% url 'two-factor-backup-tokens' %}" class="btn btn-success">No, I've changed my mind.</a>
</button> </form>
</form> </div>
</div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View file

@ -7,35 +7,38 @@
{% block profile_content %} {% block profile_content %}
<div class="row"> <div class="row">
<div class="col-md-12"> <div class="col-md-12">
<h1> <div class="panel panel-default">
{% trans "Setup Two-Factor Authentication" %} <div class="panel-heading">
</h1> <h4>Setup Two-Factor Authentication</h4>
</div>
<div class="panel-body">
<h4>
{% trans 'Step 1' %}:
</h4>
<h4> <p>
{% trans 'Step 1' %}: {% trans 'Scan the QR code below with a token generator of your choice (for instance Google Authenticator).' %}
</h4> </p>
<p> <img src="{% url 'two-factor-qr-code' %}" />
{% trans 'Scan the QR code below with a token generator of your choice (for instance Google Authenticator).' %}
</p>
<img src="{% url 'two-factor-qr-code' %}" /> <h4>
{% trans 'Step 2' %}:
</h4>
<h4> <p>
{% trans 'Step 2' %}: {% trans 'Input a token generated by the app:' %}
</h4> </p>
<p> <form method="post">
{% trans 'Input a token generated by the app:' %} {% csrf_token %}
</p> {% bootstrap_form form %}
<button class="btn btn-success" type="submit">
<form method="post"> {% trans 'Verify' %}
{% csrf_token %} </button>
{% bootstrap_form form %} </form>
<button class="btn btn-black" type="submit"> </div>
{% trans 'Verify' %} </div>
</button>
</form>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View file

@ -4,20 +4,92 @@
{% load bootstrap3 %} {% load bootstrap3 %}
{% block content %} {% block content %}
<div class="page-header">
<h2>Your BornHack Profile</h2>
</div>
<div class="row"> <div class="row">
<div class="btn-group btn-group-justified hidden-xs"> <div class="col-md-2">
{% include 'profile_base_buttons.html' %} <ul class="nav nav-pills nav-stacked">
<li><b>Profile settings</b></li>
{% url 'profiles:detail' as profile_detail_url %}
<li {% if request.path == profile_detail_url %}class="active"{% endif %}>
<a href="{{ profile_detail_url }}">
Profile
</a>
</li>
{% url 'account_change_password' as password_url %}
<li {% if request.path == password_url %}class="active"{% endif %}>
<a href="{{ password_url }}">
Password
</a>
</li>
{% url 'account_email' as email_url %}
<li {% if request.path == email_url %}class="active"{% endif %}>
<a href="{{ email_url }}">
Manage emails
</a>
</li>
{% url 'two-factor-setup' as two_factor_url %}
<li {% if "two_factor" in request.path %}class="active"{% endif %}>
<a href="{{ two_factor_url }}">
Two-Factor settings
</a>
</li>
{% if user.orders.exists %}
<li><b>Shop</b></li>
{% url 'shop:order_list' as orders_list_url %}
<li {% if "shop/order" in request.path %}class="active"{% endif %}>
<a href="{{ orders_list_url }}">
Orders
</a>
</li>
{% url 'tickets:shopticket_list' as ticket_list_url %}
<li {% if "tickets" in request.path %}class="active"{% endif %}>
<a href="{{ ticket_list_url }}">
Tickets
</a>
</li>
{% if user.creditnotes.exists %}
{% url 'shop:creditnote_list' as creditnote_list_url %}
<li {% if 'creditnotes' in request.path %}class="active"{% endif %}>
<a href="{{ creditnote_list_url }}">
Credit Notes
</a>
</li>
{% endif %}
{% endif %}
<li><b>Misc.</b></li>
{% url 'tokens:tokenfind_list' as tokenfind_list_url %}
<li {% if request.path == tokenfind_list_url %}class="active"{% endif %}>
<a href="{{ tokenfind_list_url }}">
Secret Tokens
</a>
</li>
<hr />
<p><b>You are logged in as {{ request.user.email }}</b></p>
</ul>
</div> </div>
<div class="btn-group-vertical visible-xs">
{% include 'profile_base_buttons.html' %} <div class="col-md-10">
{% block profile_content %}{% endblock %}
</div> </div>
</div> </div>
<br>
<p><i><b>You are logged in as {{ request.user.email }}</b></i></p>
<hr />
{% block profile_content %}{% endblock %}
{% endblock %} {% endblock %}

View file

@ -1,32 +0,0 @@
<a href="{% url 'profiles:detail' %}" class="btn btn-black">
Profile
</a>
<a href="{% url 'account_change_password' %}" class="btn btn-black">
Password
</a>
<a href="{% url 'two-factor-setup' %}" class="btn btn-black">
Two-Factor settings
</a>
<a href="{% url 'account_email' %}" class="btn btn-black">
Manage emails
</a>
{% if user.orders.exists %}
<a href="{% url 'shop:order_list' %}" class="btn btn-black">
Orders
</a>
<a href="{% url 'tickets:shopticket_list' %}" class="btn btn-black">
Tickets
</a>
{% if user.creditnotes.exists %}
<a href="{% url 'shop:creditnote_list' %}" class="btn btn-black">
Credit Notes
</a>
{% endif %}
{% endif %}
<a href="{% url 'tokens:tokenfind_list' %}" class="btn btn-black">
Secret Tokens
</a>
<a href="{% url 'account_logout' %}" class="btn btn-black">
Logout
</a>

View file

@ -1,26 +1,63 @@
{% extends 'profile_base.html' %} {% extends 'profile_base.html' %}
{% block profile_content %} {% block profile_content %}
<p class="lead">If you intend to join one or more teams you can use the profile to help the team responsible understand who you are and what you have to offer. Please also include availability if you are not joining us for the entire week.</p> {% if not profile.description %}
<p class="lead">Apart from Public Credit Name the information in your profile is only visible to team responsible and organisers.</p> <div class="alert alert-danger">
<h3>Your Profile</h3> <h4><strong>Attention!</strong></h4>
<table class="table table-horisontal"> <p>
<tr> You have not written anything in your "description".
<td><b>Name (not visible to the public)</b></td> </p>
<td>{{ profile.name|default:"N/A" }}</td> <p>
</tr> If you intend to join one or more teams you can use the profile to help the team responsible understand who you are and what you have to offer. Please also include availability if you are not joining us for the entire week.
<tr> </p>
<td><b>Description (not visible to the public)</b></td> </div>
<td>{{ profile.description|default:"N/A" }}</td> {% endif %}
</tr>
<tr> <div class="panel panel-default">
<td><b>Public Credit Name (visible to the public, leave empty if you want no credits on this website)</b></td> <div class="panel-heading">
<td>{{ profile.public_credit_name|default:"N/A" }} {% if profile.public_credit_name %}({% if profile.public_credit_name_approved %}<span class="text-success">approved</span>{% else %}<span class="text-danger">pending approval</span>{% endif %}){% endif %}</td> <a href="{% url 'profiles:update' %}" class="btn btn-primary pull-right clearfix"><i class="fas fa-edit"></i> Edit Profile</a>
</tr> <h4>Your information</h4>
<tr> </div>
<td><b>NickServ username (visible to the public on IRC, used to handle team channel ACLs)</b></td> <div class="panel-body">
<td>{{ profile.nickserv_username|default:"N/A" }}</td>
</tr> <table class="table table-striped">
</table> <tr>
<a href="{% url 'profiles:update' %}" class="btn btn-black"><i class="fas fa-edit"></i> Edit Profile</a> <td width="30%">
<b>Name</b><br />
<small>Not visible to the public.</small></td>
<td width="70%">
{{ profile.name|default:"N/A" }}
</td>
</tr>
<tr>
<td>
<b>Description</b><br />
<small>Not visible to the public.</small>
</td>
<td>{{ profile.description|default:"N/A" }}</td>
</tr>
<tr>
<td>
<b>Public Credit Name</b><br />
<small>Visible to the public, leave empty if you want no credits on this website.</small>
</td>
<td>
{{ profile.public_credit_name|default:"N/A" }} {% if profile.public_credit_name %}({% if profile.public_credit_name_approved %}<span class="text-success">approved</span>{% else %}<span class="text-danger">pending approval</span>{% endif %}){% endif %}</td>
</tr>
<tr>
<td>
<b>NickServ username</b><br />
<small>Visible to the public on IRC, used to handle team channel ACLs.</small>
</td>
<td>
{{ profile.nickserv_username|default:"N/A" }}
</td>
</tr>
</table>
</div>
</div>
{% endblock profile_content %} {% endblock profile_content %}

View file

@ -2,11 +2,17 @@
{% load bootstrap3 %} {% load bootstrap3 %}
{% block profile_content %} {% block profile_content %}
<h4>Update Profile</h4> <div class="panel panel-default">
<form method="POST"> <div class="panel-heading">
{% csrf_token %} <h4>Update Profile</h4>
{% bootstrap_form form %} </div>
<button type="submit" class="btn btn-black"><i class="fas fa-save"></i> Submit</button> <div class="panel-body">
<a href="{% url 'profiles:detail' %}" class="btn btn-black"><i class="fas fa-times"></i> Cancel</a> <form method="POST">
</form> {% csrf_token %}
{% bootstrap_form form %}
<button type="submit" class="btn btn-success"><i class="fas fa-save"></i> Submit</button>
<a href="{% url 'profiles:detail' %}" class="btn btn-danger"><i class="fas fa-times"></i> Cancel</a>
</form>
</div>
</div>
{% endblock profile_content %} {% endblock profile_content %}

View file

@ -0,0 +1,43 @@
{% extends 'profile_base.html' %}
{% load bootstrap3 %}
{% load shop_tags %}
{% load bornhack %}
{% block profile_content %}
<div class="panel panel-default">
<div class="panel-heading">
<h4>Credit Notes</h4>
</div>
<div class="panel-body">
<table class="table table-hover">
<thead>
<tr>
<th>Credit Note ID</th>
<th>Text</th>
<th class="text-right">Amount</th>
<th>Paid?</th>
<th>PDF</th>
</tr>
</thead>
<tbody>
{% for creditnote in creditnotes %}
<tr>
<td>{{ creditnote.id }}</td>
<td>{{ creditnote.text }}</td>
<td class="text-right">{{ creditnote.amount|currency }}</td>
<td class="text-center">{{ creditnote.paid|truefalseicon }}</td>
<td>
{% if creditnote.pdf %}
{% url 'shop:download_creditnote' pk=creditnote.pk as creditnote_download_url %}
{% bootstrap_button "PDF" icon="save-file" href=creditnote_download_url button_class="btn-primary btn-xs" %}
{% else %}
N/A
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}

View file

@ -1,8 +1,9 @@
{% extends 'shop_base.html' %} {% extends 'profile_base.html' %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load shop_tags %} {% load shop_tags %}
{% block shop_content %} {% block profile_content %}
<div class="panel-group"> <div class="panel-group">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel panel-heading"> <div class="panel panel-heading">
@ -125,8 +126,8 @@
<div class="alert alert-info" style="margin-top: 5px; margin-bottom: -10px;">* Please consider the alternatives before choosing credit card. Credit cards are expensive and difficult for us to handle. Thank you!</div> <div class="alert alert-info" style="margin-top: 5px; margin-bottom: -10px;">* Please consider the alternatives before choosing credit card. Credit cards are expensive and difficult for us to handle. Thank you!</div>
</div> </div>
<div class="panel panel-footer"> <div class="panel panel-footer">
<i>Bank transfers take up to a week to get registered, but the other <i>Bank transfers take up to a week to get registered, but the other
payment methods should be more or less instant. Please payment methods should be more or less instant. Please
<a href="{% url 'contact' %}">contact us</a> if your have questions.</i> <a href="{% url 'contact' %}">contact us</a> if your have questions.</i>
</div> </div>
</div> </div>

View file

@ -0,0 +1,55 @@
{% extends 'profile_base.html' %}
{% load bootstrap3 %}
{% load shop_tags %}
{% load bornhack %}
{% block profile_content %}
<div class="panel panel-default">
<div class="panel-heading">
<h4>Orders</h4>
</div>
<div class="panel-body">
<table class="table table-hover">
<thead>
<tr>
<th>Order ID</th>
<th>Items</th>
<th>Total amount</th>
<th>Open?</th>
<th>Paid?</th>
<th>Delivered?</th>
<th>Invoice</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for order in orders %}
{% if order.products.exists %}
<tr {% if not order.open and order.paid and order.is_fully_handed_out %}style="color: lightgrey"{% endif %}>
<td>{{ order.id }}</td>
<td>{{ order.get_number_of_items }}</td>
<td>{{ order.total|currency }}</td>
<td class="text-center">{{ order.open|truefalseicon }}</td>
<td class="text-center">{{ order.paid|truefalseicon }}</td>
<td class="text-center">{{ order.handed_out_status }}</td>
<td>
{% if order.invoice.pdf %}
{% url 'shop:download_invoice' pk=order.pk as invoice_download_url %}
{% bootstrap_button "PDF" icon="save-file" href=invoice_download_url button_class="btn-primary btn-xs" %}
{% else %}
N/A
{% endif %}
</td>
<td>
{% url 'shop:order_detail' pk=order.pk as order_detail_url %}
{% bootstrap_button "Order details" icon="th-list" href=order_detail_url button_class="btn-primary btn-xs" %}
</td>
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,24 @@
{% extends 'profile_base.html' %}
{% load bootstrap3 %}
{% load tickets_tags %}
{% block profile_content %}
<div class="panel panel-default">
<div class="panel-heading">
<h4>Your Tickets</h4>
</div>
<div class="panel-body">
<h2>{% if ticket.checked_in %}This ticket has been used{% else %}This ticket is unused{% endif %}</h2>
<form method="POST" class="form">
{% csrf_token %}
{% bootstrap_field form.name %}
{% bootstrap_field form.email %}
<button class="btn btn-primary form-control" type="submit"><i class="glyphicon glyphicon-check"></i> Save</button>
</form>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,60 @@
{% extends 'profile_base.html' %}
{% load bootstrap3 %}
{% load tickets_tags %}
{% block profile_content %}
<div class="panel panel-default">
<div class="panel-heading">
<h4>Your Tickets</h4>
</div>
<div class="panel-body">
{% if tickets %}
<table class="table table-hover">
<thead>
<tr>
<th>
Ticket owner
<th>
Product name
<th>
Price
<th>
Checked in
<th>
Actions
<tbody>
{% for ticket in tickets %}
<tr>
<td>
{% if ticket.name %}
{{ ticket.name }}
{% else %}
Anonymous
{% endif %}
<td>
{{ ticket.product.name }}
<td>
{{ ticket.product.price|currency }}
<td>
{% if ticket.checked_in %}
Yes
{% else %}
Not yet
{% endif %}
<td>
<a href="{% url 'tickets:shopticket_download' pk=ticket.pk %}" class="btn btn-primary"><i class="fas fa-download" aria-hidden="true"></i> Download PDF</a>
{% if not ticket.name %}
<a href="{% url 'tickets:shopticket_edit' pk=ticket.pk %}" class="btn btn-primary"><i class="fas fa-edit" aria-hidden="true"></i> Set name</a>
{% else %}
<a href="{% url 'tickets:shopticket_edit' pk=ticket.pk %}" class="btn btn-primary"><i class="fas fa-edit" aria-hidden="true"></i> Edit name</a>
{% endif %}
{% endfor %}
</table>
{% else %}
<h3> You don't have any tickets yet. We hope to see you at the next BornHack!</h3>
{% endif %}
</div>
{% endblock %}

View file

@ -0,0 +1,55 @@
{% extends 'profile_base.html' %}
{% load static from staticfiles %}
{% load commonmark %}$
{% block title %}
Your Secret Tokens | {{ block.super }}
{% endblock %}
{% block profile_content %}
<div class="panel panel-default">
<div class="panel-heading">
<h4>Your Secret Tokens</h4>
</div>
<div class="panel-body">
<p class="lead">You have found the following secret tokens in the BornHack Secret Token Game:</p>
<table class="table">
<thead>
<tr>
<th>Camp</th>
<th>Category</th>
<th>Token</th>
<th>Description</th>
<th>Found</th>
</tr>
</thead>
<tbody>
{% for tokenfind in object_list %}
<tr>
<td>{{ tokenfind.token.camp.title }}</td>
<td>{{ tokenfind.token.category }}</td>
<td>{{ tokenfind.token.token }}</td>
<td>{{ tokenfind.token.description }}</td>
<td>{{ tokenfind.created }}</td>
</tr>
{% endfor %}
{% for unfound in unfound_list %}
<tr>
<td>{{ unfound.camp.title }}</td>
<td>{{ unfound.category }}</td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if not unfound_list %}
<p class="lead">Congratulations! You've found all tokens we have created right now.</p>
{% endif %}
</div>
{% endblock %}

View file

@ -1,7 +1,9 @@
from django.urls import path from django.urls import path
from .views import ProfileDetail, ProfileUpdate from .views import (
ProfileDetail,
ProfileUpdate,
)
app_name = 'profiles' app_name = 'profiles'
urlpatterns = [ urlpatterns = [

View file

@ -9,6 +9,7 @@ from . import models
class ProfileDetail(LoginRequiredMixin, DetailView): class ProfileDetail(LoginRequiredMixin, DetailView):
model = models.Profile model = models.Profile
template_name = 'profile_detail.html' template_name = 'profile_detail.html'
active_menu = 'profile'
def get_object(self, queryset=None): def get_object(self, queryset=None):
return models.Profile.objects.get(user=self.request.user) return models.Profile.objects.get(user=self.request.user)
@ -30,4 +31,3 @@ class ProfileUpdate(LoginRequiredMixin, UpdateView):
form.instance.save() form.instance.save()
messages.success(self.request, 'Your profile has been updated.') messages.success(self.request, 'Your profile has been updated.')
return super().form_valid(form, **kwargs) return super().form_valid(form, **kwargs)

View file

@ -1,37 +0,0 @@
{% extends 'shop_base.html' %}
{% load bootstrap3 %}
{% load shop_tags %}
{% load bornhack %}
{% block shop_content %}
<h3>Credit Notes</h3>
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>Credit Note ID</th>
<th>Text</th>
<th class="text-right">Amount</th>
<th>Paid?</th>
<th>PDF</th>
</tr>
</thead>
<tbody>
{% for creditnote in creditnotes %}
<tr>
<td>{{ creditnote.id }}</td>
<td>{{ creditnote.text }}</td>
<td class="text-right">{{ creditnote.amount|currency }}</td>
<td class="text-center">{{ creditnote.paid|truefalseicon }}</td>
<td>
{% if creditnote.pdf %}
{% url 'shop:download_creditnote' pk=creditnote.pk as creditnote_download_url %}
{% bootstrap_button "PDF" icon="save-file" href=creditnote_download_url button_class="btn-primary btn-xs" %}
{% else %}
N/A
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View file

@ -1,49 +0,0 @@
{% extends 'shop_base.html' %}
{% load bootstrap3 %}
{% load shop_tags %}
{% load bornhack %}
{% block shop_content %}
<h3>Orders</h3>
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>Order ID</th>
<th>Items</th>
<th>Total amount</th>
<th>Open?</th>
<th>Paid?</th>
<th>Delivered?</th>
<th>Invoice</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for order in orders %}
{% if order.products.exists %}
<tr {% if not order.open and order.paid and order.is_fully_handed_out %}style="color: lightgrey"{% endif %}>
<td>{{ order.id }}</td>
<td>{{ order.get_number_of_items }}</td>
<td>{{ order.total|currency }}</td>
<td class="text-center">{{ order.open|truefalseicon }}</td>
<td class="text-center">{{ order.paid|truefalseicon }}</td>
<td class="text-center">{{ order.handed_out_status }}</td>
<td>
{% if order.invoice.pdf %}
{% url 'shop:download_invoice' pk=order.pk as invoice_download_url %}
{% bootstrap_button "PDF" icon="save-file" href=invoice_download_url button_class="btn-primary btn-xs" %}
{% else %}
N/A
{% endif %}
</td>
<td>
{% url 'shop:order_detail' pk=order.pk as order_detail_url %}
{% bootstrap_button "Order details" icon="th-list" href=order_detail_url button_class="btn-primary btn-xs" %}
</td>
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>
{% endblock %}

View file

@ -16,9 +16,6 @@
{% if user.creditnotes.exists %} {% if user.creditnotes.exists %}
<li class="pull-right"><a href="{% url 'shop:creditnote_list' %}">Credit Notes</a></li> <li class="pull-right"><a href="{% url 'shop:creditnote_list' %}">Credit Notes</a></li>
{% endif %} {% endif %}
{% if has_tickets %}
<li class="pull-right"><a href="{% url 'tickets:shopticket_list' %}">Tickets</a></li>
{% endif %}
<li class="pull-right"><a href="{% url 'shop:order_list' %}">Orders</a></li> <li class="pull-right"><a href="{% url 'shop:order_list' %}">Orders</a></li>
<li class="pull-right no-before"> <li class="pull-right no-before">

View file

@ -278,7 +278,7 @@ class ProductDetailView(FormView, DetailView):
class OrderListView(LoginRequiredMixin, ListView): class OrderListView(LoginRequiredMixin, ListView):
model = Order model = Order
template_name = "order_list.html" template_name = "shop/order_list.html"
context_object_name = 'orders' context_object_name = 'orders'
def get_queryset(self): def get_queryset(self):
@ -288,7 +288,7 @@ class OrderListView(LoginRequiredMixin, ListView):
class OrderDetailView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureOrderHasProductsMixin, EnsureOrderIsNotCancelledMixin, DetailView): class OrderDetailView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsureOrderHasProductsMixin, EnsureOrderIsNotCancelledMixin, DetailView):
model = Order model = Order
template_name = 'order_detail.html' template_name = 'shop/order_detail.html'
context_object_name = 'order' context_object_name = 'order'
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
@ -369,7 +369,7 @@ class DownloadInvoiceView(LoginRequiredMixin, EnsureUserOwnsOrderMixin, EnsurePa
class CreditNoteListView(LoginRequiredMixin, ListView): class CreditNoteListView(LoginRequiredMixin, ListView):
model = CreditNote model = CreditNote
template_name = "creditnote_list.html" template_name = "shop/creditnote_list.html"
context_object_name = 'creditnotes' context_object_name = 'creditnotes'
def get_queryset(self): def get_queryset(self):

View file

@ -81,7 +81,12 @@
</ul> </ul>
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
{% if user.is_authenticated %} {% if user.is_authenticated %}
<li><a href="{% url 'profiles:detail' %}">Account</a></li> <li><a href="{% url 'profiles:detail' %}">Profile</a></li>
<li>
<a href="{% url 'account_logout' %}">
Logout
</a>
</li>
{% else %} {% else %}
<li><a href="{% url 'account_login' %}">Login</a></li> <li><a href="{% url 'account_login' %}">Login</a></li>
{% endif %} {% endif %}

View file

@ -1,30 +0,0 @@
{% extends 'tickets_base.html' %}
{% load bootstrap3 %}
{% load tickets_tags %}
{% block tickets_content %}
<div class="well">
<div class="row">
<div class="col-md-7">
<div class="pull-right">
<h2>{% if ticket.checked_in %}This ticket has been used{% else %}This ticket is unused{% endif %}</h2>
<form method="POST" class="form">
{% csrf_token %}
{% bootstrap_field form.name %}
{% bootstrap_field form.email %}
<button class="btn btn-primary form-control" type="submit"><i class="glyphicon glyphicon-check"></i> Save</button>
</form>
<hr />
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -1,55 +0,0 @@
{% extends 'tickets_base.html' %}
{% load bootstrap3 %}
{% load tickets_tags %}
{% block tickets_content %}
{% if tickets %}
<h3>Tickets</h3>
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>
Ticket owner
<th>
Product name
<th>
Price
<th>
Checked in
<th>
Actions
<tbody>
{% for ticket in tickets %}
<tr>
<td>
{% if ticket.name %}
{{ ticket.name }}
{% else %}
Anonymous
{% endif %}
<td>
{{ ticket.product.name }}
<td>
{{ ticket.product.price|currency }}
<td>
{% if ticket.checked_in %}
Yes
{% else %}
Not yet
{% endif %}
<td>
<a href="{% url 'tickets:shopticket_download' pk=ticket.pk %}" class="btn btn-primary"><i class="fas fa-download" aria-hidden="true"></i> Download PDF</a>
{% if not ticket.name %}
<a href="{% url 'tickets:shopticket_edit' pk=ticket.pk %}" class="btn btn-primary"><i class="fas fa-edit" aria-hidden="true"></i> Set name</a>
{% else %}
<a href="{% url 'tickets:shopticket_edit' pk=ticket.pk %}" class="btn btn-primary"><i class="fas fa-edit" aria-hidden="true"></i> Edit name</a>
{% endif %}
{% endfor %}
</table>
{% else %}
<h3> You dont have any tickets yet. We hope to see you at the next BornHack!</h3>
{% endif %}
{% endblock %}

View file

@ -1,21 +0,0 @@
{% extends 'base.html' %}
{% block content %}
<div class="row">
<div class="col-md-12">
<ol class="breadcrumb">
<li><a href="{% url 'tickets:shopticket_list' %}">Tickets</a></li>
<li class="pull-right no-before">
</li>
</ol>
</div>
</div>
{% block tickets_content %}
{% endblock %}
{% endblock %}

View file

@ -20,7 +20,7 @@ logger = logging.getLogger("bornhack.%s" % __name__)
class ShopTicketListView(LoginRequiredMixin, ListView): class ShopTicketListView(LoginRequiredMixin, ListView):
model = ShopTicket model = ShopTicket
template_name = 'ticket_list.html' template_name = 'tickets/ticket_list.html'
context_object_name = 'tickets' context_object_name = 'tickets'
def get_queryset(self): def get_queryset(self):
@ -50,7 +50,7 @@ class ShopTicketDownloadView(LoginRequiredMixin, SingleObjectMixin, View):
class ShopTicketDetailView(LoginRequiredMixin, UpdateView, DetailView): class ShopTicketDetailView(LoginRequiredMixin, UpdateView, DetailView):
model = ShopTicket model = ShopTicket
template_name = 'ticket_detail.html' template_name = 'tickets/ticket_detail.html'
context_object_name = 'ticket' context_object_name = 'ticket'
fields = ['name', 'email'] fields = ['name', 'email']

View file

@ -11,6 +11,6 @@ Secret Token Found! | {{ block.super }}
<p class="lead text-center">You found a secret token:</p> <p class="lead text-center">You found a secret token:</p>
<p class="lead text-center"><span class="badge">{{ token.description }}</span></p> <p class="lead text-center"><span class="badge">{{ token.description }}</span></p>
<p class="lead text-center">Your visit has been registered! Keep hunting, there might be more tokens out there.</p> <p class="lead text-center">Your visit has been registered! Keep hunting, there might be more tokens out there.</p>
<p class="lead text-center"><a href="{% url 'tokens:tokenfind_list' %}">List All Tokens</a></p> <p class="lead text-center"><a href="{% url 'tokens:tokenfind_list' %}">List All Found Tokens</a></p>
{% endblock %} {% endblock %}

View file

@ -1,47 +0,0 @@
{% extends 'base.html' %}
{% load static from staticfiles %}
{% load commonmark %}$
{% block title %}
Your Secret Tokens | {{ block.super }}
{% endblock %}
{% block content %}
<h3>Your Secret Tokens</h3>
<p class="lead">You have found the following secret tokens in the BornHack Secret Token Game:</p>
<table class="table">
<thead>
<tr>
<th>Camp</th>
<th>Category</th>
<th>Token</th>
<th>Description</th>
<th>Found</th>
</tr>
</thead>
<tbody>
{% for tokenfind in object_list %}
<tr>
<td>{{ tokenfind.token.camp.title }}</td>
<td>{{ tokenfind.token.category }}</td>
<td>{{ tokenfind.token.token }}</td>
<td>{{ tokenfind.token.description }}</td>
<td>{{ tokenfind.created }}</td>
</tr>
{% endfor %}
{% for unfound in unfound_list %}
<tr>
<td>{{ unfound.camp.title }}</td>
<td>{{ unfound.category }}</td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if not unfound_list %}
<p class="lead">Congratulations! You've found all tokens we have created right now.</p>
{% endif %}
{% endblock %}

View file

@ -1,14 +1,10 @@
from django.urls import path, re_path, include from django.urls import re_path, path
from .views import TokenDetailView, TokenFindListView from .views import TokenDetailView, TokenFindListView
app_name = 'tokens' app_name = 'tokens'
urlpatterns = [ urlpatterns = [
path( path('', TokenFindListView.as_view(), name='tokenfind_list'),
'',
TokenFindListView.as_view(),
name='tokenfind_list'
),
re_path( re_path(
'(?P<token>[0-9a-zA-Z\.@]+)/$', '(?P<token>[0-9a-zA-Z\.@]+)/$',
TokenDetailView.as_view(), TokenDetailView.as_view(),

View file

@ -3,6 +3,7 @@ from django.views.generic import ListView, DetailView
from .models import Token, TokenFind from .models import Token, TokenFind
class TokenDetailView(LoginRequiredMixin, DetailView): class TokenDetailView(LoginRequiredMixin, DetailView):
template_name = "token_detail.html" template_name = "token_detail.html"
model = Token model = Token
@ -19,8 +20,11 @@ class TokenDetailView(LoginRequiredMixin, DetailView):
class TokenFindListView(LoginRequiredMixin, ListView): class TokenFindListView(LoginRequiredMixin, ListView):
template_name = "tokenfind_list.html" """
This class is meant to be extended in other apps like `profiles`.
"""
model = TokenFind model = TokenFind
template_name = "tokens/tokenfind_list.html"
def get_queryset(self): def get_queryset(self):
return TokenFind.objects.filter(user=self.request.user) return TokenFind.objects.filter(user=self.request.user)