PluginProbe
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 2.12.8
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v2.12.8
2.12.8 2.12.7 2.12.6 2.12.5 2.12.4 2.12.3 2.12.2 2.12.1 2.12.0 2.11.1 2.11.0 2.10.1 2.10.0 2.9.1 2.9.0 2.8.2 2.8.1 2.7.0 2.7.1 2.8.0 trunk 0.0.10 0.0.11 0.0.12 0.0.13 All 98 releases
← All changes | inc/admin-ajax.php +275 -96 0.0.11 → 2.12.8 View file →
@@ -8,10 +8,10 @@
8 8 */
9 9
10 10 namespace SRFM\Inc;
11 11
12 +use BSF_UTM_Analytics;
12 13 use SRFM\Inc\Traits\Get_Instance;
13 -use SRFM\Inc\Helper;
14 14
15 15 if ( ! defined( 'ABSPATH' ) ) {
16 16 exit; // Exit if accessed directly.
17 17 }
@@ -25,9 +25,8 @@
25 25 *
26 26 * @since 0.0.1
27 27 */
28 28 class Admin_Ajax {
29 -
30 29 use Get_Instance;
31 30
32 31 /**
33 32 * Constructor
@@ -37,13 +36,15 @@
37 36 public function __construct() {
38 37 add_action( 'wp_ajax_sureforms_recommended_plugin_activate', [ $this, 'required_plugin_activate' ] );
39 38 add_action( 'wp_ajax_sureforms_recommended_plugin_install', 'wp_ajax_install_plugin' );
40 39 add_action( 'wp_ajax_sureforms_integration', [ $this, 'generate_data_for_suretriggers_integration' ] );
40 + add_action( 'wp_ajax_srfm_download_export', [ $this, 'download_export_file' ] );
41 + add_action( 'wp_ajax_srfm_download_logs', [ $this, 'download_client_log' ] );
42 + add_action( 'wp_ajax_srfm_clear_logs', [ $this, 'clear_client_log' ] );
41 43
42 44 add_filter( SRFM_SLUG . '_admin_filter', [ $this, 'localize_script_integration' ] );
43 45 }
44 46
45 -
46 47 /**
47 48 * Required Plugin Activate
48 49 *
49 50 * @return void
@@ -52,9 +53,9 @@
52 53 public function required_plugin_activate() {
53 54
54 55 $response_data = [ 'message' => $this->get_error_msg( 'permission' ) ];
55 56
56 - if ( ! current_user_can( 'manage_options' ) ) {
57 + if ( ! Helper::current_user_can() ) {
57 58 wp_send_json_error( $response_data );
58 59 }
59 60
60 61 if ( empty( $_POST ) ) {
@@ -69,9 +70,9 @@
69 70 $response_data = [ 'message' => $this->get_error_msg( 'nonce' ) ];
70 71 wp_send_json_error( $response_data );
71 72 }
72 73
73 - if ( ! current_user_can( 'install_plugins' ) || ! isset( $_POST['init'] ) || ! sanitize_text_field( wp_unslash( $_POST['init'] ) ) ) {
74 + if ( ! isset( $_POST['init'] ) || ! sanitize_text_field( wp_unslash( $_POST['init'] ) ) ) {
74 75 wp_send_json_error(
75 76 [
76 77 'success' => false,
77 78 'message' => __( 'No plugin specified', 'sureforms' ),
@@ -78,10 +79,12 @@
78 79 ]
79 80 );
80 81 }
81 82
82 - $plugin_init = ( isset( $_POST['init'] ) ) ? sanitize_text_field( wp_unslash( $_POST['init'] ) ) : '';
83 + $plugin_init = isset( $_POST['init'] ) ? sanitize_text_field( wp_unslash( $_POST['init'] ) ) : '';
83 84
85 + $plugin_slug = isset( $_POST['slug'] ) ? sanitize_text_field( wp_unslash( $_POST['slug'] ) ) : '';
86 +
84 87 $activate = activate_plugin( $plugin_init, '', false, true );
85 88
86 89 if ( is_wp_error( $activate ) ) {
87 90 wp_send_json_error(
@@ -91,8 +94,13 @@
91 94 ]
92 95 );
93 96 }
94 97
98 + if ( class_exists( 'BSF_UTM_Analytics' ) && is_callable( 'BSF_UTM_Analytics::update_referer' ) ) {
99 + $plugin_slug = pathinfo( $plugin_slug, PATHINFO_FILENAME );
100 + BSF_UTM_Analytics::update_referer( 'sureforms', $plugin_slug );
101 + }
102 +
95 103 wp_send_json_success(
96 104 [
97 105 'success' => true,
98 106 'message' => __( 'Plugin Successfully Activated', 'sureforms' ),
@@ -125,8 +133,9 @@
125 133 * @return array<mixed>
126 134 * @since 0.0.1
127 135 */
128 136 public function localize_script_integration( $values ) {
137 + $is_screen_sureforms_menu = Helper::validate_request_context( 'sureforms_menu', 'page' );
129 138 return array_merge(
130 139 $values,
131 140 [
132 141 'ajax_url' => admin_url( 'admin-ajax.php' ),
@@ -131,16 +140,10 @@
131 140 [
132 141 'ajax_url' => admin_url( 'admin-ajax.php' ),
133 142 'sfPluginManagerNonce' => wp_create_nonce( 'sf_plugin_manager_nonce' ),
134 143 'plugin_installer_nonce' => wp_create_nonce( 'updates' ),
135 - 'plugin_activating_text' => __( 'Activating...', 'sureforms' ),
136 - 'plugin_activated_text' => __( 'Activated', 'sureforms' ),
137 - 'plugin_activate_text' => __( 'Activate', 'sureforms' ),
138 - 'integrations' => self::sureforms_get_integration(),
139 - 'plugin_installing_text' => __( 'Installing...', 'sureforms' ),
140 - 'plugin_installed_text' => __( 'Installed', 'sureforms' ),
141 144 'isRTL' => is_rtl(),
142 - 'current_screen_id' => get_current_screen() ? get_current_screen()->id : '',
145 + 'current_screen_id' => $is_screen_sureforms_menu ? 'sureforms_menu' : '',
143 146 'form_id' => get_post() ? get_post()->ID : '',
144 147 'suretriggers_nonce' => wp_create_nonce( 'suretriggers_nonce' ),
145 148 ]
146 149 );
@@ -146,65 +149,8 @@
146 149 );
147 150 }
148 151
149 152 /**
150 - * Get sureforms recommended integrations.
151 - *
152 - * @since 0.0.1
153 - * @return array<mixed>
154 - */
155 - public function sureforms_get_integration() {
156 - $suretrigger_connected = apply_filters( 'suretriggers_is_user_connected', '' );
157 - return apply_filters(
158 - 'srfm_integrated_plugins',
159 - [
160 - [
161 - 'title' => __( 'SureTriggers', 'sureforms' ),
162 - 'subtitle' => __( 'Connect SureForms to hundreds of apps, CRMs and tools such as Slack, Mailchimp, etc.', 'sureforms' ),
163 - 'description' => __( 'SureTriggers is a powerful automation platform that helps you connect your various plugins and apps together. It allows you to automate repetitive tasks, so you can focus on more important work.', 'sureforms' ),
164 - 'status' => self::get_plugin_status( 'suretriggers/suretriggers.php' ),
165 - 'slug' => 'suretriggers',
166 - 'path' => 'suretriggers/suretriggers.php',
167 - 'redirection' => admin_url( 'admin.php?page=suretriggers' ),
168 - 'logo' => self::encode_svg( is_string( file_get_contents( plugin_dir_path( SRFM_FILE ) . 'images/suretriggers.svg' ) ) ? file_get_contents( plugin_dir_path( SRFM_FILE ) . 'images/suretriggers.svg' ) : '' ),
169 - 'logo_full' => self::encode_svg( is_string( file_get_contents( plugin_dir_path( SRFM_FILE ) . 'images/suretriggers_full.svg' ) ) ? file_get_contents( plugin_dir_path( SRFM_FILE ) . 'images/suretriggers_full.svg' ) : '' ),
170 - 'connected' => $suretrigger_connected,
171 - ],
172 - ]
173 - );
174 - }
175 -
176 - /**
177 - * Encodes the given string with base64.
178 - *
179 - * @param string $logo contains svg's.
180 - * @return string
181 - */
182 - public function encode_svg( $logo ) {
183 - return 'data:image/svg+xml;base64,' . base64_encode( $logo ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
184 - }
185 - /**
186 - * Get plugin status
187 - *
188 - * @since 0.0.1
189 - *
190 - * @param string $plugin_init_file Plugin init file.
191 - * @return string
192 - */
193 - public static function get_plugin_status( $plugin_init_file ) {
194 -
195 - $installed_plugins = get_plugins();
196 -
197 - if ( ! isset( $installed_plugins[ $plugin_init_file ] ) ) {
198 - return 'Install';
199 - } elseif ( is_plugin_active( $plugin_init_file ) ) {
200 - return 'Activated';
201 - } else {
202 - return 'Installed';
203 - }
204 - }
205 -
206 - /**
207 153 * Generates data required for suretriggers integration
208 154 *
209 155 * @since 0.0.8
210 156 * @return void
@@ -209,26 +155,25 @@
209 155 * @since 0.0.8
210 156 * @return void
211 157 */
212 158 public function generate_data_for_suretriggers_integration() {
213 - if ( ! current_user_can( 'manage_options' ) ) {
214 - wp_send_json_error( [ 'message' => 'You do not have permission to access this page.' ] );
159 + if ( ! Helper::current_user_can() ) {
160 + wp_send_json_error( [ 'message' => __( 'You do not have permission to access this page.', 'sureforms' ) ] );
215 161 }
216 162
217 163 if ( ! check_ajax_referer( 'suretriggers_nonce', 'security', false ) ) {
218 - wp_send_json_error( [ 'message' => 'Invalid nonce.' ] );
164 + wp_send_json_error( [ 'message' => __( 'Invalid nonce.', 'sureforms' ) ] );
219 165 }
220 166
221 167 if ( empty( $_POST['formId'] ) ) {
222 - wp_send_json_error( [ 'message' => 'Form ID is required.' ] );
168 + wp_send_json_error( [ 'message' => __( 'Form ID is required.', 'sureforms' ) ] );
223 169 }
224 170
225 - $suretriggers_data = get_option( 'suretrigger_options', [] );
226 - if ( ! is_array( $suretriggers_data ) || empty( $suretriggers_data['secret_key'] ) || ! is_string( $suretriggers_data['secret_key'] ) ) {
171 + if ( ! Helper::is_suretriggers_ready() ) {
227 172 wp_send_json_error(
228 173 [
229 174 'code' => 'invalid_secret_key',
230 - 'message' => 'SureTriggers is not configured properly.',
175 + 'message' => __( 'OttoKit is not configured properly.', 'sureforms' ),
231 176 ]
232 177 );
233 178 }
234 179
@@ -238,10 +183,11 @@
238 183 if ( is_null( $form ) || SRFM_FORMS_POST_TYPE !== $form->post_type ) {
239 184 wp_send_json_error( [ 'message' => __( 'Invalid form ID.', 'sureforms' ) ] );
240 185 }
241 186
242 - $form_name = ! empty( $form->post_title ) ? $form->post_title : 'SureForms id: ' . $form_id;
243 - $api_url = apply_filters( 'suretriggers_get_iframe_url', SRFM_SURETRIGGERS_INTERGATION_BASE_URL );
187 + // Translators: %s: Form ID.
188 + $form_name = ! empty( $form->post_title ) ? $form->post_title : sprintf( __( 'SureForms id: %s', 'sureforms' ), $form_id );
189 + $api_url = apply_filters( 'suretriggers_get_iframe_url', SRFM_SURETRIGGERS_INTEGRATION_BASE_URL ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- SureTriggers' own filter; the name must match SureTriggers exactly to integrate.
244 190
245 191 // This is the format of data required by SureTriggers for adding iframe in target id.
246 192 $body = [
247 193 'client_id' => 'SureForms',
@@ -248,11 +194,11 @@
248 194 'st_embed_url' => $api_url,
249 195 'embedded_identifier' => $form_id,
250 196 'target' => 'suretriggers-iframe-wrapper', // div where we want SureTriggers to add iframe should have this target id.
251 197 'event' => [
252 - 'label' => 'Form Submitted',
198 + 'label' => __( 'Form Submitted', 'sureforms' ),
253 199 'value' => 'sureforms_form_submitted',
254 - 'description' => 'Runs when a form is submitted',
200 + 'description' => __( 'Runs when a form is submitted', 'sureforms' ),
255 201 ],
256 202 'summary' => $form_name,
257 203 'selected_options' => [
258 204 'form_id' => [
@@ -270,12 +216,22 @@
270 216 'data' => $this->get_form_fields( $form_id ),
271 217 ],
272 218 ];
273 219
220 + // Adding entry_id in body sample response if do_not_store_entries is not enabled.
221 + $compliance = get_post_meta( $form_id, '_srfm_compliance', true );
222 + $do_not_store_entries = is_array( $compliance ) && isset( $compliance[0]['do_not_store_entries'] )
223 + ? $compliance[0]['do_not_store_entries']
224 + : null;
225 +
226 + if ( ! $do_not_store_entries ) {
227 + $body['sample_response']['entry_id'] = 12;
228 + }
229 +
274 230 wp_send_json_success(
275 231 [
276 232 'message' => 'success',
277 - 'data' => $body,
233 + 'data' => apply_filters( 'srfm_suretriggers_integration_data_filter', $body, $form_id ),
278 234 ]
279 235 );
280 236 }
281 237
@@ -302,8 +258,20 @@
302 258 }
303 259
304 260 $blocks = parse_blocks( $post->post_content );
305 261
262 + $blocks = array_filter(
263 + $blocks,
264 + static function( $block ) {
265 + if ( 'srfm/html' === $block['blockName'] ) {
266 + return false;
267 + }
268 + return true;
269 + }
270 + );
271 +
272 + $blocks = array_values( $blocks );
273 +
306 274 if ( empty( $blocks ) ) {
307 275 return [];
308 276 }
309 277
@@ -310,8 +278,31 @@
310 278 $data = [];
311 279
312 280 foreach ( $blocks as $block ) {
313 281 if ( ! empty( $block['blockName'] ) && 0 === strpos( $block['blockName'], 'srfm/' ) ) {
282 +
283 + /**
284 + * Determine whether to skip this field from the sample data.
285 + *
286 + * @param bool $should_skip Default value indicating if field should be skipped.
287 + * @param array $block_details Array containing block attributes, including 'block_name'.
288 + *
289 + * @since 2.0.0
290 + *
291 + * @hook srfm_should_skip_field_from_sample_data
292 + */
293 + $should_skip_this_field = apply_filters(
294 + 'srfm_should_skip_field_from_sample_data',
295 + false,
296 + [
297 + 'block_name' => $block['blockName'],
298 + ]
299 + );
300 +
301 + if ( $should_skip_this_field ) {
302 + continue;
303 + }
304 +
314 305 if ( ! empty( $block['attrs']['slug'] ) ) {
315 306 $data[ $block['attrs']['slug'] ] = $this->get_sample_data( $block['blockName'] );
316 307 }
317 308 }
@@ -321,9 +312,8 @@
321 312 return [];
322 313 }
323 314
324 315 return $data;
325 -
326 316 }
327 317
328 318 /**
329 319 * Returns sample data for a block.
@@ -333,28 +323,28 @@
333 323 * @return mixed
334 324 */
335 325 public function get_sample_data( $block_name ) {
336 326 if ( empty( $block_name ) ) {
337 - return 'Sample data';
327 + return __( 'Sample data', 'sureforms' );
338 328 }
339 329
340 330 $dummy_data = [
341 - 'srfm/input' => 'Sample input data',
331 + 'srfm/input' => __( 'Sample input data', 'sureforms' ),
342 332 'srfm/email' => '[email protected]',
343 - 'srfm/textarea' => 'Sample textarea data',
333 + 'srfm/textarea' => __( 'Sample textarea data', 'sureforms' ),
344 334 'srfm/number' => 123,
345 335 'srfm/checkbox' => 'checkbox value',
346 336 'srfm/gdpr' => 'GDPR value',
347 337 'srfm/phone' => '1234567890',
348 - 'srfm/address' => 'Address data',
349 - 'srfm/address-compact' => 'Address data',
350 - 'srfm/dropdown' => 'Selected dropdown option',
351 - 'srfm/multi-choice' => 'Selected Multichoice option',
352 - 'srfm/radio' => 'Selected radio option',
353 - 'srfm/submit' => 'Submit',
338 + 'srfm/address' => __( 'Address data', 'sureforms' ),
339 + 'srfm/address-compact' => __( 'Address data', 'sureforms' ),
340 + 'srfm/dropdown' => __( 'Selected dropdown option', 'sureforms' ),
341 + 'srfm/multi-choice' => __( 'Selected Multichoice option', 'sureforms' ),
342 + 'srfm/radio' => __( 'Selected radio option', 'sureforms' ),
343 + 'srfm/submit' => __( 'Submit', 'sureforms' ),
354 344 'srfm/url' => 'https://example.com',
355 345 'srfm/date-time-picker' => '2022-01-01 12:00:00',
356 - 'srfm/hidden' => 'Hidden Value',
346 + 'srfm/hidden' => __( 'Hidden Value', 'sureforms' ),
357 347 'srfm/slider' => 50,
358 348 'srfm/password' => 'DummyPassword123',
359 349 'srfm/rating' => 4,
360 350 'srfm/upload' => 'https://example.com/uploads/file.pdf',
@@ -359,12 +349,201 @@
359 349 'srfm/rating' => 4,
360 350 'srfm/upload' => 'https://example.com/uploads/file.pdf',
361 351 ];
362 352
353 + /**
354 + * Filter the sample data for specific block types.
355 + *
356 + * Allows plugins and themes to add custom sample data for their block types
357 + * or modify existing sample data. This is particularly useful for dynamic
358 + * block types that require complex sample data structures.
359 + *
360 + * @since 0.0.8
361 + *
362 + * @param array $dummy_data {
363 + * Array of sample data keyed by block name.
364 + *
365 + * @type string|array $block_name Sample data for the block.
366 + * }
367 + * @param array $filter_args {
368 + * Additional filter arguments.
369 + *
370 + * @type string $block_name The name of the block being processed.
371 + * }
372 + */
373 + $dummy_data = Helper::apply_filters_as_array( 'srfm_sample_data_filter', $dummy_data, [ 'block_name' => $block_name ] );
374 +
363 375 if ( ! empty( $dummy_data[ $block_name ] ) ) {
364 376 return $dummy_data[ $block_name ];
365 - } else {
366 - return 'Sample data';
367 377 }
378 + return __( 'Sample data', 'sureforms' );
368 379 }
380 +
381 + /**
382 + * Download exported file.
383 + *
384 + * @since 2.0.0
385 + * @return void
386 + */
387 + public function download_export_file() {
388 + // Check user permissions.
389 + if ( ! Helper::current_user_can() ) {
390 + wp_die( esc_html__( 'You do not have permission to access this file.', 'sureforms' ) );
391 + }
392 +
393 + // Verify nonce for security.
394 + if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'srfm_download_export' ) ) {
395 + wp_die( esc_html__( 'Security check failed.', 'sureforms' ) );
396 + }
397 +
398 + // Get and sanitize the file parameter.
399 + $file = isset( $_GET['file'] ) ? sanitize_file_name( wp_unslash( $_GET['file'] ) ) : '';
400 +
401 + if ( empty( $file ) ) {
402 + wp_die( esc_html__( 'Invalid file request.', 'sureforms' ) );
403 + }
404 +
405 + // Build the full file path.
406 + $temp_dir = wp_normalize_path( trailingslashit( get_temp_dir() ) );
407 + $filepath = $temp_dir . $file;
408 +
409 + // Security check: ensure the file is in the temp directory.
410 + if ( strpos( wp_normalize_path( $filepath ), $temp_dir ) !== 0 ) {
411 + wp_die( esc_html__( 'Invalid file path.', 'sureforms' ) );
412 + }
413 +
414 + // Check if file exists.
415 + if ( ! file_exists( $filepath ) ) {
416 + wp_die( esc_html__( 'File not found.', 'sureforms' ) );
417 + }
418 +
419 + // Get file info.
420 + $file_size = filesize( $filepath );
421 + $file_info = pathinfo( $filepath );
422 +
423 + // Determine content type and filename based on file extension.
424 + $content_type = 'application/octet-stream';
425 + $filename = $file_info['basename'];
426 + if ( isset( $file_info['extension'] ) ) {
427 + if ( 'csv' === $file_info['extension'] ) {
428 + $content_type = 'text/csv';
429 + } elseif ( 'zip' === $file_info['extension'] ) {
430 + $content_type = 'application/zip';
431 + /**
432 + * Filter the user-facing filename used when serving an exported ZIP archive.
433 + *
434 + * @since 2.9.0
435 + *
436 + * @param string $filename Default ZIP filename.
437 + * @param array<string,mixed> $file_info pathinfo() result for the file being served.
438 + */
439 + $filename = (string) apply_filters( 'srfm_export_zip_filename', 'SureForms Entries.zip', $file_info );
440 + }
441 + }
442 +
443 + // Set headers for download.
444 + header( 'Content-Type: ' . $content_type );
445 + header( 'Content-Disposition: attachment; filename="' . $filename . '"' );
446 + header( 'Content-Length: ' . $file_size );
447 + header( 'Cache-Control: private, max-age=0, must-revalidate' );
448 + header( 'Pragma: public' );
449 +
450 + // Clear output buffers.
451 + if ( ob_get_level() ) {
452 + ob_end_clean();
453 + }
454 +
455 + // Output the file.
456 + readfile( $filepath ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_readfile, WordPress.WP.AlternativeFunctions.file_system_operations_readfile -- Direct file output is required to stream the download.
457 +
458 + // Clean up the temporary file.
459 + wp_delete_file( $filepath );
460 +
461 + exit;
462 + }
463 + /**
464 + * Stream the client debug log to an administrator.
465 + *
466 + * Takes no filename parameter. There is exactly one log file and the server
467 + * derives its path, which removes the path-traversal question entirely rather
468 + * than guarding against it -- and keeps the unguessable file name, which is
469 + * what actually protects the log on nginx, out of the page.
470 + *
471 + * @since 2.12.6
472 + * @return void
473 + */
474 + public function download_client_log() {
475 + $this->verify_log_request();
476 +
477 + $path = Client_Logger::get_log_path( false );
478 + $has_log = '' !== $path && file_exists( $path );
479 +
480 + // The buttons are always offered while logging is on, so downloading before
481 + // anything has failed is a normal thing to do. Hand back an explanatory file
482 + // rather than a wp_die() screen -- an empty log is the good outcome.
483 + if ( ! $has_log ) {
484 + header( 'Content-Type: text/plain; charset=utf-8' );
485 + header( 'X-Content-Type-Options: nosniff' );
486 + header( 'Content-Disposition: attachment; filename="sureforms-debug-log.txt"' );
487 +
488 + if ( ob_get_level() ) {
489 + ob_end_clean();
490 + }
491 +
492 + echo esc_html__( 'No form submission failures have been recorded.', 'sureforms' );
493 + exit;
494 + }
495 +
496 + $size = filesize( $path );
497 +
498 + header( 'Content-Type: text/plain; charset=utf-8' );
499 + header( 'X-Content-Type-Options: nosniff' );
500 + header( 'Content-Disposition: attachment; filename="sureforms-debug-log.txt"' );
501 +
502 + if ( is_int( $size ) ) {
503 + header( 'Content-Length: ' . $size );
504 + }
505 +
506 + header( 'Cache-Control: private, max-age=0, must-revalidate' );
507 +
508 + if ( ob_get_level() ) {
509 + ob_end_clean();
510 + }
511 +
512 + readfile( $path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_readfile, WordPress.WP.AlternativeFunctions.file_system_operations_readfile -- Direct file output is required to stream the download.
513 + exit;
514 + }
515 +
516 + /**
517 + * Delete the client debug log.
518 + *
519 + * @since 2.12.6
520 + * @return void
521 + */
522 + public function clear_client_log() {
523 + $this->verify_log_request();
524 +
525 + Client_Logger::clear();
526 +
527 + wp_send_json_success();
528 + }
529 +
530 + /**
531 + * Capability and nonce gate shared by both log actions.
532 + *
533 + * Capability first, ahead of the nonce, matching the ordering of the sibling
534 + * handlers in this class.
535 + *
536 + * @since 2.12.6
537 + * @return void
538 + */
539 + private function verify_log_request() {
540 + if ( ! Helper::current_user_can() ) {
541 + wp_die( esc_html__( 'You do not have permission to access this file.', 'sureforms' ) );
542 + }
543 +
544 + if ( ! isset( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ), 'srfm_client_logs' ) ) {
545 + wp_die( esc_html__( 'Security check failed.', 'sureforms' ) );
546 + }
547 + }
548 +
369 549 }
370 -