import os import re import smtplib from email.mime.text import MIMEText from xml.etree.ElementTree import Element, SubElement, tostring from fastapi import FastAPI, Form, HTTPException, Request, status from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates app = FastAPI() templates = Jinja2Templates(directory="app/templates") # Serve static files (CSS, images) app.mount("/static", StaticFiles(directory="app/static"), name="static") # add somewhere near the top-level constants EMAIL_REGEX = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$") # SMTP / email configuration via environment MAIL_ENABLED = os.getenv("MAIL_ENABLED", "false").lower() == "true" SMTP_HOST = os.getenv("SMTP_HOST", "smtp") SMTP_PORT = int(os.getenv("SMTP_PORT", "25")) MAIL_FROM = os.getenv("MAIL_FROM", "noreply@example.com") MAIL_TO = os.getenv("MAIL_TO", "destination@example.com") @app.get("/", response_class=HTMLResponse) async def landing_page(request: Request): """Landing page with service selection""" return templates.TemplateResponse("index.html", {"request": request}) @app.get("/semesterapparat", response_class=HTMLResponse) async def semesterapparat_form(request: Request): """Semesterapparat form page""" return templates.TemplateResponse("semesterapparat_form.html", {"request": request}) @app.get("/elsa", response_class=HTMLResponse) async def elsa_form(request: Request): """ELSA form page""" return templates.TemplateResponse("elsa_mono_form.html", {"request": request}) @app.post("/submit") async def handle_form( request: Request, name: str = Form(...), lastname: str = Form(...), title: str = Form(...), telno: str = Form(...), mail: str = Form(...), apparatsname: str = Form(...), subject: str = Form(...), semester_type: str = Form(...), # "summer" or "winter" semester_year: str = Form(...), authorname: list[str] = Form(...), year: list[str] = Form(...), booktitle: list[str] = Form(...), signature: list[str] = Form(...), message: str = Form(default=""), ): # Basic email validation (server-side) if not EMAIL_REGEX.match(mail): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid email address format.", ) # Build XML root = Element("form_submission") static_data = SubElement(root, "static") SubElement(static_data, "name").text = name SubElement(static_data, "lastname").text = lastname SubElement(static_data, "title").text = title SubElement(static_data, "telno").text = telno SubElement(static_data, "mail").text = mail SubElement(static_data, "apparatsname").text = apparatsname SubElement(static_data, "subject").text = subject SubElement(static_data, "semester").text = f"{semester_type} {semester_year}" if message: SubElement(static_data, "message").text = message books = SubElement(root, "books") for i in range(len(authorname)): book = SubElement(books, "book") SubElement(book, "authorname").text = authorname[i] SubElement(book, "year").text = year[i] SubElement(book, "title").text = booktitle[i] SubElement(book, "signature").text = signature[i] xml_data = tostring(root, encoding="unicode") # Send mail msg = MIMEText(xml_data, "xml") msg["Subject"] = "New Form Submission" msg["From"] = MAIL_FROM msg["To"] = MAIL_TO if MAIL_ENABLED: with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as server: server.send_message(msg) else: print("=" * 80) print("MAIL SENDING DISABLED - Would have sent:") print(f"From: {MAIL_FROM}") print(f"To: {MAIL_TO}") print(f"Subject: {msg['Subject']}") print("-" * 80) print(xml_data) print("=" * 80) return RedirectResponse("/?success=true", status_code=303) @app.post("/elsa/submit") async def handle_elsa_form(request: Request): """Handle ELSA form submission with multiple media types""" form_data = await request.form() # Extract general information name = str(form_data.get("name", "")) lastname = str(form_data.get("lastname", "")) title_field = str(form_data.get("title", "")) mail = str(form_data.get("mail", "")) subject = str(form_data.get("subject", "")) classname = str(form_data.get("classname", "")) usage_date_from = str(form_data.get("usage_date_from", "")) usage_date_to = str(form_data.get("usage_date_to", "")) availability_date = str(form_data.get("availability_date", "")) message = str(form_data.get("message", "")) # Basic email validation if not EMAIL_REGEX.match(mail): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid email address format.", ) # Build XML structure root = Element("elsa_submission") # General information general_info = SubElement(root, "general_info") SubElement(general_info, "name").text = name SubElement(general_info, "lastname").text = lastname SubElement(general_info, "title").text = title_field SubElement(general_info, "mail").text = mail SubElement(general_info, "subject").text = subject SubElement(general_info, "classname").text = classname SubElement(general_info, "usage_date_from").text = usage_date_from SubElement(general_info, "usage_date_to").text = usage_date_to SubElement(general_info, "availability_date").text = availability_date if message: SubElement(general_info, "message").text = message # Process media sections media_root = SubElement(root, "media") # Process Monografie entries if "monografie_author[]" in form_data: monografie_authors = [str(v) for v in form_data.getlist("monografie_author[]")] monografie_years = [str(v) for v in form_data.getlist("monografie_year[]")] monografie_editions = [ str(v) for v in form_data.getlist("monografie_edition[]") ] monografie_titles = [str(v) for v in form_data.getlist("monografie_title[]")] monografie_signatures = [ str(v) for v in form_data.getlist("monografie_signature[]") ] monografie_pages_from = [ str(v) for v in form_data.getlist("monografie_pages_from[]") ] monografie_pages_to = [ str(v) for v in form_data.getlist("monografie_pages_to[]") ] # Get section IDs from the form (assuming they're in data-section attributes) # Since we can't directly access data-section from form_data, we'll process sequentially monografie_section = SubElement(media_root, "monografien") for i in range(len(monografie_authors)): entry = SubElement(monografie_section, "entry") SubElement(entry, "author").text = ( monografie_authors[i] if i < len(monografie_authors) else "" ) SubElement(entry, "year").text = ( monografie_years[i] if i < len(monografie_years) else "" ) SubElement(entry, "edition").text = ( monografie_editions[i] if i < len(monografie_editions) else "" ) SubElement(entry, "title").text = ( monografie_titles[i] if i < len(monografie_titles) else "" ) SubElement(entry, "signature").text = ( monografie_signatures[i] if i < len(monografie_signatures) else "" ) SubElement(entry, "pages_from").text = ( monografie_pages_from[i] if i < len(monografie_pages_from) else "" ) SubElement(entry, "pages_to").text = ( monografie_pages_to[i] if i < len(monografie_pages_to) else "" ) # Process Zeitschriftenartikel entries if "zeitschrift_author[]" in form_data: zeitschrift_authors = [ str(v) for v in form_data.getlist("zeitschrift_author[]") ] zeitschrift_years = [str(v) for v in form_data.getlist("zeitschrift_year[]")] zeitschrift_volumes = [ str(v) for v in form_data.getlist("zeitschrift_volume[]") ] zeitschrift_article_titles = [ str(v) for v in form_data.getlist("zeitschrift_article_title[]") ] zeitschrift_journal_titles = [ str(v) for v in form_data.getlist("zeitschrift_journal_title[]") ] zeitschrift_signatures = [ str(v) for v in form_data.getlist("zeitschrift_signature[]") ] zeitschrift_pages_from = [ str(v) for v in form_data.getlist("zeitschrift_pages_from[]") ] zeitschrift_pages_to = [ str(v) for v in form_data.getlist("zeitschrift_pages_to[]") ] zeitschrift_section = SubElement(media_root, "zeitschriftenartikel") for i in range(len(zeitschrift_authors)): entry = SubElement(zeitschrift_section, "entry") SubElement(entry, "author").text = ( zeitschrift_authors[i] if i < len(zeitschrift_authors) else "" ) SubElement(entry, "year").text = ( zeitschrift_years[i] if i < len(zeitschrift_years) else "" ) SubElement(entry, "volume").text = ( zeitschrift_volumes[i] if i < len(zeitschrift_volumes) else "" ) SubElement(entry, "article_title").text = ( zeitschrift_article_titles[i] if i < len(zeitschrift_article_titles) else "" ) SubElement(entry, "journal_title").text = ( zeitschrift_journal_titles[i] if i < len(zeitschrift_journal_titles) else "" ) SubElement(entry, "signature").text = ( zeitschrift_signatures[i] if i < len(zeitschrift_signatures) else "" ) SubElement(entry, "pages_from").text = ( zeitschrift_pages_from[i] if i < len(zeitschrift_pages_from) else "" ) SubElement(entry, "pages_to").text = ( zeitschrift_pages_to[i] if i < len(zeitschrift_pages_to) else "" ) # Process Herausgeberwerk entries if "herausgeber_publisher[]" in form_data: herausgeber_publishers = [ str(v) for v in form_data.getlist("herausgeber_publisher[]") ] herausgeber_work_titles = [ str(v) for v in form_data.getlist("herausgeber_work_title[]") ] herausgeber_years = [str(v) for v in form_data.getlist("herausgeber_year[]")] herausgeber_editions = [ str(v) for v in form_data.getlist("herausgeber_edition[]") ] herausgeber_article_authors = [ str(v) for v in form_data.getlist("herausgeber_article_author[]") ] herausgeber_article_titles = [ str(v) for v in form_data.getlist("herausgeber_article_title[]") ] herausgeber_signatures = [ str(v) for v in form_data.getlist("herausgeber_signature[]") ] herausgeber_pages_from = [ str(v) for v in form_data.getlist("herausgeber_pages_from[]") ] herausgeber_pages_to = [ str(v) for v in form_data.getlist("herausgeber_pages_to[]") ] herausgeber_section = SubElement(media_root, "herausgeberwerke") for i in range(len(herausgeber_publishers)): entry = SubElement(herausgeber_section, "entry") SubElement(entry, "publisher").text = ( herausgeber_publishers[i] if i < len(herausgeber_publishers) else "" ) SubElement(entry, "work_title").text = ( herausgeber_work_titles[i] if i < len(herausgeber_work_titles) else "" ) SubElement(entry, "year").text = ( herausgeber_years[i] if i < len(herausgeber_years) else "" ) SubElement(entry, "edition").text = ( herausgeber_editions[i] if i < len(herausgeber_editions) else "" ) SubElement(entry, "article_author").text = ( herausgeber_article_authors[i] if i < len(herausgeber_article_authors) else "" ) SubElement(entry, "article_title").text = ( herausgeber_article_titles[i] if i < len(herausgeber_article_titles) else "" ) SubElement(entry, "signature").text = ( herausgeber_signatures[i] if i < len(herausgeber_signatures) else "" ) SubElement(entry, "pages_from").text = ( herausgeber_pages_from[i] if i < len(herausgeber_pages_from) else "" ) SubElement(entry, "pages_to").text = ( herausgeber_pages_to[i] if i < len(herausgeber_pages_to) else "" ) xml_data = tostring(root, encoding="unicode") # Send or print email msg = MIMEText(xml_data, "xml") msg["Subject"] = "New ELSA Form Submission" msg["From"] = MAIL_FROM msg["To"] = MAIL_TO if MAIL_ENABLED: with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as server: server.send_message(msg) else: print("=" * 80) print("MAIL SENDING DISABLED - Would have sent:") print(f"From: {MAIL_FROM}") print(f"To: {MAIL_TO}") print(f"Subject: {msg['Subject']}") print("-" * 80) print(xml_data) print("=" * 80) return RedirectResponse("/?success=true", status_code=303)