rewrote logic in newsletter scripts, refactored code, moved file locations,

This commit is contained in:
eclipse 2025-03-03 12:11:49 +01:00
parent 973d4b0345
commit d9b8177038
11 changed files with 187 additions and 154 deletions

View File

@ -1,7 +1,7 @@
title: Termine
author: Tobias Radloff
summary: Lesungen und Veranstaltungen
template: termine.noformat
template: termine
lang: de
category: Termine
slug: termine

View File

@ -5,62 +5,51 @@ require(dirname(__FILE__) . '/settings.php');
$successURL = '/newsletter/confirmed.html';
$errorURL = '/newsletter/confirm-error.html';
$err = 'Bestätigung fehlgeschlagen';
// Adds new subscriber to database. Returns an error message on failure, TRUE on success.
function AddSubscriberToDB($subscriberAddress, $subscriberName = NULL) {
global $general;
try {
$pdo = getPDO();
// create table if it doesn't exist already
$query = $pdo->prepare($general['sql']['create_table']);
if ( ! $query->execute() ) {
error_log('Datenbankfehler beim Prüfen/Erzeugen der Tabelle.');
return "Ein Fehler ist aufgetreten.";
}
// check if record exists
$query = $pdo->prepare($general['sql']['read_record']);
if ( ! $query->execute([':e' => $subscriberAddress]) ) {
error_log("Datenbankfehler: Adresscheck für Emailadresse {$subscriberAddress} ergab einen Fehler.");
return 'Datenbankfehler.';
}
if ( $query->fetch() ) {
error_log("Datenbankfehler: Emailadresse {$subscriberAddress} ist bereits eingetragen.");
return "Emailadresse ist bereits eingetragen.";
}
$pdo = getPDO();
// create record
$query = $pdo->prepare($general['sql']['create_record']);
if ( ( ! $query->execute([':e' => $subscriberAddress, ':n' => $subscriberName]) ) or ( $query->fetch() ) ) {
error_log("Datenbankfehler: Einfügen von Emailadresse {$subscriberAddress} ergab einen Fehler.");
return 'Datenbankfehler.';
}
} catch(\PDOException $e) {
error_log("Datenbankfehler: {$e->getMessage()}");
return $e->getMessage();
// check if record exists
$check = NotAlreadySubscribed($subscriberAddress, $pdo);
if ( gettype($check) == 'string' ) {
return $check;
}
// create record
global $general;
$query = $pdo->prepare($general['sql']['create_record']);
if ( ( ! $query->execute([':e' => $subscriberAddress, ':n' => $subscriberName]) ) or ( $query->fetch() ) ) {
// error_log("Datenbankfehler: Einfügen von Emailadresse {$subscriberAddress} ergab einen Fehler.");
return 'Fehler beim Eintragen in die Datenbank';
}
return TRUE;
}
if (isset($_GET['c']) && isset($_GET['e'])) {
$c = filter_var($_GET['c'], FILTER_SANITIZE_STRING);
$e = filter_var($_GET['e'], FILTER_SANITIZE_STRING);
if ( GetConfirmationHash($e) === $c ) {
$result = AddSubscriberToDB($e);
if ($result === TRUE) {
GracefulExit($successURL, 'Bestätigung erfolgt: Newsletter-Anmeldung bestätigt');
} elseif (gettype($result == 'string')) {
GracefulExit($errorURL, "Bestätigung fehlgeschlagen: {$result}");
} else {
GracefulExit($errorURL, 'Bestätigung fehlgeschlagen: Unbekannter Fehler');
}
} else {
GracefulExit($errorURL, 'Bestätigung fehlgeschlagen: Fehlerhafter Hash');
}
} else {
GracefulExit($errorURL, 'Bestätigung fehlgeschlagen: Fehlende Emailadresse oder Hash');
// check if hash and email parameters are both set
if ( ! (isset($_GET['c']) and isset($_GET['e'])) ) {
GracefulExit($errorURL, "{$err}: Fehlende Emailadresse oder Hash");
}
// check if hash is correct
$c = filter_var($_GET['c'], FILTER_SANITIZE_STRING);
$e = filter_var($_GET['e'], FILTER_SANITIZE_STRING);
if ( GetConfirmationHash($e) != $c ) {
GracefulExit($errorURL, "{$err}: Fehlerhafter Hash");
}
// add email to database
try {
$result = AddSubscriberToDB($e);
if ( gettype($result) == 'string' ) {
GracefulExit($errorURL, "{$err}: {$result}");
}
} catch(\PDOException $e) {
GracefulExit($errorURL, "{$err}: {$e->getMessage()}");
}
// success
GracefulExit($successURL, 'Bestätigung erfolgt: Newsletter-Anmeldung bestätigt');
?>

View File

@ -5,9 +5,9 @@ use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
$dname = dirname(__FILE__);
require $dname . '/Exception.php';
require $dname . '/PHPMailer.php';
require $dname . '/SMTP.php';
require $dname . '/../Exception.php';
require $dname . '/../PHPMailer.php';
require $dname . '/../SMTP.php';
// general constants
$general = [
@ -17,7 +17,7 @@ $general = [
// string concatenated with email address to create a non-recreatable md5 hash
'uniqueKey' => '***REMOVED***', // works like password salt
// file name of confirm script
'confirmScript' => '/confirm.php',
'confirmScript' => '/newsletter/confirm.php',
// status code to be used when redirection to success or error page
'statusCode' => 'HTTP/1.1 303 See Other',
// array of SQL statements used
@ -61,7 +61,7 @@ $mailConfirmation = [
// database information
$db = [
'sqlite' => [
'dsn' => 'sqlite:../newsletter.sqlite',
'dsn' => 'sqlite:../../newsletter.sqlite',
],
'mysql' => [
'dsn' => '',
@ -74,7 +74,7 @@ $db = [
function GetConfirmationHash($confEmail) {
global $general;
return md5($confEmail . $general['uniqueKey'] );
return md5($confEmail . $general['uniqueKey']);
}
// connects to database and returns PDO object
@ -83,47 +83,79 @@ function getPDO($dbType = 'sqlite') {
return new \PDO($db[$dbType]['dsn']);
}
// Sends an email to single recipient with subject and body specified in an array. Returns an error message on failure, TRUE on success.
// Sends an email to single recipient with subject and body specified in an array
function SendEmail($recipientAddress, $mailContents, $link = NULL) {
global $general, $smtp;
$mail = new PHPMailer(true);
try {
//Server settings
// $mail->SMTPDebug = SMTP::DEBUG_SERVER; //Enable verbose debug output
$mail->isSMTP();
$mail->Host = $smtp["host"];
$mail->SMTPAuth = $smtp["auth"];
$mail->Username = $smtp["username"];
$mail->Password = $smtp["password"];
//$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS; //Enable implicit TLS encryption
$mail->Port = $smtp["port"]; //TCP port to connect to; use 587 if you have set `SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS`
//Server settings
// $mail->SMTPDebug = SMTP::DEBUG_SERVER; //Enable verbose debug output
$mail->isSMTP();
$mail->Host = $smtp["host"];
$mail->SMTPAuth = $smtp["auth"];
$mail->Username = $smtp["username"];
$mail->Password = $smtp["password"];
//$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS; //Enable implicit TLS encryption
$mail->Port = $smtp["port"]; //TCP port to connect to; use 587 if you have set `SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS`
//Recipients
$mail->setFrom($smtp["fromAddress"], $smtp["fromName"]);
$mail->addAddress($recipientAddress); //Add a recipient
//Recipients
$mail->setFrom($smtp["fromAddress"], $smtp["fromName"]);
$mail->addAddress($recipientAddress); //Add a recipient
//Content
if (isset($link)) {
$mailContents["bodyHTML"] = str_replace("%Placeholder%", $link, $mailContents["bodyHTML"]);
$mailContents["bodyText"] = str_replace("%Placeholder%", $link, $mailContents["bodyText"]);
}
$mail->CharSet = "UTF-8";
$mail->isHTML(true);
$mail->Subject = $mailContents["subject"];
$mail->Body = $mailContents["bodyHTML"];
$mail->AltBody = $mailContents["bodyText"];
$mail->send();
return TRUE;
} catch (Exception $e) {
error_log("Message error: {$e}");
return FALSE;
//Content
if (isset($link)) {
$mailContents["bodyHTML"] = str_replace("%Placeholder%", $link, $mailContents["bodyHTML"]);
$mailContents["bodyText"] = str_replace("%Placeholder%", $link, $mailContents["bodyText"]);
}
$mail->CharSet = "UTF-8";
$mail->isHTML(true);
$mail->Subject = $mailContents["subject"];
$mail->Body = $mailContents["bodyHTML"];
$mail->AltBody = $mailContents["bodyText"];
$mail->send();
}
// redirects to specified URL via GET request and conveys an optional message
function MakeSureTableExists($pdo) {
global $general;
$query = $pdo->prepare($general['sql']['create_table']);
if ( ! $query->execute() ) {
// error_log('Unbekannter Datenbankfehler beim Prüfen/Erzeugen der Tabelle.');
return "Unbekannter Datenbankfehler";
}
return TRUE;
}
// returns true if record does not yet exist in database; error string otherwise
function NotAlreadySubscribed($email, $pdo = NULL) {
if (!isset($pdo)) {
$pdo = getPDO();
}
$result = MakeSureTableExists($pdo);
if ( gettype($result) == "string" ) {
// error_log("Datenbankfehler beim Adresscheck: {$result}");
return $result;
}
global $general;
$query = $pdo->prepare($general['sql']['read_record']);
if ( ! $query->execute([':e' => $email]) ) {
// error_log("Datenbankfehler: Adresscheck für Emailadresse {$email} ergab einen Fehler.");
return "Fehler beim Zugriff auf Datenbank";
}
if ( $query->fetch() ) {
// error_log("Adresscheck: Emailadresse {$email} ist bereits eingetragen.");
return "Emailadresse {$email} ist bereits eingetragen";
}
// success
return TRUE;
}
// redirects to specified URL via GET request and conveys an optional message; then exits
function GracefulExit($location, $message = NULL) {
global $general;
header($general['statusCode']);
@ -132,5 +164,6 @@ function GracefulExit($location, $message = NULL) {
error_log($location);
}
header("Location: {$location}");
exit;
}
?>

View File

@ -1,28 +1,42 @@
<?php
// inspired by https://www.mailgun.com/blog/email/double-opt-in-with-php-mailgun/
$successURL = '/newsletter/subscribed.html';
$errorURL = '/newsletter/subscribe-error.html';
require(dirname(__FILE__) . '/settings.php');
if (isset($_POST['email'])) {
$email = filter_var(trim($_POST['email'], FILTER_SANITIZE_STRING));
$successURL = '/newsletter/subscribed.html';
$errorURL = '/newsletter/subscribe-error.html';
$err = 'Anmeldung fehlgeschlagen';
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
$confirmQuery = http_build_query(['c' => GetConfirmationHash($email), 'e' => $email]);
$confirmLink = $general['siteURL'] . $general['confirmScript'] . "?" . $confirmQuery;
$result = SendEmail($email, $mailConfirmation, $confirmLink);
if ( $result === TRUE ) {
GracefulExit($successURL, 'Anmeldung wird fortgesetzt: Email mit Bestätigungslink wurde versandt.');
} else {
GracefulExit($errorURL, 'Anmeldung fehlgeschlagen: Fehler beim Versenden der Bestätigungs-Email.');
}
} else {
GracefulExit($errorURL, 'Anmeldung fehlgeschlagen: Ungültige Emailadresse.');
}
} else {
GracefulExit($errorURL, 'Anmeldung fehlgeschlagen: Keine Emailadresse angegeben.');
// check if email parameter is set
if ( ! isset($_POST['email']) ) {
GracefulExit($errorURL, "{$err}: Keine Emailadresse angegeben.");
}
// check if it's a well-formed email address
$email = filter_var(trim($_POST['email'], FILTER_SANITIZE_STRING));
if ( ! filter_var($email, FILTER_VALIDATE_EMAIL)) {
GracefulExit($errorURL, "{$err}: Ungültige Emailadresse {$email}");
}
// check whether address is already subscribed
try {
$check = NotAlreadySubscribed($email);
if ( gettype($check) == 'string' ) {
GracefulExit($errorURL, "{$err}: {$check}.");
}
} catch (\PDOException $e) {
GracefulExit($errorURL, "{$err}: {$e->getMessage()}");
}
// send email with confirmation link
$confirmQuery = http_build_query(['c' => GetConfirmationHash($email), 'e' => $email]);
$confirmLink = $general['siteURL'] . $general['confirmScript'] . "?" . $confirmQuery;
try {
SendEmail($email, $mailConfirmation, $confirmLink);
} catch (Exception $e) {
GracefulExit($errorURL, "{$err}: {$e->getMessage()}");
}
// success
GracefulExit($successURL, "Anmeldung erfolgreich: Email mit Bestätigungslink wurde an {$email} versandt.");
?>

View File

@ -4,49 +4,45 @@ require(dirname(__FILE__) . '/settings.php');
$successURL = '/newsletter/unsubscribed.html';
$errorURL = '/newsletter/unsubscribe-error.html';
$err = "Abmeldung fehlgeschlagen";
function RemoveSubscriberFromDB($subscriberAddress) {
$pdo = getPDO();
// make sure record exists
$check = NotAlreadySubscribed($subscriberAddress, $pdo);
if ( gettype($check) == 'string' ) {
return $check;
}
// delete record
global $general;
try {
$pdo = getPDO();
// make sure record exists
$query = $pdo->prepare($general['sql']['read_record']);
if ( ! $query->execute([':e' => $subscriberAddress]) ) {
error_log("Datenbankfehler: Adresscheck für {$subscriberAddress} ergab einen Fehler.");
return 'Datenbankfehler.';
}
if ( ! $query->fetch() ) {
error_log("Datenbankfehler: Emailadresse {$subscriberAddress} nicht in Datenbank vorhanden.");
return "Emailadresse nicht bekannt.";
}
// delete record
$query = $pdo->prepare($general['sql']['delete_record']);
if ( ! $query->execute([':e' => $subscriberAddress])) {
error_log("Datenbankfehler: Löschen von Emailadresse {$subscriberAddress} ergab einen Fehler.");
return 'Fehler beim Löschen des Datenbankeintrags.';
}
} catch(\PDOException $e) {
error_log("Datenbankfehler: {$e->getMessage()}");
return $e->getMessage();
$query = $pdo->prepare($general['sql']['delete_record']);
if ( ! $query->execute([':e' => $subscriberAddress])) {
return "Fehler beim Löschen des Datenbankeintrags für {$subscriberAddress}.";
}
return TRUE;
}
if (isset($_GET['e'])) {
$e = filter_var($_GET["e"], FILTER_SANITIZE_STRING);
// check if hash and email parameters are both set
if ( ! (isset($_GET['c']) and isset($_GET['e'])) ) {
GracefulExit($errorURL, "{$err}: Fehlende Emailadresse oder Hash");
}
// check if hash is correct
$c = filter_var($_GET['c'], FILTER_SANITIZE_STRING);
$e = filter_var($_GET['e'], FILTER_SANITIZE_STRING);
if ( ! GetConfirmationHash($e) === $c ) {
GracefulExit($errorURL, "{$err}: Fehlerhafter Hash");
}
// remove email from database
try {
$result = RemoveSubscriberFromDB($e);
if ($result === TRUE) {
GracefulExit($successURL, "Abmeldung für {$e} erfolgreich.");
} elseif (gettype($result == 'string')) {
GracefulExit($errorURL, "Abmeldung fehlgeschlagen: {$result}");
} else {
GracefulExit($errorURL, 'Abmeldung fehlgeschlagen: Unbekannter Fehler');
}
} else {
GracefulExit($errorURL, 'Abmeldung fehlgeschlagen: Fehlerhafte Emailadresse');
if (gettype($result) == 'string') {
GracefulExit($errorURL, "{$err}: {$result}");
}
} catch(\PDOException $e) {
GracefulExit($error, "{$err}: {$e->getMessage()}");
}
?>

View File

@ -27,11 +27,11 @@ IGNORE_FILES = ['**/.*', '__pycache__', 'favicon-from-svg.sh', '*.metadata']
EXTRA_PATH_METADATA = {
'favicon/favicon.ico': {'path': 'favicon.ico'},
'php/settings.php': {'path': 'settings.php'},
'php/subscribe.php': {'path': 'subscribe.php'},
'php/confirm.php': {'path': 'confirm.php'},
'php/unsubscribe.php': {'path': 'unsubscribe.php'},
'php/contact.php': {'path': 'contact.php'},
'php/settings.php': {'path': 'newsletter/settings.php'},
'php/subscribe.php': {'path': 'newsletter/subscribe.php'},
'php/confirm.php': {'path': 'newsletter/confirm.php'},
'php/unsubscribe.php': {'path': 'newsletter/unsubscribe.php'},
'php/contact.php': {'path': 'kontakt/contact.php'},
'php/Exception.php': {'path': 'Exception.php'},
'php/PHPMailer.php': {'path': 'PHPMailer.php'},
'php/SMTP.php': {'path': 'SMTP.php'},

View File

@ -1,6 +1,6 @@
{% extends "page.html" %}
{% block content_body %}
<form id="contact-form" method="post" action="{{ SITEURL }}/contact.php">
<form id="contact-form" method="post" action="{{ SITEURL }}/kontakt/contact.php">
<fieldset>
<label>
Name

View File

@ -2,7 +2,7 @@
<h2>Abonniere meinen Newsletter</h2>
<p>Erfahre zuerst von Neuerscheinungen, Lesungen und dem ganzen Rest. Mein Dankeschön für dich: ein unveröffentlichtes Gedicht.</p>
</hgroup>
<form class="newsletter-form" method="post" action="{{ SITEURL }}/subscribe.php">
<form class="newsletter-form" method="post" action="{{ SITEURL }}/newsletter/subscribe.php">
<input class="newsletter-email" type="email" name="email" placeholder="Emailadresse" autocomplete="email" aria-label="Emailadresse" required/>
<input class="newsletter-submit" type="submit" value="Abonnieren" />
</form>

View File

@ -2,7 +2,7 @@
<h2>Newsletter-Abmeldung</h2>
<p>Komm gerne wieder, irgendwann.</p>
</hgroup>
<form class="newsletter-form" method="get" action="{{ SITEURL }}/unsubscribe.php">
<form class="newsletter-form" method="get" action="{{ SITEURL }}/newsletter/unsubscribe.php">
<input class="newsletter-email" type="email" name="e" placeholder="Emailadresse" autocomplete="email" aria-label="Emailadresse" required/>
<input class="newsletter-submit" type="submit" value="Abmelden" />
</form>

View File

@ -22,5 +22,6 @@
</div>
{% endfor %}
{% endfor %}
{% include "includes/subscribe.html" %} {%
endblock content_body %}
{% include "includes/subscribe.html" %}
{% include "includes/unsubscribe.html" %}
{% endblock content_body %}

View File

@ -1,5 +1,5 @@
{% extends "page.html" %}
{# note that this template's filename is ''termine.noformat.html'' and not ''termine.html''. This is because vscodium's autoformat breaks the ''replace'' filters in the summary line, which is bad. By setting an exlude rule for all "noformat.html" files in vscodium's settings.json file, autoformat will now ignore this file. #}
{% if page.termine is defined %}
{% set date_format = "%d.%m." %}
{% set time_format = "%H:%M" %}