PluginProbe
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 1.9.0
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v1.9.0
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 0.0.2 0.0.3 All 96 releases
sureforms / admin / views / single-entry.php
single-entry.php
553 lines 19.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * SureForms Single Entries Page.
4 *
5 * @since 0.0.13
6 * @package sureforms.
7 */
8
9 namespace SRFM\Admin\Views;
10
11 use SRFM\Inc\Database\Tables\Entries;
12 use SRFM\Inc\Helper;
13
14 /**
15 * Exit if accessed directly.
16 */
17 if ( ! defined( 'ABSPATH' ) ) {
18 exit;
19 }
20
21 /**
22 * Single entry page.
23 *
24 * @since 0.0.13
25 */
26 class Single_Entry {
27 /**
28 * Stores the entry ID.
29 *
30 * @var string|null $entry_id ID for the specific entry.
31 * @since 0.0.13
32 */
33 private $entry_id;
34
35 /**
36 * Stores the entry data for the specified entry ID.
37 *
38 * @var array<mixed>|null $entry Entry data for the specified entry ID.
39 * @since 0.0.13
40 */
41 private $entry;
42
43 /**
44 * Initialize the properties.
45 *
46 * @since 0.0.13
47 */
48 public function __construct() {
49 if ( isset( $_GET['_wpnonce'] ) && ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'srfm_entries_action' ) ) {
50 return;
51 }
52 $this->entry_id = isset( $_GET['entry_id'] ) ? intval( sanitize_text_field( wp_unslash( $_GET['entry_id'] ) ) ) : null;
53 $this->entry = $this->entry_id ? Entries::get( $this->entry_id ) : null;
54 }
55
56 /**
57 * Render the single entry page if an entry is found.
58 *
59 * @since 0.0.13
60 * @return void
61 */
62 public function render() {
63 if ( ! $this->entry ) {
64 return;
65 }
66 $entry_status = $this->entry['status'];
67 $submitted_on = gmdate( 'Y/m/d \a\t g:i a', strtotime( $this->entry['created_at'] ) );
68 // Translators: %d is the form ID.
69 $form_name = ! empty( get_the_title( $this->entry['form_id'] ) ) ? get_the_title( $this->entry['form_id'] ) : sprintf( esc_html__( 'SureForms Form #%d', 'sureforms' ), intval( $this->entry['form_id'] ) );
70 $meta_data = $this->entry['form_data'];
71 $excluded_fields = [ 'srfm-honeypot-field', 'g-recaptcha-response', 'srfm-sender-email-field' ];
72 ?>
73 <div class="wrap">
74 <h1 class="wp-heading-inline">
75 <?php
76 /* Translators: %s is the entry id. */
77 printf( esc_html__( 'Entry #%s', 'sureforms' ), esc_html( $this->entry_id ) );
78 ?>
79 </h1>
80
81 <form method="post" action="<?php echo esc_url( admin_url( "admin.php?page=sureforms_entries&entry_id={$this->entry_id}&view=details" ) ); ?>"> <!-- check for nonce, referrer, etc. -->
82 <?php
83 /**
84 * Action hook right after entry form opening tag.
85 *
86 * @since 1.3.0
87 */
88 do_action( 'srfm_after_entry_form_opening_tag', $this->entry, $this );
89 ?>
90
91 <div id="poststuff">
92 <div id="post-body" class="metabox-holder columns-2">
93 <div id="postbox-container-1" class="postbox-container">
94 <?php
95 /**
96 * Action hook right before entry form opening tag.
97 *
98 * @since 1.3.0
99 */
100 do_action( 'srfm_before_entry_submission_info', $this->entry, $this );
101 $this->render_upsell_placeholder( 'notes' );
102
103 /**
104 * Action hook right after the entry notes.
105 *
106 * @since 1.9.0
107 */
108 do_action( 'srfm_after_entry_notes', $this->entry, $this );
109
110 $this->render_submission_info( $form_name, $entry_status, $submitted_on );
111
112 /**
113 * Action hook right after entry form opening tag.
114 *
115 * @since 1.3.0
116 */
117 do_action( 'srfm_after_entry_submission_info', $this->entry, $this );
118 $this->render_upsell_placeholder( 'resend-notification' );
119 ?>
120 </div>
121 <div id="postbox-container-2" class="postbox-container">
122 <?php $this->render_form_data( $meta_data, $excluded_fields ); ?>
123 </div>
124 <div id="postbox-container-3" class="postbox-container">
125 <?php $this->render_entry_logs( $this->entry['logs'] ); ?>
126 </div>
127 </div><!-- /post-body -->
128 <br class="clear">
129 </div>
130 <!-- /poststuff -->
131
132 <?php
133 /**
134 * Action hook right before entry form closing tag.
135 *
136 * @since 1.3.0
137 */
138 do_action( 'srfm_before_entry_form_closing_tag', $this->entry, $this );
139 ?>
140 </form>
141 </div>
142 <?php
143 }
144
145 /**
146 * Add tooltip wrapper.
147 *
148 * @param string $position Tooltip position.
149 * @param callable $element_cb Element callback.
150 * @param callable $tooltip_cb Tooltip callback.
151 * @since 1.3.0
152 * @return void
153 */
154 protected function add_tooltip( $position, $element_cb, $tooltip_cb ) {
155 $upsell_url = Helper::get_sureforms_website_url( 'pricing', [ 'utm_medium' => 'srfm_entries_management' ] );
156 ?>
157 <div class="srfm-tooltip">
158 <?php call_user_func( $element_cb ); ?>
159 <div class="tooltip-wrap <?php echo esc_attr( $position ); ?>">
160 <div class="tooltip-content">
161 <div class="tooltip-text">
162 <?php call_user_func( $tooltip_cb ); ?>
163 </div>
164 <a target="_blank" href="<?php echo esc_url( $upsell_url ); ?>"><?php esc_html_e( 'Upgrade', 'sureforms' ); ?></a>
165 </div>
166 <i></i>
167 </div>
168 </div>
169 <?php
170 }
171
172 /**
173 * Renders the upsell placeholder.
174 *
175 * @param string $for The placeholder type.
176 * @since 1.3.0
177 * @return void
178 */
179 protected function render_upsell_placeholder( $for ) {
180 if ( Helper::has_pro() ) {
181 return;
182 }
183
184 switch ( $for ) {
185 case 'edit-button':
186 $position = 'top';
187 $element_cb = static function() {
188 ?>
189 <button class="button button-link srfm-edit-entry" type="button">
190 <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
191 <path d="M11.2411 2.99111L12.3661 1.86612C12.8543 1.37796 13.6457 1.37796 14.1339 1.86612C14.622 2.35427 14.622 3.14573 14.1339 3.63388L7.05479 10.713C6.70234 11.0654 6.26762 11.3245 5.78993 11.4668L4 12L4.53319 10.2101C4.67548 9.73239 4.93456 9.29767 5.28701 8.94522L11.2411 2.99111ZM11.2411 2.99111L13 4.74999M12 9.33333V12.5C12 13.3284 11.3284 14 10.5 14H3.5C2.67157 14 2 13.3284 2 12.5V5.49999C2 4.67157 2.67157 3.99999 3.5 3.99999H6.66667" stroke="#2271b1" stroke-linecap="round" stroke-linejoin="round"/>
192 </svg>
193 <?php esc_html_e( 'Edit', 'sureforms' ); ?>
194 </button>
195 <?php
196 };
197 $tooltip_cb = static function() {
198 ?>
199 <h3><?php esc_html_e( 'Unlock Edit Form Entires', 'sureforms' ); ?></h3>
200 <p><?php esc_html_e( 'With the SureForms Starter plan, you can easily edit your entries to suit your needs.', 'sureforms' ); ?></p>
201 <?php
202 };
203 break;
204
205 case 'resend-notification':
206 $position = 'left';
207 $element_cb = static function() {
208 ?>
209 <button type="button" class="button srfm-resend-notification-trigger-btn"><?php esc_html_e( 'Resend Notification', 'sureforms' ); ?></button>
210 <?php
211 };
212 $tooltip_cb = static function() {
213 ?>
214 <h3><?php esc_html_e( 'Unlock Resend Email Notification', 'sureforms' ); ?></h3>
215 <p><?php esc_html_e( 'With the SureForms Starter plan, you can effortlessly resend email notifications, ensuring your important updates reach their recipients with ease.', 'sureforms' ); ?></p>
216 <?php
217 };
218 break;
219
220 case 'notes':
221 $position = 'left';
222 $element_cb = static function() {
223 ?>
224 <div id="submitdiv" class="postbox entry-notes">
225 <div class="postbox-header">
226 <h2><?php esc_html_e( 'Entry Notes', 'sureforms' ); ?></h2>
227 <button type="button" id="srfm-add-entry-note" class="srfm-add-entry-note-button">
228 <?php esc_html_e( 'Add Note', 'sureforms' ); ?>
229 <svg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'><path d='M8 3.33594V12.6693' stroke='black' stroke-width='1.25' stroke-linecap='round' stroke-linejoin='round'/><path d='M3.33337 8H12.6667' stroke='black' stroke-width='1.25' stroke-linecap='round' stroke-linejoin='round'/></svg>
230 </button>
231 </div>
232 <div class="inside">
233 <div class="srfm-entry-note-wrapper">
234 <div class="entry-notes-container"></div>
235 <div class="add-notes-field">
236 <textarea disabled id="srfm-entry-note" rows="5"></textarea>
237 <button id="srfm-add-note" type="button" class="button"><?php esc_html_e( 'Submit Note', 'sureforms' ); ?></button>
238 </div>
239 </div>
240 </div>
241 </div>
242 <?php
243 };
244 $tooltip_cb = static function() {
245 ?>
246 <h3><?php esc_html_e( 'Unlock Add Note', 'sureforms' ); ?></h3>
247 <p><?php esc_html_e( 'With the SureForms Starter plan, enhance your submitted form entries by adding personalized notes for better clarity and tracking.', 'sureforms' ); ?></p>
248 <?php
249 };
250 break;
251
252 default:
253 // Do nothing.
254 return;
255 }
256
257 $this->add_tooltip( $position, $element_cb, $tooltip_cb );
258 }
259
260 /**
261 * Render the submission information for a specific entry.
262 *
263 * @param string $form_name The form title/name.
264 * @param string $entry_status The entry status (read/unread).
265 * @param string $submitted_on The submission date.
266 * @since 0.0.13
267 * @return void
268 */
269 private function render_submission_info( $form_name, $entry_status, $submitted_on ) {
270 $mark_as_unread_url = add_query_arg( 'action', 'unread' );
271 $user_id = Helper::get_integer_value( $this->entry['user_id'] );
272 $user_info = 0 !== $user_id ? get_userdata( $user_id ) : null;
273 $user_name = $user_info ? $user_info->display_name : '';
274 $user_profile_url = $user_info ? get_author_posts_url( $user_id ) : '';
275 ?>
276 <div id="sureform_form_name_meta" class="postbox ">
277 <div class="postbox-header">
278 <!-- Removed "hndle ui-sortable-handle" class from h2 to remove the draggable stylings. -->
279 <h2><?php esc_html_e( 'Submission Info', 'sureforms' ); ?></h2>
280 </div>
281 <div class="inside">
282 <table style="border-collapse: separate; border-spacing: 5px 5px;">
283 <tbody>
284 <!-- TODO: Add Type and User info. -->
285 <tr>
286 <td><b><?php esc_html_e( 'Entry:', 'sureforms' ); ?></b></td>
287 <td>#<?php echo esc_attr( $this->entry_id ); ?></td>
288 </tr>
289 <tr>
290 <td><b><?php esc_html_e( 'Form Name:', 'sureforms' ); ?></b></td>
291 <td><a target="_blank" rel="noopener" href="<?php the_permalink( $this->entry['form_id'] ); ?>"><?php echo esc_attr( $form_name ); ?></a></td>
292 </tr>
293 <?php if ( ! empty( $this->entry['submission_info']['user_ip'] ) ) { ?>
294 <tr>
295 <td><b><?php esc_html_e( 'User IP:', 'sureforms' ); ?></b></td>
296 <td><a target="_blank" rel="noopener" href="https://ipinfo.io/"><?php echo esc_attr( $this->entry['submission_info']['user_ip'] ); ?></a></td>
297 </tr>
298 <?php } ?>
299 <tr>
300 <td><b><?php esc_html_e( 'Browser:', 'sureforms' ); ?></b></td>
301 <td><?php echo esc_attr( $this->entry['submission_info']['browser_name'] ); ?></td>
302 </tr>
303 <tr>
304 <td><b><?php esc_html_e( 'Device:', 'sureforms' ); ?></b></td>
305 <td><?php echo esc_attr( $this->entry['submission_info']['device_name'] ); ?></td>
306 </tr>
307 <?php if ( 0 !== $user_id ) { ?>
308 <tr>
309 <td><b><?php esc_html_e( 'User:', 'sureforms' ); ?></b></td>
310 <td><a target="_blank" rel="noopener" href="<?php echo esc_url( $user_profile_url ); ?>"><?php echo esc_attr( $user_name ); ?></a></td>
311 </tr>
312 <?php } ?>
313 <tr>
314 <td><b><?php esc_html_e( 'Status:', 'sureforms' ); ?></b></td>
315 <td>
316 <span style="text-transform: capitalize;">
317 <?php echo esc_attr( $entry_status ); ?>
318 </span>
319 <?php if ( 'read' === $entry_status ) { ?>
320 <span> | <a href="<?php echo esc_url( $mark_as_unread_url ); ?>" id="srfm-entry-mark-unread" style="font-size: 12px;"><?php esc_html_e( 'Mark as Unread', 'sureforms' ); ?></a></span>
321 <?php } ?>
322 </td>
323 </tr>
324 <tr>
325 <td><b><?php esc_html_e( 'Submitted On:', 'sureforms' ); ?></b></td>
326 <td><?php echo esc_attr( $submitted_on ); ?></td>
327 </tr>
328 </tbody>
329 </table>
330 </div>
331 </div>
332 <?php
333 }
334
335 /**
336 * Render the form data for a specific entry.
337 *
338 * @param array<mixed> $meta_data The form meta data.
339 * @param array<string> $excluded_fields Fields to exlude from display.
340 * @since 0.0.13
341 * @return void
342 */
343 private function render_form_data( $meta_data, $excluded_fields ) {
344 ?>
345 <div id="sureform_entry_meta" class="postbox srfm-form-data">
346 <div class="postbox-header">
347 <!-- Removed "hndle ui-sortable-handle" class from h2 to remove the draggable stylings. -->
348 <h2><?php esc_html_e( 'Form Data', 'sureforms' ); ?></h2>
349 <?php
350 /**
351 * Action hook right after entry form opening tag.
352 *
353 * @since 1.3.0
354 */
355 do_action( 'srfm_after_entry_postbox_title', $this->entry, $this );
356 $this->render_upsell_placeholder( 'edit-button' );
357 ?>
358 </div>
359 <div class="inside">
360 <table class="widefat striped">
361 <tbody>
362 <tr>
363 <th><b><?php esc_html_e( 'Fields', 'sureforms' ); ?></b></th>
364 <th><b><?php esc_html_e( 'Values', 'sureforms' ); ?></b></th>
365 </tr>
366 <?php
367 foreach ( $meta_data as $field_name => $value ) {
368 if ( in_array( $field_name, $excluded_fields, true ) ) {
369 continue;
370 }
371 if ( false === str_contains( $field_name, '-lbl-' ) ) {
372 continue;
373 }
374 $label = explode( '-lbl-', $field_name )[1];
375 // Getting the encrypted label. we are removing the block slug here.
376 $label = explode( '-', $label )[0];
377 ?>
378 <tr>
379 <style>
380 .file-cards-container {
381 display: flex;
382 flex-wrap: wrap;
383 gap: 10px;
384 }
385 .file-card {
386 border: 1px solid #ddd;
387 border-radius: 4px;
388 padding: 10px;
389 width: 100px; /* Reduced width */
390 text-align: center;
391 background: #f9f9f9;
392 font-size: 12px; /* Reduced font size for smaller cards */
393 }
394 .file-card-image img {
395 max-width: 80px; /* Reduced max width */
396 max-height: 80px; /* Reduced max height */
397 object-fit: cover;
398 }
399 .file-card-icon {
400 font-size: 24px; /* Reduced icon size */
401 margin-bottom: 5px;
402 }
403 .file-card-details {
404 margin-bottom: 5px;
405 font-weight: bold;
406 }
407 .file-card-url a {
408 color: #007bff;
409 text-decoration: none;
410 font-size: 12px; /* Reduced font size */
411 }
412 .file-card-url a:hover {
413 text-decoration: underline;
414 }
415 </style>
416 <td><b><?php echo $label ? wp_kses_post( html_entity_decode( Helper::decrypt( $label ) ) ) : ''; ?></b></td>
417 <?php
418 if ( false !== strpos( $field_name, 'srfm-upload' ) ) {
419 ?>
420 <td>
421 <div class="file-cards-container">
422 <?php
423 $upload_values = $value;
424 if ( ! empty( $upload_values ) && is_array( $upload_values ) ) {
425 foreach ( $upload_values as $file_url ) {
426 $file_url = urldecode( Helper::get_string_value( $file_url ) );
427
428 if ( ! empty( $file_url ) ) {
429 if ( ! file_exists( Helper::convert_fileurl_to_filepath( $file_url ) ) ) {
430 continue;
431 }
432
433 $file_type = pathinfo( $file_url, PATHINFO_EXTENSION );
434 $is_image = in_array( $file_type, [ 'gif', 'png', 'bmp', 'jpg', 'jpeg', 'svg' ], true );
435 ?>
436 <div class="file-card" data-fileurl-hash="<?php echo esc_attr( md5( $file_url ) ); ?>">
437 <?php if ( $is_image ) { ?>
438 <div class="file-card-image">
439 <a target="_blank" href="<?php echo esc_attr( $file_url ); ?>">
440 <img src="<?php echo esc_attr( $file_url ); ?>" alt="<?php esc_attr_e( 'Image', 'sureforms' ); ?>" />
441 </a>
442 </div>
443 <?php } else { ?>
444 <div class="file-card-icon">
445 <?php // Display a file icon for non-image files. ?>
446 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M4 16.333V4.667a1.333 1.333 0 011.333-1.333h13.334a1.333 1.333 0 011.333 1.333v11.666a1.333 1.333 0 01-1.333 1.333H5.333A1.333 1.333 0 014 16.333zm8-8h2v6h-2v-6zm-2 8h6v2H10v-2zm-6-6h4v6H4v-6zm0-4h16v2H4V6z"/></svg>
447 </div>
448 <div class="file-card-details">
449 <span><?php echo esc_html( strtoupper( $file_type ) ); ?></span>
450 </div>
451 <?php } ?>
452 <div class="file-card-url">
453 <a target="_blank" href="<?php echo esc_attr( $file_url ); ?>"><?php echo esc_html__( 'Open', 'sureforms' ); ?></a>
454 </div>
455 </div>
456 <?php
457 }
458 }
459 }
460 ?>
461 </div>
462 </td>
463 <?php } elseif ( false !== strpos( $field_name, 'srfm-url' ) ) { ?>
464 <td><a target="_blank" href="<?php echo esc_url( $value ); ?>"><?php echo esc_url( $value ); ?></a></td>
465 <?php
466 // we need to html_entity_decode the value to display the html content. and check field textarea.
467 } elseif ( strpos( $field_name, 'srfm-textarea' ) !== false ) {
468 ?>
469 <td><div class='sureform_entry_meta_textarea'><?php echo Helper::esc_textarea( html_entity_decode( $value ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- using a custom escaping function. ?></div></td>
470 <?php } elseif ( apply_filters( 'srfm_entry_render_field_custom_value', false, $field_name ) ) { ?>
471 <?php echo wp_kses_post( apply_filters( 'srfm_entry_custom_value', '', $value ) ); ?>
472 <?php } else { ?>
473 <td><?php echo false !== strpos( $value, PHP_EOL ) ? wp_kses_post( wpautop( $value ) ) : wp_kses_post( $value ); ?></td>
474 <?php } ?>
475 </tr>
476 <?php } ?>
477 </tbody>
478 </table>
479 </div>
480 </div>
481 <?php
482 }
483 /**
484 * Render the entry logs for a specific entry.
485 *
486 * @param array<mixed> $entry_logs Entry logs stored in the database.
487 * @since 0.0.13
488 * @return void
489 */
490 private function render_entry_logs( $entry_logs ) {
491 ob_start();
492 ?>
493 <div id="sureform_entry_meta" class="postbox srfm-entry-logs">
494 <div class="postbox-header">
495 <!-- Removed "hndle ui-sortable-handle" class from h2 to remove the draggable stylings. -->
496 <h2><?php esc_html_e( 'Entry Logs', 'sureforms' ); ?></h2>
497 </div>
498 <div class="inside">
499 <table class="striped entry-logs-table">
500 <tbody>
501 <?php if ( ! empty( $entry_logs ) ) { ?>
502 <?php foreach ( $entry_logs as $log ) { ?>
503 <tr>
504 <td class="entry-log-container">
505 <div class="entry-log">
506 <h4 class="entry-log-title">
507 <?php echo esc_html( $log['title'] ); ?>
508 <?php echo esc_html( gmdate( '\a\t Y-m-d H:i:s', $log['timestamp'] ) ); ?>
509 </h4>
510 <div class="entry-log-messages">
511 <?php foreach ( $log['messages'] as $message ) { ?>
512 <p><?php echo wp_kses_post( $message ); ?></p>
513 <?php } ?>
514 </div>
515 </div>
516 </td>
517 </tr>
518 <?php } ?>
519 <?php } else { ?>
520 <p class="no-logs-found"><?php esc_html_e( 'No logs found for this entry.', 'sureforms' ); ?></p>
521 <?php } ?>
522 </tbody>
523 </table>
524 </div>
525 </div>
526 <?php
527 $content = ob_get_clean();
528
529 $allowed_tags = array_merge(
530 wp_kses_allowed_html( 'post' ),
531 [
532 'svg' => [
533 'width' => true,
534 'height' => true,
535 'fill' => true,
536 'viewbox' => true,
537 'xmlns' => true,
538 ],
539 'path' => [
540 'd' => true,
541 'opacity' => true,
542 'class' => true,
543 'stroke-width' => true,
544 'stroke-linecap' => true,
545 'stroke-linejoin' => true,
546 ],
547 ]
548 );
549
550 echo wp_kses( apply_filters( 'srfm_entry_logs_markup', $content, $entry_logs ), $allowed_tags );
551 }
552 }
553