PluginProbe
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification / 3.1.0
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification v3.1.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
double-opt-in / compatibility / OptInFrontend.class.php

OptInFrontend.class.php in Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification 3.1.0, at compatibility/OptInFrontend.class.php

845 lines 24.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace forge12\contactform7\CF7DoubleOptIn;
4
5
6 use Forge12\Shared\Logger;
7 use Forge12\Shared\LoggerInterface;
8
9 if ( ! defined( 'ABSPATH' ) ) {
10 exit;
11 }
12
13 abstract class OptInFrontend {
14 /**
15 * The Type of the OptIn Form System
16 *
17 * @var string
18 */
19 protected string $type = '';
20
21 private LoggerInterface $logger;
22
23 /**
24 * Constructor for the class.
25 *
26 * This constructor registers the necessary actions to be performed,
27 * by hooking methods of the current class, for the following events:
28 * - 'f12_cf7_doubleoptin_before_send_default_mail'
29 * - 'f12_cf7_doubleoptin_after_send_default_mail'
30 * - 'f12_cf7_doubleoptin_trigger_default_mail'
31 * - 'shutdown'
32 *
33 * @return void
34 */
35 public function __construct( LoggerInterface $logger, string $type ) {
36 $this->logger = $logger;
37 $this->type = $type;
38
39 $this->get_logger()->debug( 'Base integration constructor called', [
40 'plugin' => 'double-opt-in',
41 'class' => __CLASS__,
42 'method' => __METHOD__,
43 'type' => $type,
44 ] );
45
46 add_action( 'f12_cf7_doubleoptin_before_send_default_mail', [ $this, 'beforeSendDefaultMail' ], 10, 1 );
47 $this->get_logger()->debug( 'Hook f12_cf7_doubleoptin_before_send_default_mail registered', [
48 'plugin' => 'double-opt-in',
49 ] );
50
51 add_action( 'f12_cf7_doubleoptin_after_send_default_mail', [ $this, 'afterSendDefaultMail' ], 10, 1 );
52 $this->get_logger()->debug( 'Hook f12_cf7_doubleoptin_after_send_default_mail registered', [
53 'plugin' => 'double-opt-in',
54 ] );
55
56 add_action( 'f12_cf7_doubleoptin_trigger_default_mail', [ $this, 'sendDefaultMail' ], 10, 1 );
57 $this->get_logger()->debug( 'Hook f12_cf7_doubleoptin_trigger_default_mail registered', [
58 'plugin' => 'double-opt-in',
59 ] );
60
61 add_action( 'shutdown', [ $this, 'removeFiles' ] );
62 $this->get_logger()->debug( 'Hook shutdown registered for removeFiles', [
63 'plugin' => 'double-opt-in',
64 ] );
65
66 add_action( 'init', [ $this, 'validateOptIn' ] );
67 $this->get_logger()->debug( 'Hook init registered for validateOptIn', [
68 'plugin' => 'double-opt-in',
69 ] );
70
71 add_filter( 'f12_cf7_doubleoptin_get_recipient_' . $this->type, [ $this, 'getRecipient' ], 10, 3 );
72 $this->get_logger()->debug( 'Filter f12_cf7_doubleoptin_get_recipient_' . $this->type . ' registered', [
73 'plugin' => 'double-opt-in',
74 ] );
75 }
76
77
78 public function get_logger() {
79 return $this->logger;
80 }
81
82 /**
83 * Retrieves the recipient for a given recipient name, form parameters, and post parameters.
84 *
85 * @param string $recipient The name of the recipient to retrieve.
86 * @param array $formParameter The array of form parameters.
87 * @param array $postParameter The array of post parameters.
88 *
89 * @return string The recipient for the given parameters.
90 */
91 abstract public function getRecipient( string $recipient, array $formParameter, array $postParameter ): string;
92
93 /**
94 * Sends a default mail for the given OptIn object.
95 *
96 * This method is declared as abstract, meaning that it must be implemented
97 * by any child class that extends the current class. The method takes
98 * a single parameter, $OptIn, of type OptIn. This parameter represents
99 * the OptIn object for which the default mail needs to be sent.
100 *
101 * This method should be overridden by child classes to define the specific
102 * logic for sending the default mail for the given OptIn object.
103 *
104 * Note that the implementation details of this method vary depending on the
105 * specific subclass. Therefore, the implementation code is not provided here.
106 *
107 * @param OptIn $OptIn The OptIn object for which the default mail needs to be sent.
108 *
109 * @return void
110 */
111 abstract public function sendDefaultMail( OptIn $OptIn ): void;
112
113 /**
114 * This method is used to perform necessary actions before sending the default mail.
115 *
116 * This method removes the filter for the forge12 spam captcha if the class
117 * '\forge12\contactform7\CF7Captcha\TimerValidatorCF7' exists. It removes the filters 'wpcf7_spam' for the methods
118 * '\forge12\contactform7\CF7Captcha::isSpam' and
119 * '\forge12\contactform7\CF7Captcha\CF7IPLog::isSpam', and also removes the action 'wpcf7_mail_sent' for the
120 * method
121 * '\forge12\contactform7\CF7Captcha\CF7IPLog::doLogIP', if these filters and action exist.
122 *
123 * Additionally, this method removes the filter 'wpcf7_spam' for the method 'wpcf7_recaptcha_verify_response' with
124 * a priority of 9.
125 *
126 * @return void
127 */
128 public function beforeSendDefaultMail() {
129 $this->get_logger()->debug( 'beforeSendDefaultMail called', [
130 'plugin' => 'double-opt-in',
131 'class' => __CLASS__,
132 'method' => __METHOD__,
133 ] );
134
135 // Remove the filter for the Forge12 spam captcha
136 if ( class_exists( '\forge12\contactform7\CF7Captcha\TimerValidatorCF7' ) ) {
137 remove_filter( 'wpcf7_spam', '\forge12\contactform7\CF7Captcha::isSpam' );
138 remove_filter( 'wpcf7_spam', '\forge12\contactform7\CF7Captcha\CF7IPLog::isSpam' );
139 remove_action( 'wpcf7_mail_sent', '\forge12\contactform7\CF7Captcha\CF7IPLog::doLogIP' );
140
141 $this->get_logger()->debug( 'Forge12 CF7Captcha filters and actions removed', [
142 'plugin' => 'double-opt-in',
143 ] );
144 }
145
146 // Remove the filter for the Google reCAPTCHA validation
147 remove_filter( 'wpcf7_spam', 'wpcf7_recaptcha_verify_response', 9 );
148
149 $this->get_logger()->debug( 'Google reCAPTCHA filter removed', [
150 'plugin' => 'double-opt-in',
151 ] );
152 }
153
154
155 /**
156 * Performs actions after sending the default mail.
157 *
158 * In this method, two filters and an action are added to the WordPress hooks system.
159 *
160 * The first filter is added with the hook name 'wpcf7_spam' and the callback
161 * function is set to 'wpcf7_recaptcha_verify_response'. The priority is set to 9
162 * and the number of accepted arguments for the callback function is 2.
163 * This filter is added only if the function 'wpcf7_recaptcha_verifiy_response'
164 * exists.
165 *
166 * The second filter is added with the hook name 'wpcf7_spam' and the callback
167 * function is set to '\forge12\contactform7\CF7Captcha\TimerValidatorCF7::isSpam'.
168 * The priority is set to 100 and the number of accepted arguments for the callback
169 * function is 2. This filter is added only if the class '\forge12\contactform7\CF7Captcha\TimerValidatorCF7'
170 * exists.
171 *
172 * The third filter is added with the hook name 'wpcf7_spam' and the callback
173 * function is set to '\forge12\contactform7\CF7Captcha\CF7IPLog::isSpam'.
174 * The priority is set to 100 and the number of accepted arguments for the callback
175 * function is 2. This filter is added only if the class '\forge12\contactform7\CF7Captcha\CF7IPLog'
176 * exists.
177 *
178 * An action is added with the hook name 'wpcf7_mail_sent' and the callback function
179 * is set to '\forge12\contactform7\CF7Captcha\CF7IPLog::doLogIP'. The priority is set
180 * to 100 and the number of accepted arguments for the callback function is 1.
181 * This action is added only if the class '\forge12\contactform7\CF7Captcha\CF7IPLog' exists.
182 *
183 * @return void
184 */
185 public function afterSendDefaultMail() {
186 $this->get_logger()->debug( 'afterSendDefaultMail called', [
187 'plugin' => 'double-opt-in',
188 'class' => __CLASS__,
189 'method' => __METHOD__,
190 ] );
191
192 // re-add the filter to ensure for all other forms the reCAPTCHA is used
193 if ( function_exists( 'wpcf7_recaptcha_verifiy_response' ) ) {
194 add_filter( 'wpcf7_spam', 'wpcf7_recaptcha_verify_response', 9, 2 );
195 $this->get_logger()->debug( 'Google reCAPTCHA filter re-added', [
196 'plugin' => 'double-opt-in',
197 ] );
198 }
199
200 // re-add the filter for the Forge12 spam captcha
201 if ( class_exists( '\forge12\contactform7\CF7Captcha\TimerValidatorCF7' ) ) {
202 add_filter( 'wpcf7_spam', '\forge12\contactform7\CF7Captcha\TimerValidatorCF7::isSpam', 100, 2 );
203 add_filter( 'wpcf7_spam', '\forge12\contactform7\CF7Captcha\CF7IPLog::isSpam', 100, 2 );
204 add_action( 'wpcf7_mail_sent', '\forge12\contactform7\CF7Captcha\CF7IPLog::doLogIP', 100, 1 );
205
206 $this->get_logger()->debug( 'Forge12 CF7Captcha filters and actions re-added', [
207 'plugin' => 'double-opt-in',
208 ] );
209 }
210 }
211
212 /**
213 * Updates the opt-in status by hash.
214 *
215 * @param string $hash The opt-in hash.
216 * @param int $value The opt-in value to set.
217 * @param OptIn|null $OptIn The opt-in object. Optional.
218 *
219 * @return int Returns 0 if the opt-in is already confirmed, 1 if the opt-in is successfully updated.
220 */
221 protected function updateOptInByHash( string $hash, int $value, ?OptIn $OptIn = null ): int {
222 $this->get_logger()->debug( 'updateOptInByHash called', [
223 'plugin' => 'double-opt-in',
224 'class' => __CLASS__,
225 'method' => __METHOD__,
226 'hash' => $hash,
227 'value' => $value,
228 ] );
229
230 $OptIn = $OptIn ?? OptIn::get_by_hash( $hash );
231
232 if ( ! $OptIn ) {
233 $this->get_logger()->warning( 'No OptIn found for hash', [
234 'plugin' => 'double-opt-in',
235 'hash' => $hash,
236 ] );
237 return 0;
238 }
239
240 if ( $OptIn->is_confirmed() ) {
241 $this->get_logger()->info( 'OptIn already confirmed, skipping update', [
242 'plugin' => 'double-opt-in',
243 'hash' => $hash,
244 'optin_id' => $OptIn->get_id(),
245 ] );
246
247 do_action( 'f12_cf7_doubleoptin_already_confirmed', $hash, $OptIn );
248 return 0;
249 }
250
251 do_action( 'f12_cf7_doubleoptin_before_confirm', $hash, $OptIn );
252
253 $OptIn->set_doubleoptin( $value );
254 $OptIn->set_updatetime( time() );
255 $OptIn->set_ipaddr_confirmation( IPHelper::getIPAdress() );
256
257 $telemetry = new Telemetry( $this->get_logger() );
258
259 $result = $OptIn->save();
260
261 if ( $result ) {
262 if ( (int)$value === 1 ) {
263 $telemetry->increment( 'confirmed_optins' );
264 }
265
266 $this->get_logger()->info( 'OptIn updated successfully', [
267 'plugin' => 'double-opt-in',
268 'hash' => $hash,
269 'optin_id' => $OptIn->get_id(),
270 ] );
271 do_action( 'f12_cf7_doubleoptin_after_confirm', $hash, $OptIn );
272 } else {
273 $this->get_logger()->error( 'Failed to update OptIn', [
274 'plugin' => 'double-opt-in',
275 'hash' => $hash,
276 'optin_id' => $OptIn->get_id(),
277 ] );
278 }
279
280 return (int) $result;
281 }
282
283
284 /**
285 * Add additional placeholder like time, date, subject
286 *
287 * @formatter:off
288 *
289 * @param string $body The content containing the placeholder that will be replaced.
290 * @param OptIn $OptIn The OptIn Object.
291 *
292 * @param array $parameter {
293 * @type string $formUrl The URL where the form is displayed.
294 * @type string $subject The Subject of the Form
295 * }
296 * #
297 * @formatter:on
298 */
299 protected function addPlaceholders( string $body, OptIn $OptIn, array $parameter ): string {
300 $this->get_logger()->debug( 'addPlaceholders called', [
301 'plugin' => 'double-opt-in',
302 'class' => __CLASS__,
303 'method' => __METHOD__,
304 'optin_id' => $OptIn->get_id(),
305 'parameter' => $parameter,
306 ] );
307
308 // set the default timezone
309 $timezone = get_option( 'timezone_string' );
310
311 // set fallback timezone
312 if ( empty( $timezone ) ) {
313 $timezone = 'Europe/Berlin';
314 $this->get_logger()->debug( 'No timezone set in options, using fallback Europe/Berlin', [
315 'plugin' => 'double-opt-in',
316 ] );
317 }
318
319 date_default_timezone_set( $timezone );
320
321 $placeholder = [
322 'doubleoptin_form_url' => $parameter['formUrl'] ?? '',
323 'doubleoptin_form_subject' => $parameter['subject'] ?? '',
324 'doubleoptin_form_date' => date( get_option( 'date_format' ) ),
325 'doubleoptin_form_time' => date( get_option( 'time_format' ) ),
326 'doubleoptin_form_email' => get_option( 'admin_email' ),
327 'doubleoptinlink' => $OptIn->get_link_optin( $parameter ),
328 'doubleoptoutlink' => $OptIn->get_link_optout(),
329 ];
330
331
332 foreach ( $placeholder as $key => $value ) {
333 if ( is_array( $value ) || is_object( $value ) ) {
334 // Array/Objekte serialisieren oder in JSON umwandeln
335 $value = wp_json_encode( $value );
336 }
337
338 $replacement = (string) ( $value ?? '' );
339
340 $body = str_replace( '[' . $key . ']', $replacement, $body );
341
342 // Logging je Platzhalter
343 $this->get_logger()->debug( 'Placeholder replaced', [
344 'plugin' => 'double-opt-in',
345 'placeholder' => '[' . $key . ']',
346 'value' => $replacement,
347 ] );
348 }
349
350 // Logging nach allen Ersetzungen
351 $this->get_logger()->debug( 'Placeholders replaced in body', [
352 'plugin' => 'double-opt-in',
353 'placeholders' => array_keys( $placeholder ),
354 'body_length' => strlen( $body ), // nur Info, wie groß der Text ist
355 ] );
356
357 return $body;
358 }
359
360
361 /**
362 * Add Stylesheets
363 */
364 public function validateOptIn(): bool {
365 /**
366 * Skip if the hash has not been submitted.
367 */
368 if ( ! isset( $_GET['optin'] ) ) {
369 $this->get_logger()->debug( 'No optin hash found in request, skipping', [
370 'plugin' => 'double-opt-in',
371 ] );
372 return false;
373 }
374
375 /**
376 * Get the Hash
377 */
378 $hash = sanitize_text_field( $_GET['optin'] );
379
380 /**
381 * Load the OptIn
382 */
383 $OptIn = OptIn::get_by_hash( $hash );
384
385 /**
386 * Skip if the OptIn does not exist
387 */
388 if ( null == $OptIn ) {
389 $this->get_logger()->warning( 'OptIn not found for hash', [
390 'plugin' => 'double-opt-in',
391 'hash' => $hash,
392 ] );
393 return false;
394 }
395
396 /**
397 * Skip if the OptIn is not from Type cf7.
398 */
399 if ( ! $OptIn->isType( $this->type ) ) {
400 $this->get_logger()->warning( 'OptIn type mismatch', [
401 'plugin' => 'double-opt-in',
402 'hash' => $hash,
403 'type' => $this->type,
404 'optin_id' => $OptIn->get_id(),
405 ] );
406 return false;
407 }
408
409 /**
410 * Skip if the OptIn has been confirmed already or could not be updated.
411 */
412 if ( $this->updateOptInByHash( $hash, 1 ) <= 0 ) {
413 $this->get_logger()->info( 'OptIn already confirmed or update failed', [
414 'plugin' => 'double-opt-in',
415 'hash' => $hash,
416 'optin_id' => $OptIn->get_id(),
417 ] );
418 return false;
419 }
420
421 /**
422 * Enable / Disable default mail.
423 *
424 * Filter to allow developer to enable / disable the default mail.
425 *
426 * @param bool $status Enable (true) or disable (false) the default mail.
427 * @param int $postId The ID of the Post / Form.
428 *
429 * @since 2.3.3
430 */
431 if ( ! apply_filters( 'f12_cf7_doubleoptin_send_default_mail', true, $OptIn->get_cf_form_id() ) ) {
432 $this->get_logger()->info( 'Default mail disabled for OptIn', [
433 'plugin' => 'double-opt-in',
434 'form_id' => $OptIn->get_cf_form_id(),
435 'optin_id' => $OptIn->get_id(),
436 ] );
437 return false;
438 }
439
440 /**
441 * Hook triggers before the default mail will be send.
442 *
443 * Action to allow developers to do custom actions before the default mail will be triggered.
444 *
445 * @param OptIn $OptIn
446 *
447 * @since 2.3.3
448 */
449 $this->get_logger()->debug( 'Triggering before_send_default_mail hook', [
450 'plugin' => 'double-opt-in',
451 'optin_id' => $OptIn->get_id(),
452 ] );
453 do_action( 'f12_cf7_doubleoptin_before_send_default_mail', $OptIn );
454
455 /**
456 * Hook triggers the default mail
457 *
458 * Action to allow developers to do custom actions before the default mail will be triggered.
459 *
460 * @param OptIn $OptIn
461 *
462 * @since 2.3.3
463 */
464 $this->get_logger()->info( 'Triggering send_default_mail hook', [
465 'plugin' => 'double-opt-in',
466 'optin_id' => $OptIn->get_id(),
467 ] );
468 do_action( 'f12_cf7_doubleoptin_trigger_default_mail', $OptIn );
469
470 /**
471 * Hook triggers after the default mail has been sent
472 *
473 * Action to allow developers to do custom actions after the default mail has been triggered.
474 *
475 * @param OptIn $OptIn
476 *
477 * @since 2.3.3
478 */
479 $this->get_logger()->debug( 'Triggering after_send_default_mail hook', [
480 'plugin' => 'double-opt-in',
481 'optin_id' => $OptIn->get_id(),
482 ] );
483 do_action( 'f12_cf7_doubleoptin_after_send_default_mail', $OptIn );
484
485 return true;
486 }
487
488
489
490 /**
491 * Store the files
492 *
493 * @param array $inFiles
494 *
495 * @return array
496 */
497 private function maybeStoreFiles( array $inFiles ): array {
498 $this->get_logger()->debug( 'maybeStoreFiles called', [
499 'plugin' => 'double-opt-in',
500 'class' => __CLASS__,
501 'method' => __METHOD__,
502 'files_in' => $inFiles,
503 ] );
504
505 $outFiles = [];
506
507 if ( empty( $inFiles ) ) {
508 $this->get_logger()->debug( 'No files provided to maybeStoreFiles', [
509 'plugin' => 'double-opt-in',
510 ] );
511 return $outFiles;
512 }
513
514 foreach ( $inFiles as $key => $subfiles ) {
515 foreach ( $subfiles as $file ) {
516 $newFile = $this->copyAndRenameFile( $file );
517 if ( $newFile ) {
518 $outFiles[] = $newFile;
519 $this->get_logger()->debug( 'File stored successfully', [
520 'plugin' => 'double-opt-in',
521 'original' => $file,
522 'new' => $newFile,
523 ] );
524 } else {
525 $this->get_logger()->warning( 'File could not be stored', [
526 'plugin' => 'double-opt-in',
527 'original' => $file,
528 ] );
529 }
530 }
531 }
532
533 $this->get_logger()->debug( 'maybeStoreFiles completed', [
534 'plugin' => 'double-opt-in',
535 'files_out' => $outFiles,
536 ] );
537
538 return $outFiles;
539 }
540
541
542 /**
543 * Copy and rename a file
544 *
545 * @param string $file The path to the file to copy and rename
546 *
547 * @return string|null The path to the copied and renamed file, or null if the copy operation failed
548 */
549 private function copyAndRenameFile( string $file ): ?string {
550 $this->get_logger()->debug( 'copyAndRenameFile called', [
551 'plugin' => 'double-opt-in',
552 'class' => __CLASS__,
553 'method' => __METHOD__,
554 'file' => $file,
555 ] );
556
557 $newFile = explode( '/', $file );
558 $name = $newFile[ count( $newFile ) - 1 ];
559 $name = time() . '_' . $name;
560 $newFile[ count( $newFile ) - 1 ] = $name;
561 $newFile = implode( "/", $newFile );
562
563 if ( copy( $file, $newFile ) ) {
564 $this->get_logger()->info( 'File copied and renamed successfully', [
565 'plugin' => 'double-opt-in',
566 'original' => $file,
567 'new_file' => $newFile,
568 ] );
569 return $newFile;
570 }
571
572 $this->get_logger()->error( 'Failed to copy and rename file', [
573 'plugin' => 'double-opt-in',
574 'original' => $file,
575 'target' => $newFile,
576 ] );
577
578 return null;
579 }
580
581
582 /**
583 * Create the OptIn
584 *
585 * @param int $formId The identifier of the form
586 * @param string $formHtml The HTML code of the form
587 * @param array $parameter The Post Parameter of the form.
588 * @param array $files The Files attached to the form.
589 *
590 * @return OptIn|null
591 */
592 protected function maybeCreateOptIn( int $formId, string $formHtml, array $parameter, array $files = array() ): ?OptIn {
593 $this->get_logger()->debug( 'maybeCreateOptIn called', [
594 'plugin' => 'double-opt-in',
595 'class' => __CLASS__,
596 'method' => __METHOD__,
597 'formId' => $formId,
598 'formHtml' => substr( $formHtml, 0, 200 ),
599 'files' => $files,
600 ] );
601
602 /**
603 * Mögliche Dateien speichern, um sie während der Opt-In-Bestätigung vorzuhalten
604 */
605 $files = $this->maybeStoreFiles( $files );
606 $this->get_logger()->debug( 'Files checked and possibly stored', [
607 'plugin' => 'double-opt-in',
608 'files' => $files,
609 ] );
610
611 /**
612 * Filter, um die Parameter vor dem Speichern in der Datenbank zu manipulieren
613 */
614 $parameter = \apply_filters( 'f12_cf7_doubleoptin_add_request_parameter', $parameter );
615 $this->get_logger()->debug( 'Request parameters filtered', [
616 'plugin' => 'double-opt-in',
617 'parameter' => $parameter,
618 ] );
619
620 /**
621 * Globale Einstellungen für das Formular abrufen
622 */
623 $formParameter = CF7DoubleOptIn::getInstance()->getParameter( $formId );
624
625 /**
626 * Filter, um den Empfänger zu ermitteln, bevor das OptIn-Objekt erstellt wird
627 */
628 $recipient = \apply_filters( 'f12_cf7_doubleoptin_get_recipient_' . $this->type, '', $formParameter, $parameter );
629
630 /**
631 * Wenn keine E-Mail-Adresse gefunden wurde, Abbruch
632 */
633 if ( empty( $recipient ) ) {
634 $this->get_logger()->warning( 'No recipient found, skipping OptIn creation', [
635 'plugin' => 'double-opt-in',
636 ] );
637 return null;
638 }
639
640 /**
641 * Eigenschaften des OptIn-Objekts festlegen
642 */
643 $properties = [
644 'cf_form_id' => $formId,
645 'doubleoptin' => 0,
646 'createtime' => time(),
647 'content' => maybe_serialize( $parameter ),
648 'files' => maybe_serialize( $files ),
649 'ipaddr_register' => IPHelper::getIPAdress(),
650 'category' => (int) $formParameter['category'],
651 'form' => $formHtml,
652 'email' => $recipient,
653 ];
654
655 $this->get_logger()->debug( 'OptIn properties created', [
656 'plugin' => 'double-opt-in',
657 'properties' => $properties,
658 ] );
659
660 /**
661 * OptIn-Objekt erstellen
662 */
663 $OptIn = new OptIn($this->get_logger(), $properties );
664 $this->get_logger()->debug( 'OptIn object instantiated', [
665 'plugin' => 'double-opt-in',
666 'OptIn' => $OptIn,
667 ] );
668
669 /**
670 * OptIn speichern
671 */
672 if ( $OptIn->save() ) {
673 $this->get_logger()->info( 'OptIn object saved successfully', [
674 'plugin' => 'double-opt-in',
675 'OptIn' => $OptIn,
676 ] );
677 return $OptIn;
678 }
679
680 $this->get_logger()->error( 'Failed to save OptIn object', [
681 'plugin' => 'double-opt-in',
682 ] );
683
684 return null;
685 }
686
687
688 /**
689 * Validate if the optin is enabled.
690 */
691 protected function isOptinEnabled( int $formId ): bool {
692 // Disable optin sending if the optin flag is set.
693 if ( isset( $_GET['optin'] ) ) {
694 $this->get_logger()->debug( 'Optin disabled due to optin flag in GET request', [
695 'plugin' => 'double-opt-in',
696 'class' => __CLASS__,
697 'method' => __METHOD__,
698 ] );
699 return false;
700 }
701
702 $parameter = CF7DoubleOptIn::getInstance()->getParameter( $formId );
703
704 if ( (int) $parameter['enable'] != 1 ) {
705 $this->get_logger()->debug( 'Optin not enabled in form parameter', [
706 'plugin' => 'double-opt-in',
707 'class' => __CLASS__,
708 'method' => __METHOD__,
709 'parameter' => $parameter,
710 ] );
711 return false;
712 }
713
714 // Check the custom condition
715 if ( isset( $parameter['conditions'] ) ) {
716 $condition = sanitize_text_field( $parameter['conditions'] );
717
718 if ( ( $condition != 'disable' && $condition !== 'disabled' ) && ( ! isset( $_POST[ $condition ] ) || empty( $_POST[ $condition ] ) ) ) {
719 $this->get_logger()->debug( 'Optin disabled due to unmet custom condition', [
720 'plugin' => 'double-opt-in',
721 'class' => __CLASS__,
722 'method' => __METHOD__,
723 'condition' => $condition,
724 'post' => $_POST,
725 ] );
726 return false;
727 }
728 }
729
730 $this->get_logger()->debug( 'Optin enabled', [
731 'plugin' => 'double-opt-in',
732 'class' => __CLASS__,
733 'method' => __METHOD__,
734 ] );
735
736 return true;
737 }
738
739
740 /**
741 * Removes files associated with the optin parameter.
742 *
743 * This method checks if the optin parameter is set and
744 * loads the OptIn object based on the hash value. If
745 * the OptIn does not exist or no files are found,
746 * the method will return. Otherwise, it will iterate
747 * through the files and delete each one.
748 *
749 * @return void
750 */
751 public function removeFiles(): void {
752 /**
753 * Skip if the optin parameter is not set.
754 */
755 if ( ! isset( $_GET['optin'] ) ) {
756 $this->get_logger()->debug( 'No optin parameter found, skipping file removal', [
757 'plugin' => 'double-opt-in',
758 'class' => __CLASS__,
759 'method' => __METHOD__,
760 ] );
761 return;
762 }
763
764 $hash = esc_sql( $_GET['optin'] );
765
766 /**
767 * Load the OptIn
768 */
769 $OptIn = OptIn::get_by_hash( $hash );
770
771 /**
772 * Skip if the OptIn does not exist
773 */
774 if ( null == $OptIn ) {
775 $this->get_logger()->warning( 'OptIn not found, skipping file removal', [
776 'plugin' => 'double-opt-in',
777 'class' => __CLASS__,
778 'method' => __METHOD__,
779 'hash' => $hash,
780 ] );
781 return;
782 }
783
784 /**
785 * Load all files
786 */
787 $files = maybe_unserialize( $OptIn->get_files() );
788
789 /**
790 * Skip if no files found
791 */
792 if ( empty( $files ) ) {
793 $this->get_logger()->debug( 'No files found in OptIn, skipping removal', [
794 'plugin' => 'double-opt-in',
795 'class' => __CLASS__,
796 'method' => __METHOD__,
797 'hash' => $hash,
798 ] );
799 return;
800 }
801
802 foreach ( $files as $file ) {
803 /**
804 * Skip if empty
805 */
806 if ( empty( $file ) ) {
807 continue;
808 }
809
810 /**
811 * Skip if no file found
812 */
813 if ( ! is_file( $file ) ) {
814 $this->get_logger()->warning( 'File not found, skipping', [
815 'plugin' => 'double-opt-in',
816 'class' => __CLASS__,
817 'method' => __METHOD__,
818 'file' => $file,
819 ] );
820 continue;
821 }
822
823 /**
824 * Delete the file
825 */
826 if ( unlink( $file ) ) {
827 $this->get_logger()->info( 'File deleted successfully', [
828 'plugin' => 'double-opt-in',
829 'class' => __CLASS__,
830 'method' => __METHOD__,
831 'file' => $file,
832 ] );
833 } else {
834 $this->get_logger()->error( 'Could not delete file', [
835 'plugin' => 'double-opt-in',
836 'class' => __CLASS__,
837 'method' => __METHOD__,
838 'file' => $file,
839 ] );
840 }
841 }
842 }
843
844
845 }