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 / core / Ajax.class.php

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

286 lines 10.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace forge12\contactform7\CF7DoubleOptIn {
4
5 use Forge12\Shared\Logger;
6 use Forge12\Shared\LoggerInterface;
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit;
10 }
11
12 /**
13 * Class Ajax
14 * Responsible to handle the admin settings for the double opt-in field
15 *
16 * @package forge12\contactform7\CF7OptIn
17 */
18 class Ajax {
19 private LoggerInterface $logger;
20
21 /**
22 * Admin constructor.
23 */
24 public function __construct( LoggerInterface $logger ) {
25 $this->logger = $logger;
26
27 $this->get_logger()->debug( 'Initializing AJAX handlers', [
28 'plugin' => 'double-opt-in',
29 'class' => __CLASS__,
30 'method' => __METHOD__,
31 ] );
32
33 add_action( 'wp_ajax_f12_doi_details', [ $this, 'getDetails' ] );
34 add_action( 'wp_ajax_f12_doi_templateloader', [ $this, 'getTemplate' ] );
35
36 $this->get_logger()->info( 'AJAX handlers registered', [
37 'plugin' => 'double-opt-in',
38 'class' => __CLASS__,
39 'method' => __METHOD__,
40 ] );
41 }
42
43
44 public function get_logger() {
45 return $this->logger;
46 }
47
48 /**
49 * Load and get the template we need.
50 */
51 public function getTemplate() {
52 // A nonce proves intent, not authorization — gate on capability too.
53 if ( ! current_user_can( 'manage_options' ) ) {
54 wp_die( -1, 403 );
55 }
56 $this->get_logger()->debug( 'getTemplate called', [
57 'plugin' => 'double-opt-in',
58 'class' => __CLASS__,
59 'method' => __METHOD__,
60 'post' => $_POST,
61 ] );
62
63 $content = '';
64 if ( isset( $_POST['template'] ) && wp_verify_nonce( wp_unslash( $_POST['nonce'] ), 'f12_doi_templateloader' ) ) {
65 // Use sanitize_file_name() to prevent path traversal attacks
66 $template_name = sanitize_file_name( wp_unslash( $_POST['template'] ) );
67 $mails_dir = plugin_dir_path( dirname( __FILE__ ) ) . 'mails/';
68 $template_path = $mails_dir . $template_name . '.html';
69
70 // Verify the resolved path is within the allowed directory (prevent path traversal)
71 $real_template_path = realpath( $template_path );
72 $real_mails_dir = realpath( $mails_dir );
73
74 if ( $real_template_path && $real_mails_dir && strpos( $real_template_path, $real_mails_dir ) === 0 && file_exists( $real_template_path ) ) {
75 $this->get_logger()->debug( 'Template file found, loading content', [
76 'plugin' => 'double-opt-in',
77 'class' => __CLASS__,
78 'method' => __METHOD__,
79 'template_path' => $template_path,
80 ] );
81 $content = file_get_contents( $real_template_path );
82 } else {
83 $this->get_logger()->warning( 'Template file not found', [
84 'plugin' => 'double-opt-in',
85 'class' => __CLASS__,
86 'method' => __METHOD__,
87 'template_path' => $template_path,
88 ] );
89 }
90 } else {
91 $this->get_logger()->warning( 'Invalid or missing nonce/template parameter', [
92 'plugin' => 'double-opt-in',
93 'class' => __CLASS__,
94 'method' => __METHOD__,
95 'post' => $_POST,
96 ] );
97 }
98
99 echo wp_json_encode( [ 'status' => 200, 'content' => $content ] );
100 wp_die();
101 }
102
103
104 /**
105 * Return the Popup for the HASH DOI
106 */
107 public function getDetails() {
108 // Exposes opt-in PII (email, IPs, all form fields). A nonce proves
109 // intent, not authorization — require the capability explicitly.
110 if ( ! current_user_can( 'manage_options' ) ) {
111 wp_die( -1, 403 );
112 }
113 $this->get_logger()->debug( 'getDetails called', [
114 'plugin' => 'double-opt-in',
115 'class' => __CLASS__,
116 'method' => __METHOD__,
117 'post' => $_POST,
118 ] );
119
120 if ( isset( $_POST['hash'] ) && wp_verify_nonce( wp_unslash( $_POST['nonce'] ), 'f12_doi_details' ) ) {
121 global $wpdb;
122 $tableName = $wpdb->prefix . 'f12_cf7_doubleoptin';
123 $hash = sanitize_text_field( $_POST['hash'] );
124
125 $this->get_logger()->debug( 'Looking up OptIn by hash', [
126 'plugin' => 'double-opt-in',
127 'class' => __CLASS__,
128 'method' => __METHOD__,
129 'hash' => $hash,
130 ] );
131
132 $OptIn = OptIn::get_by_hash( $hash );
133
134 if ( null == $OptIn ) {
135 $this->get_logger()->warning( 'OptIn not found for hash', [
136 'plugin' => 'double-opt-in',
137 'class' => __CLASS__,
138 'method' => __METHOD__,
139 'hash' => $hash,
140 ] );
141
142 ob_start();
143 ?>
144 <h2><?php _e( 'Ooops!', 'double-opt-in' ); ?></h2>
145 <p>
146 <?php _e( 'Something went wrong. The given DOI wasn\'t found. Maybe it got removed?', 'double-opt-in' ); ?>
147 </p>
148 <?php
149 $content = ob_get_contents();
150 ob_end_clean();
151 echo wp_json_encode( [ 'status' => 200, 'content' => $content ] );
152 wp_die();
153 }
154
155 $this->get_logger()->info( 'OptIn found', [
156 'plugin' => 'double-opt-in',
157 'class' => __CLASS__,
158 'method' => __METHOD__,
159 'optin' => [
160 'id' => $OptIn->get_id(),
161 'hash' => $OptIn->get_hash(),
162 'email' => $OptIn->get_email(),
163 ],
164 ] );
165
166 $formfields = maybe_unserialize( $OptIn->get_content() );
167 // Handle nested content structure (e.g., Avada stores {data: {...}, field_labels: {...}, ...})
168 if ( is_array( $formfields ) && isset( $formfields['data'] ) && is_array( $formfields['data'] ) && ! isset( $formfields['fields'] ) ) {
169 $formfields = $formfields['data'];
170 }
171 ob_start();
172 ?>
173 <h2><?php echo esc_html( $OptIn->get_hash() ); ?></h2>
174 <?php if ( current_user_can( 'manage_options' ) ): ?>
175 <div class="options">
176 <?php
177 do_action( 'f12_cf7_doubleoptin_ui_view_optin_options', $OptIn );
178 ?>
179 <a class="button"
180 href="<?php echo esc_url( $OptIn->get_link_delete() ); ?>"><?php _e( 'Delete DOI', 'double-opt-in' ); ?></a>
181 <a class="button"
182 href="<?php echo esc_url( $OptIn->get_link_ui() ); ?>"><?php _e( 'Details', 'double-opt-in' ); ?></a>
183 </div>
184 <?php endif; ?>
185 <table>
186 <tr>
187 <td><?php _e( 'Key', 'double-opt-in' ); ?></td>
188 <td><?php _e( 'Value', 'double-opt-in' ); ?></td>
189 </tr>
190 <tr>
191 <td><?php _e( 'ID', 'double-opt-in' ); ?></td>
192 <td><?php echo esc_html( $OptIn->get_id() ); ?></td>
193 </tr>
194 <tr>
195 <td><?php _e( 'CF7 Form ID', 'double-opt-in' ); ?></td>
196 <td><?php echo esc_html( $OptIn->get_cf_form_id() ); ?></td>
197 </tr>
198 <tr>
199 <td><?php _e( 'Registration Date', 'double-opt-in' ); ?></td>
200 <td><?php echo esc_html( $OptIn->get_createtime( 'formatted' ) ); ?></td>
201 </tr>
202 <tr>
203 <td><?php _e( 'Registration IP', 'double-opt-in' ); ?></td>
204 <td><?php echo esc_html( $OptIn->get_ipaddr_register() ); ?></td>
205 </tr>
206 <tr>
207 <td><?php _e( 'Confirmation Date', 'double-opt-in' ); ?></td>
208 <td><?php if ( $OptIn->is_confirmed() ) {
209 echo esc_html( $OptIn->get_updatetime( 'formatted' ) );
210 } ?></td>
211 </tr>
212 <tr>
213 <td><?php _e( 'Confirmation IP', 'double-opt-in' ); ?></td>
214 <td><?php echo esc_html( $OptIn->get_ipaddr_confirmation() ); ?></td>
215 </tr>
216 </table>
217
218 <h3><?php _e( 'Form Fields', 'double-opt-in' ); ?></h3>
219 <table>
220 <tr>
221 <td><?php _e( 'Key', 'double-opt-in' ); ?></td>
222 <td><?php _e( 'Value', 'double-opt-in' ); ?></td>
223 </tr>
224 <?php if ( isset( $formfields['fields'] ) ): ?>
225 <?php foreach ( $formfields['fields'] as $key => $value ): ?>
226 <tr>
227 <td><?php esc_attr_e( $key ); ?></td>
228 <td><?php echo is_array( $value ) ? esc_html( implode( ',', $value ) ) : esc_html( $value ); ?></td>
229 </tr>
230 <?php endforeach; ?>
231 <?php else: ?>
232 <?php foreach ( $formfields as $key => $value ): ?>
233 <tr>
234 <td><?php esc_attr_e( $key ); ?></td>
235 <td><?php echo is_array( $value ) ? esc_html( implode( ',', $value ) ) : esc_html( $value ); ?></td>
236 </tr>
237 <?php endforeach; ?>
238 <?php endif; ?>
239 </table>
240 <?php
241 $content = ob_get_contents();
242 ob_end_clean();
243 echo wp_json_encode( [ 'status' => 200, 'content' => $content ] );
244 exit;
245 }
246
247 $this->get_logger()->warning( 'Invalid request for getDetails (missing or invalid nonce/hash)', [
248 'plugin' => 'double-opt-in',
249 'class' => __CLASS__,
250 'method' => __METHOD__,
251 'post' => $_POST,
252 ] );
253
254 wp_die( 0 );
255 }
256
257
258 /**
259 * Add the styles for the form
260 */
261 public function addStyles( $hook ) {
262 $this->get_logger()->debug( 'addStyles called', [
263 'plugin' => 'double-opt-in',
264 'class' => __CLASS__,
265 'method' => __METHOD__,
266 'hook' => $hook,
267 ] );
268
269 if ( $hook == 'tools_page_f12doubleoptin' ) {
270 $ver = defined( 'FORGE12_OPTIN_VERSION' ) ? FORGE12_OPTIN_VERSION : false;
271 wp_enqueue_style( 'f12-cf7-doubleoptin-admin', plugins_url( 'assets/admin-style.css', __FILE__ ), array(), $ver );
272 wp_enqueue_script( 'f12-cf7-doubleoptin-admin', plugins_url( 'assets/f12-cf7-popup.js', __FILE__ ), [ 'jquery' ], $ver, true );
273
274 $this->get_logger()->info( 'Admin styles and scripts enqueued for DOI tools page', [
275 'plugin' => 'double-opt-in',
276 'class' => __CLASS__,
277 'method' => __METHOD__,
278 'hook' => $hook,
279 ] );
280 }
281 }
282
283 }
284
285 new Ajax( Logger::getInstance() );
286 }