mirror of
https://codeberg.org/angestoepselt/homepage.git
synced 2025-05-24 14:46:16 +00:00
Revert "Merge pull request 'Diverse Formular-Verbessungen' (#155) from form-tweaks into main"
Das Formular funktioniert nicht mehr sauber This reverts commit74cb447ac9, reversing changes made tof36e4d1548.
This commit is contained in:
parent
74cb447ac9
commit
a054bc1b07
1 changed files with 20 additions and 75 deletions
|
|
@ -1,17 +1,17 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import base64
|
import base64
|
||||||
|
import io
|
||||||
import cgi
|
import cgi
|
||||||
import collections
|
import collections
|
||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
import hmac
|
import hmac
|
||||||
import json
|
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import os
|
|
||||||
import re
|
import re
|
||||||
|
import os
|
||||||
import secrets
|
import secrets
|
||||||
|
import json
|
||||||
from typing import Any, Optional, overload
|
from typing import Any, Optional, overload
|
||||||
from urllib.parse import urljoin
|
|
||||||
|
|
||||||
import itsdangerous
|
import itsdangerous
|
||||||
import requests
|
import requests
|
||||||
|
|
@ -34,20 +34,6 @@ except IOError:
|
||||||
|
|
||||||
|
|
||||||
HONEYPOT_FIELD_NAME = "addressline1"
|
HONEYPOT_FIELD_NAME = "addressline1"
|
||||||
# This regex merely validates what the in-browser form validation already checks and
|
|
||||||
# isn't all too strict.
|
|
||||||
EMAIL_REGEX = re.compile(r"^[^ ]+@[^ ]+\.[^ ]+$")
|
|
||||||
|
|
||||||
# Mapping from site-defined devices (see sites/angestoepselt/_data/config.json in this
|
|
||||||
# repository) to the corresponding Zammad categories:
|
|
||||||
# https://codeberg.org/angestoepselt/homepage/issues/120#issuecomment-1727768
|
|
||||||
# This is a (str | int) -> str map because some keys we check against below might be
|
|
||||||
# integers and it's just easier to type this way.
|
|
||||||
FORM_CATEGORY_MAP: dict[str | int, str] = {
|
|
||||||
"Laptop": "laptop",
|
|
||||||
"Laptop ohne Akku": "laptop-battery-missing",
|
|
||||||
"Desktop-Computer": "desktop",
|
|
||||||
}
|
|
||||||
|
|
||||||
SITE_DIRECTORY = os.environ.get("SITE_DIRECTORY", "")
|
SITE_DIRECTORY = os.environ.get("SITE_DIRECTORY", "")
|
||||||
request_uri = os.environ.get("REQUEST_URI", "").lower().rstrip("/")
|
request_uri = os.environ.get("REQUEST_URI", "").lower().rstrip("/")
|
||||||
|
|
@ -142,16 +128,15 @@ if form_disabled:
|
||||||
form = cgi.FieldStorage()
|
form = cgi.FieldStorage()
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def get_form_value(name: str, default: None = ..., *, cast: type[bytes]) -> tuple[str, bytes]:...
|
def get_form_value(name: str, default: Optional[str], cast: type[str] = str) -> str: ...
|
||||||
@overload
|
@overload
|
||||||
def get_form_value(name: str, default: Optional[int] = ..., *, cast: type[int]) -> int:...
|
def get_form_value(name: str, default: Optional[int], cast: type[int]) -> int:...
|
||||||
@overload
|
@overload
|
||||||
def get_form_value(name: str, default: Optional[str] = ..., *, cast: type[str] = ...) -> str: ...
|
def get_form_value(name: str, default: None = ..., cast: type[bytes] = ...) -> tuple[str, bytes]:...
|
||||||
def get_form_value(
|
def get_form_value(
|
||||||
name: str,
|
name: str,
|
||||||
default: Any = None,
|
default: Any = None,
|
||||||
*,
|
cast: type[str] | type[int] | type[io.BytesIO] = str,
|
||||||
cast: type[str] | type[int] | type[bytes] = str,
|
|
||||||
) -> Any:
|
) -> Any:
|
||||||
if name not in form:
|
if name not in form:
|
||||||
if default is None:
|
if default is None:
|
||||||
|
|
@ -169,10 +154,7 @@ def get_form_value(
|
||||||
return (value_object.filename or "upload"), value_object.file.read()
|
return (value_object.filename or "upload"), value_object.file.read()
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
result = cast(form.getfirst(name))
|
return cast(form.getfirst(name))
|
||||||
if isinstance(result, str):
|
|
||||||
result = result.strip()
|
|
||||||
return result
|
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
fail("400 Bad Request", f"Invalid value for field: {name}")
|
fail("400 Bad Request", f"Invalid value for field: {name}")
|
||||||
|
|
||||||
|
|
@ -181,7 +163,7 @@ def get_form_value(
|
||||||
# constant-time string comparison here.
|
# constant-time string comparison here.
|
||||||
given_csrf_token = get_form_value("csrftoken")
|
given_csrf_token = get_form_value("csrftoken")
|
||||||
if not hmac.compare_digest(csrf_token, given_csrf_token):
|
if not hmac.compare_digest(csrf_token, given_csrf_token):
|
||||||
fail("400 Bad Request", "Invalid CSRF token")
|
fail("400 Bad Request", f"Invalid CSRF token")
|
||||||
|
|
||||||
|
|
||||||
# If the honeypot field was not empty, back off.
|
# If the honeypot field was not empty, back off.
|
||||||
|
|
@ -201,21 +183,15 @@ if not isinstance(hcaptcha_data, Mapping) or not hcaptcha_data.get("success", Fa
|
||||||
# Extract all the actually provided form data. This is different from form to
|
# Extract all the actually provided form data. This is different from form to
|
||||||
# form (see the match block below).
|
# form (see the match block below).
|
||||||
contact_name = get_form_value("contactname")
|
contact_name = get_form_value("contactname")
|
||||||
contact_names = contact_name.split(" ")
|
|
||||||
|
|
||||||
contact_email = get_form_value("contactemail")
|
contact_email = get_form_value("contactemail")
|
||||||
if not EMAIL_REGEX.fullmatch(contact_email):
|
|
||||||
fail("400 Bad Request", "Invalid Email address")
|
|
||||||
|
|
||||||
message = get_form_value("message", "[Keine Nachricht hinterlassen]")
|
message = get_form_value("message", "[Keine Nachricht hinterlassen]")
|
||||||
attachment: Optional[tuple[str, bytes]] = None
|
attachment: Optional[tuple[str, bytes]] = None
|
||||||
|
|
||||||
ticket_details = collections.OrderedDict[str, str | int]()
|
ticket_details = collections.OrderedDict()
|
||||||
ticket_details["Kontaktperson"] = contact_name
|
ticket_details["Kontaktperson"] = contact_name
|
||||||
ticket_details["Email"] = contact_email
|
ticket_details["Email"] = contact_email
|
||||||
|
|
||||||
form_group = "csw-Allgemein"
|
form_group = "csw-Allgemein"
|
||||||
form_category: str | None = None
|
|
||||||
|
|
||||||
match request_uri:
|
match request_uri:
|
||||||
case "/kontakt":
|
case "/kontakt":
|
||||||
|
|
@ -237,17 +213,14 @@ match request_uri:
|
||||||
ticket_details["Adresse"] = get_form_value("addressline")
|
ticket_details["Adresse"] = get_form_value("addressline")
|
||||||
ticket_details["PLZ"] = get_form_value("postalcode")
|
ticket_details["PLZ"] = get_form_value("postalcode")
|
||||||
ticket_details["Stadt"] = get_form_value("city")
|
ticket_details["Stadt"] = get_form_value("city")
|
||||||
ticket_details["Anzahl Desktops"] = get_form_value("desktopcount", 0, cast=int)
|
ticket_details["Anzahl Desktops"] = get_form_value("desktopcount", 0, int)
|
||||||
ticket_details["Anzahl Laptops"] = get_form_value("laptopcount", 0, cast=int)
|
ticket_details["Anzahl Laptops"] = get_form_value("laptopcount", 0, int)
|
||||||
ticket_details["Anzahl Drucker"] = get_form_value("printercount", 0, cast=int)
|
ticket_details["Anzahl Drucker"] = get_form_value("printercount", 0, int)
|
||||||
|
|
||||||
case "/computer-beantragen/privat":
|
case "/computer-beantragen/privat":
|
||||||
form_name = "Computerantrag (privat)"
|
form_name = "Computerantrag (privat)"
|
||||||
form_group = "csw-Anfragen"
|
form_group = "csw-Anfragen"
|
||||||
|
|
||||||
ticket_details["Gewünschte Hardware"] = get_form_value("hardware", default="Unbekannt")
|
ticket_details["Gewünschte Hardware"] = get_form_value("hardware", default="Unbekannt")
|
||||||
form_category = FORM_CATEGORY_MAP.get(ticket_details["Gewünschte Hardware"], None)
|
|
||||||
|
|
||||||
ticket_details["Adresse"] = get_form_value("addressline")
|
ticket_details["Adresse"] = get_form_value("addressline")
|
||||||
ticket_details["PLZ"] = get_form_value("postalcode")
|
ticket_details["PLZ"] = get_form_value("postalcode")
|
||||||
ticket_details["Stadt"] = get_form_value("city")
|
ticket_details["Stadt"] = get_form_value("city")
|
||||||
|
|
@ -276,13 +249,13 @@ match request_uri:
|
||||||
ticket_details["Teilnehmenden-Name"] = get_form_value("participantname", "-")
|
ticket_details["Teilnehmenden-Name"] = get_form_value("participantname", "-")
|
||||||
ticket_details["Telefonnummer"] = get_form_value("contactphone", "-")
|
ticket_details["Telefonnummer"] = get_form_value("contactphone", "-")
|
||||||
ticket_details["Fotos?"] = get_form_value("photos")
|
ticket_details["Fotos?"] = get_form_value("photos")
|
||||||
|
|
||||||
case "/party":
|
case "/party":
|
||||||
form_name = "CoderDojo Minecraft LAN"
|
form_name = "CoderDojo Minecraft LAN"
|
||||||
form_group = "CoderDojo"
|
form_group = "CoderDojo"
|
||||||
ticket_details["Java-Spielername"] = get_form_value("javaname", "")
|
ticket_details["Java-Spielername"] = get_form_value("javaname", "")
|
||||||
ticket_details["Bedrock-Spielername"] = get_form_value("bedrockname", "")
|
ticket_details["Bedrock-Spielername"] = get_form_value("bedrockname", "")
|
||||||
|
|
||||||
case "/freizeit":
|
case "/freizeit":
|
||||||
form_name = "CoderCamp Umfrage"
|
form_name = "CoderCamp Umfrage"
|
||||||
form_group = "CoderDojo"
|
form_group = "CoderDojo"
|
||||||
|
|
@ -303,46 +276,18 @@ ticket_details["Kontaktformular"] = form_name
|
||||||
# testing).
|
# testing).
|
||||||
form_group = os.environ.get("ZAMMAD_GROUP", "") or form_group
|
form_group = os.environ.get("ZAMMAD_GROUP", "") or form_group
|
||||||
|
|
||||||
ZAMMAD_URL = os.environ.get("ZAMMAD_URL", "")
|
ZAMMAD_URL = os.environ.get("ZAMMAD_URL", "").rstrip("/")
|
||||||
ZAMMAD_TOKEN = os.environ.get("ZAMMAD_TOKEN", "")
|
ZAMMAD_TOKEN = os.environ.get("ZAMMAD_TOKEN", "")
|
||||||
session.headers.update(Authorization=f"Token token={ZAMMAD_TOKEN}")
|
session.headers.update(Authorization=f"Token token={ZAMMAD_TOKEN}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Create a new user for the client. For some reason, using "guess:{email}" as the
|
|
||||||
# customer_id when creating the ticket doesn't really work, as described in the
|
|
||||||
# Zammad documentation [1]. Instead, we sometimes need to explictily create the
|
|
||||||
# user beforehand. See this discussion [2] for more details.
|
|
||||||
# [1]: https://docs.zammad.org/en/latest/api/ticket/index.html#create
|
|
||||||
# [2]: https://codeberg.org/angestoepselt/homepage/issues/141
|
|
||||||
response = session.post(
|
|
||||||
urljoin(ZAMMAD_URL, "api/v1/users"),
|
|
||||||
json=dict(
|
|
||||||
# Yes, yes... This goes against pretty much all best practices for parsing
|
|
||||||
# names. But: it's only internal and we save the name verbatim again below
|
|
||||||
# so we're going to go ahead and do it anyway.
|
|
||||||
firstname=" ".join(contact_names[:-1]) if len(contact_names) >= 2 else "?",
|
|
||||||
lastname=contact_names[-1],
|
|
||||||
email=contact_email,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if response.status_code == 422:
|
|
||||||
# This email address is already in use by another user.
|
|
||||||
customer_id = f"guess:{contact_email}"
|
|
||||||
else:
|
|
||||||
response.raise_for_status()
|
|
||||||
customer_id = response.json()["id"]
|
|
||||||
assert isinstance(customer_id, (str, int))
|
|
||||||
|
|
||||||
# Add the actual ticket to the system.
|
# Add the actual ticket to the system.
|
||||||
response = session.post(
|
response = session.post(
|
||||||
urljoin(ZAMMAD_URL, "api/v1/tickets"),
|
f"{ZAMMAD_URL}/api/v1/tickets",
|
||||||
headers={
|
|
||||||
"X-On-Behalf-Of": contact_email,
|
|
||||||
},
|
|
||||||
json=dict(
|
json=dict(
|
||||||
title=f"Kontaktformular {contact_name} – {form_name}",
|
title=f"Kontaktformular {contact_name} – {form_name}",
|
||||||
group=form_group,
|
group=form_group,
|
||||||
customer_id=customer_id,
|
customer_id=f"guess:{contact_email}",
|
||||||
article=dict(
|
article=dict(
|
||||||
type="web",
|
type="web",
|
||||||
internal=True,
|
internal=True,
|
||||||
|
|
@ -364,7 +309,7 @@ try:
|
||||||
# Add a second article to the ticket that contains all the other information
|
# Add a second article to the ticket that contains all the other information
|
||||||
# from the contact form.
|
# from the contact form.
|
||||||
response = session.post(
|
response = session.post(
|
||||||
urljoin(ZAMMAD_URL, "api/v1/ticket_articles"),
|
f"{ZAMMAD_URL}/api/v1/ticket_articles",
|
||||||
json=dict(
|
json=dict(
|
||||||
ticket_id=ticket_id,
|
ticket_id=ticket_id,
|
||||||
type="note",
|
type="note",
|
||||||
|
|
@ -379,7 +324,7 @@ try:
|
||||||
|
|
||||||
# Add a tag to the ticket, denoting which contact form it came from.
|
# Add a tag to the ticket, denoting which contact form it came from.
|
||||||
response = session.post(
|
response = session.post(
|
||||||
urljoin(ZAMMAD_URL, "api/v1/tags/add"),
|
f"{ZAMMAD_URL}/api/v1/tags/add",
|
||||||
json=dict(
|
json=dict(
|
||||||
object="Ticket",
|
object="Ticket",
|
||||||
o_id=ticket_id,
|
o_id=ticket_id,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue