| @@ -487,8 +487,81 @@ | ||
| 487 | 487 | return null; |
| 488 | 488 | } |
| 489 | 489 | |
| 490 | 490 | /** |
| 491 | + * The consent gate, asked at the form plugin's own validation stage. | |
| 492 | + * | |
| 493 | + * For integrations whose submit hook runs after the form plugin has | |
| 494 | + * already accepted the submission (WPForms `wpforms_process_complete`, | |
| 495 | + * Gravity Forms `gform_after_submission`). By then the form has been | |
| 496 | + * replaced by its confirmation, and a refused consent could only be | |
| 497 | + * reported in a toast over an empty page (5.6.2 click test). Asked from | |
| 498 | + * `wpforms_process` / `gform_validation` instead, the form plugin marks | |
| 499 | + * the checkbox like a missed required field and keeps the input. | |
| 500 | + * | |
| 501 | + * Only a refusal that would stand is returned: DOI on for the form, not | |
| 502 | + * skipped by `f12_cf7_doubleoptin_skip_option`, verdict NOT_GIVEN, gate | |
| 503 | + * enforced. Everything else — a stale field name, the gate switched off | |
| 504 | + * by filter — is left to createOptIn(), which logs it as before. | |
| 505 | + * | |
| 506 | + * @param FormDataInterface $formData The submission, normalized the same | |
| 507 | + * way the submit hook will normalize it. | |
| 508 | + * @param mixed $rawFields What the skip filter receives in the | |
| 509 | + * submit hook of this integration. | |
| 510 | + * | |
| 511 | + * @return OptInError|null The refusal, or null to let the form through. | |
| 512 | + * | |
| 513 | + * @since 5.6.2 | |
| 514 | + */ | |
| 515 | + public function refusedConsentBeforeSubmit( FormDataInterface $formData, $rawFields = array() ): ?OptInError { | |
| 516 | + $formId = $formData->getFormId(); | |
| 517 | + | |
| 518 | + if ( ! $this->isOptInEnabled( $formId ) ) { | |
| 519 | + return null; | |
| 520 | + } | |
| 521 | + | |
| 522 | + if ( apply_filters( 'f12_cf7_doubleoptin_skip_option', false, $formId, $rawFields, $this->getIdentifier() ) ) { | |
| 523 | + return null; | |
| 524 | + } | |
| 525 | + | |
| 526 | + $consentField = (string) ( $this->getFormParameter( $formId )['consent_field'] ?? '' ); | |
| 527 | + if ( $consentField === '' ) { | |
| 528 | + return null; | |
| 529 | + } | |
| 530 | + | |
| 531 | + $verdict = ConsentGate::evaluate( | |
| 532 | + $consentField, | |
| 533 | + $formData->getFields(), | |
| 534 | + $this->getKnownFieldNames( $formId ) | |
| 535 | + ); | |
| 536 | + | |
| 537 | + if ( $verdict !== ConsentGate::NOT_GIVEN || ! ConsentGate::isEnforced( $formId, $this->getIdentifier() ) ) { | |
| 538 | + return null; | |
| 539 | + } | |
| 540 | + | |
| 541 | + $this->getLogger()->info( | |
| 542 | + 'Consent acceptance not given, rejecting submission at validation', | |
| 543 | + array( | |
| 544 | + 'plugin' => 'double-opt-in', | |
| 545 | + 'form_id' => $formId, | |
| 546 | + 'integration' => $this->getIdentifier(), | |
| 547 | + 'consent_field' => $consentField, | |
| 548 | + ) | |
| 549 | + ); | |
| 550 | + | |
| 551 | + /** This action is documented in createOptIn(). */ | |
| 552 | + do_action( 'f12_cf7_doubleoptin_consent_not_given', $formId, $consentField ); | |
| 553 | + | |
| 554 | + return OptInError::fromCode( | |
| 555 | + OptInError::CONSENT_NOT_GIVEN, | |
| 556 | + array( | |
| 557 | + 'form_id' => $formId, | |
| 558 | + 'consent_field' => $consentField, | |
| 559 | + ) | |
| 560 | + ); | |
| 561 | + } | |
| 562 | + | |
| 563 | + /** | |
| 491 | 564 | * Validate the consent-acceptance gate (GDPR Art. 7). |
| 492 | 565 | * |
| 493 | 566 | * The decision itself lives in {@see ConsentGate} — this method only |
| 494 | 567 | * turns it into the return value `createOptIn()` expects and writes |