2
0
mirror of https://expo.survex.com/repositories/troggle/.git synced 2025-03-29 21:51:51 +00:00

Signup form now saving data (and encrypting it)

This commit is contained in:
Philip Sargent 2025-01-25 19:56:36 +00:00
parent c479345b6c
commit 0d105d40da
7 changed files with 190 additions and 44 deletions

@ -135,7 +135,6 @@ def make_new_expo_dir(year):
if not p.is_file():
write_and_commit( [(p, content, "utf8")], f"Auto new year {ff} file creation", "Auto New Year <make_new_expo_dir@troggle.expo>")
def current_expo():
"""Returns the current expo year, but also checks if the most recent expo year is the same
as this year. If it is not, then it creates an empty Expedition and fixes some files and

@ -23,19 +23,10 @@ class login_required_if_public(object):
def __call__(self, *args, **kwargs):
return self.f(*args, **kwargs)
# This is copied from CUYC.cuy.website.view.auth
# If we want to do the whole online-email thing, we would also need to copy across the code in these
# imported files and delete what is superfluous.
# Or we could just load the latest version of django-registration app.
# from cuy.club.models import Member, Message
# from ..forms import WebsiteLoginForm, WebsiteRegisterForm
# from ...common import mail_site_error
# from .generic import user_is_active
"""The login and logout functions.
This is also where we would manage registration: for people wanting to create and validate their individual
logon accounts/forgottenpassword"""
TO DO : check that we don't have another set of these active somewhere
"""
############################
# Authentication Functions #

