| @@ -134,12 +134,13 @@ | ||
| 134 | 134 | : ''; |
| 135 | 135 | |
| 136 | 136 | // Consent acknowledgment field — name of the form field that |
| 137 | 137 | // captures the user's explicit consent (e.g. CF7 [acceptance]). |
| 138 | - // Stored as a sanitize_key string since it must match a real | |
| 139 | - // form-field name at submit time. | |
| 138 | + // It has to match a real form-field name at submit time, so it | |
| 139 | + // is normalised to what a field name can legally contain — but | |
| 140 | + // NOT lowercased. See sanitizeFieldName(). | |
| 140 | 141 | $dto->consentField = isset( $data['consentField'] ) || isset( $data['consent_field'] ) |
| 141 | - ? sanitize_key( (string) ( $data['consentField'] ?? $data['consent_field'] ) ) | |
| 142 | + ? $this->sanitizeFieldName( (string) ( $data['consentField'] ?? $data['consent_field'] ) ) | |
| 142 | 143 | : ''; |
| 143 | 144 | |
| 144 | 145 | // Field-mapping (placeholder-tag → form-field-name) for the |
| 145 | 146 | // Mapping tab. Sanitize each key + value to text-safe strings |
| @@ -190,8 +191,43 @@ | ||
| 190 | 191 | ) |
| 191 | 192 | ); |
| 192 | 193 | |
| 193 | 194 | return $messages[ $field ] ?? __( 'Required field is missing.', 'double-opt-in' ); |
| 195 | + } | |
| 196 | + | |
| 197 | + /** | |
| 198 | + * Normalise a form-field name the admin picked from a dropdown. | |
| 199 | + * | |
| 200 | + * `sanitize_key()` used to do this job and was the wrong tool: it | |
| 201 | + * lowercases. Form systems do not. | |
| 202 | + * | |
| 203 | + * Elementor's field-id control states its own rule as "This field | |
| 204 | + * allows A-z 0-9 & underscore chars without spaces" — capitals | |
| 205 | + * included — and a German site names its consent checkbox | |
| 206 | + * `Datenschutz`. That was stored as `datenschutz`, never matched the | |
| 207 | + * form's field list again, and the settings page showed "the | |
| 208 | + * selected acceptance field does not exist on this form" for good, | |
| 209 | + * because re-picking it from the dropdown lowercased it again | |
| 210 | + * (customer report 2026-08-27). On integrations that enforce the | |
| 211 | + * consent gate the same mangling rejected every submission outright. | |
| 212 | + * | |
| 213 | + * Same character allow-list as before, plus the column width | |
| 214 | + * (`consent_field` is `varchar(64)`; a longer value would be | |
| 215 | + * truncated by MySQL and then never match either). Only the | |
| 216 | + * lowercasing is gone. | |
| 217 | + * | |
| 218 | + * @param string $value Raw field name from the request. | |
| 219 | + * | |
| 220 | + * @return string Sanitized field name. | |
| 221 | + */ | |
| 222 | + private function sanitizeFieldName( string $value ): string { | |
| 223 | + $value = preg_replace( '/[^A-Za-z0-9_\-]/', '', trim( $value ) ); | |
| 224 | + | |
| 225 | + if ( ! is_string( $value ) ) { | |
| 226 | + return ''; | |
| 227 | + } | |
| 228 | + | |
| 229 | + return substr( $value, 0, 64 ); | |
| 194 | 230 | } |
| 195 | 231 | |
| 196 | 232 | /** |
| 197 | 233 | * Check if a value is a valid email or placeholder. |