single-entry.php
| 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 | $entry_logs = $this->entry['logs']; |
| 73 | ?> |
| 74 | <div class="wrap"> |
| 75 | <h1 class="wp-heading-inline"><?php esc_html_e( 'View Entry', 'sureforms' ); ?></h1> |
| 76 | <form method="get" id="get"> <!-- check for nonce, referrer, etc. --> |
| 77 | <div id="poststuff"> |
| 78 | <div id="post-body" class="metabox-holder columns-2"> |
| 79 | <div id="post-body-content"> |
| 80 | <div id="titlediv"> |
| 81 | <div id="titlewrap"> |
| 82 | <label class="screen-reader-text" id="title-prompt-text" for="title"><?php esc_html_e( 'Add title', 'sureforms' ); ?></label> |
| 83 | <input type="text" name="post_title" size="30" value="Entry #<?php echo esc_attr( $this->entry_id ); ?>" id="title" spellcheck="true" autocomplete="off" readonly> |
| 84 | </div> |
| 85 | </div><!-- /titlediv --> |
| 86 | </div><!-- /post-body-content --> |
| 87 | <div id="postbox-container-1" class="postbox-container"> |
| 88 | <?php $this->render_submission_info( $form_name, $entry_status, $submitted_on ); ?> |
| 89 | </div> |
| 90 | <div id="postbox-container-2" class="postbox-container"> |
| 91 | <?php $this->render_form_data( $meta_data, $excluded_fields ); ?> |
| 92 | </div> |
| 93 | <div id="postbox-container-3" class="postbox-container"> |
| 94 | <?php $this->render_entry_logs( $entry_logs ); ?> |
| 95 | </div> |
| 96 | </div><!-- /post-body --> |
| 97 | <br class="clear"> |
| 98 | </div><!-- /poststuff --> |
| 99 | </form> |
| 100 | </div> |
| 101 | <?php |
| 102 | } |
| 103 | |
| 104 | /** |
| 105 | * Render the submission information for a specific entry. |
| 106 | * |
| 107 | * @param string $form_name The form title/name. |
| 108 | * @param string $entry_status The entry status (read/unread). |
| 109 | * @param string $submitted_on The submission date. |
| 110 | * @since 0.0.13 |
| 111 | * @return void |
| 112 | */ |
| 113 | private function render_submission_info( $form_name, $entry_status, $submitted_on ) { |
| 114 | $mark_as_unread_url = add_query_arg( 'action', 'unread' ); |
| 115 | $user_id = Helper::get_integer_value( $this->entry['user_id'] ); |
| 116 | $user_info = 0 !== $user_id ? get_userdata( $user_id ) : null; |
| 117 | $user_name = $user_info ? $user_info->display_name : ''; |
| 118 | $user_profile_url = $user_info ? get_author_posts_url( $user_id ) : ''; |
| 119 | ?> |
| 120 | <div id="sureform_form_name_meta" class="postbox "> |
| 121 | <div class="postbox-header"> |
| 122 | <!-- Removed "hndle ui-sortable-handle" class from h2 to remove the draggable stylings. --> |
| 123 | <h2><?php esc_html_e( 'Submission Info', 'sureforms' ); ?></h2> |
| 124 | </div> |
| 125 | <div class="inside"> |
| 126 | <table style="border-collapse: separate; border-spacing: 5px 5px;"> |
| 127 | <tbody> |
| 128 | <!-- TODO: Add Type and User info. --> |
| 129 | <tr style="margin-bottom: 10px;"> |
| 130 | <td><b><?php esc_html_e( 'Entry:', 'sureforms' ); ?></b></td> |
| 131 | <td>#<?php echo esc_attr( $this->entry_id ); ?></td> |
| 132 | </tr> |
| 133 | <tr style="margin-bottom: 10px;"> |
| 134 | <td><b><?php esc_html_e( 'Form Name:', 'sureforms' ); ?></b></td> |
| 135 | <td><a target="_blank" rel="noopener" href="<?php the_permalink( $this->entry['form_id'] ); ?>"><?php echo esc_attr( $form_name ); ?></a></td> |
| 136 | </tr> |
| 137 | <?php if ( ! empty( $this->entry['submission_info']['user_ip'] ) ) { ?> |
| 138 | <tr style="margin-bottom: 10px;"> |
| 139 | <td><b><?php esc_html_e( 'User IP:', 'sureforms' ); ?></b></td> |
| 140 | <td><a target="_blank" rel="noopener" href="https://ipinfo.io/"><?php echo esc_attr( $this->entry['submission_info']['user_ip'] ); ?></a></td> |
| 141 | </tr> |
| 142 | <?php } ?> |
| 143 | <tr style="margin-bottom: 10px;"> |
| 144 | <td><b><?php esc_html_e( 'Browser:', 'sureforms' ); ?></b></td> |
| 145 | <td><?php echo esc_attr( $this->entry['submission_info']['browser_name'] ); ?></td> |
| 146 | </tr> |
| 147 | <tr style="margin-bottom: 10px;"> |
| 148 | <td><b><?php esc_html_e( 'Device:', 'sureforms' ); ?></b></td> |
| 149 | <td><?php echo esc_attr( $this->entry['submission_info']['device_name'] ); ?></td> |
| 150 | </tr> |
| 151 | <?php if ( 0 !== $user_id ) { ?> |
| 152 | <tr style="margin-bottom: 10px;"> |
| 153 | <td><b><?php esc_html_e( 'User:', 'sureforms' ); ?></b></td> |
| 154 | <td><a target="_blank" rel="noopener" href="<?php echo esc_url( $user_profile_url ); ?>"><?php echo esc_attr( $user_name ); ?></a></td> |
| 155 | </tr> |
| 156 | <?php } ?> |
| 157 | <tr style="margin-bottom: 10px;"> |
| 158 | <td><b><?php esc_html_e( 'Status:', 'sureforms' ); ?></b></td> |
| 159 | <td> |
| 160 | <span style="text-transform: capitalize;"> |
| 161 | <?php echo esc_attr( $entry_status ); ?> |
| 162 | </span> |
| 163 | <?php if ( 'read' === $entry_status ) { ?> |
| 164 | <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> |
| 165 | <?php } ?> |
| 166 | </td> |
| 167 | </tr> |
| 168 | <tr style="margin-bottom: 10px;"> |
| 169 | <td><b><?php esc_html_e( 'Submitted On:', 'sureforms' ); ?></b></td> |
| 170 | <td><?php echo esc_attr( $submitted_on ); ?></td> |
| 171 | </tr> |
| 172 | </tbody> |
| 173 | </table> |
| 174 | </div> |
| 175 | </div> |
| 176 | <?php |
| 177 | } |
| 178 | |
| 179 | /** |
| 180 | * Render the form data for a specific entry. |
| 181 | * |
| 182 | * @param array<mixed> $meta_data The form meta data. |
| 183 | * @param array<string> $excluded_fields Fields to exlude from display. |
| 184 | * @since 0.0.13 |
| 185 | * @return void |
| 186 | */ |
| 187 | private function render_form_data( $meta_data, $excluded_fields ) { |
| 188 | ?> |
| 189 | <div id="sureform_entry_meta" class="postbox"> |
| 190 | <div class="postbox-header"> |
| 191 | <!-- Removed "hndle ui-sortable-handle" class from h2 to remove the draggable stylings. --> |
| 192 | <h2><?php esc_html_e( 'Form Data', 'sureforms' ); ?></h2> |
| 193 | <!-- <div class="handle-actions hide-if-no-js"><button type="button" class="handle-order-higher" aria-disabled="false" aria-describedby="sureform_entry_meta-handle-order-higher-description"><span class="screen-reader-text">Move up</span><span class="order-higher-indicator" aria-hidden="true"></span></button><span class="hidden" id="sureform_entry_meta-handle-order-higher-description">Move Form Data box up</span><button type="button" class="handle-order-lower" aria-disabled="false" aria-describedby="sureform_entry_meta-handle-order-lower-description"><span class="screen-reader-text">Move down</span><span class="order-lower-indicator" aria-hidden="true"></span></button><span class="hidden" id="sureform_entry_meta-handle-order-lower-description">Move Form Data box down</span><button type="button" class="handlediv" aria-expanded="true"><span class="screen-reader-text">Toggle panel: Form Data</span><span class="toggle-indicator" aria-hidden="true"></span></button></div> --> |
| 194 | </div> |
| 195 | <div class="inside"> |
| 196 | <table class="widefat striped"> |
| 197 | <tbody> |
| 198 | <tr> |
| 199 | <th><b><?php esc_html_e( 'Fields', 'sureforms' ); ?></b></th> |
| 200 | <th><b><?php esc_html_e( 'Values', 'sureforms' ); ?></b></th> |
| 201 | </tr> |
| 202 | <?php |
| 203 | foreach ( $meta_data as $field_name => $value ) { |
| 204 | if ( in_array( $field_name, $excluded_fields, true ) ) { |
| 205 | continue; |
| 206 | } |
| 207 | if ( false === str_contains( $field_name, '-lbl-' ) ) { |
| 208 | continue; |
| 209 | } |
| 210 | $label = explode( '-lbl-', $field_name )[1]; |
| 211 | // Getting the encrypted label. we are removing the block slug here. |
| 212 | $label = explode( '-', $label )[0]; |
| 213 | ?> |
| 214 | <tr> |
| 215 | <td><b><?php echo $label ? wp_kses_post( html_entity_decode( Helper::decrypt( $label ) ) ) : ''; ?></b></td> |
| 216 | <?php |
| 217 | if ( false !== strpos( $field_name, 'srfm-upload' ) ) { |
| 218 | ?> |
| 219 | <style> |
| 220 | .file-cards-container { |
| 221 | display: flex; |
| 222 | flex-wrap: wrap; |
| 223 | gap: 10px; |
| 224 | } |
| 225 | .file-card { |
| 226 | border: 1px solid #ddd; |
| 227 | border-radius: 4px; |
| 228 | padding: 10px; |
| 229 | width: 100px; /* Reduced width */ |
| 230 | text-align: center; |
| 231 | background: #f9f9f9; |
| 232 | font-size: 12px; /* Reduced font size for smaller cards */ |
| 233 | } |
| 234 | .file-card-image img { |
| 235 | max-width: 80px; /* Reduced max width */ |
| 236 | max-height: 80px; /* Reduced max height */ |
| 237 | object-fit: cover; |
| 238 | } |
| 239 | .file-card-icon { |
| 240 | font-size: 24px; /* Reduced icon size */ |
| 241 | margin-bottom: 5px; |
| 242 | } |
| 243 | .file-card-details { |
| 244 | margin-bottom: 5px; |
| 245 | font-weight: bold; |
| 246 | } |
| 247 | .file-card-url a { |
| 248 | color: #007bff; |
| 249 | text-decoration: none; |
| 250 | font-size: 12px; /* Reduced font size */ |
| 251 | } |
| 252 | .file-card-url a:hover { |
| 253 | text-decoration: underline; |
| 254 | } |
| 255 | </style> |
| 256 | <td> |
| 257 | <div class="file-cards-container"> |
| 258 | <?php |
| 259 | $upload_values = $value; |
| 260 | if ( ! empty( $upload_values ) && is_array( $upload_values ) ) { |
| 261 | foreach ( $upload_values as $file_url ) { |
| 262 | $file_url = Helper::get_string_value( $file_url ); |
| 263 | if ( ! empty( $file_url ) ) { |
| 264 | $file_type = pathinfo( $file_url, PATHINFO_EXTENSION ); |
| 265 | $is_image = in_array( $file_type, [ 'gif', 'png', 'bmp', 'jpg', 'jpeg', 'svg' ], true ); |
| 266 | ?> |
| 267 | <div class="file-card"> |
| 268 | <?php if ( $is_image ) { ?> |
| 269 | <div class="file-card-image"> |
| 270 | <a target="_blank" href="<?php echo esc_attr( urldecode( $file_url ) ); ?>"> |
| 271 | <img src="<?php echo esc_attr( urldecode( $file_url ) ); ?>" alt="<?php esc_attr_e( 'Image', 'sureforms' ); ?>" /> |
| 272 | </a> |
| 273 | </div> |
| 274 | <?php } else { ?> |
| 275 | <div class="file-card-icon"> |
| 276 | <?php // Display a file icon for non-image files. ?> |
| 277 | <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> |
| 278 | </div> |
| 279 | <div class="file-card-details"> |
| 280 | <span><?php echo esc_html( strtoupper( $file_type ) ); ?></span> |
| 281 | </div> |
| 282 | <?php } ?> |
| 283 | <div class="file-card-url"> |
| 284 | <a target="_blank" href="<?php echo esc_attr( urldecode( $file_url ) ); ?>"><?php echo esc_html__( 'Open', 'sureforms' ); ?></a> |
| 285 | </div> |
| 286 | </div> |
| 287 | <?php |
| 288 | } |
| 289 | } |
| 290 | } |
| 291 | ?> |
| 292 | </div> |
| 293 | </td> |
| 294 | <?php } elseif ( false !== strpos( $field_name, 'srfm-url' ) ) { ?> |
| 295 | <td><a target="_blank" href="<?php echo esc_url( $value ); ?>"><?php echo esc_url( $value ); ?></a></td> |
| 296 | <?php } else { ?> |
| 297 | <td><?php echo false !== strpos( $value, PHP_EOL ) ? wp_kses_post( wpautop( $value ) ) : wp_kses_post( $value ); ?></td> |
| 298 | <?php } ?> |
| 299 | </tr> |
| 300 | <?php } ?> |
| 301 | </tbody> |
| 302 | </table> |
| 303 | </div> |
| 304 | </div> |
| 305 | <?php |
| 306 | } |
| 307 | |
| 308 | /** |
| 309 | * Render the entry logs for a specific entry. |
| 310 | * |
| 311 | * @param array<mixed> $entry_logs Entry logs stored in the database. |
| 312 | * @since 0.0.13 |
| 313 | * @return void |
| 314 | */ |
| 315 | private function render_entry_logs( $entry_logs ) { |
| 316 | ?> |
| 317 | <div id="sureform_entry_meta" class="postbox"> |
| 318 | <div class="postbox-header"> |
| 319 | <!-- Removed "hndle ui-sortable-handle" class from h2 to remove the draggable stylings. --> |
| 320 | <h2><?php esc_html_e( 'Entry Logs', 'sureforms' ); ?></h2> |
| 321 | <!-- <div class="handle-actions hide-if-no-js"><button type="button" class="handle-order-higher" aria-disabled="false" aria-describedby="sureform_entry_meta-handle-order-higher-description"><span class="screen-reader-text">Move up</span><span class="order-higher-indicator" aria-hidden="true"></span></button><span class="hidden" id="sureform_entry_meta-handle-order-higher-description">Move Form Data box up</span><button type="button" class="handle-order-lower" aria-disabled="false" aria-describedby="sureform_entry_meta-handle-order-lower-description"><span class="screen-reader-text">Move down</span><span class="order-lower-indicator" aria-hidden="true"></span></button><span class="hidden" id="sureform_entry_meta-handle-order-lower-description">Move Form Data box down</span><button type="button" class="handlediv" aria-expanded="true"><span class="screen-reader-text">Toggle panel: Form Data</span><span class="toggle-indicator" aria-hidden="true"></span></button></div> --> |
| 322 | </div> |
| 323 | <div class="inside"> |
| 324 | <table class="widefat striped entry-logs-table"> |
| 325 | <tbody> |
| 326 | <?php if ( ! empty( $entry_logs ) ) { ?> |
| 327 | <?php foreach ( $entry_logs as $log ) { ?> |
| 328 | <tr> |
| 329 | <td class="entry-log-container"> |
| 330 | <div class="entry-log"> |
| 331 | <h4 class="entry-log-title"> |
| 332 | <?php echo esc_html( $log['title'] ); ?> |
| 333 | <?php echo esc_html( gmdate( '\a\t Y-m-d H:i:s', $log['timestamp'] ) ); ?> |
| 334 | </h4> |
| 335 | <div class="entry-log-messages"> |
| 336 | <?php foreach ( $log['messages'] as $message ) { ?> |
| 337 | <p><?php echo esc_html( $message ); ?></p> |
| 338 | <?php } ?> |
| 339 | </div> |
| 340 | </div> |
| 341 | </td> |
| 342 | </tr> |
| 343 | <?php } ?> |
| 344 | <?php } else { ?> |
| 345 | <p><?php esc_html_e( 'No logs found for this entry.', 'sureforms' ); ?></p> |
| 346 | <?php } ?> |
| 347 | </tbody> |
| 348 | </table> |
| 349 | </div> |
| 350 | </div> |
| 351 | <?php |
| 352 | } |
| 353 | } |
| 354 |