wip: Mobile-Theme-Testphase und PHP-8.2-Guards (Stand Juni 2026)

This commit is contained in:
Thomas Bartelt
2026-09-13 19:10:56 +02:00
parent 08beac8d67
commit 20d55f77b8
1448 changed files with 16015 additions and 583 deletions
+132 -40
View File
@@ -301,9 +301,7 @@ class Revocation {
}
$msg .= $this->mail_row('IP (anon.)', $revocation->ip_anonymized ?? '–');
$msg .= '</table>';
$msg .= '<p style="margin-top:20px;"><strong>Bitte Rückzahlung über die Warenwirtschaft '
. 'veranlassen.</strong> Die Zahlung muss über dieselbe Zahlungsart wie die ursprüngliche '
. 'Bestellung erfolgen.</p>';
$msg .= '<p style="margin-top:20px;"><strong>Bitte prüfen und E-Mail an die Kundin / den Kunden schreiben.</strong></p>';
$msg .= '<p>Admin: <a href="https://'.($_SERVER['HTTP_HOST'] ?? 'www.intelectra.de')
. '/index.php?admin_modul=admin_revocation_list">Widerrufsliste öffnen</a></p>';
@@ -324,27 +322,19 @@ class Revocation {
$order_number = $order->order_number ?? ('#'.$revocation->order_id);
$name = trim(($order->billing_firstname ?? '').' '.($order->billing_surname ?? ''));
$subject = 'Bestätigung Ihres Widerrufs – Bestellung '.$order_number;
$ts = strtotime($revocation->submitted_at);
$datum = date('d.m.Y', $ts);
$uhrzeit = date('H:i', $ts);
$msg = '<p>Sehr geehrte Kundin, sehr geehrter Kunde'.($name ? ' '.htmlspecialchars($name) : '').',</p>';
$msg .= '<p>hiermit bestätigen wir den Eingang Ihrer Widerrufserklärung zu Bestellung '
. '<strong>'.htmlspecialchars($order_number).'</strong> am '
. date('d.m.Y H:i', strtotime($revocation->submitted_at)).' Uhr.</p>';
$msg .= '<h3>Nächste Schritte</h3>';
$msg .= '<p>Bitte senden Sie die Waren unverzüglich, spätestens binnen 14 Tagen, an:</p>';
$msg .= '<p style="background:#f4f4f4;padding:12px;border-left:4px solid #c00;">'
. '<strong>'.REVOCATION_RETURN_COMPANY.'</strong><br>'
. REVOCATION_RETURN_STREET.'<br>'
. REVOCATION_RETURN_CITY.'</p>';
$msg .= '<p>'.REVOCATION_RETURN_COSTS_NOTE.'</p>';
$msg .= '<p>Die Rückzahlung erfolgt innerhalb von 14 Tagen nach Eingang Ihres Widerrufs über '
. 'dieselbe Zahlungsart, die Sie bei der ursprünglichen Bestellung verwendet haben – '
. 'es sei denn, mit Ihnen wurde ausdrücklich etwas anderes vereinbart.</p>';
$msg .= '<p>Bei Rückfragen erreichen Sie uns jederzeit unter '
. '<a href="mailto:'.REVOCATION_OWNER_EMAIL.'">'.REVOCATION_OWNER_EMAIL.'</a>.</p>';
$msg .= '<hr><p style="font-size:0.85em;color:#666;">'.REVOCATION_RETURN_COMPANY.'</p>';
$ok = $mailer->send_mail($subject, $msg, $revocation->customer_email);
$ok = $this->send_neutral_eingangsbestaetigung(
$revocation->customer_email,
$name,
$revocation->customer_email,
$order_number,
$datum,
$uhrzeit,
$revocation->reason_text ?? ''
);
if ($ok) {
$this->db->query(
@@ -487,20 +477,25 @@ class Revocation {
}
/**
* Widerruf ohne Token – Lookup anhand Bestellnr + E-Mail.
* Wenn noch kein revocation-Record existiert, wird er inline angelegt.
* Widerruf ohne Token.
* Pflichtfelder: Name + E-Mail. Button-Klick = Erklärung.
* Bestellnummer + Artikel sind OPTIONAL (Händlerbund-Linie). Wenn Order
* gefunden → DB-Tracking. Sonst → reiner Anonym-Pfad mit Owner-Mail zur
* manuellen Zuordnung. Der Widerruf wird in jedem Fall entgegengenommen.
*/
public function submit_anonymous($name, $order_number, $email, $reason_id, $reason_text, $ip, $user_agent) {
public function submit_anonymous($name, $order_number, $email, $article, $ip, $user_agent) {
if (!REVOCATION_ENABLED) {
$this->error = 'feature_disabled';
return false;
}
$name = trim((string)$name);
$order_number = trim((string)$order_number);
$email = trim(strtolower((string)$email));
$article = trim((string)$article);
if ($order_number === '') {
$this->error = 'missing_order';
if ($name === '') {
$this->error = 'missing_name';
return false;
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
@@ -508,22 +503,24 @@ class Revocation {
return false;
}
$order = $this->fetch_order_by_number_email($order_number, $email);
// Order-Lookup nur informativ. Wenn keine Order angegeben oder
// keine gefunden oder nicht widerrufsfähig → Anonym-Pfad.
$order = ($order_number !== '')
? $this->fetch_order_by_number_email($order_number, $email)
: null;
if (!$order) {
$this->error = 'order_not_found';
return false;
return $this->process_anonymous_unmatched($name, $order_number, $email, $article, $ip, $user_agent);
}
$group_id = RevocationHelper::resolve_group_id($this->db, $order->customer_id);
if (!RevocationHelper::is_b2c($group_id)) {
$this->error = 'not_eligible';
return false;
return $this->process_anonymous_unmatched($name, $order_number, $email, $article, $ip, $user_agent);
}
$expires_at = RevocationHelper::calc_expires_at($order->order_date);
if (strtotime($expires_at) < time()) {
$this->error = 'expired';
return false;
return $this->process_anonymous_unmatched($name, $order_number, $email, $article, $ip, $user_agent);
}
// Existing Eintrag wiederverwenden, sonst inline anlegen
@@ -563,11 +560,10 @@ class Revocation {
$stmt->close();
}
$reason_id = ((int)$reason_id > 0) ? (int)$reason_id : null;
$reason_text = trim((string)$reason_text);
if (mb_strlen($reason_text) > 2000) {
$reason_text = mb_substr($reason_text, 0, 2000);
}
$reason_id = null;
$reason_text = ($article !== '' && mb_strlen($article) > 2000)
? mb_substr($article, 0, 2000)
: $article;
$ip_anon = RevocationHelper::anonymize_ip($ip);
$ua = $user_agent ? mb_substr($user_agent, 0, 255) : null;
@@ -623,4 +619,100 @@ class Revocation {
if (!$res || $res->num_rows === 0) return false;
return $res->fetch_object();
}
// ============================================================
// Anonym-Pfad: ohne (passende) Bestellung
// ============================================================
/**
* Widerruf ohne Order-Match.
* Kein DB-Eintrag, dafür Owner-Mail mit Hinweis auf manuelle Zuordnung
* und Eingangsbestätigung an den Kunden.
*/
private function process_anonymous_unmatched($name, $order_number, $email, $article, $ip, $user_agent) {
$ip_anon = RevocationHelper::anonymize_ip($ip);
$ua = $user_agent ? mb_substr((string)$user_agent, 0, 255) : null;
$article = trim((string)$article);
if (mb_strlen($article) > 2000) {
$article = mb_substr($article, 0, 2000);
}
$this->send_anonymous_owner_notification($name, $order_number, $email, $article, $ip_anon, $ua);
$this->send_anonymous_customer_confirmation($name, $order_number, $email, $article);
return true;
}
private function send_anonymous_owner_notification($name, $order_number, $email, $article, $ip_anon, $ua) {
$mailer = new mail_tools($this->base_object);
$subject = 'Widerruf eingegangen (manuelle Zuordnung erforderlich)';
$msg = '<h2>Widerruf eingegangen</h2>';
$msg .= '<p style="background:#fff3cd;border-left:4px solid #ffa500;padding:10px;">';
$msg .= '<strong>Bitte manuell zuordnen.</strong> Der Widerruf wurde ohne (oder mit nicht gefundener) Bestellnummer eingereicht. ';
$msg .= 'Es wurde kein automatischer DB-Eintrag erzeugt.';
$msg .= '</p>';
$msg .= '<table cellpadding="4" cellspacing="0" border="0" style="font-family:Arial,sans-serif;">';
$msg .= $this->mail_row('Name', htmlspecialchars($name));
$msg .= $this->mail_row('E-Mail', htmlspecialchars($email));
$msg .= $this->mail_row('Bestellnummer', $order_number !== '' ? htmlspecialchars($order_number) : '(nicht angegeben)');
$msg .= $this->mail_row('Artikel/Bemerkung', $article !== '' ? nl2br(htmlspecialchars($article)) : '(nicht angegeben)');
$msg .= $this->mail_row('Datum/Uhrzeit', date('d.m.Y H:i:s'));
$msg .= $this->mail_row('IP (anon.)', $ip_anon ?? '–');
$msg .= '</table>';
$msg .= '<p style="margin-top:20px;"><strong>Bitte prüfen und E-Mail an die Kundin / den Kunden schreiben.</strong></p>';
return $mailer->send_mail($subject, $msg, REVOCATION_OWNER_EMAIL);
}
private function send_anonymous_customer_confirmation($name, $order_number, $email, $article) {
return $this->send_neutral_eingangsbestaetigung(
$email,
$name,
$email,
$order_number !== '' ? $order_number : '(nicht angegeben)',
date('d.m.Y'),
date('H:i'),
$article
);
}
/**
* Neutrale Eingangsbestätigung gemäß Händlerbund-Vorlage.
*
* WICHTIG: Diese E-Mail darf NUR den Eingang der Widerrufserklärung
* bestätigen und KEINE wirksame Bestätigung des Widerrufs selbst sein.
* Keine Rücksendeadresse, keine Frist-Hinweise, keine Werbung.
*
* @param string $to Empfänger-Mail
* @param string $consumer_name Name des Verbrauchers für die Anrede
* @param string $consumer_email E-Mail aus dem Formular (für „Inhalt")
* @param string $order_number Bestellnr aus dem Formular ('' = nicht angegeben)
* @param string $date_str Eingangsdatum (dd.mm.yyyy)
* @param string $time_str Eingangsuhrzeit (HH:MM)
* @param string $article Artikel/Bemerkung (optional)
*/
private function send_neutral_eingangsbestaetigung($to, $consumer_name, $consumer_email,
$order_number, $date_str, $time_str, $article) {
$mailer = new mail_tools($this->base_object);
$subject = 'Ihr Widerruf ist bei uns eingegangen';
$name_safe = htmlspecialchars(trim((string)$consumer_name));
$anrede = $name_safe !== '' ? 'Hallo '.$name_safe.',' : 'Hallo,';
$msg = '<p>'.$anrede.'</p>';
$msg .= '<p>wir best&auml;tigen hiermit den Eingang Ihres Widerrufs mit folgendem Inhalt:</p>';
$msg .= '<ul style="line-height:1.6;">';
$msg .= '<li><strong>Name:</strong> '.$name_safe.'</li>';
$msg .= '<li><strong>E-Mail:</strong> '.htmlspecialchars($consumer_email).'</li>';
$msg .= '<li><strong>Bestellnummer:</strong> '.htmlspecialchars($order_number).'</li>';
if (!empty($article)) {
$msg .= '<li><strong>Artikel/Bemerkung:</strong> '.nl2br(htmlspecialchars($article)).'</li>';
}
$msg .= '<li><strong>Eingang am:</strong> '.htmlspecialchars($date_str).' um '.htmlspecialchars($time_str).' Uhr</li>';
$msg .= '</ul>';
$msg .= '<p>Wir pr&uuml;fen Ihr Anliegen und melden uns zeitnah bei Ihnen.</p>';
$msg .= '<p>Viele Gr&uuml;&szlig;e<br>Ihre '.htmlspecialchars(REVOCATION_RETURN_COMPANY).'</p>';
return $mailer->send_mail($subject, $msg, $to);
}
}