contact_form ersetzen — dieselbe Webform wie auf /experten-finden-lassen.
Die Webform wird auf die Salesforce-Web-to-Lead-Feldnamen umgebaut (SF-HTML „Leadformulare" ist führend), bekommt den
bestehenden Salesforce-Handler zusätzlich zum E-Mail-Handler, 13 versteckte Tracking-Felder mit Browser-Speicherlogik
und feuert nach erfolgreichem Submit das DataLayer-Event lead_submit (Kunden-Lead-Szenario, P2503-109).
Vollständige Begriffsklärung: CONTEXT.md → Web-to-Lead-Pfad, Tracking-Felder, Kunden Lead.
Fünf Slices, jede einzeln testbar. Reihenfolge ist Abhängigkeitsreihenfolge: erst Webform-Kontrakt, dann Modul, dann JS, dann Seiten-Umbau, dann End-to-End-Verify. TDD gegen die bestehenden Handler-/Mapper-Tests.
Element-Keys 1:1 auf SF-Namen: anrede→salutation (radios, Werte Mr./Ms./Mx./Not specified, deutsche Labels), vorname→first_name, nachname→last_name, geschaeftliche_email→email, telefon→phone, unternehmen→company, message→description. Neu sichtbar: zip, city (optional). Raus: kontaktweg, erreichbarkeit. Pflicht: first_name, last_name, email, company, description (+ Datenschutz). Neu hidden: 13 Tracking-Felder (utm_source/medium/campaign/term/content, gclid, msclkid, landing_page, first_landing_page, referrer, first_referrer, page_url, consent) + die 3 Handler-Status-Felder (salesforce_web_to_lead_status/_sent_at/_error, #access: false). SF-Handler aktivieren, E-Mail-Handler bleibt. Webform-Config salesforce_web_to_lead löschen.
Mapper: Konstanten LEAD_SOURCE/DETAIL_LEAD_SOURCE + Felder 00NSZ00000Ju6Hm/J4 entfernen. Tracking-Felder mit Übersetzungstabelle Webform-Key → SF-Parametername mappen — initial identisch (sprechende Namen, Servlet verwirft unbekannte folgenlos), nach ID-Lieferung von Fr. Baur nur Tabellenwerte auf 00N… tauschen. Unit-/Kernel-Tests entsprechend anpassen (Lead-Quelle-Assertions raus, Tracking-Passthrough-Assertions rein).
Neue Library solcom_seo/salesforce-web-to-lead-tracking: Speicherlogik nach Referenz-Script v2 (localStorage sf_tracking_* für First-Touch/Kampagnen-Werte, sessionStorage sf_session_* für Session-Werte), befüllt die Hidden-Felder der Webform; utm/gclid-Befüllung Usercentrics-gated, consent immer als true/false aus UC-Status. Nach Submit leitet die Webform auf die Danke-Seite /Formular-Danke weiter (confirmation_type: url; Seite existiert auf Staging, lokal wird eine minimale canvas_page angelegt). Für lead_submit: der Handler/postSave legt die Payload (email_type per Private-Domain-Liste aus P2503-109-Doku, SHA-256-Hash der normalisierten E-Mail, form_location = Ursprungsseite der Submission) in den PrivateTempStore; die Danke-Seite gibt sie einmalig via drupalSettings aus und löscht sie (serverseitiger Once-Guard). JS pusht Event mit lead_type: customer, lead_subtype: contact_request, lead_category: "Kunden Lead", lead_action: "Kontaktanfrage", form_name: "Kunden Kontaktformular", form_id: "contact_form". E-Mail nie im Klartext in den DataLayer.
Per drush php:script auf canvas_page 100: Delta 31 (sdc.byte_theme.salesforce-web-to-lead-form) durch block.webform_block (webform_id: contact_form) ersetzen, Delta 97 (sdc.byte_theme.salesforce-marketing-form) ersatzlos entfernen. Anker-Ziel des Hero-CTAs (#freiberufler-finden) prüfen/erhalten. Die SDC selbst bleibt im Codebase. canvas_page 9 (/experten-finden-lassen) braucht keinen Umbau — dort hängt der contact_form-Block bereits. Achtung: Canvas-Pages sind lokal kuratierter Content — der Umbau muss auf Staging/Prod wiederholt werden (Script mitliefern).
PHPUnit (Modul-Tests + WebformPrefill-Suite), PHPCS/PHPStan lokal (CI läuft nur auf PR), Browser-Verify auf beiden Seiten: Formular rendert, Pflichtfelder greifen, Submit → Bestätigung → lead_submit im DataLayer sichtbar, Hidden-Felder tragen Werte nach Aufruf mit ?gclid=test1234&utm_source=google…. Kein Live-POST an Salesforce aus DDEV heraus verifizieren (nur Request-Payload loggen/asserten). PR gegen develop, kein Merge durch KI.
Durchgezogen = Lead-Datenpfad (first-party, serverseitig ab Submit). Gestrichelt = Analytics-Pfad nach erfolgreicher Übermittlung — blockiert den Lead-Pfad nie.
Durchgezogen = Lead-Pfad (Browser → Drupal → Salesforce, serverseitig). Gestrichelt = Analytics-Pfad; feuert nur nach erfolgreicher Übermittlung, E-Mail nur als SHA-256-Hash.
Das Salesforce-Web-to-Lead-HTML „Leadformulare" ist führend: Webform-Keys und Select-Werte heißen exakt wie die SF-Input-Namen. Tracking-Felder senden bis zur ID-Lieferung ihre sprechenden Namen (Übersetzungstabelle im Mapper, Werte später auf 00N…-IDs tauschbar).
| Key / SF-Feld | Typ | Pflicht | Hinweis |
|---|---|---|---|
salutation | radios | nein | Werte Mr./Ms./Mx./Not specified, Labels Herr/Frau/Divers/Keine Angabe |
first_name | textfield | ja | maxlength 40 |
last_name | textfield | ja | maxlength 80 |
email | ja | maxlength 80 | |
phone | tel | nein | maxlength 40 |
company | textfield | ja | maxlength 40 |
zip / city | textfield | nein | neu; maxlength 20/40 |
description | textarea | ja | bisher message |
datenschutz_bestaetigung | checkbox | ja | Drupal-only, geht nicht an SF |
| Hidden-Key | SF-Feld (existiert seit 03.07.) | Quelle im Browser |
|---|---|---|
utm_source/_medium/_campaign/_term/_content | SC_UTM_*__c | URL-Param → localStorage |
gclid / msclkid | SC_GCLID__c / SC_MSCLKID__c | URL-Param → localStorage |
landing_page | SC_LandingPage__c | sessionStorage, once pro Session |
first_landing_page | SC_FirstLandingPage__c | localStorage, once ever |
referrer / first_referrer | SC_Referrer__c / SC_FirstReferrer__c | document.referrer (session/local) |
page_url | kein SF-Feld — nachgefragt | window.location.href |
consent | SC_Consentzugestimmt__c | Usercentrics-Status true/false |
Web-to-Lead-Parametername pro Feld = 00N…-ID, liegt noch nicht vor (Anfrage an Fr. Baur). Bis dahin sendet der Mapper die sprechenden Namen — Servlet verwirft sie folgenlos, Werte bleiben in der Drupal-Submission erhalten.
Die zwei fehleranfälligsten Stellen: die Mapper-Übersetzungstabelle (ID-Tausch als Einzeiler vorbereiten) und der lead_submit-Push (erst auf Bestätigung, Hash serverseitig).
// Webform-Key => SF-Web-to-Lead-Parametername. // Sprechende Namen bis die 00N…-IDs der SC_*-Felder // vorliegen (Anfrage Fr. Baur) — dann NUR Werte tauschen. private const TRACKING_FIELDS = [ 'utm_source' => 'utm_source', // → 00N… 'utm_medium' => 'utm_medium', 'utm_campaign' => 'utm_campaign', 'utm_term' => 'utm_term', 'utm_content' => 'utm_content', 'gclid' => 'gclid', 'msclkid' => 'msclkid', 'landing_page' => 'landing_page', 'first_landing_page' => 'first_landing_page', 'referrer' => 'referrer', 'first_referrer' => 'first_referrer', 'page_url' => 'page_url', 'consent' => 'consent', ]; // map(): Lead-Quelle-Konstanten + 00NSZ00000Ju6Hm/J4 // ersatzlos entfernen; TRACKING_FIELDS anhängen, // leere Werte auslassen.
// 1) Storage nach Referenz-Script v2: // setLocal(utm_*, gclid, msclkid) bei URL-Treffer, // setLocalOnce(first_landing_page, first_referrer), // setSessionOnce(landing_page), setSession(referrer, page_url) // 2) Hidden-Felder befüllen — nur mit UC-Consent // („Salesforce Marketing Cloud", Muster salesforce-consent.js): function fillHiddenFields(form, data) { Object.entries(data).forEach(([key, value]) => { const input = form.querySelector( 'input[name="' + key + '"]'); if (input && value) input.value = value; }); } // 3) lead_submit auf der Bestätigungsansicht: // PHP liefert drupalSettings.solcomSeo.leadSubmit // (email_type, sha256_email_address, form_location) — // E-Mail-Normalisierung + Hash serverseitig, nie Klartext. window.dataLayer.push({ event: 'lead_submit', lead_type: 'customer', lead_subtype: 'contact_request', lead_category: 'Kunden Lead', lead_action: 'Kontaktanfrage', form_name: 'Kunden Kontaktformular', form_id: 'contact_form', form_location: settings.formLocation, email_type: settings.emailType, user_data: { sha256_email_address: settings.emailHash } });
TRACKING_FIELDS. Anfrage an Fr. Baur läuft parallel._default-Body zieht neue Keys automatisch, custom Tokens würden brechen._default (verifiziert) — kein Token-Bruch. Alte Submissions bleiben lesbar (Keys bleiben in DB erhalten). WebformPrefill-Kernel-Tests im solcom_project_offer-Modul auf contact_form-Referenzen prüfen.drush php:script) versioniert mitliefern und im PR als Deploy-Schritt dokumentieren; nach Deploy canvas_page-Cache-Tag invalidieren (bekannte Falle: stale Page-Cache nach Content-Änderung).UC_UI_INITIALIZED/UC_CONSENT_CHANGED, Muster salesforce-consent.js); consent-Feld trägt den tatsächlichen Status, SF-seitig auswertbar.TRACKING_FIELDS tauschen, ein Commit.page_url erfasst und in der Drupal-Submission gespeichert.