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.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 3.0.72 All 35 releases
← All changes | core/OptIn.class.php +626 -1106 3.0.35.5.0 View file →
@@ -1,1259 +1,779 @@
1 1 <?php
2 +/**
3 + * OptIn Facade
4 + *
5 + * Backward-compatible facade for the legacy OptIn API.
6 + * Internally uses the new Repository pattern.
7 + *
8 + * @package forge12\contactform7\CF7DoubleOptIn
9 + * @since 4.0.0
10 + */
2 11
3 -namespace forge12\contactform7\CF7DoubleOptIn {
4 - if ( ! defined( 'ABSPATH' ) ) {
5 - exit;
6 - }
12 +namespace forge12\contactform7\CF7DoubleOptIn;
7 13
14 +use Forge12\DoubleOptIn\Container\Container;
15 +use Forge12\DoubleOptIn\Entity\OptIn as OptInEntity;
16 +use Forge12\DoubleOptIn\Repository\OptInRepositoryInterface;
17 +use Forge12\Shared\Logger;
18 +use Forge12\Shared\LoggerInterface;
19 +
20 +if ( ! defined( 'ABSPATH' ) ) {
21 + exit;
22 +}
23 +
24 +/**
25 + * Class OptIn
26 + *
27 + * This class provides backward compatibility with the legacy API
28 + * while using the new Repository pattern internally.
29 + */
30 +class OptIn {
31 +
32 + private LoggerInterface $logger;
33 + private OptInEntity $entity;
34 +
8 35 /**
9 - * Class Frontend
10 - * Responsible to handle the frontend of the Double OptIn
36 + * Constructor.
11 37 *
12 - * @package forge12\contactform7\CF7DoubleOptIn
38 + * @param LoggerInterface $logger The logger instance.
39 + * @param array $properties Optional properties to initialize.
13 40 */
14 - class OptIn {
15 - /**
16 - * Stores the DB ID for the entry
17 - *
18 - * @var int
19 - */
20 - private $id = 0;
41 + public function __construct( LoggerInterface $logger, array $properties = [] ) {
42 + $this->logger = $logger;
21 43
22 - /**
23 - * Stores the ID of the contact form 7 form.
24 - *
25 - * @var int
26 - */
27 - private $cf_form_id = 0;
28 - /**
29 - * Stores if the optin has been confirmed (1) or not (0)
30 - *
31 - * @var int
32 - */
33 - private $doubleoptin = 0;
34 - /**
35 - * Stores the content of the contact form 7 form content as a serialized string.
36 - *
37 - * @var string
38 - */
39 - private $content = "";
40 - /**
41 - * Stores the unique hash used to identify the opt-ins
42 - *
43 - * @var string
44 - */
45 - private $hash = "";
46 - /**
47 - * Stores the timestamp the opt-in mail has been sent to the user.
48 - *
49 - * @var string
50 - */
51 - private $createtime = "";
52 - /**
53 - * Stores the timestamp the opt-in has been confirmed by the user.
54 - */
55 - private $updatetime = "";
56 - /**
57 - * Stores the timestamp the opt-out has been confirmed by the user.
58 - */
59 - private $optouttime = "";
60 - /**
61 - * IP Address used to trigger the opt-in mail.
62 - *
63 - * @var string
64 - */
65 - private $ipaddr_register = "";
66 - /**
67 - * IP Address used to confirm the opt-in mail.
68 - */
69 - private $ipaddr_confirmation = "";
70 - /**
71 - * IP Address used to output.
72 - */
73 - private $ipaddr_optout = "";
74 - /**
75 - * Stores the files used within this form as a serialized string.
76 - */
77 - private $files = "";
78 - /**
79 - * Store the Category ID
80 - */
81 - private $category = 0;
82 - /**
83 - * Stores the form as html
84 - */
85 - private $form = '';
86 - /**
87 - * Stores the Mail send for the optin
88 - *
89 - * @var string
90 - */
91 - private $mail_optin = '';
92 - /**
93 - * Stores the E-Mail
94 - *
95 - * @var string
96 - */
97 - private $email = '';
44 + if ( ! empty( $properties ) ) {
45 + $this->entity = OptInEntity::fromArray( $this->mapToEntityArray( $properties ) );
46 + } else {
47 + $this->entity = OptInEntity::create();
48 + }
98 49
99 - /**
100 - * The constructor
101 - */
102 - public function __construct( array $properties ) {
103 - foreach ( $properties as $name => $value ) {
104 - if ( isset( $this->{$name} ) ) {
105 - $this->{$name} = $value;
106 - }
107 - }
108 - }
50 + $this->logger->debug( 'OptIn facade initialized', [
51 + 'plugin' => 'double-opt-in',
52 + 'id' => $this->entity->getId(),
53 + ] );
54 + }
109 55
110 - /**
111 - * @param int $cf_form_id
112 - */
113 - public function set_cf_form_id( $cf_form_id ) {
114 - $this->cf_form_id = (int) $cf_form_id;
115 - }
56 + /**
57 + * Get the logger instance.
58 + *
59 + * @return LoggerInterface
60 + */
61 + public function get_logger(): LoggerInterface {
62 + return $this->logger;
63 + }
116 64
117 - /**
118 - * @return int
119 - */
120 - public function get_cf_form_id() {
121 - return $this->cf_form_id;
122 - }
65 + /**
66 + * Get the underlying entity.
67 + *
68 + * @return OptInEntity
69 + */
70 + public function getEntity(): OptInEntity {
71 + return $this->entity;
72 + }
123 73
124 - /**
125 - * Return the ID of the Optin
126 - *
127 - * @return int
128 - */
129 - public function get_id() {
130 - return $this->id;
131 - }
74 + // =========================================================================
75 + // STATIC FACTORY METHODS
76 + // =========================================================================
132 77
133 - /**
134 - * Return the hash of the optin.
135 - *
136 - * @return string
137 - */
138 - public function get_hash() {
139 - return $this->hash;
140 - }
78 + /**
79 + * Get OptIn by hash.
80 + *
81 + * @param string $hash The hash.
82 + *
83 + * @return OptIn|null
84 + */
85 + public static function get_by_hash( string $hash ): ?OptIn {
86 + $logger = Logger::getInstance();
87 + $logger->debug( 'get_by_hash called', [
88 + 'plugin' => 'double-opt-in',
89 + 'hash' => $hash,
90 + ] );
141 91
142 - /**
143 - * Return true|false depending on confirm status
144 - *
145 - * @return bool
146 - */
147 - public function is_confirmed() {
148 - return (bool) $this->get_doubleoptin();
149 - }
92 + try {
93 + $repository = self::getRepository();
94 + $entity = $repository->findByHash( $hash );
150 95
151 - /**
152 - * Return true|false wether the form has been confirmed or not.
153 - *
154 - * @return bool
155 - */
156 - public function is_optout() {
157 - if ( true != $this->get_doubleoptin() && ! empty( $this->get_ipaddr_optout() ) && ! empty( $this->get_optouttime() ) ) {
158 - return true;
96 + if ( ! $entity ) {
97 + return null;
159 98 }
160 99
161 - return false;
162 - }
100 + $optIn = new self( $logger );
101 + $optIn->entity = $entity;
163 102
164 - /**
165 - * update the optin value
166 - *
167 - * @param int $confirmed
168 - */
169 - public function set_doubleoptin( $confirmed ) {
170 - $this->doubleoptin = (int) $confirmed;
103 + return $optIn;
104 + } catch ( \Exception $e ) {
105 + $logger->error( 'Failed to get OptIn by hash', [
106 + 'plugin' => 'double-opt-in',
107 + 'hash' => $hash,
108 + 'error' => $e->getMessage(),
109 + ] );
110 + return null;
171 111 }
112 + }
172 113
173 - /**
174 - * Return the optin value for the form.
175 - *
176 - * @return int
177 - */
178 - public function get_doubleoptin() {
179 - return $this->doubleoptin;
114 + /**
115 + * Get count by form ID.
116 + *
117 + * @param int $formId The form ID.
118 + *
119 + * @return int
120 + */
121 + public static function get_count( int $formId ): int {
122 + try {
123 + return self::getRepository()->countByFormId( $formId );
124 + } catch ( \Exception $e ) {
125 + return 0;
180 126 }
127 + }
181 128
182 - /**
183 - * @param string $timestamp
184 - */
185 - public function set_createtime( $timestamp ) {
186 - $this->createtime = $timestamp;
187 - }
129 + /**
130 + * Get list of OptIns with pagination.
131 + *
132 + * @param array $atts Query attributes.
133 + * @param int|null $numberOfPages Reference to store page count.
134 + *
135 + * @return array<OptIn>
136 + */
137 + public static function get_list( array $atts = [], ?int &$numberOfPages = null ): array {
138 + $logger = Logger::getInstance();
188 139
189 - /**
190 - * Return the Date how long the opt in will be valid
191 - *
192 - * @return string
193 - */
194 - public function get_valid_until() {
195 - $settings = CF7DoubleOptIn::getInstance()->getSettings();
140 + try {
141 + $repository = self::getRepository();
142 + $entities = $repository->findAll( $atts, $numberOfPages );
196 143
197 - $createtime = $this->get_createtime();
198 - $dt = new \DateTime();
199 - $dt->setTimestamp( $createtime );
200 -
201 - if ( $this->is_confirmed() ) {
202 - $month = (int) $settings['delete'];
203 - $period = $settings['delete_period'];
204 - } else {
205 - $month = (int) $settings['delete_unconfirmed'];
206 - $period = $settings['delete_unconfirmed_period'];
207 - }
208 -
209 - $dt->modify( '+' . (int) $month . ' ' . $period );
210 - $dt->modify( '+1 day' );
211 -
212 - return $dt->format( 'd.m.Y' );
144 + return array_map( function ( OptInEntity $entity ) use ( $logger ) {
145 + $optIn = new self( $logger );
146 + $optIn->entity = $entity;
147 + return $optIn;
148 + }, $entities );
149 + } catch ( \Exception $e ) {
150 + $logger->error( 'Failed to get OptIn list', [
151 + 'plugin' => 'double-opt-in',
152 + 'error' => $e->getMessage(),
153 + ] );
154 + return [];
213 155 }
156 + }
214 157
215 - /**
216 - * Return a timestamp
217 - *
218 - * @param string $view Select how to return the content. raw is the stored db value. use formatted to return a
219 - * formatted string.
220 - *
221 - * @return string
222 - */
223 - public function get_createtime( $view = "raw" ) {
224 - if ( $view != "raw" ) {
225 - if ( ! is_numeric( $this->createtime ) ) {
226 - $date = date( 'd.m.Y', strtotime( $this->createtime ) );
227 - $time = date( 'H:i:s', strtotime( $this->createtime ) );
228 - } else {
229 - $date = date( 'd.m.Y', $this->createtime );
230 - $time = date( 'H:i:s', $this->createtime );
231 - }
158 + /**
159 + * Get list by category ID.
160 + *
161 + * @param int $categoryId The category ID.
162 + * @param array $atts Query attributes.
163 + * @param int|null $numberOfPages Reference to store page count.
164 + *
165 + * @return array<OptIn>
166 + */
167 + public static function get_list_by_category_id( int $categoryId, array $atts = [], ?int &$numberOfPages = null ): array {
168 + $logger = Logger::getInstance();
232 169
233 - return $date . ' ' . __( '/', 'double-opt-in' ) . ' ' . $time;
234 - } else {
235 - return $this->createtime;
236 - }
237 - }
170 + try {
171 + $repository = self::getRepository();
238 172
239 - /**
240 - * @param string $timestamp
241 - */
242 - public function set_updatetime( $timestamp ) {
243 - $this->updatetime = $timestamp;
244 - }
173 + // Get total count for pagination
174 + if ( $numberOfPages !== null ) {
175 + $keyword = $atts['keyword'] ?? '';
176 + $perPage = max( 1, (int) ( $atts['perPage'] ?? 10 ) );
177 + $total = $repository->countByCategory( $categoryId, $keyword );
178 + $numberOfPages = $total > 0 ? (int) ceil( $total / $perPage ) : 0;
245 179
246 - /**
247 - * Return a timestamp
248 - *
249 - * @param string $view Select how to return the content. raw is the stored db value. use formatted to return a
250 - * formatted string.
251 - *
252 - * @return string
253 - */
254 - public function get_updatetime( $view = 'raw' ) {
255 - if ( $view != "raw" ) {
256 - if ( ! is_numeric( $this->updatetime ) ) {
257 - $date = date( 'd.m.Y', strtotime( $this->updatetime ) );
258 - $time = date( 'H:i:s', strtotime( $this->updatetime ) );
259 - } else {
260 - $date = date( 'd.m.Y', $this->updatetime );
261 - $time = date( 'H:i:s', $this->updatetime );
180 + if ( $numberOfPages === 0 ) {
181 + return [];
262 182 }
263 -
264 - return $date . ' ' . __( '/', 'double-opt-in' ) . ' ' . $time;
265 - } else {
266 - return $this->updatetime;
267 183 }
268 - }
269 184
270 - /**
271 - * @param string $timestamp
272 - */
273 - public function set_optouttime( $timestamp ) {
274 - $this->optouttime = $timestamp;
275 - }
185 + $entities = $repository->findByCategory( $categoryId, $atts );
276 186
277 - /**
278 - * Return a timestamp
279 - *
280 - * @param string $view Select how to return the content. raw is the stored db value. use formatted to return a
281 - * formatted string.
282 - *
283 - * @return string
284 - */
285 - public function get_optouttime( $view = 'raw' ) {
286 - if ( $view != "raw" ) {
287 - if ( ! is_numeric( $this->optouttime ) ) {
288 - $date = date( 'd.m.Y', strtotime( $this->optouttime ) );
289 - $time = date( 'H:i:s', strtotime( $this->optouttime ) );
290 - } else {
291 - $date = date( 'd.m.Y', $this->optouttime );
292 - $time = date( 'H:i:s', $this->optouttime );
293 - }
294 -
295 - return $date . ' ' . __( '/', 'f12-cf7-doubleoptin' ) . ' ' . $time;
296 - } else {
297 - return $this->optouttime;
298 - }
187 + return array_map( function ( OptInEntity $entity ) use ( $logger ) {
188 + $optIn = new self( $logger );
189 + $optIn->entity = $entity;
190 + return $optIn;
191 + }, $entities );
192 + } catch ( \Exception $e ) {
193 + $logger->error( 'Failed to get OptIn list by category', [
194 + 'plugin' => 'double-opt-in',
195 + 'category' => $categoryId,
196 + 'error' => $e->getMessage(),
197 + ] );
198 + return [];
299 199 }
200 + }
300 201
301 - /**
302 - * @param string $ip_addr
303 - */
304 - public function set_ipaddr_optout( $ip_addr ) {
305 - $this->ipaddr_optout = $ip_addr;
306 - }
202 + /**
203 + * Get list by email.
204 + *
205 + * @param string $email The email address.
206 + *
207 + * @return array<OptIn>
208 + */
209 + public static function get_list_by_email( string $email ): array {
210 + $logger = Logger::getInstance();
307 211
308 - /**
309 - * @return string
310 - */
311 - public function get_ipaddr_optout() {
312 - return $this->ipaddr_optout;
313 - }
212 + try {
213 + $repository = self::getRepository();
214 + $entities = $repository->findByEmail( $email );
314 215
315 - /**
316 - * @param string $ip_addr
317 - */
318 - public function set_ipaddr_register( $ip_addr ) {
319 - $this->ipaddr_register = $ip_addr;
216 + return array_map( function ( OptInEntity $entity ) use ( $logger ) {
217 + $optIn = new self( $logger );
218 + $optIn->entity = $entity;
219 + return $optIn;
220 + }, $entities );
221 + } catch ( \Exception $e ) {
222 + return [];
320 223 }
224 + }
321 225
322 - /**
323 - * @return string
324 - */
325 - public function get_ipaddr_register() {
326 - return $this->ipaddr_register;
327 - }
226 + /**
227 + * Get confirmed list by email.
228 + *
229 + * @param string $email The email address.
230 + *
231 + * @return array<OptIn>
232 + */
233 + public static function get_list_by_email_confirmed( string $email ): array {
234 + $logger = Logger::getInstance();
328 235
329 - /**
330 - * @param string $ip_addr
331 - */
332 - public function set_ipaddr_confirmation( $ip_addr ) {
333 - $this->ipaddr_confirmation = $ip_addr;
334 - }
236 + try {
237 + $repository = self::getRepository();
238 + $entities = $repository->findConfirmedByEmail( $email );
335 239
336 - /**
337 - * @return string
338 - */
339 - public function get_ipaddr_confirmation() {
340 - return $this->ipaddr_confirmation;
240 + return array_map( function ( OptInEntity $entity ) use ( $logger ) {
241 + $optIn = new self( $logger );
242 + $optIn->entity = $entity;
243 + return $optIn;
244 + }, $entities );
245 + } catch ( \Exception $e ) {
246 + return [];
341 247 }
248 + }
342 249
343 - /**
344 - * Return a serialized string
345 - *
346 - * @return mixed
347 - */
348 - public function get_content() {
349 - return $this->content;
350 - }
250 + /**
251 + * Get unconfirmed list by email.
252 + *
253 + * @param string $email The email address.
254 + *
255 + * @return array<OptIn>
256 + */
257 + public static function get_list_by_email_unconfirmed( string $email ): array {
258 + $logger = Logger::getInstance();
351 259
352 - /**
353 - * @param string - a serialized string
354 - */
355 - public function set_content( $content ) {
356 - $this->content = $content;
357 - }
260 + try {
261 + $repository = self::getRepository();
262 + $entities = $repository->findUnconfirmedByEmail( $email );
358 263
359 - /**
360 - * @param string - a serialized string
361 - */
362 - public function set_files( $files ) {
363 - $this->files = $files;
264 + return array_map( function ( OptInEntity $entity ) use ( $logger ) {
265 + $optIn = new self( $logger );
266 + $optIn->entity = $entity;
267 + return $optIn;
268 + }, $entities );
269 + } catch ( \Exception $e ) {
270 + return [];
364 271 }
272 + }
365 273
366 - /**
367 - * @return string use maybe_unserialize to deserialize the string
368 - */
369 - public function get_files() {
370 - return $this->files;
274 + /**
275 + * Bulk update category.
276 + *
277 + * @param int $fromId The source category ID.
278 + * @param int $toId The target category ID.
279 + *
280 + * @return int
281 + */
282 + public static function bulk_update_category( int $fromId, int $toId ): int {
283 + try {
284 + return self::getRepository()->bulkUpdateCategory( $fromId, $toId );
285 + } catch ( \Exception $e ) {
286 + return 0;
371 287 }
288 + }
372 289
373 - /**
374 - * Return the assigned Category ID
375 - *
376 - * @return int
377 - */
378 - public function get_category() {
379 - if ( ! is_numeric( $this->category ) ) {
380 - return 0;
381 - }
382 -
383 - return $this->category;
384 - }
385 -
386 - /**
387 - * Assign a ID to the category
388 - *
389 - * @param int $id
390 - *
391 - * @return void
392 - */
393 - public function set_category( $id ) {
394 - $this->category = $id;
395 - }
396 -
397 - /**
398 - * Check if the given Opt In was created by Contact Form 7
399 - *
400 - * @return bool
401 - */
402 - public function isTypeCF7() {
403 - $form_id = $this->get_cf_form_id();
404 - $form = get_post( $form_id );
405 -
406 - if ( 'wpcf7_contact_form' === $form->post_type ) {
407 - return true;
408 - }
409 -
290 + /**
291 + * Update category by OptIn ID.
292 + *
293 + * @param int $optInId The OptIn ID.
294 + * @param int $categoryId The new category ID.
295 + *
296 + * @return bool
297 + */
298 + public static function update_category_by_id( int $optInId, int $categoryId ): bool {
299 + try {
300 + return self::getRepository()->updateCategoryById( $optInId, $categoryId );
301 + } catch ( \Exception $e ) {
410 302 return false;
411 303 }
304 + }
412 305
413 - /**
414 - * Check if the given Opt In was created by Elementor
415 - *
416 - * @return bool
417 - */
418 - public function isTypeElementor() {
419 - if ( ! class_exists( 'ElementorPro\Plugin' ) ) {
420 - return false;
421 - }
306 + // =========================================================================
307 + // GETTERS
308 + // =========================================================================
422 309
423 - $page_id = $this->get_cf_form_id();
310 + public function get_id(): int {
311 + return $this->entity->getId();
312 + }
424 313
425 - $post = get_post( $page_id );
314 + public function get_hash(): string {
315 + return $this->entity->getHash();
316 + }
426 317
427 - // skip if post not existing anymore
428 - if ( null === $post ) {
429 - return false;
430 - }
318 + public function get_cf_form_id(): int {
319 + return $this->entity->getFormId();
320 + }
431 321
432 - // skip if page is not build with elementor
433 - if ( ! \Elementor\Plugin::$instance->documents->get( $page_id )->is_built_with_elementor() ) {
434 - return false;
435 - }
322 + public function is_confirmed(): bool {
323 + return $this->entity->isConfirmed();
324 + }
436 325
437 - // Load the page's data
438 - $document = \Elementor\Plugin::$instance->documents->get( $page_id );
326 + public function get_doubleoptin(): int {
327 + return $this->entity->isConfirmed() ? 1 : 0;
328 + }
439 329
440 - // Skip if document not exist
441 - if ( ! $document ) {
442 - return false;
443 - }
330 + public function is_optout(): bool {
331 + return $this->entity->isOptedOut();
332 + }
444 333
445 - // Loop through the elements
446 - $elements_data = $document->get_elements_data();
334 + public function get_content(): string {
335 + return $this->entity->getContent();
336 + }
447 337
448 - $content = maybe_unserialize( $this->get_content() );
449 -
450 - // skip if form id not available
451 - if ( ! isset( $content['form_id'] ) ) {
452 - return false;
453 - }
454 -
455 - $form_id = $content['form_id'];
456 -
457 - // Find the Form Element
458 - foreach ( $elements_data as $element ) {
459 - if ( isset( $element['elements'] ) ) {
460 - foreach ( $element['elements'] as $el ) {
461 - if ( $form_id !== $el['id'] || 'form' !== $el['widgetType'] ) {
462 - continue;
463 - }
464 -
465 - return true;
466 - }
467 - return true;
468 - }
469 -
470 - // Check if this is the form widget
471 - if ( isset( $element['widgetType'] ) && isset( $element['id'] ) && $element['widgetType'] === 'form' && $element['id'] === $form_id ) {
472 - // Return the form settings
473 - return true;
474 - }
475 - }
476 - return false;
338 + public function get_createtime( string $view = 'raw' ): string {
339 + if ( $view !== 'raw' ) {
340 + return $this->entity->getCreateTimeFormatted( 'd.m.Y / H:i:s' );
477 341 }
342 + return (string) $this->entity->getCreateTime();
343 + }
478 344
479 - /**
480 - * Check if the given Opt In was created by Avada
481 - *
482 - * @return bool|void
483 - */
484 - public function isTypeAvada() {
485 - $form_id = $this->get_cf_form_id();
486 - $form = get_post( $form_id );
487 -
488 - if ( 'fusion_form' === $form->post_type ) {
489 - return true;
490 - }
491 -
492 - return false;
345 + public function get_updatetime( string $view = 'raw' ): string {
346 + if ( $view !== 'raw' ) {
347 + return $this->entity->getUpdateTimeFormatted( 'd.m.Y / H:i:s' );
493 348 }
349 + return (string) $this->entity->getUpdateTime();
350 + }
494 351
495 - /**
496 - * Check if the Opt In was created by 'cf7' or 'avada'.
497 - *
498 - * @param string $type use either 'cf7' or 'avada'
499 - *
500 - * @return bool
501 - */
502 - public function isType( $type ) {
503 - if ( $type == 'cf7' ) {
504 - return $this->isTypeCF7();
505 - } elseif ( $type == 'elementor' ) {
506 - return $this->isTypeElementor();
507 - } else {
508 - return $this->isTypeAvada();
509 - }
352 + public function get_optouttime( string $view = 'raw' ): string {
353 + $time = $this->entity->getOptOutTime();
354 + if ( $view !== 'raw' && $time > 0 ) {
355 + return wp_date( 'd.m.Y / H:i:s', $time );
510 356 }
357 + return (string) $time;
358 + }
511 359
512 - /**
513 - * Load an OptIn by the given hash code. Returns either an Object of Type OptIn or null if not found.
514 - *
515 - * @param string $hash
516 - *
517 - * @return OptIn|null
518 - */
519 - public static function get_by_hash( string $hash ) {
520 - global $wpdb;
521 - $table = $wpdb->prefix . 'f12_cf7_doubleoptin';
360 + /**
361 + * Get create time as ISO 8601 string.
362 + *
363 + * @return string
364 + */
365 + public function get_createtime_iso(): string {
366 + return $this->entity->getCreateTimeISO();
367 + }
522 368
523 - if ( null == $wpdb ) {
524 - return null;
525 - }
369 + /**
370 + * Get update time as ISO 8601 string.
371 + *
372 + * @return string
373 + */
374 + public function get_updatetime_iso(): string {
375 + return $this->entity->getUpdateTimeISO();
376 + }
526 377
527 - $row = $wpdb->get_row( 'SELECT * FROM ' . $table . ' WHERE hash="' . $hash . '"', ARRAY_A );
378 + /**
379 + * Get opt-out time as ISO 8601 string.
380 + *
381 + * @return string
382 + */
383 + public function get_optouttime_iso(): string {
384 + return $this->entity->getOptOutTimeISO();
385 + }
528 386
529 - if ( null == $row ) {
530 - return null;
531 - }
387 + public function get_ipaddr_register(): string {
388 + return $this->entity->getIpRegister();
389 + }
532 390
533 - return new OptIn( $row );
534 - }
391 + public function get_ipaddr_confirmation(): string {
392 + return $this->entity->getIpConfirmation();
393 + }
535 394
536 - /**
537 - * Get number of optins for the given post.
538 - *
539 - * @param $post_id
540 - *
541 - * @return int
542 - */
543 - public static function get_count( $post_id ) {
544 - global $wpdb;
395 + public function get_ipaddr_optout(): string {
396 + return $this->entity->getIpOptOut();
397 + }
545 398
546 - if ( ! $wpdb ) {
547 - return 0;
548 - }
399 + public function get_files(): string {
400 + return $this->entity->getFiles();
401 + }
549 402
550 - $tableName = $wpdb->prefix . 'f12_cf7_doubleoptin';
551 - $result = $wpdb->get_results( $wpdb->prepare( 'SELECT count(*) AS counter FROM ' . $tableName . ' WHERE cf_form_id=%d ', $post_id ) );
403 + public function get_category(): int {
404 + return $this->entity->getCategory();
405 + }
552 406
553 - if ( is_array( $result ) ) {
554 - return $result[0]->counter;
555 - }
407 + public function get_email(): string {
408 + return $this->entity->getEmail();
409 + }
556 410
557 - return 0;
558 - }
411 + public function get_mail_optin(): string {
412 + return $this->entity->getMailOptIn();
413 + }
559 414
560 - /**
561 - * Return a list of opt ins stored in the database.
562 - *
563 - * @param int $category_id
564 - *
565 - * @param array $atts array (
566 - * 'perPage' => 10, // Posts per page
567 - * 'page' => 0, // Current page
568 - * 'order' => DESC, // Order (ASC/DESC)
569 - * );
570 - *
571 - * @param null $numberOfPages - Stores the number of pages found if limited
572 - *
573 - * @return array
574 - */
575 - public static function get_list_by_category_id( $category_id, $atts = array(), &$numberOfPages = null ) {
576 - global $wpdb;
415 + public function get_consent_text(): string {
416 + return $this->entity->getConsentText();
417 + }
577 418
578 - $attr = array(
579 - 'perPage' => 10,
580 - 'page' => 1,
581 - 'order' => 'DESC',
582 - 'keyword' => ''
583 - );
584 -
585 - /**
586 - * Parameter
587 - */
588 - foreach ( $atts as $key => $value ) {
589 - if ( isset( $attr[ $key ] ) ) {
590 - $attr[ $key ] = $atts[ $key ];
591 - }
592 - }
593 -
594 - /*
595 - * Update keyword
596 - */
597 - $where = '';
598 - if ( ! empty( $attr['keyword'] ) ) {
599 - $where = ' AND content LIKE "%%' . $attr['keyword'] . '%%"';
600 - }
601 -
602 -
603 - $tableName = $wpdb->prefix . 'f12_cf7_doubleoptin';
604 - $pageNum = (int) $attr['page'] - 1;
605 -
606 - if ( $numberOfPages !== null ) {
607 - $result = $wpdb->get_results( $wpdb->prepare( 'SELECT count(*) AS counter FROM ' . $tableName . ' WHERE category=%d ' . $where, $category_id ) );
608 -
609 - $itemCounter = 0;
610 -
611 - if ( is_array( $result ) ) {
612 - $itemCounter = $result[0]->counter;
613 - }
614 -
615 - $numberOfPages = 0;
616 -
617 - if ( $itemCounter != 0 ) {
618 - $numberOfPages = ceil( $itemCounter / (int) $attr['perPage'] );
619 - }
620 - }
621 -
622 - $list = array();
623 -
624 - if ( $numberOfPages == 0 ) {
625 - return $list;
626 - }
627 -
628 - $offset = $pageNum * (int) $attr['perPage'];
629 -
630 - $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 );
631 -
632 - foreach ( $rows as $row ) {
633 - $list[] = new OptIn( $row );
634 - }
635 -
636 - return $list;
419 + public function get_form( bool $encoded = false ): string {
420 + $form = $this->entity->getForm();
421 + if ( $encoded ) {
422 + return base64_encode( $form );
637 423 }
424 + return $form;
425 + }
638 426
639 - /**
640 - * Return a list of opt ins stored in the database.
641 - *
642 - * @param string $email
643 - * @param int $confirmed Either recieve confirmed or unconfirmed only
644 - *
645 - * @return array
646 - */
647 - public static function get_list_by_email( $email ) {
648 - global $wpdb;
427 + /**
428 + * Get the validity end date.
429 + *
430 + * @return string
431 + */
432 + public function get_valid_until(): string {
433 + $settings = CF7DoubleOptIn::getInstance()->getSettings();
434 + $dt = new \DateTime();
435 + $dt->setTimestamp( $this->entity->getCreateTime() );
649 436
650 - $tableName = $wpdb->prefix . 'f12_cf7_doubleoptin';
651 -
652 - $list = array();
653 -
654 - $rows = $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM ' . $tableName . ' WHERE email=%s', $email ), ARRAY_A );
655 -
656 - foreach ( $rows as $row ) {
657 - $list[] = new OptIn( $row );
658 - }
659 -
660 - return $list;
437 + if ( $this->is_confirmed() ) {
438 + $amount = (int) ( $settings['delete'] ?? 0 );
439 + $period = $settings['delete_period'] ?? 'months';
440 + } else {
441 + $amount = (int) ( $settings['delete_unconfirmed'] ?? 0 );
442 + $period = $settings['delete_unconfirmed_period'] ?? 'months';
661 443 }
662 444
663 - /**
664 - * Return a list of opt ins stored in the database.
665 - *
666 - * @param $atts array (
667 - * 'perPage' => 10, // Posts per page
668 - * 'page' => 0, // Current page
669 - * 'order' => DESC, // Order (ASC/DESC)
670 - * );
671 - *
672 - * @param null $numberOfPages - Stores the number of pages found if limited
673 - *
674 - * @return array
675 - */
676 - public static function get_list( $atts = array(), &$numberOfPages = null ) {
677 - global $wpdb;
445 + $dt->modify( '+' . $amount . ' ' . $period );
446 + $dt->modify( '+1 day' );
678 447
679 - $attr = array(
680 - 'perPage' => 10,
681 - 'page' => 1,
682 - 'order' => 'DESC',
683 - 'keyword' => '',
684 - 'cf_form_id' => '',
685 - );
448 + return $dt->format( 'd.m.Y' );
449 + }
686 450
687 - /*
688 - * Update atts
689 - */
690 - foreach ( $atts as $key => $value ) {
691 - if ( isset( $attr[ $key ] ) ) {
692 - $attr[ $key ] = $atts[ $key ];
693 - }
694 - }
451 + // =========================================================================
452 + // SETTERS
453 + // =========================================================================
695 454
696 - /*
697 - * Update WHERE for Query
698 - */
699 - $where = '';
700 - $where_condition = [];
455 + public function set_cf_form_id( int $formId ): void {
456 + $this->entity = $this->entity->withFormId( $formId );
457 + }
701 458
702 - /*
703 - * Add Keyword
704 - */
705 - if ( ! empty( $attr['keyword'] ) ) {
706 - $where_condition[] = 'content LIKE "%%' . $attr['keyword'] . '%%"';
707 - }
459 + public function set_doubleoptin( int $confirmed ): void {
460 + $this->entity->setConfirmed( (bool) $confirmed );
461 + }
708 462
709 - /*
710 - * Add Form ID
711 - */
712 - if ( ! empty( $attr['cf_form_id'] ) ) {
713 - $where_condition[] = 'cf_form_id="' . $attr['cf_form_id'] . '"';
714 - }
463 + public function set_createtime( $timestamp ): void {
464 + $this->entity = $this->entity->withCreateTime( (int) $timestamp );
465 + }
715 466
716 - /*
717 - * build where string
718 - */
719 - if ( ! empty( $where_condition ) ) {
720 - $where = ' WHERE ' . implode( ' AND ', $where_condition );
721 - }
467 + public function set_updatetime( $timestamp ): void {
468 + $this->entity->setUpdateTime( (int) $timestamp );
469 + }
722 470
723 - /*
724 - * set table
725 - */
726 - $tableName = $wpdb->prefix . 'f12_cf7_doubleoptin';
727 - $pageNum = (int) $attr['page'] - 1;
471 + public function set_optouttime( $timestamp ): void {
472 + $this->entity = $this->entity->withOptOutTime( (int) $timestamp );
473 + }
728 474
729 - if ( $numberOfPages !== null ) {
730 - $result = $wpdb->get_results( 'SELECT count(*) as counter FROM ' . $tableName . $where );
475 + public function set_ipaddr_register( string $ip ): void {
476 + $this->entity = $this->entity->withIpRegister( $ip );
477 + }
731 478
732 - $itemCounter = 0;
479 + public function set_ipaddr_confirmation( string $ip ): void {
480 + $this->entity->setIpConfirmation( $ip );
481 + }
733 482
734 - if ( is_array( $result ) ) {
735 - $itemCounter = $result[0]->counter;
736 - }
483 + public function set_ipaddr_optout( string $ip ): void {
484 + $this->entity = $this->entity->withIpOptOut( $ip );
485 + }
737 486
738 - $numberOfPages = 0;
487 + public function set_content( string $content ): void {
488 + $this->entity = $this->entity->withContent( $content );
489 + }
739 490
740 - if ( $itemCounter != 0 ) {
741 - $numberOfPages = ceil( $itemCounter / (int) $attr['perPage'] );
742 - }
743 - }
491 + public function set_files( string $files ): void {
492 + $this->entity = $this->entity->withFiles( $files );
493 + }
744 494
745 - $list = array();
495 + public function set_category( int $categoryId ): void {
496 + $this->entity = $this->entity->withCategory( $categoryId );
497 + }
746 498
747 - if ( $numberOfPages == 0 ) {
748 - return $list;
749 - }
499 + public function set_form( string $form ): void {
500 + $this->entity = $this->entity->withForm( $form );
501 + }
750 502
751 - $offset = $pageNum * (int) $attr['perPage'];
503 + public function set_email( string $email ): void {
504 + $this->entity = $this->entity->withEmail( $email );
505 + }
752 506
753 - $rows = $wpdb->get_results( $wpdb->prepare( 'SELECT * FROM ' . $tableName . $where . ' ORDER BY ID ' . $attr['order'] . ' LIMIT ' . $attr['perPage'] . ' OFFSET % d', $offset ), ARRAY_A );
507 + public function set_mail_optin( string $mail ): void {
508 + $this->entity = $this->entity->withMailOptIn( $mail );
509 + }
754 510
755 - foreach ( $rows as $row ) {
756 - $list[] = new OptIn( $row );
757 - }
511 + public function set_consent_text( string $text ): void {
512 + $this->entity = $this->entity->withConsentText( $text );
513 + }
758 514
759 - return $list;
760 - }
515 + // =========================================================================
516 + // TYPE CHECKS
517 + // =========================================================================
761 518
762 - /**
763 - * This will update all categories from A to B, e.g.: All Opt-Ins with id 1 to category id 2.
764 - *
765 - * @param int $from_id The origin Category ID
766 - * @param int $to_id The new Category ID
767 - *
768 - * @return bool|int|\mysqli_result|resource|null Return the number of rows affected by the query.
769 - */
770 - public static function bulk_update_category( $from_id, $to_id ) {
771 - global $wpdb;
772 - $table = $wpdb->prefix . 'f12_cf7_doubleoptin';
519 + public function isTypeCF7(): bool {
520 + return $this->isType( 'cf7' );
521 + }
773 522
774 - return $wpdb->query( $wpdb->prepare( 'UPDATE ' . $table . ' SET category =%d WHERE category =%d', $to_id, $from_id ) );
775 - }
523 + public function isTypeAvada(): bool {
524 + return $this->isType( 'avada' );
525 + }
776 526
777 - /**
778 - * Update the category of the given opt in id.
779 - *
780 - * @param int $optin_id The ID of the OptIn
781 - * @param int $category_id The ID of the Category
782 - *
783 - * @return bool|int|\mysqli_result|resource|null
784 - */
785 - public static function update_category_by_id( $optin_id, $category_id ) {
786 - global $wpdb;
787 - $table = $wpdb->prefix . 'f12_cf7_doubleoptin';
527 + public function isTypeElementor(): bool {
528 + return $this->isType( 'elementor' );
529 + }
788 530
789 - return $wpdb->query( $wpdb->prepare( 'UPDATE ' . $table . ' SET category =%d WHERE id =%d', $category_id, $optin_id ) );
790 - }
531 + public function isTypeWPForms(): bool {
532 + return $this->isType( 'wpforms' );
533 + }
791 534
792 - /**
793 - * Update or create the given Object
794 - */
795 - public function save() {
796 - global $wpdb;
797 - $table = $wpdb->prefix . 'f12_cf7_doubleoptin';
535 + public function isTypeGravityForms(): bool {
536 + return $this->isType( 'gravityforms' );
537 + }
798 538
799 - if ( ! empty( $this->get_hash() ) ) {
800 - return $wpdb->update( $table, array(
801 - 'doubleoptin' => $this->get_doubleoptin(),
802 - 'createtime' => $this->get_createtime(),
803 - 'updatetime' => $this->get_updatetime(),
804 - 'ipaddr_confirmation' => $this->get_ipaddr_confirmation(),
805 - 'ipaddr_register' => $this->get_ipaddr_register(),
806 - 'cf_form_id' => $this->get_cf_form_id(),
807 - 'content' => $this->get_content(),
808 - 'files' => $this->get_files(),
809 - 'category' => $this->get_category(),
810 - 'optouttime' => $this->get_optouttime(),
811 - 'ipaddr_optout' => $this->get_ipaddr_optout(),
812 - 'mail_optin' => $this->get_mail_optin(),
813 - ), array(
814 - 'hash' => $this->get_hash()
815 - ) );
816 - } else {
817 - $result = $wpdb->insert( $table, array(
818 - 'cf_form_id' => $this->get_cf_form_id(),
819 - 'doubleoptin' => $this->get_doubleoptin(),
820 - 'createtime' => $this->get_createtime(),
821 - 'updatetime' => $this->get_updatetime(),
822 - 'ipaddr_confirmation' => $this->get_ipaddr_confirmation(),
823 - 'ipaddr_register' => $this->get_ipaddr_register(),
824 - 'content' => $this->get_content(),
825 - 'files' => $this->get_files(),
826 - 'category' => $this->get_category(),
827 - 'form' => $this->get_form(),
828 - 'mail_optin' => $this->get_mail_optin(),
829 - 'email' => $this->get_email()
830 - ) );
539 + public function isType( string $type ): bool {
540 + $formId = $this->get_cf_form_id();
831 541
832 - if ( $result > 0 ) {
833 - $this->id = $wpdb->insert_id;
834 -
835 - return $this->createHash();
542 + switch ( $type ) {
543 + case 'cf7':
544 + return get_post_type( $formId ) === 'wpcf7_contact_form';
545 + case 'avada':
546 + return get_post_type( $formId ) === 'fusion_form';
547 + case 'elementor':
548 + // Elementor forms are embedded in pages/posts, not stored as separate post types.
549 + // Check if the post has Elementor data AND DOI settings configured.
550 + $hasElementorData = ! empty( get_post_meta( $formId, '_elementor_data', true ) );
551 + $hasDoiSettings = ! empty( get_post_meta( $formId, 'f12-cf7-doubleoptin', true ) );
552 + return $hasElementorData && $hasDoiSettings;
553 + case 'wpforms':
554 + return get_post_type( $formId ) === 'wpforms';
555 + case 'gravityforms':
556 + // Gravity Forms stores forms in a custom table, not as posts.
557 + // Check if the form ID exists in the GF forms table.
558 + if ( class_exists( 'GFAPI' ) ) {
559 + $form = \GFAPI::get_form( $formId );
560 + return $form !== false && ! is_wp_error( $form );
836 561 }
837 - }
838 -
839 - return false;
562 + return false;
563 + default:
564 + return false;
840 565 }
566 + }
841 567
842 - /**
843 - * Creates a unique hash code and adds it to the current OptIn Object
844 - */
845 - private function createHash() {
846 - global $wpdb;
568 + // =========================================================================
569 + // PERSISTENCE
570 + // =========================================================================
847 571
848 - if ( null == $wpdb ) {
849 - return false;
850 - }
572 + /**
573 + * Save the OptIn to the database.
574 + *
575 + * @return bool|int
576 + */
577 + public function save() {
578 + $this->logger->info( 'Saving OptIn', [
579 + 'plugin' => 'double-opt-in',
580 + 'id' => $this->entity->getId(),
581 + 'hash' => $this->entity->getHash(),
582 + ] );
851 583
852 - $table = $wpdb->prefix . 'f12_cf7_doubleoptin';
584 + try {
585 + $repository = self::getRepository();
586 + $this->entity = $repository->save( $this->entity );
853 587
854 - // add the hash to the element
588 + $this->logger->info( 'OptIn saved successfully', [
589 + 'plugin' => 'double-opt-in',
590 + 'id' => $this->entity->getId(),
591 + 'hash' => $this->entity->getHash(),
592 + ] );
855 593
856 - $this->hash = base64_encode( $this->get_createtime() . $this->get_id() . $this->get_cf_form_id() );
857 -
858 - return $wpdb->update( $table, array(
859 - 'hash' => $this->hash,
860 - ), array(
861 - 'id' => $this->get_id()
862 - ) );
594 + return true;
595 + } catch ( \Exception $e ) {
596 + $this->logger->error( 'Failed to save OptIn', [
597 + 'plugin' => 'double-opt-in',
598 + 'error' => $e->getMessage(),
599 + ] );
600 + return false;
863 601 }
602 + }
864 603
865 - /**
866 - * Return the Form
867 - *
868 - * @return string
869 - */
870 - public function get_form() {
871 - return $this->form;
872 - }
604 + // =========================================================================
605 + // LINK GENERATION
606 + // =========================================================================
873 607
874 - /**
875 - * Returns the Name of the form if available
876 - *
877 - * @return string The name of the Form
878 - * @since 2.4.0
879 - */
880 - public function get_form_name() {
881 - $post = get_post( $this->get_cf_form_id() );
608 + /**
609 + * Get the opt-in confirmation link.
610 + *
611 + * @param array $parameter Additional parameters.
612 + * @param int $formId Optional form ID override.
613 + *
614 + * @return string
615 + */
616 + public function get_link_optin( array $parameter = [], int $formId = 0 ): string {
617 + $formId = $formId > 0 ? $formId : $this->get_cf_form_id();
882 618
883 - if ( null === $post ) {
884 - return __( 'Deleted', 'double-opt-in' );
885 - }
619 + $formParameter = CF7DoubleOptIn::getInstance()->getParameter( $formId );
620 + $pageId = (int) ( $formParameter['page'] ?? 0 );
886 621
887 - return $post->post_title;
622 + if ( $pageId <= 0 ) {
623 + return home_url( '?optin=' . $this->get_hash() );
888 624 }
889 625
890 - /**
891 - * Returns the URL to the form depending on the Form Type.
892 - *
893 - * @return string The Backend URL to the Form, empty if the post is not found.
894 - * @since 2.4.0
895 - *
896 - */
897 - public function get_form_link() {
898 - if ( $this->isTypeElementor() ) {
899 - return get_edit_post_link( $this->get_cf_form_id() );
900 - } elseif ( $this->isTypeAvada() ) {
901 - return get_edit_post_link( $this->get_cf_form_id() );
902 - } elseif ( $this->isTypeCF7() ) {
903 - $post = get_post( $this->get_cf_form_id() );
904 -
905 - if ( ! $post ) {
906 - return '';
907 - }
908 -
909 - return admin_url( 'admin.php?page=wpcf7&post=' . esc_attr( $this->get_cf_form_id() ) . '&action=edit' );
910 - }
911 -
912 - return '';
626 + $pageUrl = get_permalink( $pageId );
627 + if ( ! $pageUrl ) {
628 + return home_url( '?optin=' . $this->get_hash() );
913 629 }
914 630
631 + $separator = strpos( $pageUrl, '?' ) !== false ? '&' : '?';
915 632
916 - /**
917 - * Retrieves the form settings.
918 - *
919 - * If the form is created using Elementor, retrieves the settings using the `get_settings_by_elementor()`
920 - * method.
921 - *
922 - * @return array The form settings.
923 - * @since 2.4.0
924 - *
925 - */
926 - public function get_form_settings() {
927 - if ( $this->isTypeElementor() ) {
928 - return $this->get_settings_by_elementor();
929 - } else if ( $this->isTypeCF7() ) {
930 - return $this->get_settings_by_cf7();
931 - } else if ( $this->isTypeAvada() ) {
932 - return $this->get_settings_by_avada();
933 - }
633 + return $pageUrl . $separator . 'optin=' . $this->get_hash();
634 + }
934 635
935 - return [];
936 - }
636 + /**
637 + * Get the opt-out link.
638 + *
639 + * @return string
640 + */
641 + public function get_link_optout(): string {
642 + $settings = CF7DoubleOptIn::getInstance()->getSettings();
643 + $pageId = (int) ( $settings['optout_page'] ?? 0 );
937 644
938 - /**
939 - * Retrieves the settings of a specific element using avada.
940 - *
941 - * @return array
942 - * @since 2.4.0
943 - *
944 - * @see OptIn::maybe_normalize_settings() for details
945 - */
946 - private function get_settings_by_avada() {
947 -
948 - $form_id = $this->get_cf_form_id();
949 -
950 - $settings = CF7DoubleOptIn::getInstance()->getParameter( $form_id );
951 -
952 - return $this->maybe_normalize_settings( $settings );
645 + if ( $pageId <= 0 ) {
646 + return home_url( '?optout=' . $this->get_hash() );
953 647 }
954 648
955 - /**
956 - * Retrieves the settings of a specific element using cf7.
957 - *
958 - * @return array
959 - * @since 2.4.0
960 - *
961 - * @see OptIn::maybe_normalize_settings() for details
962 - */
963 - private function get_settings_by_cf7() {
964 - $form_id = $this->get_cf_form_id();
965 -
966 - $settings = get_post_meta( $form_id, 'f12-cf7-doubleoptin', true );
967 -
968 - if ( null == $settings || ! is_array( $settings ) ) {
969 - return [];
970 - }
971 -
972 - $settings = $this->maybe_normalize_settings( $settings );
973 -
974 - return $settings;
649 + $pageUrl = get_permalink( $pageId );
650 + if ( ! $pageUrl ) {
651 + return home_url( '?optout=' . $this->get_hash() );
975 652 }
976 653
977 - /**
978 - * Retrieves the settings of a specific element using Elementor.
979 - *
980 - * @return array
981 - * @since 2.4.0
982 - *
983 - * @see OptIn::maybe_normalize_settings() for details
984 - */
985 - private function get_settings_by_elementor() {
986 - $page_id = $this->get_cf_form_id();
654 + $separator = strpos( $pageUrl, '?' ) !== false ? '&' : '?';
987 655
988 - $post = get_post( $page_id );
656 + return $pageUrl . $separator . 'optout=' . $this->get_hash();
657 + }
989 658
990 - // skip if post not existing anymore
991 - if ( null === $post ) {
992 - return [];
993 - }
659 + /**
660 + * Get the UI edit link.
661 + *
662 + * @return string
663 + */
664 + public function get_link_ui(): string {
665 + return admin_url( 'admin.php?page=' . FORGE12_OPTIN_SLUG . '&view=single&hash=' . $this->get_hash() );
666 + }
994 667
995 - // skip if page is not build with elementor
996 - if ( ! \Elementor\Plugin::$instance->documents->get( $page_id )->is_built_with_elementor() ) {
997 - return [];
998 - }
668 + /**
669 + * Get the delete link.
670 + *
671 + * @return string
672 + */
673 + public function get_link_delete(): string {
674 + $nonce = wp_create_nonce( 'delete_optin_' . $this->get_hash() );
675 + return admin_url( 'admin.php?page=' . FORGE12_OPTIN_SLUG . '&action=delete&hash=' . $this->get_hash() . '&_wpnonce=' . $nonce );
676 + }
999 677
1000 - // Load the page's data
1001 - $document = \Elementor\Plugin::$instance->documents->get( $page_id );
678 + /**
679 + * Get the form name.
680 + *
681 + * @return string
682 + */
683 + public function get_form_name(): string {
684 + $formId = $this->get_cf_form_id();
1002 685
1003 - // Skip if document not exist
1004 - if ( ! $document ) {
1005 - return [];
1006 - }
1007 -
1008 - // Loop through the elements
1009 - $elements_data = $document->get_elements_data();
1010 -
1011 - $content = maybe_unserialize( $this->get_content() );
1012 -
1013 - // skip if form id not available
1014 - if ( ! isset( $content['form_id'] ) ) {
1015 - return [];
1016 - }
1017 -
1018 - $form_id = $content['form_id'];
1019 -
1020 - $form_element = null;
1021 -
1022 - // Find the Form Element
1023 - foreach ( $elements_data as $element ) {
1024 - if ( isset( $element['elements'] ) ) {
1025 - foreach ( $element['elements'] as $el ) {
1026 - if ( $form_id !== $el['id'] || 'form' !== $el['widgetType'] ) {
1027 - continue;
1028 - }
1029 -
1030 - $form_element = $el;
1031 - }
1032 - }
1033 -
1034 - // Check if this is the form widget
1035 - if ( isset( $element['widgetType'] ) && isset( $element['id'] ) && $element['widgetType'] === 'form' && $element['id'] === $form_id ) {
1036 - // Return the form settings
1037 - $form_element = $element;
1038 - }
1039 - }
1040 -
1041 - // skip if form element not found on the page anymore
1042 - if ( $form_element == null || ! isset( $form_element['settings'] ) ) {
1043 - return [];
1044 - }
1045 -
1046 - // Normalize Settings
1047 - return $this->maybe_normalize_settings( $form_element['settings'] );
686 + if ( $formId <= 0 ) {
687 + return __( 'Unknown', 'double-opt-in' );
1048 688 }
1049 689
1050 - /**
1051 - * Normalizes the settings by converting the value of the settings
1052 - *
1053 - * @param array $settings The settings array to be normalized.
1054 - * @formatter:off
1055 - *
1056 - * @return array {
1057 - * The settings of the element if found or an empty array if nothing was found.
1058 - * @type string $doi_email_to
1059 - * @type string $doi_email_subject
1060 - * @type string $doi_email_content
1061 - * @type string $doi_email_from
1062 - * @type string $doi_email_from_name
1063 - * @type string $doi_email_template
1064 - * @type string $doi_email_body
1065 - * }
1066 - *
1067 - * @formatter:on
1068 - */
1069 - private function maybe_normalize_settings( $settings ) {
1070 - if ( isset( $settings['sender'] ) ) {
1071 - $settings['doi_email_from'] = $settings['sender'];
1072 - }
1073 -
1074 - if ( isset( $settings['sender_name'] ) ) {
1075 - $settings['doi_email_from_name'] = $settings['sender_name'];
1076 - }
1077 -
1078 - if ( isset( $settings['subject'] ) ) {
1079 - $settings['doi_email_subject'] = $settings['subject'];
1080 - }
1081 -
1082 - if ( isset( $settings['recipient'] ) ) {
1083 - $settings['doi_email_to'] = $settings['recipient'];
1084 - }
1085 -
1086 - if ( isset( $settings['template'] ) ) {
1087 - $settings['doi_email_template'] = $settings['template'];
1088 - }
1089 -
1090 - if ( isset( $settings['body'] ) ) {
1091 - $settings['doi_email_body'] = $settings['body'];
1092 - }
1093 -
1094 - /**
1095 - * Normalize the settings between the different form plugins
1096 - *
1097 - * @formatter:off
1098 - *
1099 - * @param array {
1100 - * @type string $doi_email_to
1101 - * @type string $doi_email_subject
1102 - * @type string $doi_email_content
1103 - * @type string $doi_email_from
1104 - * @type string $doi_email_from_name
1105 - * @type string $doi_email_template
1106 - * @type string $doi_email_body
1107 - * }
1108 - *
1109 - * @formatter:on
1110 - *
1111 - * @since 3.0.0
1112 - */
1113 - return apply_filters( 'f12_cf7_doubleoption_normalize_settings', $settings );
690 + $post = get_post( $formId );
691 + if ( ! $post ) {
692 + return __( 'Deleted Form', 'double-opt-in' ) . ' (ID: ' . $formId . ')';
1114 693 }
1115 694
1116 - /**
1117 - * Set the Form as HTML
1118 - *
1119 - * @param string $form
1120 - *
1121 - * @return void
1122 - */
1123 - public function set_form( $form ) {
1124 - $this->form = $form;
1125 - }
695 + return $post->post_title ?: __( 'Untitled Form', 'double-opt-in' );
696 + }
1126 697
1127 - /**
1128 - * Return the Form
1129 - *
1130 - * @return string
1131 - */
1132 - public function get_mail_optin() {
1133 - return $this->mail_optin;
1134 - }
698 + /**
699 + * Get the form edit link.
700 + *
701 + * @return string
702 + */
703 + public function get_form_link(): string {
704 + $formId = $this->get_cf_form_id();
705 + $postType = get_post_type( $formId );
1135 706
1136 - /**
1137 - * Return the E-mail
1138 - *
1139 - * @return string
1140 - */
1141 - public function get_email() {
1142 - return $this->email;
707 + if ( $postType === 'wpcf7_contact_form' ) {
708 + return admin_url( 'admin.php?page=wpcf7&post=' . $formId . '&action=edit' );
1143 709 }
1144 710
1145 - /**
1146 - * Store the email
1147 - *
1148 - * @param string $email
1149 - *
1150 - * @return void
1151 - */
1152 - public function set_email( $email ) {
1153 - $this->email = $email;
711 + if ( $postType === 'fusion_form' ) {
712 + return admin_url( 'post.php?post=' . $formId . '&action=edit' );
1154 713 }
1155 714
1156 - /**
1157 - * Set the Form as HTML
1158 - *
1159 - * @param string $form
1160 - *
1161 - * @return void
1162 - */
1163 - public function set_mail_optin( $mail_html ) {
1164 - $this->mail_optin = $mail_html;
1165 - }
715 + return admin_url( 'post.php?post=' . $formId . '&action=edit' );
716 + }
1166 717
1167 - /**
1168 - * Return the link to the detail view
1169 - *
1170 - * @return string
1171 - */
1172 - public function get_link_ui() {
1173 - return admin_url( 'admin.php?page=f12-cf7-doubleoptin_optin_view&hash=' . $this->get_hash() );
1174 - }
718 + /**
719 + * Get form settings.
720 + *
721 + * @return array
722 + */
723 + public function get_form_settings(): array {
724 + return CF7DoubleOptIn::getInstance()->getParameter( $this->get_cf_form_id() );
725 + }
1175 726
1176 - /**
1177 - * Return the link to delete
1178 - *
1179 - * @return string
1180 - */
1181 - public function get_link_delete() {
1182 - return admin_url( 'admin.php?page=f12-cf7-doubleoptin&option=delete&hash=' . $this->get_hash() );
1183 - }
727 + // =========================================================================
728 + // HELPERS
729 + // =========================================================================
1184 730
1185 - /**
1186 - * Return the link to the export
1187 - *
1188 - * @return string
1189 - */
1190 - public function get_link_export() {
1191 - return admin_url( 'admin.php?page=f12-cf7-doubleoptin&export=csv&id=' . $this->get_id() );
1192 - }
731 + /**
732 + * Get the repository instance.
733 + *
734 + * @return OptInRepositoryInterface
735 + */
736 + private static function getRepository(): OptInRepositoryInterface {
737 + $container = Container::getInstance();
738 + return $container->get( OptInRepositoryInterface::class );
739 + }
1193 740
1194 - /**
1195 - * Return the link to the export
1196 - *
1197 - * @return string
1198 - */
1199 - public function get_link_export_v2() {
1200 - return admin_url( 'admin.php?page=f12-cf7-doubleoptin&export=csv_v2&id=' . $this->get_id() );
1201 - }
741 + /**
742 + * Map legacy property names to entity array keys.
743 + *
744 + * @param array $properties Legacy properties.
745 + *
746 + * @return array
747 + */
748 + private function mapToEntityArray( array $properties ): array {
749 + $mapping = [
750 + 'cf_form_id' => 'cf_form_id',
751 + 'doubleoptin' => 'doubleoptin',
752 + 'content' => 'content',
753 + 'hash' => 'hash',
754 + 'createtime' => 'createtime',
755 + 'updatetime' => 'updatetime',
756 + 'optouttime' => 'optouttime',
757 + 'ipaddr_register' => 'ipaddr_register',
758 + 'ipaddr_confirmation' => 'ipaddr_confirmation',
759 + 'ipaddr_optout' => 'ipaddr_optout',
760 + 'files' => 'files',
761 + 'category' => 'category',
762 + 'form' => 'form',
763 + 'mail_optin' => 'mail_optin',
764 + 'email' => 'email',
765 + 'id' => 'id',
766 + 'consent_text' => 'consent_text',
767 + 'consent_field' => 'consent_field',
768 + ];
1202 769
1203 - /**
1204 - * Return the link to the optin
1205 - *
1206 - * @return string
1207 - */
1208 - public function get_link_optin( $parameter = array() ) {
1209 - if ( $this->get_cf_form_id() == 0 ) {
1210 - return get_home_url() . '?optin=' . $this->get_hash();
770 + $result = [];
771 + foreach ( $mapping as $legacy => $entity ) {
772 + if ( isset( $properties[ $legacy ] ) ) {
773 + $result[ $entity ] = $properties[ $legacy ];
1211 774 }
1212 -
1213 - $parameter = array_merge( CF7DoubleOptIn::getInstance()->getParameter( $this->get_cf_form_id() ), $parameter );
1214 -
1215 - $page_id = $parameter['page'] ?? - 1;
1216 -
1217 - if ( $page_id == - 1 || ! $page_id || $page_id == 0 ) {
1218 - $link = get_home_url() . '?optin=' . $this->get_hash();
1219 - } else {
1220 -
1221 - $link = get_permalink( $page_id );
1222 -
1223 - if ( strpos( $link, "?" ) !== false ) {
1224 - $link .= '&optin=' . $this->get_hash();
1225 - } else {
1226 - $link .= '?optin=' . $this->get_hash();
1227 - }
1228 - }
1229 -
1230 - return $link;
1231 775 }
1232 776
1233 - /**
1234 - * Return the link to the optout
1235 - *
1236 - * @return string
1237 - */
1238 - public function get_link_optout() {
1239 - $settings = CF7DoubleOptIn::getInstance()->getSettings();
1240 -
1241 - if ( ! isset( $settings['optout_page'] ) || $settings['optout_page'] == 0 ) {
1242 - return get_home_url() . '?optout=' . $this->get_hash();
1243 - } else {
1244 - $url = get_permalink( $settings['optout_page'] );
1245 -
1246 - if ( str_contains( $url, '?' ) ) {
1247 - // If '?' exists in the URL, it means there are already parameters, so append with '&'
1248 - $url .= '&optout=' . $this->get_hash();
1249 - } else {
1250 - // Else, there are no parameters yet, so append with '?'
1251 - $url .= '?optout=' . $this->get_hash();
1252 - }
1253 -
1254 - return $url;
1255 -
1256 - }
1257 - }
777 + return $result;
1258 778 }
1259 -}
779 +}