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.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 3.1.0 All 34 releases
double-opt-in / compatibility / cf7 / CF7Backend.class.php

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

469 lines 15.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace forge12\contactform7\CF7DoubleOptIn {
4
5 use forge12\plugins\ContactForm7;
6 use Forge12\Shared\LoggerInterface;
7 use Forge12\DoubleOptIn\EmailTemplates\PlaceholderMapper;
8
9 if ( ! defined( 'ABSPATH' ) ) {
10 exit;
11 }
12
13 /**
14 * Class Backend
15 * Responsible to handle the admin settings for the double opt-in field
16 *
17 * @package forge12\contactform7\CF7OptIn
18 *
19 * @deprecated 4.0.0 Use \Forge12\DoubleOptIn\Integration\CF7Integration instead.
20 * This class is maintained for backward compatibility only.
21 * @see \Forge12\DoubleOptIn\Integration\CF7Integration
22 * @see \Forge12\DoubleOptIn\Integration\AdminPanelInterface
23 */
24 class CF7Backend {
25 private LoggerInterface $logger;
26
27 /**
28 * Admin constructor.
29 */
30 public function __construct( LoggerInterface $logger ) {
31 $this->logger = $logger;
32
33 $this->get_logger()->debug( 'Admin constructor called', [
34 'plugin' => 'double-opt-in',
35 'class' => __CLASS__,
36 'method' => __METHOD__,
37 ] );
38
39 add_action( 'admin_init', [ $this, 'addHooks' ] );
40 $this->get_logger()->debug( 'Hook admin_init registered', [
41 'plugin' => 'double-opt-in',
42 ] );
43
44 add_action( 'admin_enqueue_scripts', [ $this, 'addStyles' ] );
45 $this->get_logger()->debug( 'Hook admin_enqueue_scripts registered', [
46 'plugin' => 'double-opt-in',
47 ] );
48 }
49
50
51 public function get_logger() {
52 return $this->logger;
53 }
54
55 /**
56 * Add the styles for the form
57 */
58 public function addStyles( $hook ) {
59 // Only load the DOI admin scripts — and their privileged
60 // `f12_doi_details` nonce — on the screens that actually use them
61 // (Contact Form 7's admin/editor and the DOI admin), NOT on every
62 // wp-admin page. Broadcasting the nonce everywhere let a
63 // low-privilege user scrape it from e.g. profile.php.
64 $hook = (string) $hook;
65 $onPluginScreen = ( strpos( $hook, 'wpcf7' ) !== false )
66 || ( strpos( $hook, 'f12-doi' ) !== false )
67 || ( strpos( $hook, 'f12-cf7-doubleoptin' ) !== false );
68 if ( ! $onPluginScreen ) {
69 return;
70 }
71
72 $this->get_logger()->debug( 'Enqueuing admin styles and scripts', [
73 'plugin' => 'double-opt-in',
74 'class' => __CLASS__,
75 'method' => __METHOD__,
76 'hook' => $hook,
77 ] );
78
79 wp_enqueue_script(
80 'f12-cf7-doubleoptin-admin',
81 plugins_url( 'assets/f12-cf7-popup.js', __FILE__ ),
82 [ 'jquery' ],
83 defined( 'FORGE12_OPTIN_VERSION' ) ? FORGE12_OPTIN_VERSION : false,
84 true
85 );
86 $this->get_logger()->debug( 'Admin popup script enqueued', [
87 'plugin' => 'double-opt-in',
88 ] );
89
90 wp_localize_script(
91 'f12-cf7-doubleoptin-admin',
92 'doi',
93 [
94 'ajax_url' => admin_url( 'admin-ajax.php' ),
95 'nonce' => wp_create_nonce( 'f12_doi_details' ),
96 ]
97 );
98 $this->get_logger()->debug( 'Admin popup script localized', [
99 'plugin' => 'double-opt-in',
100 ] );
101
102 wp_enqueue_script(
103 'f12-cf7-doubleoptin-templateloader',
104 plugins_url( 'assets/f12-cf7-templateloader.js', __FILE__ ),
105 [ 'jquery' ],
106 defined( 'FORGE12_OPTIN_VERSION' ) ? FORGE12_OPTIN_VERSION : false,
107 true
108 );
109 $this->get_logger()->debug( 'Template loader script enqueued', [
110 'plugin' => 'double-opt-in',
111 ] );
112
113 wp_localize_script(
114 'f12-cf7-doubleoptin-templateloader',
115 'templateloader',
116 [
117 'ajax_url' => admin_url( 'admin-ajax.php' ),
118 'nonce' => wp_create_nonce( 'f12_doi_templateloader' ),
119 'label_placeholder' => __( 'Please wait while we load the template...', 'double-opt-in' ),
120 ]
121 );
122 $this->get_logger()->debug( 'Template loader script localized', [
123 'plugin' => 'double-opt-in',
124 ] );
125 }
126
127
128 /**
129 * Add the hooks responsible to handle wordpress functions
130 */
131 public function addHooks() {
132 $this->get_logger()->debug( 'Registering CF7 admin hooks', [
133 'plugin' => 'double-opt-in',
134 'class' => __CLASS__,
135 'method' => __METHOD__,
136 ] );
137
138 add_filter( 'wpcf7_editor_panels', [ $this, 'addPanel' ], 10, 1 );
139 $this->get_logger()->debug( 'Filter wpcf7_editor_panels registered', [
140 'plugin' => 'double-opt-in',
141 ] );
142
143 add_action( 'wpcf7_save_contact_form', [ $this, 'save' ], 10, 3 );
144 $this->get_logger()->debug( 'Action wpcf7_save_contact_form registered', [
145 'plugin' => 'double-opt-in',
146 ] );
147 }
148
149
150 /**
151 * Add a custom panel to the contact form 7 options
152 */
153 public function addPanel( array $panels ) {
154 $this->get_logger()->debug( 'Adding CF7 editor panel', [
155 'plugin' => 'double-opt-in',
156 'class' => __CLASS__,
157 'method' => __METHOD__,
158 ] );
159
160 $panels['optin'] = [
161 'title' => __( 'Double-Opt-in', 'double-opt-in' ),
162 'callback' => [ $this, 'render' ],
163 ];
164
165 $this->get_logger()->debug( 'CF7 editor panel added', [
166 'plugin' => 'double-opt-in',
167 ] );
168
169 return $panels;
170 }
171
172
173 /**
174 * On Contact Form save store the information in the database
175 *
176 * @param \WPCF7_ContactForm $contact_form
177 * @param $args
178 * @param $context
179 */
180 public function save( $contact_form, $args, $context ) {
181 $postID = $contact_form->id();
182
183 $this->get_logger()->debug( 'Saving CF7 Double Opt-In settings', [
184 'plugin' => 'double-opt-in',
185 'class' => __CLASS__,
186 'method' => __METHOD__,
187 'post_id' => $postID,
188 ] );
189
190 // Validate nonce
191 if ( ! isset( $_POST['f12_cf7_doubleoptin_save_form_nonce'] ) || ! wp_verify_nonce( wp_unslash( $_POST['f12_cf7_doubleoptin_save_form_nonce'] ), 'f12_cf7_doubleoptin_save_form_action' ) ) {
192 $this->get_logger()->warning( 'Nonce validation failed during save', [
193 'plugin' => 'double-opt-in',
194 'post_id' => $postID,
195 ] );
196 return;
197 }
198
199 // Check if the double opt-in settings is set
200 if ( ! $postID || ! isset( $_POST['doubleoptin'] ) ) {
201 update_post_meta( $postID, 'f12-cf7-doubleoptin', [] );
202 $this->get_logger()->debug( 'No doubleoptin data found, cleared settings', [
203 'plugin' => 'double-opt-in',
204 'post_id' => $postID,
205 ] );
206 return;
207 }
208
209 $parameter = SanitizeHelper::sanitize_array( $_POST['doubleoptin'] );
210 $metadata = CF7DoubleOptIn::getInstance()->getParameter( $postID );
211
212 foreach ( $metadata as $key => $value ) {
213 if ( isset( $parameter[ $key ] ) ) {
214 if ( $key === 'body' ) {
215 $metadata[ $key ] = $parameter[ $key ];
216 } elseif ( $key === 'enable' ) {
217 $metadata[ $key ] = (int) $parameter[ $key ];
218 } elseif ( $key === 'sender' ) {
219 $metadata[ $key ] = $parameter[ $key ];
220 } else {
221 $metadata[ $key ] = $parameter[ $key ];
222 }
223 } elseif ( $key === 'enable' ) {
224 $metadata[ $key ] = 0;
225 }
226 }
227
228 $metadata = apply_filters( 'f12_cf7_doubleoptin_metadata_cf7', $metadata );
229 $metadata = apply_filters( 'f12_cf7_doubleoptin_save_form', $metadata );
230
231 update_post_meta( $postID, 'f12-cf7-doubleoptin', $metadata );
232
233 // Save placeholder mapping
234 if ( isset( $_POST['doubleoptin']['placeholder_mapping'] ) && is_array( $_POST['doubleoptin']['placeholder_mapping'] ) ) {
235 $mapping = array_map( 'sanitize_text_field', $_POST['doubleoptin']['placeholder_mapping'] );
236 PlaceholderMapper::saveCustomMapping( $postID, $mapping, 'cf7' );
237
238 $this->get_logger()->debug( 'Placeholder mapping saved', [
239 'plugin' => 'double-opt-in',
240 'post_id' => $postID,
241 'mapping' => $mapping,
242 ] );
243 }
244
245 $this->get_logger()->info( 'Double Opt-In settings saved', [
246 'plugin' => 'double-opt-in',
247 'post_id' => $postID,
248 'data' => $metadata,
249 ] );
250 }
251
252
253 /**
254 * Get all available templates including custom ones.
255 *
256 * @return array Array of template key => label.
257 */
258 private function getAvailableTemplates(): array {
259 $templates = [
260 'blank' => 'blank',
261 'newsletter_en' => 'newsletter_en',
262 'newsletter_en_2' => 'newsletter_en_2',
263 'newsletter_en_3' => 'newsletter_en_3',
264 ];
265
266 // Add custom templates from the Email Template Editor
267 try {
268 $container = \Forge12\DoubleOptIn\Container\Container::getInstance();
269 $integration = $container->get( \Forge12\DoubleOptIn\EmailTemplates\EmailTemplateIntegration::class );
270 $customTemplates = $integration->getCustomTemplates();
271
272 foreach ( $customTemplates as $template ) {
273 $templates[ 'custom_' . $template['id'] ] = $template['title'] . ' (' . __( 'Custom', 'double-opt-in' ) . ')';
274 }
275 } catch ( \Exception $e ) {
276 // Silently fail if integration is not available
277 $this->get_logger()->warning( 'Failed to load custom templates: ' . $e->getMessage(), [
278 'plugin' => 'double-opt-in',
279 ] );
280 }
281
282 return $templates;
283 }
284
285 /**
286 * Return an option list containing all tags for the condition and a default field to disable the condition.
287 *
288 * @param \WPCF7_ContactForm $post
289 *
290 * @return array
291 */
292 private function getTags( $post ) {
293 $this->get_logger()->debug( 'Fetching CF7 form tags', [
294 'plugin' => 'double-opt-in',
295 'class' => __CLASS__,
296 'method' => __METHOD__,
297 'post_id' => $post->id() ?? null,
298 ] );
299
300 $tags = [];
301 $arrayTags = $post->scan_form_tags();
302
303 foreach ( $arrayTags as $formTag /** @var \WPCF7_FormTag $formTag */ ) {
304 if ( ! empty( $formTag->name ) ) {
305 $tags[ $formTag->name ] = $formTag->name;
306 }
307 }
308
309 $this->get_logger()->debug( 'Form tags fetched', [
310 'plugin' => 'double-opt-in',
311 'count' => count( $tags ),
312 ] );
313
314 return $tags;
315 }
316
317
318 /**
319 * Show the backend double opt in options
320 *
321 * Displays a notice with link to central form management.
322 * Full settings are now managed centrally in the Forms admin page.
323 *
324 * @param \WPCF7_ContactForm $post
325 */
326 public function render( $post ) {
327 if ( ! $post ) {
328 return;
329 }
330
331 $metadata = CF7DoubleOptIn::getInstance()->getParameter( $post->id() );
332 $centralUrl = admin_url( 'admin.php?page=f12-doi-admin#/forms' );
333 $isEnabled = isset( $metadata['enable'] ) && $metadata['enable'] == 1;
334
335 $this->get_logger()->debug( 'Rendering CF7 panel notice', [
336 'plugin' => 'double-opt-in',
337 'form_id' => $post->id(),
338 'enabled' => $isEnabled,
339 ] );
340 ?>
341 <div class="doi-cf7-notice" style="padding: 20px;">
342 <div style="background: #fff; border: 1px solid #c3c4c7; border-radius: 4px; padding: 20px;">
343 <h2 style="margin-top: 0;"><?php _e( 'Double Opt-In Settings', 'double-opt-in' ); ?></h2>
344
345 <div style="display: flex; align-items: center; gap: 15px; margin-bottom: 20px;">
346 <span style="font-weight: 600;"><?php _e( 'Status:', 'double-opt-in' ); ?></span>
347 <?php if ( $isEnabled ) : ?>
348 <span style="display: inline-block; padding: 4px 12px; background: #d4edda; color: #155724; border-radius: 3px; font-weight: 500;">
349 <?php _e( 'Enabled', 'double-opt-in' ); ?>
350 </span>
351 <?php else : ?>
352 <span style="display: inline-block; padding: 4px 12px; background: #f8d7da; color: #721c24; border-radius: 3px; font-weight: 500;">
353 <?php _e( 'Disabled', 'double-opt-in' ); ?>
354 </span>
355 <?php endif; ?>
356 </div>
357
358 <p style="color: #666; margin-bottom: 20px;">
359 <?php _e( 'Double Opt-In settings are now managed centrally. Use the button below to configure this form.', 'double-opt-in' ); ?>
360 </p>
361
362 <a href="<?php echo esc_url( $centralUrl ); ?>" class="button button-primary">
363 <?php _e( 'Configure Double Opt-In', 'double-opt-in' ); ?>
364 </a>
365 </div>
366 </div>
367 <?php
368 }
369
370 /**
371 * Render the placeholder mapping UI.
372 *
373 * @param \WPCF7_ContactForm $post The contact form.
374 * @param array $metadata Form metadata.
375 * @param string $id Form element ID prefix.
376 * @return void
377 */
378 private function renderPlaceholderMappingUI( $post, array $metadata, string $id ): void {
379 $formFields = array_keys( $this->getTags( $post ) );
380 $standardPlaceholders = PlaceholderMapper::getStandardPlaceholders();
381 $autoMapping = PlaceholderMapper::autoDetectMapping( $formFields );
382 $customMapping = PlaceholderMapper::getCustomMapping( $post->id(), 'cf7' );
383
384 // Merge for display (custom takes precedence)
385 $effectiveMapping = array_merge( $autoMapping, $customMapping );
386 ?>
387 <div class="box" style="width:100%;">
388 <h2><?php _e( 'Standard Placeholders Mapping', 'double-opt-in' ); ?></h2>
389 <p class="description" style="margin-bottom: 15px;">
390 <?php _e( 'Map your form fields to standard placeholders. This allows you to use the same email template across different forms. Fields are auto-detected based on common naming patterns, but you can override them.', 'double-opt-in' ); ?>
391 </p>
392
393 <table class="widefat" style="margin-bottom: 20px;">
394 <thead>
395 <tr>
396 <th style="width: 200px;"><?php _e( 'Standard Placeholder', 'double-opt-in' ); ?></th>
397 <th style="width: 150px;"><?php _e( 'Auto-Detected', 'double-opt-in' ); ?></th>
398 <th><?php _e( 'Custom Mapping (Override)', 'double-opt-in' ); ?></th>
399 </tr>
400 </thead>
401 <tbody>
402 <?php foreach ( $standardPlaceholders as $placeholder => $config ) :
403 $autoDetected = isset( $autoMapping[ $placeholder ] ) ? $autoMapping[ $placeholder ] : '';
404 $customValue = isset( $customMapping[ $placeholder ] ) ? $customMapping[ $placeholder ] : '';
405 ?>
406 <tr>
407 <td>
408 <code>[<?php echo esc_html( $placeholder ); ?>]</code>
409 <br>
410 <small style="color: #666;"><?php echo esc_html( $config['label'] ); ?></small>
411 </td>
412 <td>
413 <?php if ( $autoDetected ) : ?>
414 <span style="color: #46b450;">
415 <code>[<?php echo esc_html( $autoDetected ); ?>]</code>
416 </span>
417 <?php else : ?>
418 <span style="color: #999;"><?php _e( 'Not detected', 'double-opt-in' ); ?></span>
419 <?php endif; ?>
420 </td>
421 <td>
422 <select name="doubleoptin[placeholder_mapping][<?php echo esc_attr( $placeholder ); ?>]"
423 style="width: 100%; max-width: 300px;">
424 <option value=""><?php
425 if ( $autoDetected ) {
426 printf( __( 'Use auto-detected: [%s]', 'double-opt-in' ), $autoDetected );
427 } else {
428 _e( '-- Select field --', 'double-opt-in' );
429 }
430 ?></option>
431 <?php foreach ( $formFields as $field ) : ?>
432 <option value="<?php echo esc_attr( $field ); ?>"
433 <?php selected( $customValue, $field ); ?>>
434 [<?php echo esc_html( $field ); ?>]
435 </option>
436 <?php endforeach; ?>
437 </select>
438 </td>
439 </tr>
440 <?php endforeach; ?>
441 </tbody>
442 </table>
443
444 <div style="background: #f9f9f9; padding: 15px; border-radius: 4px; border: 1px solid #ddd;">
445 <h4 style="margin-top: 0;"><?php _e( 'How to use', 'double-opt-in' ); ?></h4>
446 <p style="margin-bottom: 10px;">
447 <?php _e( 'In your email template, use these standard placeholders instead of form-specific ones:', 'double-opt-in' ); ?>
448 </p>
449 <div style="display: flex; flex-wrap: wrap; gap: 10px;">
450 <?php foreach ( $standardPlaceholders as $placeholder => $config ) : ?>
451 <code style="background: #fff; padding: 3px 8px; border: 1px solid #ddd; border-radius: 3px;">
452 [<?php echo esc_html( $placeholder ); ?>]
453 </code>
454 <?php endforeach; ?>
455 </div>
456 <p style="margin-top: 15px; margin-bottom: 0;">
457 <strong><?php _e( 'System Placeholders (always available):', 'double-opt-in' ); ?></strong><br>
458 <code>[doubleoptinlink]</code>,
459 <code>[doubleoptoutlink]</code>,
460 <code>[doubleoptin_form_date]</code>,
461 <code>[doubleoptin_form_time]</code>,
462 <code>[doubleoptin_form_url]</code>
463 </p>
464 </div>
465 </div>
466 <?php
467 }
468 }
469 }