PluginProbe
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification / 5.5.0
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification v5.5.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 3.0.0 3.0.1 3.0.2 3.0.3 3.0.5 3.0.51 3.0.60 3.0.61 3.0.62 All 38 releases
← All changes | compatibility/cf7/CF7Frontend.class.php +197 -145 3.0.71 → 5.5.0 View file →
@@ -5,8 +5,9 @@
5 5 exit;
6 6 }
7 7
8 8 use forge12\plugins\ContactForm7;
9 + use Forge12\Shared\LoggerInterface;
9 10
10 11 /**
11 12 * Class Frontend
12 13 * Responsible to handle the frontend of the Double OptIn
@@ -11,8 +12,12 @@
11 12 * Class Frontend
12 13 * Responsible to handle the frontend of the Double OptIn
13 14 *
14 15 * @package forge12\contactform7\CF7DoubleOptIn
16 + *
17 + * @deprecated 4.0.0 Use \Forge12\DoubleOptIn\Integration\CF7Integration instead.
18 + * This class is maintained for backward compatibility only.
19 + * @see \Forge12\DoubleOptIn\Integration\CF7Integration
15 20 */
16 21 class CF7Frontend extends OptInFrontend {
17 22 protected $OptIn = null;
18 23
@@ -18,14 +23,24 @@
18 23
19 24 /**
20 25 * Admin constructor.
21 26 */
22 - public function __construct() {
27 + public function __construct( LoggerInterface $logger ) {
28 + parent::__construct( $logger, 'cf7' );
23 29
24 - parent::__construct( 'cf7' );
25 - add_action( 'wpcf7_before_send_mail', array( $this, 'onSubmit' ), 5, 3 );
30 + $this->get_logger()->debug( 'CF7 integration constructor called', [
31 + 'plugin' => 'double-opt-in',
32 + 'class' => __CLASS__,
33 + 'method' => __METHOD__,
34 + ] );
35 +
36 + add_action( 'wpcf7_before_send_mail', [ $this, 'onSubmit' ], 5, 3 );
37 + $this->get_logger()->debug( 'Action wpcf7_before_send_mail registered', [
38 + 'plugin' => 'double-opt-in',
39 + ] );
26 40 }
27 41
42 +
28 43 /**
29 44 * Get the recipient email address
30 45 *
31 46 * @param string $recipient The recipient email address
@@ -34,32 +49,50 @@
34 49 *
35 50 * @return string The sanitized recipient email address
36 51 */
37 52 public function getRecipient( string $recipient, array $formParameter, array $postParameter ): string {
38 - /**
39 - * Ensure the recipient has been defined in the settings
40 - */
53 + $this->get_logger()->debug( 'getRecipient called', [
54 + 'plugin' => 'double-opt-in',
55 + 'class' => __CLASS__,
56 + 'method' => __METHOD__,
57 + 'formParameter' => $formParameter,
58 + ] );
59 +
60 + // Ensure the recipient has been defined in the settings
41 61 if ( ! isset( $formParameter['recipient'] ) ) {
62 + $this->get_logger()->warning( 'Recipient not defined in form parameters, returning fallback', [
63 + 'plugin' => 'double-opt-in',
64 + 'recipient' => $recipient,
65 + ] );
66 +
42 67 return $recipient;
43 68 }
44 69
45 70 $recipient = $formParameter['recipient'];
46 71
47 - $recipient = str_replace( '[', '', $recipient );
48 - $recipient = str_replace( ']', '', $recipient );
72 + $recipient = str_replace( [ '[', ']' ], '', $recipient );
49 73
50 - /**
51 - * Get the Recipient from the post parameter
52 - */
74 + // Get the Recipient from the post parameter
53 75 if ( isset( $postParameter[ $recipient ] ) ) {
54 - $recipient = sanitize_email( $_POST[ $recipient ] );
76 + $recipient = sanitize_email( $_POST[ $recipient ] ); // phpcs:ignore WordPress.Security.NonceVerification
77 + $this->get_logger()->debug( 'Recipient resolved from post parameter', [
78 + 'plugin' => 'double-opt-in',
79 + 'recipient' => $recipient,
80 + ] );
81 + } else {
82 + $this->get_logger()->warning( 'Recipient not found in postParameter, using fallback', [
83 + 'plugin' => 'double-opt-in',
84 + 'recipient' => $recipient,
85 + ] );
55 86 }
56 87
57 88 return $recipient;
58 89 }
59 90
91 +
60 92 /**
61 93 * Add File after submission and confirming opt in.
94 + *
62 95 * @param $form
63 96 * @param $abort
64 97 * @param $submission
65 98 *
@@ -65,18 +98,39 @@
65 98 *
66 99 * @return void
67 100 */
68 101 public function attachExtraAttachments( $form, $abort, $submission ) {
69 - if ( $this->OptIn != null && $this->OptIn->get_files() != null ) {
70 - $fileWrapper = maybe_unserialize($this->OptIn->get_files());
71 - if(is_array($fileWrapper)) {
102 + $this->get_logger()->debug( 'attachExtraAttachments called', [
103 + 'plugin' => 'double-opt-in',
104 + 'class' => __CLASS__,
105 + 'method' => __METHOD__,
106 + ] );
107 +
108 + if ( $this->OptIn !== null && $this->OptIn->get_files() !== null ) {
109 + $fileWrapper = maybe_unserialize( $this->OptIn->get_files() );
110 +
111 + if ( is_array( $fileWrapper ) ) {
72 112 foreach ( $fileWrapper as $file ) {
73 113 $submission->add_extra_attachments( $file );
114 +
115 + $this->get_logger()->debug( 'Extra attachment added', [
116 + 'plugin' => 'double-opt-in',
117 + 'file' => $file,
118 + ] );
74 119 }
120 + } else {
121 + $this->get_logger()->warning( 'File wrapper is not an array, skipping attachments', [
122 + 'plugin' => 'double-opt-in',
123 + ] );
75 124 }
125 + } else {
126 + $this->get_logger()->debug( 'No OptIn files found, skipping attachments', [
127 + 'plugin' => 'double-opt-in',
128 + ] );
76 129 }
77 130 }
78 131
132 +
79 133 /**
80 134 * Send the default email for the given OptIn. (Original Mail after Opt-In Confirmation)
81 135 *
82 136 * @param OptIn $OptIn The OptIn object containing the email content and form ID.
@@ -83,39 +137,78 @@
83 137 *
84 138 * @return void
85 139 */
86 140 public function sendDefaultMail( OptIn $OptIn ): void {
141 + $this->get_logger()->debug( 'sendDefaultMail called', [
142 + 'plugin' => 'double-opt-in',
143 + 'class' => __CLASS__,
144 + 'method' => __METHOD__,
145 + 'optin_id' => $OptIn->get_id(),
146 + 'form_id' => $OptIn->get_cf_form_id(),
147 + ] );
148 +
87 149 if ( ! function_exists( 'wpcf7' ) ) {
150 + $this->get_logger()->warning( 'wpcf7 function not available, aborting sendDefaultMail', [
151 + 'plugin' => 'double-opt-in',
152 + ] );
153 +
88 154 return;
89 155 }
90 156
91 157 $this->OptIn = $OptIn;
92 - /**
93 - * Prepare the Post Data
94 - */
95 - $data = maybe_unserialize( $OptIn->get_content() );
96 158
97 - /**
98 - * Sanitize the Data
99 - */
159 + $data = maybe_unserialize( $OptIn->get_content() );
100 160 $_POST = SanitizeHelper::sanitize_array( $data );
101 161
102 - /**
103 - * Prepare the Contact Form
104 - */
105 162 $ContactForm = \WPCF7_ContactForm::get_instance( $OptIn->get_cf_form_id() );
163 + if ( ! $ContactForm ) {
164 + $this->get_logger()->warning( 'Contact form not found, aborting sendDefaultMail', [
165 + 'plugin' => 'double-opt-in',
166 + 'form_id' => $OptIn->get_cf_form_id(),
167 + 'optin_id' => $OptIn->get_id(),
168 + ] );
106 169
107 - /**
108 - * Attach Files before send mail
109 - */
170 + return;
171 + }
172 +
110 173 add_action( 'wpcf7_before_send_mail', [ $this, 'attachExtraAttachments' ], 10, 3 );
174 + $this->get_logger()->debug( 'Hook wpcf7_before_send_mail registered for attachments', [
175 + 'plugin' => 'double-opt-in',
176 + ] );
111 177
112 - /**
113 - * Prepare the Submission Instance
114 - */
178 + // Disable CF7 validation and spam checks for confirmation mail resend.
179 + // CF7 re-runs all form validations (required fields, quiz, acceptance checkboxes)
180 + // when creating a WPCF7_Submission instance, which fails in a GET request context.
181 + $clearValidation = function( $result ) {
182 + return new \WPCF7_Validation();
183 + };
184 + add_filter( 'wpcf7_validate', $clearValidation, 999 );
185 + add_filter( 'wpcf7_spam', '__return_false', 0 );
186 + add_filter( 'wpcf7_skip_spam_check', '__return_true', 0 );
187 +
115 188 $submission = \WPCF7_Submission::get_instance( $ContactForm );
189 +
190 + // Re-enable validation and spam checks
191 + remove_filter( 'wpcf7_validate', $clearValidation, 999 );
192 + remove_filter( 'wpcf7_spam', '__return_false', 0 );
193 + remove_filter( 'wpcf7_skip_spam_check', '__return_true', 0 );
194 +
195 + if ( $submission ) {
196 + $this->get_logger()->info( 'WPCF7 submission prepared for sendDefaultMail', [
197 + 'plugin' => 'double-opt-in',
198 + 'form_id' => $OptIn->get_cf_form_id(),
199 + 'optin_id' => $OptIn->get_id(),
200 + ] );
201 + } else {
202 + $this->get_logger()->warning( 'Failed to prepare WPCF7 submission', [
203 + 'plugin' => 'double-opt-in',
204 + 'form_id' => $OptIn->get_cf_form_id(),
205 + 'optin_id' => $OptIn->get_id(),
206 + ] );
207 + }
116 208 }
117 209
210 +
118 211 /**
119 212 * On Form Submit add the double optin if enabled
120 213 *
121 214 * @param $form \WPCF7_ContactForm
@@ -122,80 +215,73 @@
122 215 * @param bool
123 216 * @param $submission \WPCF7_Submission
124 217 */
125 218 public function onSubmit( $form, &$abort, $submission ) {
219 + $this->get_logger()->debug( 'onSubmit called', [
220 + 'plugin' => 'double-opt-in',
221 + 'class' => __CLASS__,
222 + 'method' => __METHOD__,
223 + 'form_id' => $form->id(),
224 + ] );
225 +
126 226 $parameter = CF7DoubleOptIn::getInstance()->getParameter( $form->id() );
127 227
128 228 if ( $this->isOptinEnabled( $form->id() ) ) {
229 + $this->get_logger()->debug( 'Opt-in enabled, processing', [
230 + 'plugin' => 'double-opt-in',
231 + 'form_id' => $form->id(),
232 + ] );
233 +
129 234 // Remove Contact Form 7 DB hook to ensure the optin mail is not saved
130 235 remove_action( 'wpcf7_before_send_mail', 'cfdb7_before_send_mail' );
131 236
132 237 if ( apply_filters( 'f12_cf7_doubleoptin_skip_option', false, $form->id(), $submission->get_posted_data(), $this->type ) ) {
133 - return; // Skip if requested
238 + $this->get_logger()->info( 'Opt-in skipped by filter', [
239 + 'plugin' => 'double-opt-in',
240 + 'form_id' => $form->id(),
241 + ] );
242 +
243 + return;
134 244 }
135 - // Get the OptIn Object
136 - $OptIn = $this->maybeCreateOptIn( $form->id(), $form->form_html(), $submission->get_posted_data(), $submission->uploaded_files() );
137 245
138 - /**
139 - * Skip if OptIn not created
140 - */
246 + $OptIn = $this->maybeCreateOptIn(
247 + $form->id(),
248 + $form->form_html(),
249 + $submission->get_posted_data(),
250 + $submission->uploaded_files()
251 + );
252 +
141 253 if ( ! $OptIn ) {
254 + $this->get_logger()->warning( 'OptIn object could not be created', [
255 + 'plugin' => 'double-opt-in',
256 + 'form_id' => $form->id(),
257 + ] );
258 +
142 259 return;
143 260 }
144 261
145 - // Add the Form URL to the parameters
146 262 $parameter['formUrl'] = $submission->get_meta( 'url' );
147 263
148 - // Get the Body content
149 - $body = $parameter['body'];
150 -
151 - // Replace the Placeholders within the body content.
152 - $body = $this->addPlaceholders( $body, $OptIn, $parameter );
153 -
154 264 /**
155 - * Body
265 + * Filter to render custom email templates.
156 266 *
157 - * Filters the Body of the OptIn Mail. Allows developers to adjust the content
158 - * before transmission.
267 + * Allows replacing the email body with content from custom templates.
159 268 *
160 - * @param string $body The content of the OptIn Mail.
269 + * @since 4.0.0
161 270 *
162 - * @since 2.3.3
271 + * @param string $body The email body content.
272 + * @param string $template The template key (e.g., 'blank', 'custom_123').
273 + * @param array $parameter The form parameters.
274 + * @param OptIn $OptIn The OptIn object.
163 275 */
276 + $body = apply_filters( 'f12_cf7_doubleoptin_template_body', $parameter['body'], $parameter['template'], $parameter, $OptIn );
277 +
278 + $body = $this->addPlaceholders( $body, $OptIn, $parameter );
164 279 $body = apply_filters( 'f12_cf7_doubleoptin_body', $body );
165 280
166 - /**
167 - * Set the OptIn Mail Content to the OptIn Object.
168 - */
169 281 $OptIn->set_mail_optin( $body );
170 282 $OptIn->save();
171 283
172 - /**
173 - * Recipient
174 - */
175 - $additional_headers = '';
176 -
177 - /**
178 - * OptIn Mail Arguments
179 - *
180 - * Filters the Arguments for the OptIn Mail. This hook allows developers
181 - * to add additional content to the Mail.
182 - *
183 - * @formatter:off
184 - *
185 - * @param $args {
186 - * @type string $subject The Subject of the OptIn Mail
187 - * @type string $body The Body of the OptIn Mail,
188 - * @type string $sender The Sender of the OptIn Mail
189 - * @type string $recipient The recipient of the OptIn Mail
190 - * @type bool $use_html Enable/Disable HTML for the OptIn Mail. Default: true
191 - * @type string $additional_headers Additional Header Information for the Mail.
192 - * }
193 - *
194 - * @formatter:on
195 - *
196 - * @since 2.3.3
197 - */
198 284 $args = apply_filters( 'f12-cf7-doubleoptin-cf7-args', [
199 285 'subject' => $parameter['subject'],
200 286 'body' => $body,
201 287 'sender' => $parameter['sender'],
@@ -201,107 +287,73 @@
201 287 'sender' => $parameter['sender'],
202 288 'sender_name' => $parameter['sender_name'],
203 289 'recipient' => $parameter['recipient'],
204 290 'use_html' => true,
205 - 'additional_headers' => $additional_headers
291 + 'additional_headers' => '',
206 292 ] );
207 293
208 - /**
209 - * Set the Header
210 - */
211 294 if ( ! empty( $parameter['sender_name'] ) ) {
212 295 $args['additional_headers'] .= 'From: ' . $args['sender_name'] . ' <' . $args['sender'] . '>';
213 296 }
214 297
215 - // Prepare the Opt-In Mail
216 298 \WPCF7_Mail::send( $args, 'mail' );
299 + $this->get_logger()->info( 'OptIn mail sent', [
300 + 'plugin' => 'double-opt-in',
301 + 'form_id' => $form->id(),
302 + 'recipient' => $args['recipient'],
303 + 'subject' => $args['subject'],
304 + ] );
217 305
218 - // Skip all mails following
306 + $telemetry = new Telemetry( $this->get_logger() );
307 + $telemetry->increment( 'total_optins' );
308 + $telemetry->increment( 'cf7_optins' );
309 +
219 310 add_filter( 'wpcf7_skip_mail', '__return_true' );
220 -
221 - /**
222 - * Action after the OptIn Mail has been sent.
223 - *
224 - * Allows other developers to do additional tasks after the OptIn Mail has been submitted.
225 - *
226 - * @formatter:off
227 - *
228 - * @param \WPCF7_ContactForm $form The Contact Form 7 Form Object
229 - * @param int $formId The Post ID of the CF7 Form.
230 - *
231 - * @formatter:on
232 - *
233 - * @since 2.3.3
234 - */
235 311 do_action( 'f12_cf7_doubleoptin_sent', $form, $form->id() );
236 312
237 - } else if ( isset( $_GET['optin'] ) ) {
238 - /**
239 - * Add Files / Attachments to the OptIn Mail.
240 - */
241 - $hash = sanitize_text_field( $_GET['optin'] );
313 + } elseif ( isset( $_GET['optin'] ) ) {
314 + $hash = sanitize_text_field( $_GET['optin'] );
315 + $OptIn = OptIn::get_by_hash( $hash );
242 316
243 - /**
244 - * Get the OptIn Object
245 - */
246 - $OptIn = OptIn::get_by_hash( $hash );
317 + if ( $OptIn === null ) {
318 + $this->get_logger()->warning( 'OptIn not found for hash', [
319 + 'plugin' => 'double-opt-in',
320 + 'hash' => $hash,
321 + ] );
247 322
248 - /**
249 - * Skip if the OptIn has not been found.
250 - */
251 - if ( null == $OptIn ) {
252 323 return;
253 324 }
254 325
255 - /**
256 - * Get the Files from the OptIn Object
257 - */
258 326 $files = maybe_unserialize( $OptIn->get_files() );
259 327
260 - /**
261 - * Skip if no files has been found
262 - */
263 328 if ( empty( $files ) ) {
329 + $this->get_logger()->debug( 'No files found for OptIn', [
330 + 'plugin' => 'double-opt-in',
331 + 'optinId' => $OptIn->get_id(),
332 + ] );
333 +
264 334 return;
265 335 }
266 336
267 - /**
268 - * Loop through all attachments/files.
269 - */
270 337 foreach ( $files as $file ) {
271 - /**
272 - * Skip file if the name is empty
273 - */
274 - if ( ! empty( $file ) ) {
338 + if ( empty( $file ) ) {
275 339 continue;
276 340 }
277 341
278 - /**
279 - * Enable / Disable the OptIn Mail Attachment for Mail 1.
280 - *
281 - * Allows Developers to Enable/Disable Mail attachments dynamically.
282 - *
283 - * @param bool $status Either add attachments (true) or not (false). Default: true
284 - * @param OptIn $OptIn The OptIn Object.
285 - *
286 - * @since 2.3.3
287 - */
288 342 if ( apply_filters( 'f12_cf7_doubleoptin_files_mail_1', true, $OptIn ) ) {
289 343 $submission->add_extra_attachments( $file );
344 + $this->get_logger()->debug( 'Attachment added to mail_1', [
345 + 'plugin' => 'double-opt-in',
346 + 'file' => $file,
347 + ] );
290 348 }
291 349
292 - /**
293 - * Enable / Disable the OptIn Mail Attachment for Mail 2.
294 - *
295 - * Allows Developers to Enable/Disable Mail attachments dynamically.
296 - *
297 - * @param bool $status Either add attachments (true) or not (false). Default: true
298 - * @param OptIn $OptIn The OptIn Object.
299 - *
300 - * @since 2.3.3
301 - */
302 350 if ( apply_filters( 'f12_cf7_doubleoptin_files_mail_2', true, $OptIn ) ) {
303 351 $submission->add_extra_attachments( $file, 'mail_2' );
352 + $this->get_logger()->debug( 'Attachment added to mail_2', [
353 + 'plugin' => 'double-opt-in',
354 + 'file' => $file,
355 + ] );
304 356 }
305 357 }
306 358 }
307 359 }