PluginProbe
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification / 5.12.0
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification v5.12.0
5.12.0 5.13.0 5.13.1 5.11.0 5.10.0 5.9.0 5.8.0 5.8.1 5.7.0 5.6.2 5.6.3 5.6.1 5.6.0 5.5.0 5.4.0 5.3.2 5.3.1 5.1.6 5.1.5 trunk 2.1.5 2.11 2.12 2.13 2.15 All 47 releases
← All changes | src/Integration/AbstractFormIntegration.php +76 -3 5.6.0 → 5.12.0 View file →
@@ -360,9 +360,8 @@
360 360 $this->getLogger()->warning(
361 361 'Rate limit exceeded for IP',
362 362 array(
363 363 'plugin' => 'double-opt-in',
364 - 'ip' => $ip,
365 364 'form_id' => $formData->getFormId(),
366 365 )
367 366 );
368 367 do_action( 'f12_cf7_doubleoptin_rate_limited', 'ip', $ip, $formData->getFormId() );
@@ -383,9 +382,8 @@
383 382 $this->getLogger()->warning(
384 383 'Rate limit exceeded for email',
385 384 array(
386 385 'plugin' => 'double-opt-in',
387 - 'email' => $recipient,
388 386 'form_id' => $formData->getFormId(),
389 387 )
390 388 );
391 389 do_action( 'f12_cf7_doubleoptin_rate_limited', 'email', $recipient, $formData->getFormId() );
@@ -423,9 +421,8 @@
423 421 $this->getLogger()->warning(
424 422 'Recipient validation failed',
425 423 array(
426 424 'plugin' => 'double-opt-in',
427 - 'email' => $recipient,
428 425 'form_id' => $formData->getFormId(),
429 426 'reason' => $errorMsg,
430 427 )
431 428 );
@@ -487,8 +484,81 @@
487 484 return null;
488 485 }
489 486
490 487 /**
488 + * The consent gate, asked at the form plugin's own validation stage.
489 + *
490 + * For integrations whose submit hook runs after the form plugin has
491 + * already accepted the submission (WPForms `wpforms_process_complete`,
492 + * Gravity Forms `gform_after_submission`). By then the form has been
493 + * replaced by its confirmation, and a refused consent could only be
494 + * reported in a toast over an empty page (5.6.2 click test). Asked from
495 + * `wpforms_process` / `gform_validation` instead, the form plugin marks
496 + * the checkbox like a missed required field and keeps the input.
497 + *
498 + * Only a refusal that would stand is returned: DOI on for the form, not
499 + * skipped by `f12_cf7_doubleoptin_skip_option`, verdict NOT_GIVEN, gate
500 + * enforced. Everything else — a stale field name, the gate switched off
501 + * by filter — is left to createOptIn(), which logs it as before.
502 + *
503 + * @param FormDataInterface $formData The submission, normalized the same
504 + * way the submit hook will normalize it.
505 + * @param mixed $rawFields What the skip filter receives in the
506 + * submit hook of this integration.
507 + *
508 + * @return OptInError|null The refusal, or null to let the form through.
509 + *
510 + * @since 5.6.2
511 + */
512 + public function refusedConsentBeforeSubmit( FormDataInterface $formData, $rawFields = array() ): ?OptInError {
513 + $formId = $formData->getFormId();
514 +
515 + if ( ! $this->isOptInEnabled( $formId ) ) {
516 + return null;
517 + }
518 +
519 + if ( apply_filters( 'f12_cf7_doubleoptin_skip_option', false, $formId, $rawFields, $this->getIdentifier() ) ) {
520 + return null;
521 + }
522 +
523 + $consentField = (string) ( $this->getFormParameter( $formId )['consent_field'] ?? '' );
524 + if ( $consentField === '' ) {
525 + return null;
526 + }
527 +
528 + $verdict = ConsentGate::evaluate(
529 + $consentField,
530 + $formData->getFields(),
531 + $this->getKnownFieldNames( $formId )
532 + );
533 +
534 + if ( $verdict !== ConsentGate::NOT_GIVEN || ! ConsentGate::isEnforced( $formId, $this->getIdentifier() ) ) {
535 + return null;
536 + }
537 +
538 + $this->getLogger()->info(
539 + 'Consent acceptance not given, rejecting submission at validation',
540 + array(
541 + 'plugin' => 'double-opt-in',
542 + 'form_id' => $formId,
543 + 'integration' => $this->getIdentifier(),
544 + 'consent_field' => $consentField,
545 + )
546 + );
547 +
548 + /** This action is documented in createOptIn(). */
549 + do_action( 'f12_cf7_doubleoptin_consent_not_given', $formId, $consentField );
550 +
551 + return OptInError::fromCode(
552 + OptInError::CONSENT_NOT_GIVEN,
553 + array(
554 + 'form_id' => $formId,
555 + 'consent_field' => $consentField,
556 + )
557 + );
558 + }
559 +
560 + /**
491 561 * Validate the consent-acceptance gate (GDPR Art. 7).
492 562 *
493 563 * The decision itself lives in {@see ConsentGate} — this method only
494 564 * turns it into the return value `createOptIn()` expects and writes
@@ -655,8 +725,11 @@
655 725 'form' => $formData->getFormHtml(),
656 726 'email' => $recipient,
657 727 'consent_text' => (string) ( $formParameter['consent_text'] ?? '' ),
658 728 'consent_field' => (string) ( $formParameter['consent_field'] ?? '' ),
729 + // Instance of the form inside the form id (Elementor widget id).
730 + // Integrations that cannot tell instances apart leave it empty.
731 + 'form_ref' => (string) $formData->getMetaValue( 'form_ref', '' ),
659 732 );
660 733
661 734 /**
662 735 * Filter the OptIn properties array before the record is