@ -1,8 +1,8 @@
import os
import re
import json
from pathlib import Path
import django.forms as forms
from django.contrib.auth.models import User
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import redirect, render
from django.urls import reverse
@ -16,62 +16,174 @@ from troggle.core.utils import (
git_string,
write_and_commit,
)
from troggle.core.models.troggle import DataIssue, Person
from troggle.core.views.editor_helpers import HTMLarea
from troggle.core.utils import (
add_commit,
)
from troggle.parsers.users import get_encryptor, ENCRYPTED_DIR
"""The new user signup form and expo user management system in 2025.
"""
SIGNUPS_FILE = "signups.json"
SIGNEDUP = "SIGNEDUP"
SIGNUP_YEAR = "2025"
SIGNUP_DATES = "30th June - 3rd August"
def signupok(request):
signup_user = request.user
return render(
request, "login/signupok.html",
{"year": SIGNUP_YEAR, "dates": SIGNUP_DATES, "signup_user": signup_user },
)
def signup(request):
current_user = request.user
"""Display and processes the applicant signup form for the forthcoming expo
The user must be logged-on as a personal login and that is
who is being sighned up. You can't signup someone else.
"""
signup_user = request.user
personal_login = True
if current_user.is_anonymous:
if signup_user.is_anonymous:
personal_login = False
elif current_user.username in ["expo", "expoadmin"]:
elif signup_user.username in ["expo", "expoadmin"]:
personal_login = False
else:
personal_login = True
if personal_login:
people = Person.objects.filter(user=signup_user)
if len(people) != 1:
# someone like "fluffy-bunny" not associated with a Person
return HttpResponseRedirect("/accounts/login/?next=/signup")
signup_person = people[0]
editor = f"{signup_person.fullname} <{signup_user.email}>"
if request.method == "POST": # If the form has been submitted...
pageform = ExpoSignupForm(request.POST) # A form bound to the POST data
if pageform.is_valid():
print(f"form OK")
who = pageform.cleaned_data["name"]
who = git_string(name)
print(f"{who=}")
clean = pageform.cleaned_data
print(f" # Signup form OK {clean['name']}")
save_signups(editor, signup_person.slug, clean)
return HttpResponseRedirect("/signupok")
else:
print(f" # Signup form INVALID\n{pageform.errors} ")
return render(
request,
"login/signup.html",
request, "login/signup.html",
{"form": pageform, "personal_login": personal_login,
"year": "2025", "dates": "30th June - 3rd August",
"year": SIGNUP_YEAR, "dates": SIGNUP_DATES,
}
)
else:
pageform = ExpoSignupForm(initial={"allergies":"None",
initial_context = {"allergies":"None",
"medication":"None",
"medic_info":"None",
"veggie": "mostly",
"student": "no",
"top_tent_cap": 2,
"base_tent_cap": 3,
})
}
if personal_login:
initial_context["name"] = signup_person.fullname
initial_context["email"] = signup_user.email
pageform = ExpoSignupForm(initial=initial_context)
return render(
request,
"login/signup.html",
{"form": pageform,
"year": "2025", "dates": "30th June - 3rd August",
request, "login/signup.html",
{"form": pageform, "personal_login": personal_login,
"year": SIGNUP_YEAR, "dates": SIGNUP_DATES,
},
)
def read_signups():
print(f" + READ signups")
f = get_encryptor()
signups_dir = settings.EXPOWEB / ENCRYPTED_DIR / current_expo()
if not signups_dir.is_dir():
signups_dir.mkdir(parents=True, exist_ok=True)
signupsfile = signups_dir / SIGNUPS_FILE
if not signupsfile.is_file():
return { SIGNEDUP: {} } # dict where e.g. {"philip-sargent": encrypted_form_data, more users etc.}
with open(signupsfile, 'r', encoding='utf-8') as json_f:
message = ""
try:
signups_single_dict = json.load(json_f)
except FileNotFoundError:
message = f"File {signupsfile} not found!"
except json.JSONDecodeError:
message = f"Invalid JSON format! - JSONDecodeError for {signupsfile}"
except Exception as e:
message = f"! Troggle USERs. Failed to load {signupsfile} JSON file. Exception <{e}>"
if message:
print(message)
DataIssue.objects.update_or_create(parser="_signups", message=message, url="") ###########################
return { SIGNEDUP: {} }
signups_dict = signups_single_dict[SIGNEDUP]
# print("ALL:",signups_dict)
signups_clear ={}
for su, content in signups_dict.items():
clear_text = f.decrypt(content).decode()
print(f"\n - C signups_dict {su} - {clear_text}")
signups_clear[su] = clear_text
return signups_clear
def save_signups(editor, person_slug, clean):
# print(f" + SAVE: Saving all signups")
f = get_encryptor()
signups_clear = read_signups()
# print(f" SAVE: {len(signups_clear)} signups read")
signups_clear[person_slug] = clean
signups_crypt = {}
for su in signups_clear:
# re-encrypt everything
signups_crypt[su] = f.encrypt(json.dumps(signups_clear[su]).encode("utf8")).decode()
print(f" SAVE after adding: {len(signups_crypt)} signups")
encryptedfile = settings.EXPOWEB / ENCRYPTED_DIR / current_expo() / SIGNUPS_FILE
try:
print(f"- Rewriting the entire encrypted set of signups to disc ")
write_signups(signups_crypt, encryptedfile, editor)
except:
message = f'! - Users encrypted data saving failed - \n!! Permissions failure ?! on attempting to save file "{encryptedfile}"'
print(message)
raise
return render(request, "errors/generic.html", {"message": message})
def write_signups(signups, encryptedfile, editor):
jsondict = { SIGNEDUP: signups }
try:
with open(encryptedfile, 'w', encoding='utf-8') as json_f:
json.dump(jsondict, json_f, indent=1)
except Exception as e:
print(f" ! Exception dumping json <{e}>")
raise
commit_msg = f"Online signup to next expo"
try:
add_commit(encryptedfile, commit_msg, editor)
except Exception as e:
print(f" ! Exception doing git add/commit <{e}>")
raise
return True
class ExpoSignupForm(forms.Form):
name = forms.CharField(label='Full name', max_length=100, widget=forms.TextInput(attrs={'tabindex': 1, 'placeholder': 'Anathema Device'}))
address = forms.CharField(widget=forms.Textarea(attrs={'rows': 7, 'cols': 20, 'tabindex': 2, 'placeholder': 'The Airfield,\nTadfield'}))
phone = forms.CharField(max_length=15, widget=forms.TextInput(attrs={'tabindex': 3, 'placeholder': '+44.1234567890'}))
email = forms.EmailField(widget=forms.TextInput(attrs={'tabindex': 4, 'placeholder': 'a.device@potatohut.expo'}))
kinname = forms.CharField(label='Next of Kin name', max_length=100, widget=forms.TextInput(attrs={'tabindex': 5, 'placeholder': 'Newton Pulsifer'}))
kinaddress = forms.CharField(widget=forms.Textarea(attrs={'rows': 7, 'cols': 20, 'tabindex': 6, 'placeholder': 'c/o The Old Ship Inn,\nLower Tadfield'}))
kinphone = forms.CharField(max_length=15, widget=forms.TextInput(attrs={'tabindex': 7, 'placeholder': '+44.0987654321'}))
kinemail = forms.EmailField(widget=forms.TextInput(attrs={'tabindex': 8, 'placeholder': 'n.pulsifer@basecamp.expo'}))
kin_name = forms.CharField(label='Next of Kin name', max_length=100, widget=forms.TextInput(attrs={'tabindex': 5, 'placeholder': 'Newton Pulsifer'}))
kin_address = forms.CharField(widget=forms.Textarea(attrs={'rows': 7, 'cols': 20, 'tabindex': 6, 'placeholder': 'c/o The Old Ship Inn,\nLower Tadfield'}))
kin_phone = forms.CharField(max_length=15, widget=forms.TextInput(attrs={'tabindex': 7, 'placeholder': '+44.0987654321'}))
kin_email = forms.EmailField(widget=forms.TextInput(attrs={'tabindex': 8, 'placeholder': 'n.pulsifer@basecamp.expo'}))
relation = forms.CharField(label='Relation to you', max_length=100, widget=forms.TextInput(attrs={'tabindex': 9, 'placeholder': 'Beau'}))
VEGGIE_CHOICES = [

@ -177,7 +177,6 @@ def load_people_expos():
print("", flush=True)
ensure_users_are_persons()
def ensure_users_are_persons():
# Just ensure this is up to date.
print(f"# ensure_users_are_persons()")

@ -75,6 +75,10 @@ Loser Expo 2025 SIGN-UP Form
</button>
{% endif %}
<hr>
<span style="color:red; font-family: monospace; font-weight: bold;">
{{ form.errors }} <br /> <!-- why are the field errors not being reported against the field as a red outline?? -->
{{ form.non_field_errors }} <!-- form validation errors appear here, and also at the top of the form-->
</span>
<form action="{% url 'signup' %}" method="post">
{% csrf_token %}
@ -89,25 +93,25 @@ Loser Expo 2025 SIGN-UP Form
<td align="right"><i>Full</i> name:</td>
<td>{{ form.name }}</td>
<td align="right">Name:</td>
<td>{{ form.kinname }}</td>
<td>{{ form.kin_name }}</td>
</tr>
<tr>
<td align="right">Address:</td>
<td>{{ form.address }}</td>
<td align="right">Address:</td>
<td>{{ form.kinaddress }}</td>
<td>{{ form.kin_address }}</td>
</tr>
<tr>
<td align="right">Phone:</td>
<td>{{ form.phone }}</td>
<td align="right">Phone:</td>
<td>{{ form.kinphone }}</td>
<td>{{ form.kin_phone }}</td>
</tr>
<tr>
<td align="right">Email:</td>
<td>{{ form.email }}</td>
<td align="right">Email:</td>
<td>{{ form.kinemail }}</td>
<td>{{ form.kin_email }}</td>
</tr>
<tr>
<td colspan="2"></td>

@ -0,0 +1,40 @@
<!-- frontpage.html - this text visible because this template has been included -->
{% extends "base.html" %}
{% block title %}Cambridge Expeditions to Austria{% endblock %}
{% block content %}
<title>Loser Expo {{year}} Sign-up Form Completed</title>
<h1>Expo {{year}} {{dates}}</h1>
<div id="col1">
<h3>You have signed up to attend the next Expo</h3>
<p class="indent">
You have signed up as {{signup_user.username}} - {{signup_user.email}}
<p class="indent">
Your signup has been registered. Please now register your email on the email announcements list if
you have not already done so:
<ul>
<li><a href="https://lists.wookware.org/cgi-bin/mailman/listinfo/expo">lists.wookware.org : expo</a>
</ul>
</p>
<p class="indent">
This is the beginning of becoming part of the team for Expo {{year}}. There are training events and
organisation to do, kit-lists to organise, and your dates on expo need to be agreed with Expo. We
particularly need adequate manpower to de-rig the caves and pack up at the end of Expo.
<p class="indent">
You next jobs to do: see the <a href="/kanboard/board/2">Expo Kanboard</a>.
<br />
<!-- <img src="/expofiles/photos/2007/selected/eishoehle5nial.jpg">-->
<figure align=center>
<img src="/expofiles/photos/2007/selected/eishoehle4nial.jpg" width="250px">
<figcaption>Nial in <a href="/expofiles/photos/2007/selected/">Eishohle in 2007</a>.</figcaption>
</figure>
{% endblock content %}
{% block margins %}
{% endblock margins %}

@ -57,7 +57,7 @@ from troggle.core.views.other import controlpanel, exportlogbook, frontpage, tod
from troggle.core.views.prospect import prospecting
from troggle.core.views.user_registration import register, newregister, reset_done, ExpoPasswordResetForm
from troggle.core.views.scans import allscans, cavewallets, scansingle, walletslistperson, walletslistyear
from troggle.core.views.signup import signup
from troggle.core.views.signup import signup, signupok
from troggle.core.views.uploads import dwgupload, expofilerename, gpxupload, photoupload
from troggle.core.views.wallets_edit import walletedit
@ -144,6 +144,7 @@ trogglepatterns = [
path('logbookedit/', logbookedit, name='logbookedit'),
path('logbookedit/<slug:slug>', logbookedit, name='logbookedit'),
path('signup', signup, name='signup'),
path('signupok', signupok, name='signupok'),
# Renaming an uploaded file
path('expofilerename/<path:filepath>', expofilerename, name='expofilerename'),