PluginProbe
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification / 3.0.71
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification v3.0.71
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 3.0.70 3.0.71 All 36 releases
double-opt-in / core / OptIn.class.php

OptIn.class.php in Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification 3.0.71, at core/OptIn.class.php

1,535 lines 45.2 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 if (!defined('ABSPATH')) {
5 exit;
6 }
7
8 /**
9 * Class Frontend
10 * Responsible to handle the frontend of the Double OptIn
11 *
12 * @package forge12\contactform7\CF7DoubleOptIn
13 */
14 class OptIn
15 {
16 /**
17 * Stores the DB ID for the entry
18 *
19 * @var int
20 */
21 private $id = 0;
22
23 /**
24 * Stores the ID of the contact form 7 form.
25 *
26 * @var int
27 */
28 private $cf_form_id = 0;
29 /**
30 * Stores if the optin has been confirmed (1) or not (0)
31 *
32 * @var int
33 */
34 private $doubleoptin = 0;
35 /**
36 * Stores the content of the contact form 7 form content as a serialized string.
37 *
38 * @var string
39 */
40 private $content = "";
41 /**
42 * Stores the unique hash used to identify the opt-ins
43 *
44 * @var string
45 */
46 private $hash = "";
47 /**
48 * Stores the timestamp the opt-in mail has been sent to the user.
49 *
50 * @var string
51 */
52 private $createtime = "";
53 /**
54 * Stores the timestamp the opt-in has been confirmed by the user.
55 */
56 private $updatetime = "";
57 /**
58 * Stores the timestamp the opt-out has been confirmed by the user.
59 */
60 private $optouttime = "";
61 /**
62 * IP Address used to trigger the opt-in mail.
63 *
64 * @var string
65 */
66 private $ipaddr_register = "";
67 /**
68 * IP Address used to confirm the opt-in mail.
69 */
70 private $ipaddr_confirmation = "";
71 /**
72 * IP Address used to output.
73 */
74 private $ipaddr_optout = "";
75 /**
76 * Stores the files used within this form as a serialized string.
77 */
78 private $files = "";
79 /**
80 * Store the Category ID
81 */
82 private $category = 0;
83 /**
84 * Stores the form as html
85 */
86 private $form = '';
87 /**
88 * Stores the Mail send for the optin
89 *
90 * @var string
91 */
92 private $mail_optin = '';
93 /**
94 * Stores the E-Mail
95 *
96 * @var string
97 */
98 private $email = '';
99
100 /**
101 * The constructor
102 */
103 public function __construct(array $properties)
104 {
105 foreach ($properties as $name => $value) {
106 if (isset($this->{$name})) {
107 $this->{$name} = $value;
108 }
109 }
110 }
111
112 /**
113 * @param int $cf_form_id
114 */
115 public function set_cf_form_id($cf_form_id)
116 {
117 $this->cf_form_id = (int)$cf_form_id;
118 }
119
120 /**
121 * @return int
122 */
123 public function get_cf_form_id()
124 {
125 return $this->cf_form_id;
126 }
127
128 /**
129 * Return the ID of the Optin
130 *
131 * @return int
132 */
133 public function get_id()
134 {
135 return $this->id;
136 }
137
138 /**
139 * Return the hash of the optin.
140 *
141 * @return string
142 */
143 public function get_hash()
144 {
145 return $this->hash;
146 }
147
148 /**
149 * Return true|false depending on confirm status
150 *
151 * @return bool
152 */
153 public function is_confirmed()
154 {
155 return (bool)$this->get_doubleoptin();
156 }
157
158 /**
159 * Return true|false wether the form has been confirmed or not.
160 *
161 * @return bool
162 */
163 public function is_optout()
164 {
165 if (true != $this->get_doubleoptin() && !empty($this->get_ipaddr_optout()) && !empty($this->get_optouttime())) {
166 return true;
167 }
168
169 return false;
170 }
171
172 /**
173 * update the optin value
174 *
175 * @param int $confirmed
176 */
177 public function set_doubleoptin($confirmed)
178 {
179 $this->doubleoptin = (int)$confirmed;
180 }
181
182 /**
183 * Return the optin value for the form.
184 *
185 * @return int
186 */
187 public function get_doubleoptin()
188 {
189 return $this->doubleoptin;
190 }
191
192 /**
193 * @param string $timestamp
194 */
195 public function set_createtime($timestamp)
196 {
197 $this->createtime = $timestamp;
198 }
199
200 /**
201 * Return the Date how long the opt in will be valid
202 *
203 * @return string
204 */
205 public function get_valid_until()
206 {
207 $settings = CF7DoubleOptIn::getInstance()->getSettings();
208
209 $createtime = $this->get_createtime();
210 $dt = new \DateTime();
211 $dt->setTimestamp($createtime);
212
213 if ($this->is_confirmed()) {
214 $month = (int)$settings['delete'];
215 $period = $settings['delete_period'];
216 } else {
217 $month = (int)$settings['delete_unconfirmed'];
218 $period = $settings['delete_unconfirmed_period'];
219 }
220
221 $dt->modify('+' . (int)$month . ' ' . $period);
222 $dt->modify('+1 day');
223
224 return $dt->format('d.m.Y');
225 }
226
227 /**
228 * Return a timestamp
229 *
230 * @param string $view Select how to return the content. raw is the stored db value. use formatted to return a
231 * formatted string.
232 *
233 * @return string
234 */
235 public function get_createtime($view = "raw")
236 {
237 if ($view != "raw") {
238 if (!is_numeric($this->createtime)) {
239 $date = date('d.m.Y', strtotime($this->createtime));
240 $time = date('H:i:s', strtotime($this->createtime));
241 } else {
242 $date = date('d.m.Y', $this->createtime);
243 $time = date('H:i:s', $this->createtime);
244 }
245
246 return $date . ' ' . __('/', 'double-opt-in') . ' ' . $time;
247 } else {
248 return $this->createtime;
249 }
250 }
251
252 /**
253 * @param string $timestamp
254 */
255 public function set_updatetime($timestamp)
256 {
257 $this->updatetime = $timestamp;
258 }
259
260 /**
261 * Return a timestamp
262 *
263 * @param string $view Select how to return the content. raw is the stored db value. use formatted to return a
264 * formatted string.
265 *
266 * @return string
267 */
268 public function get_updatetime($view = 'raw')
269 {
270 if ($view != "raw") {
271 if (!is_numeric($this->updatetime)) {
272 $date = date('d.m.Y', strtotime($this->updatetime));
273 $time = date('H:i:s', strtotime($this->updatetime));
274 } else {
275 $date = date('d.m.Y', $this->updatetime);
276 $time = date('H:i:s', $this->updatetime);
277 }
278
279 return $date . ' ' . __('/', 'double-opt-in') . ' ' . $time;
280 } else {
281 return $this->updatetime;
282 }
283 }
284
285 /**
286 * @param string $timestamp
287 */
288 public function set_optouttime($timestamp)
289 {
290 $this->optouttime = $timestamp;
291 }
292
293 /**
294 * Return a timestamp
295 *
296 * @param string $view Select how to return the content. raw is the stored db value. use formatted to return a
297 * formatted string.
298 *
299 * @return string
300 */
301 public function get_optouttime($view = 'raw')
302 {
303 if ($view != "raw") {
304 if (!is_numeric($this->optouttime)) {
305 $date = date('d.m.Y', strtotime($this->optouttime));
306 $time = date('H:i:s', strtotime($this->optouttime));
307 } else {
308 $date = date('d.m.Y', $this->optouttime);
309 $time = date('H:i:s', $this->optouttime);
310 }
311
312 return $date . ' ' . __('/', 'f12-cf7-doubleoptin') . ' ' . $time;
313 } else {
314 return $this->optouttime;
315 }
316 }
317
318 /**
319 * @param string $ip_addr
320 */
321 public function set_ipaddr_optout($ip_addr)
322 {
323 $this->ipaddr_optout = $ip_addr;
324 }
325
326 /**
327 * @return string
328 */
329 public function get_ipaddr_optout()
330 {
331 return $this->ipaddr_optout;
332 }
333
334 /**
335 * @param string $ip_addr
336 */
337 public function set_ipaddr_register($ip_addr)
338 {
339 $this->ipaddr_register = $ip_addr;
340 }
341
342 /**
343 * @return string
344 */
345 public function get_ipaddr_register()
346 {
347 return $this->ipaddr_register;
348 }
349
350 /**
351 * @param string $ip_addr
352 */
353 public function set_ipaddr_confirmation($ip_addr)
354 {
355 $this->ipaddr_confirmation = $ip_addr;
356 }
357
358 /**
359 * @return string
360 */
361 public function get_ipaddr_confirmation()
362 {
363 return $this->ipaddr_confirmation;
364 }
365
366 /**
367 * Return a serialized string
368 *
369 * @return mixed
370 */
371 public function get_content()
372 {
373 return $this->content;
374 }
375
376 /**
377 * @param string - a serialized string
378 */
379 public function set_content($content)
380 {
381 $this->content = $content;
382 }
383
384 /**
385 * @param string - a serialized string
386 */
387 public function set_files($files)
388 {
389 $this->files = $files;
390 }
391
392 /**
393 * @return string use maybe_unserialize to deserialize the string
394 */
395 public function get_files()
396 {
397 return $this->files;
398 }
399
400 /**
401 * Return the assigned Category ID
402 *
403 * @return int
404 */
405 public function get_category()
406 {
407 if (!is_numeric($this->category)) {
408 return 0;
409 }
410
411 return $this->category;
412 }
413
414 /**
415 * Assign a ID to the category
416 *
417 * @param int $id
418 *
419 * @return void
420 */
421 public function set_category($id)
422 {
423 $this->category = $id;
424 }
425
426 /**
427 * Check if the given Opt In was created by Contact Form 7
428 *
429 * @return bool
430 */
431 public function isTypeCF7()
432 {
433 $form_id = $this->get_cf_form_id();
434 $form = get_post($form_id);
435
436 if ('wpcf7_contact_form' === $form->post_type) {
437 return true;
438 }
439
440 return false;
441 }
442
443 /**
444 * Check if the given Opt In was created by Elementor
445 *
446 * @return bool
447 */
448 public function isTypeElementor()
449 {
450 if (!class_exists('ElementorPro\Plugin')) {
451 return false;
452 }
453
454 $page_id = $this->get_cf_form_id();
455
456 $post = get_post($page_id);
457
458 // skip if post not existing anymore
459 if (null === $post) {
460 return false;
461 }
462
463 // skip if page is not build with elementor
464 if (!\Elementor\Plugin::$instance->documents->get($page_id)->is_built_with_elementor()) {
465 return false;
466 }
467
468 // Load the page's data
469 $document = \Elementor\Plugin::$instance->documents->get($page_id);
470
471 // Skip if document not exist
472 if (!$document) {
473 return false;
474 }
475
476 // Loop through the elements
477 $elements_data = $document->get_elements_data();
478
479 $content = maybe_unserialize($this->get_content());
480
481 // skip if form id not available
482 if (!isset($content['form_id'])) {
483 return false;
484 }
485
486 $form_id = $content['form_id'];
487
488 // Find the Form Element
489 foreach ($elements_data as $element) {
490 if (isset($element['elements'])) {
491 foreach ($element['elements'] as $el) {
492 if ($form_id !== $el['id'] || 'form' !== $el['widgetType']) {
493 continue;
494 }
495
496 return true;
497 }
498 return true;
499 }
500
501 // Check if this is the form widget
502 if (isset($element['widgetType']) && isset($element['id']) && $element['widgetType'] === 'form' && $element['id'] === $form_id) {
503 // Return the form settings
504 return true;
505 }
506 }
507 return false;
508 }
509
510 /**
511 * Check if the given Opt In was created by Avada
512 *
513 * @return bool|void
514 */
515 public function isTypeAvada()
516 {
517 $form_id = $this->get_cf_form_id();
518 $form = get_post($form_id);
519
520 if ('fusion_form' === $form->post_type) {
521 return true;
522 }
523
524 return false;
525 }
526
527 /**
528 * Check if the Opt In was created by 'cf7' or 'avada'.
529 *
530 * @param string $type use either 'cf7' or 'avada'
531 *
532 * @return bool
533 */
534 public function isType($type)
535 {
536 if ($type == 'cf7') {
537 return $this->isTypeCF7();
538 } elseif ($type == 'elementor') {
539 return $this->isTypeElementor();
540 } else {
541 return $this->isTypeAvada();
542 }
543 }
544
545 /**
546 * Load an OptIn by the given hash code. Returns either an Object of Type OptIn or null if not found.
547 *
548 * @param string $hash
549 *
550 * @return OptIn|null
551 */
552 public static function get_by_hash(string $hash)
553 {
554 global $wpdb;
555 $table = $wpdb->prefix . 'f12_cf7_doubleoptin';
556
557 if (null == $wpdb) {
558 return null;
559 }
560
561 $row = $wpdb->get_row('SELECT * FROM ' . $table . ' WHERE hash="' . $hash . '"', ARRAY_A);
562
563 if (null == $row) {
564 return null;
565 }
566
567 return new OptIn($row);
568 }
569
570 /**
571 * Get number of optins for the given post.
572 *
573 * @param $post_id
574 *
575 * @return int
576 */
577 public static function get_count($post_id)
578 {
579 global $wpdb;
580
581 if (!$wpdb) {
582 return 0;
583 }
584
585 $tableName = $wpdb->prefix . 'f12_cf7_doubleoptin';
586 $result = $wpdb->get_results($wpdb->prepare('SELECT count(*) AS counter FROM ' . $tableName . ' WHERE cf_form_id=%d ', $post_id));
587
588 if (is_array($result)) {
589 return $result[0]->counter;
590 }
591
592 return 0;
593 }
594
595 /**
596 * Return a list of opt ins stored in the database.
597 *
598 * @param int $category_id
599 *
600 * @param array $atts array (
601 * 'perPage' => 10, // Posts per page
602 * 'page' => 0, // Current page
603 * 'order' => DESC, // Order (ASC/DESC)
604 * );
605 *
606 * @param null $numberOfPages - Stores the number of pages found if limited
607 *
608 * @return array
609 */
610 public static function get_list_by_category_id($category_id, $atts = [], &$numberOfPages = null)
611 {
612 global $wpdb;
613
614 $attr = [
615 'perPage' => 10,
616 'page' => 1,
617 'order' => 'DESC',
618 'keyword' => ''
619 ];
620
621 /**
622 * Parameter
623 */
624 foreach ($atts as $key => $value) {
625 if (isset($attr[$key])) {
626 $attr[$key] = $atts[$key];
627 }
628 }
629
630 /*
631 * Update keyword
632 */
633 $where = '';
634 if (!empty($attr['keyword'])) {
635 $where = ' AND content LIKE "%%' . $attr['keyword'] . '%%"';
636 }
637
638
639 $tableName = $wpdb->prefix . 'f12_cf7_doubleoptin';
640 $pageNum = (int)$attr['page'] - 1;
641
642 if ($numberOfPages !== null) {
643 $result = $wpdb->get_results($wpdb->prepare('SELECT count(*) AS counter FROM ' . $tableName . ' WHERE category=%d ' . $where, $category_id));
644
645 $itemCounter = 0;
646
647 if (is_array($result)) {
648 $itemCounter = $result[0]->counter;
649 }
650
651 $numberOfPages = 0;
652
653 if ($itemCounter != 0) {
654 $numberOfPages = ceil($itemCounter / (int)$attr['perPage']);
655 }
656 }
657
658 $list = [];
659
660 if ($numberOfPages == 0) {
661 return $list;
662 }
663
664 $offset = $pageNum * (int)$attr['perPage'];
665
666 $rows = $wpdb->get_results($wpdb->prepare('SELECT * FROM ' . $tableName . ' WHERE category=%d ' . $where . ' ORDER BY ID ' . $attr['order'] . ' LIMIT ' . $attr['perPage'] . ' OFFSET %d', $category_id, $offset), ARRAY_A);
667
668 foreach ($rows as $row) {
669 $list[] = new OptIn($row);
670 }
671
672 return $list;
673 }
674
675 /**
676 * Return a list of opt ins stored in the database.
677 *
678 * @param string $email
679 *
680 * @return array
681 */
682 public static function get_list_by_email($email)
683 {
684 global $wpdb;
685
686 $tableName = $wpdb->prefix . 'f12_cf7_doubleoptin';
687
688 $list = [];
689
690 $rows = $wpdb->get_results($wpdb->prepare('SELECT * FROM ' . $tableName . ' WHERE email=%s', $email), ARRAY_A);
691
692 foreach ($rows as $row) {
693 $list[] = new OptIn($row);
694 }
695
696 return $list;
697 }
698
699 /**
700 * Return a list of opt ins stored in the database.
701 *
702 * @param string $email
703 *
704 * @return array
705 */
706 public static function get_list_by_email_confirmed($email)
707 {
708 global $wpdb;
709
710 $tableName = $wpdb->prefix . 'f12_cf7_doubleoptin';
711
712 $list = [];
713
714 $rows = $wpdb->get_results($wpdb->prepare('SELECT * FROM ' . $tableName . ' WHERE email=%s AND doubleoptin=1', $email), ARRAY_A);
715
716 foreach ($rows as $row) {
717 $list[] = new OptIn($row);
718 }
719
720 return $list;
721 }
722
723 /**
724 * Return a list of opt ins stored in the database.
725 *
726 * @param string $email
727 *
728 * @return array
729 */
730 public static function get_list_by_email_unconfirmed($email)
731 {
732 global $wpdb;
733
734 $tableName = $wpdb->prefix . 'f12_cf7_doubleoptin';
735
736 $list = [];
737
738 $rows = $wpdb->get_results($wpdb->prepare('SELECT * FROM ' . $tableName . ' WHERE email=%s AND doubleoptin=0', $email), ARRAY_A);
739
740 foreach ($rows as $row) {
741 $list[] = new OptIn($row);
742 }
743
744 return $list;
745 }
746
747 /**
748 * Return a list of opt ins stored in the database.
749 *
750 * @param $atts array (
751 * 'perPage' => 10, // Posts per page
752 * 'page' => 0, // Current page
753 * 'order' => DESC, // Order (ASC/DESC)
754 * );
755 *
756 * @param null $numberOfPages - Stores the number of pages found if limited
757 *
758 * @return array
759 */
760 public static function get_list($atts = [], &$numberOfPages = null)
761 {
762 global $wpdb;
763
764 $attr = [
765 'perPage' => 10,
766 'page' => 1,
767 'order' => 'DESC',
768 'keyword' => '',
769 'cf_form_id' => '',
770 ];
771
772 /*
773 * Update atts
774 */
775 foreach ($atts as $key => $value) {
776 if (isset($attr[$key])) {
777 $attr[$key] = $atts[$key];
778 }
779 }
780
781 /*
782 * Update WHERE for Query
783 */
784 $where = '';
785 $where_condition = [];
786
787 /*
788 * Add Keyword
789 */
790 if (!empty($attr['keyword'])) {
791 $where_condition[] = 'content LIKE "%%' . $attr['keyword'] . '%%"';
792 }
793
794 /*
795 * Add Form ID
796 */
797 if (!empty($attr['cf_form_id'])) {
798 $where_condition[] = 'cf_form_id="' . $attr['cf_form_id'] . '"';
799 }
800
801 /*
802 * build where string
803 */
804 if (!empty($where_condition)) {
805 $where = ' WHERE ' . implode(' AND ', $where_condition);
806 }
807
808 /*
809 * set table
810 */
811 $tableName = $wpdb->prefix . 'f12_cf7_doubleoptin';
812 $pageNum = (int)$attr['page'] - 1;
813
814 if ($numberOfPages !== null) {
815 $result = $wpdb->get_results('SELECT count(*) as counter FROM ' . $tableName . $where);
816
817 $itemCounter = 0;
818
819 if (is_array($result)) {
820 $itemCounter = $result[0]->counter;
821 }
822
823 $numberOfPages = 0;
824
825 if ($itemCounter != 0) {
826 $numberOfPages = ceil($itemCounter / (int)$attr['perPage']);
827 }
828 }
829
830 $list = [];
831
832 if ($numberOfPages == 0) {
833 return $list;
834 }
835
836 $offset = $pageNum * (int)$attr['perPage'];
837
838 $rows = $wpdb->get_results($wpdb->prepare('SELECT * FROM ' . $tableName . $where . ' ORDER BY ID ' . $attr['order'] . ' LIMIT ' . $attr['perPage'] . ' OFFSET % d', $offset), ARRAY_A);
839
840 foreach ($rows as $row) {
841 $list[] = new OptIn($row);
842 }
843
844 return $list;
845 }
846
847 /**
848 * This will update all categories from A to B, e.g.: All Opt-Ins with id 1 to category id 2.
849 *
850 * @param int $from_id The origin Category ID
851 * @param int $to_id The new Category ID
852 *
853 * @return bool|int|\mysqli_result|resource|null Return the number of rows affected by the query.
854 */
855 public static function bulk_update_category($from_id, $to_id)
856 {
857 global $wpdb;
858 $table = $wpdb->prefix . 'f12_cf7_doubleoptin';
859
860 return $wpdb->query($wpdb->prepare('UPDATE ' . $table . ' SET category =%d WHERE category =%d', $to_id, $from_id));
861 }
862
863 /**
864 * Update the category of the given opt in id.
865 *
866 * @param int $optin_id The ID of the OptIn
867 * @param int $category_id The ID of the Category
868 *
869 * @return bool|int|\mysqli_result|resource|null
870 */
871 public static function update_category_by_id($optin_id, $category_id)
872 {
873 global $wpdb;
874 $table = $wpdb->prefix . 'f12_cf7_doubleoptin';
875
876 return $wpdb->query($wpdb->prepare('UPDATE ' . $table . ' SET category =%d WHERE id =%d', $category_id, $optin_id));
877 }
878
879 /**
880 * Update or create the given Object
881 */
882 public function save()
883 {
884 global $wpdb;
885 $table = $wpdb->prefix . 'f12_cf7_doubleoptin';
886
887 f12_debug_log('save method called', [
888 'table' => $table,
889 'hash' => $this->get_hash(),
890 ]);
891
892 /**
893 * Wenn ein Hash existiert, wird die Zeile aktualisiert
894 */
895 if (!empty($this->get_hash())) {
896 f12_debug_log('Updating existing record', [
897 'hash' => $this->get_hash(),
898 'cf_form_id' => $this->get_cf_form_id(),
899 'doubleoptin' => $this->get_doubleoptin(),
900 ]);
901
902 $result = $wpdb->update($table, [
903 'doubleoptin' => $this->get_doubleoptin(),
904 'createtime' => $this->get_createtime(),
905 'updatetime' => $this->get_updatetime(),
906 'ipaddr_confirmation' => $this->get_ipaddr_confirmation(),
907 'ipaddr_register' => $this->get_ipaddr_register(),
908 'cf_form_id' => $this->get_cf_form_id(),
909 'content' => $this->get_content(),
910 'files' => $this->get_files(),
911 'category' => $this->get_category(),
912 'optouttime' => $this->get_optouttime(),
913 'ipaddr_optout' => $this->get_ipaddr_optout(),
914 'mail_optin' => $this->get_mail_optin(),
915 ], [
916 'hash' => $this->get_hash()
917 ]);
918
919 f12_debug_log('Update operation completed', ['result' => $result]);
920
921 return $result;
922 }
923
924 /**
925 * Wenn kein Hash existiert, wird ein neuer Datensatz eingefügt
926 */
927 f12_debug_log('Inserting new record', [
928 'cf_form_id' => $this->get_cf_form_id(),
929 'email' => $this->get_email(),
930 'doubleoptin' => $this->get_doubleoptin(),
931 ]);
932
933 $result = $wpdb->insert(
934 $table,
935 $data = [ // Daten in einer Variablen speichern, um sie anschließend zu loggen
936 'cf_form_id' => $this->get_cf_form_id(),
937 'doubleoptin' => $this->get_doubleoptin(),
938 'createtime' => $this->get_createtime(),
939 'updatetime' => $this->get_updatetime(),
940 'ipaddr_confirmation' => $this->get_ipaddr_confirmation(),
941 'ipaddr_register' => $this->get_ipaddr_register(),
942 'content' => $this->get_content(),
943 'files' => $this->get_files(),
944 'category' => $this->get_category(),
945 'form' => $this->get_form(true),
946 'mail_optin' => $this->get_mail_optin(),
947 'email' => $this->get_email(),
948 ]
949 );
950
951 $formValue = $this->get_form();
952 f12_debug_log('Form Data Information', [
953 'form_length' => strlen($formValue), // Länge des Inhalts
954 'form_data_sample' => substr($formValue, 0, 500), // Nur die ersten 500 Zeichen loggen
955 ]);
956
957 $log_data = $data;
958 $log_data['form'] = substr($formValue, 0, 200);
959
960 f12_debug_log('Preparing to insert new record', [
961 'table' => $table,
962 'data' => $log_data
963 ]);
964
965 if (false === $result) {
966 f12_debug_log('Insert operation failed', [
967 'mysql_error' => $wpdb->last_error
968 ]);
969
970 $this->validateEncoding($formValue);
971 $this->validateMaxAllowedPacket($formValue);
972 $this->validateFormField();
973
974 return false;
975 }
976
977 f12_debug_log('Insert operation completed', [
978 'result' => $result,
979 'insert_id' => $wpdb->insert_id,
980 ]);
981
982 if ($result > 0) {
983 $this->id = $wpdb->insert_id;
984 f12_debug_log('New record inserted, ID assigned', ['id' => $this->id]);
985
986 $hashResult = $this->createHash();
987 f12_debug_log('Hash created for new record', ['hashResult' => $hashResult]);
988
989 return $hashResult;
990 }
991
992 f12_debug_log('Failed to insert new record');
993 return false;
994 }
995
996 private function validateFormField()
997 {
998 global $wpdb;
999 // SQL-Anfrage, um die Felddefinitionen abzurufen
1000 $table = $wpdb->prefix . 'f12_cf7_doubleoptin';
1001
1002 $columns = $wpdb->get_results("SHOW FIELDS FROM $table");
1003
1004 // Überprüfung der Feldtypen
1005 foreach ($columns as $column) {
1006 f12_debug_log('Field Info', [
1007 'Field' => $column->Field,
1008 'Type' => $column->Type,
1009 'Null' => $column->Null,
1010 'Default' => $column->Default
1011 ]);
1012 }
1013 }
1014
1015 private function validateEncoding(string $data)
1016 {
1017 if (!mb_check_encoding($data, 'UTF-8')) {
1018 f12_debug_log('Encoding Issue', ['form_data' => substr($data, 0, 500)]);
1019 return false;
1020 }
1021 f12_debug_log('Encoding OK', []);
1022 return true;
1023 }
1024
1025 private function validateMaxAllowedPacket(string $data)
1026 {
1027 $maxAllowedPacketMB = $this->getMaxAllowedPacket();
1028
1029 $dataSize = strlen($data); // Size in bytes
1030 $dataSizeMB = $dataSize / (1024 * 1024); // Convert bytes to MB
1031
1032 f12_debug_log('Data Size Check', [
1033 'data_size_bytes' => $dataSize,
1034 'data_size_mb' => $dataSizeMB,
1035 ]);
1036
1037 if ($dataSize > $maxAllowedPacketMB) {
1038 f12_debug_log('max_allowed_packet Too Small', [
1039 'data_size_mb' => $dataSizeMB,
1040 'max_allowed_packet_mb' => $maxAllowedPacketMB
1041 ]);
1042 return false;
1043 } else {
1044 f12_debug_log('max_allowed_packet OK', []);
1045 return true;
1046 }
1047 }
1048
1049 private function getMaxAllowedPacket()
1050 {
1051 global $wpdb;
1052
1053 $result = $wpdb->get_row("SHOW VARIABLES LIKE 'max_allowed_packet'", OBJECT);
1054
1055 /**
1056 * Prüfen, ob die Variable erfolgreich abgerufen wurde
1057 * Die zweite Spalte der Abfrage enthält den Wert (Value)
1058 */
1059 // Abfrage, um den aktuellen Wert von max_allowed_packet zu erhalten
1060 if ($result) {
1061 $maxAllowedPacket = $result->Value; // Wert aus der zweiten Spalte
1062 f12_debug_log('MySQL max_allowed_packet Value', [
1063 'max_allowed_packet_bytes' => $maxAllowedPacket,
1064 'max_allowed_packet_mb' => $maxAllowedPacket / (1024 * 1024) // In MB
1065 ]);
1066 } else {
1067 // Falls das Ergebnis leer ist (z. B. Fehler oder keine Berechtigung)
1068 f12_debug_log('Error fetching max_allowed_packet', [
1069 'error' => $wpdb->last_error
1070 ]);
1071 return 0;
1072 }
1073 return $maxAllowedPacket;
1074 }
1075
1076 /**
1077 * Creates a unique hash code and adds it to the current OptIn Object
1078 */
1079 private function createHash()
1080 {
1081 global $wpdb;
1082
1083 if (null == $wpdb) {
1084 return false;
1085 }
1086
1087 $table = $wpdb->prefix . 'f12_cf7_doubleoptin';
1088
1089 // add the hash to the element
1090
1091 $this->hash = base64_encode($this->get_createtime() . $this->get_id() . $this->get_cf_form_id());
1092
1093 return $wpdb->update($table, [
1094 'hash' => $this->hash,
1095 ], [
1096 'id' => $this->get_id()
1097 ]);
1098 }
1099
1100 private function is_base64_encoded($string)
1101 {
1102 // Prüfen, ob der String Base64-kompatible Zeichen enthält
1103 if (preg_match('/^[A-Za-z0-9+\/=]*$/', $string)) {
1104 // Prüfen, ob die Länge des Strings ein Vielfaches von 4 ist
1105 return strlen($string) % 4 === 0;
1106 }
1107 return false;
1108 }
1109
1110 /**
1111 * Return the Form
1112 *
1113 * @return string
1114 */
1115 public function get_form($encoded = false)
1116 {
1117 if (!$encoded) {
1118 if ($this->is_base64_encoded($this->form)) {
1119 return base64_decode($this->form);
1120 } else {
1121 return $this->form;
1122 }
1123 } else {
1124 if ($this->is_base64_encoded($this->form)) {
1125 return $this->form;
1126 } else {
1127 return base64_encode($this->form);
1128 }
1129 }
1130 }
1131
1132 /**
1133 * Returns the Name of the form if available
1134 *
1135 * @return string The name of the Form
1136 * @since 2.4.0
1137 */
1138 public function get_form_name()
1139 {
1140 $post = get_post($this->get_cf_form_id());
1141
1142 if (null === $post) {
1143 return __('Deleted', 'double-opt-in');
1144 }
1145
1146 return $post->post_title;
1147 }
1148
1149 /**
1150 * Returns the URL to the form depending on the Form Type.
1151 *
1152 * @return string The Backend URL to the Form, empty if the post is not found.
1153 * @since 2.4.0
1154 *
1155 */
1156 public function get_form_link()
1157 {
1158 if ($this->isTypeElementor()) {
1159 return get_edit_post_link($this->get_cf_form_id());
1160 } elseif ($this->isTypeAvada()) {
1161 return get_edit_post_link($this->get_cf_form_id());
1162 } elseif ($this->isTypeCF7()) {
1163 $post = get_post($this->get_cf_form_id());
1164
1165 if (!$post) {
1166 return '';
1167 }
1168
1169 return admin_url('admin.php?page=wpcf7&post=' . esc_attr($this->get_cf_form_id()) . '&action=edit');
1170 }
1171
1172 return '';
1173 }
1174
1175
1176 /**
1177 * Retrieves the form settings.
1178 *
1179 * If the form is created using Elementor, retrieves the settings using the `get_settings_by_elementor()`
1180 * method.
1181 *
1182 * @return array The form settings.
1183 * @since 2.4.0
1184 *
1185 */
1186 public function get_form_settings()
1187 {
1188 if ($this->isTypeElementor()) {
1189 return $this->get_settings_by_elementor();
1190 } else if ($this->isTypeCF7()) {
1191 return $this->get_settings_by_cf7();
1192 } else if ($this->isTypeAvada()) {
1193 return $this->get_settings_by_avada();
1194 }
1195
1196 return [];
1197 }
1198
1199 /**
1200 * Retrieves the settings of a specific element using avada.
1201 *
1202 * @return array
1203 * @since 2.4.0
1204 *
1205 * @see OptIn::maybe_normalize_settings() for details
1206 */
1207 private function get_settings_by_avada()
1208 {
1209
1210 $form_id = $this->get_cf_form_id();
1211
1212 $settings = CF7DoubleOptIn::getInstance()->getParameter($form_id);
1213
1214 return $this->maybe_normalize_settings($settings);
1215 }
1216
1217 /**
1218 * Retrieves the settings of a specific element using cf7.
1219 *
1220 * @return array
1221 * @since 2.4.0
1222 *
1223 * @see OptIn::maybe_normalize_settings() for details
1224 */
1225 private function get_settings_by_cf7()
1226 {
1227 $form_id = $this->get_cf_form_id();
1228
1229 $settings = get_post_meta($form_id, 'f12-cf7-doubleoptin', true);
1230
1231 if (null == $settings || !is_array($settings)) {
1232 return [];
1233 }
1234
1235 $settings = $this->maybe_normalize_settings($settings);
1236
1237 return $settings;
1238 }
1239
1240 /**
1241 * Retrieves the settings of a specific element using Elementor.
1242 *
1243 * @return array
1244 * @since 2.4.0
1245 *
1246 * @see OptIn::maybe_normalize_settings() for details
1247 */
1248 private function get_settings_by_elementor()
1249 {
1250 $page_id = $this->get_cf_form_id();
1251
1252 $post = get_post($page_id);
1253
1254 // skip if post not existing anymore
1255 if (null === $post) {
1256 return [];
1257 }
1258
1259 // skip if page is not build with elementor
1260 if (!\Elementor\Plugin::$instance->documents->get($page_id)->is_built_with_elementor()) {
1261 return [];
1262 }
1263
1264 // Load the page's data
1265 $document = \Elementor\Plugin::$instance->documents->get($page_id);
1266
1267 // Skip if document not exist
1268 if (!$document) {
1269 return [];
1270 }
1271
1272 // Loop through the elements
1273 $elements_data = $document->get_elements_data();
1274
1275 $content = maybe_unserialize($this->get_content());
1276
1277 // skip if form id not available
1278 if (!isset($content['form_id'])) {
1279 return [];
1280 }
1281
1282 $form_id = $content['form_id'];
1283
1284 $form_element = null;
1285
1286 // Find the Form Element
1287 foreach ($elements_data as $element) {
1288 if (isset($element['elements'])) {
1289 foreach ($element['elements'] as $el) {
1290 if ($form_id !== $el['id'] || 'form' !== $el['widgetType']) {
1291 continue;
1292 }
1293
1294 $form_element = $el;
1295 }
1296 }
1297
1298 // Check if this is the form widget
1299 if (isset($element['widgetType']) && isset($element['id']) && $element['widgetType'] === 'form' && $element['id'] === $form_id) {
1300 // Return the form settings
1301 $form_element = $element;
1302 }
1303 }
1304
1305 // skip if form element not found on the page anymore
1306 if ($form_element == null || !isset($form_element['settings'])) {
1307 return [];
1308 }
1309
1310 // Normalize Settings
1311 return $this->maybe_normalize_settings($form_element['settings']);
1312 }
1313
1314 /**
1315 * Normalizes the settings by converting the value of the settings
1316 *
1317 * @param array $settings The settings array to be normalized.
1318 * @formatter:off
1319 *
1320 * @return array {
1321 * The settings of the element if found or an empty array if nothing was found.
1322 * @type string $doi_email_to
1323 * @type string $doi_email_subject
1324 * @type string $doi_email_content
1325 * @type string $doi_email_from
1326 * @type string $doi_email_from_name
1327 * @type string $doi_email_template
1328 * @type string $doi_email_body
1329 * }
1330 *
1331 * @formatter:on
1332 */
1333 private function maybe_normalize_settings($settings)
1334 {
1335 if (isset($settings['sender'])) {
1336 $settings['doi_email_from'] = $settings['sender'];
1337 }
1338
1339 if (isset($settings['sender_name'])) {
1340 $settings['doi_email_from_name'] = $settings['sender_name'];
1341 }
1342
1343 if (isset($settings['subject'])) {
1344 $settings['doi_email_subject'] = $settings['subject'];
1345 }
1346
1347 if (isset($settings['recipient'])) {
1348 $settings['doi_email_to'] = $settings['recipient'];
1349 }
1350
1351 if (isset($settings['template'])) {
1352 $settings['doi_email_template'] = $settings['template'];
1353 }
1354
1355 if (isset($settings['body'])) {
1356 $settings['doi_email_body'] = $settings['body'];
1357 }
1358
1359 /**
1360 * Normalize the settings between the different form plugins
1361 *
1362 * @formatter:off
1363 *
1364 * @param array {
1365 * @type string $doi_email_to
1366 * @type string $doi_email_subject
1367 * @type string $doi_email_content
1368 * @type string $doi_email_from
1369 * @type string $doi_email_from_name
1370 * @type string $doi_email_template
1371 * @type string $doi_email_body
1372 * }
1373 *
1374 * @formatter:on
1375 *
1376 * @since 3.0.0
1377 */
1378 return apply_filters('f12_cf7_doubleoption_normalize_settings', $settings);
1379 }
1380
1381 /**
1382 * Set the Form as HTML
1383 *
1384 * @param string $form
1385 *
1386 * @return void
1387 */
1388 public function set_form($form)
1389 {
1390 $this->form = $form;
1391 }
1392
1393 /**
1394 * Return the Form
1395 *
1396 * @return string
1397 */
1398 public function get_mail_optin()
1399 {
1400 return $this->mail_optin;
1401 }
1402
1403 /**
1404 * Return the E-mail
1405 *
1406 * @return string
1407 */
1408 public function get_email()
1409 {
1410 return $this->email;
1411 }
1412
1413 /**
1414 * Store the email
1415 *
1416 * @param string $email
1417 *
1418 * @return void
1419 */
1420 public function set_email($email)
1421 {
1422 $this->email = $email;
1423 }
1424
1425 /**
1426 * Set the Form as HTML
1427 *
1428 * @param string $form
1429 *
1430 * @return void
1431 */
1432 public function set_mail_optin($mail_html)
1433 {
1434 $this->mail_optin = $mail_html;
1435 }
1436
1437 /**
1438 * Return the link to the detail view
1439 *
1440 * @return string
1441 */
1442 public function get_link_ui()
1443 {
1444 return admin_url('admin.php?page=f12-cf7-doubleoptin_optin_view&hash=' . $this->get_hash());
1445 }
1446
1447 /**
1448 * Return the link to delete
1449 *
1450 * @return string
1451 */
1452 public function get_link_delete()
1453 {
1454 return admin_url('admin.php?page=f12-cf7-doubleoptin&option=delete&hash=' . $this->get_hash());
1455 }
1456
1457 /**
1458 * Return the link to the export
1459 *
1460 * @return string
1461 */
1462 public function get_link_export()
1463 {
1464 return admin_url('admin.php?page=f12-cf7-doubleoptin&export=csv&id=' . $this->get_id());
1465 }
1466
1467 /**
1468 * Return the link to the export
1469 *
1470 * @return string
1471 */
1472 public function get_link_export_v2()
1473 {
1474 return admin_url('admin.php?page=f12-cf7-doubleoptin&export=csv_v2&id=' . $this->get_id());
1475 }
1476
1477 /**
1478 * Return the link to the optin
1479 *
1480 * @return string
1481 */
1482 public function get_link_optin($parameter = [])
1483 {
1484 if ($this->get_cf_form_id() == 0) {
1485 return get_home_url() . '?optin=' . $this->get_hash();
1486 }
1487
1488 $parameter = array_merge(CF7DoubleOptIn::getInstance()->getParameter($this->get_cf_form_id()), $parameter);
1489
1490 $page_id = $parameter['page'] ?? -1;
1491
1492 if ($page_id == -1 || !$page_id || $page_id == 0) {
1493 $link = get_home_url() . '?optin=' . $this->get_hash();
1494 } else {
1495
1496 $link = get_permalink($page_id);
1497
1498 if (strpos($link, "?") !== false) {
1499 $link .= '&optin=' . $this->get_hash();
1500 } else {
1501 $link .= '?optin=' . $this->get_hash();
1502 }
1503 }
1504
1505 return $link;
1506 }
1507
1508 /**
1509 * Return the link to the optout
1510 *
1511 * @return string
1512 */
1513 public function get_link_optout()
1514 {
1515 $settings = CF7DoubleOptIn::getInstance()->getSettings();
1516
1517 if (!isset($settings['optout_page']) || $settings['optout_page'] == 0) {
1518 return get_home_url() . '?optout=' . $this->get_hash();
1519 } else {
1520 $url = get_permalink($settings['optout_page']);
1521
1522 if (str_contains($url, '?')) {
1523 // If '?' exists in the URL, it means there are already parameters, so append with '&'
1524 $url .= '&optout=' . $this->get_hash();
1525 } else {
1526 // Else, there are no parameters yet, so append with '?'
1527 $url .= '?optout=' . $this->get_hash();
1528 }
1529
1530 return $url;
1531
1532 }
1533 }
1534 }
1535 }