0 ) { self::render_detail( $entry_id ); } else { self::render_list(); } } // ------------------------------------------------------------------------- // List View // ------------------------------------------------------------------------- private static function render_list(): void { if ( ! class_exists( 'EPPRO_DB' ) ) { echo '

' . esc_html__( 'Form Entries', 'admore-widgets-suite' ) . '

'; echo '

' . esc_html__( 'Essential Pro Pack core is still loading. Please ensure Elementor is active, then reload this page.', 'admore-widgets-suite' ) . '

'; return; } // Read-only list filters / pagination / notices. // phpcs:disable WordPress.Security.NonceVerification.Recommended $form_id = sanitize_key( wp_unslash( $_GET['form_id'] ?? '' ) ); $status = sanitize_key( wp_unslash( $_GET['status'] ?? '' ) ); $paged = max( 1, absint( $_GET['paged'] ?? 1 ) ); $deleted = isset( $_GET['deleted'] ) ? absint( $_GET['deleted'] ) : null; // phpcs:enable WordPress.Security.NonceVerification.Recommended $result = EPPRO_DB::get_entries( [ 'form_id' => $form_id, 'status' => $status, 'per_page' => self::PER_PAGE, 'paged' => $paged, ] ); $entries = $result['entries']; $total = $result['total']; $num_pages = (int) ceil( $total / self::PER_PAGE ); $forms = EPPRO_DB::get_form_list(); ?>

0 ) : ?>
1 ) : ?>

'detail', 'entry_id' => $entry['id'] ] ); $badge_map = [ 'unread' => 'eppro-badge-draft', 'read' => 'eppro-badge-inactive', 'starred' => 'eppro-badge-active', 'trash' => 'eppro-badge-inactive', ]; $badge_cls = $badge_map[ $entry['status'] ] ?? 'eppro-badge-inactive'; ?>
# —

' . esc_html__( 'Entry not found.', 'admore-widgets-suite' ) . '

'; return; } // Auto-mark as read on view if ( 'unread' === $entry['status'] ) { EPPRO_DB::update_entry_status( $entry_id, 'read' ); $entry['status'] = 'read'; } $fields = json_decode( $entry['fields'], true ) ?: []; $back_url = remove_query_arg( [ 'view', 'entry_id' ] ); ?>

'eppro-badge-draft', 'read' => 'eppro-badge-inactive', 'starred' => 'eppro-badge-active' ]; $badge_cls = $badge_map[ $entry['status'] ] ?? 'eppro-badge-inactive'; ?>

$value ) : $is_url = is_string( $value ) && filter_var( $value, FILTER_VALIDATE_URL ); $display = is_array( $value ) ? implode( ', ', array_map( 'esc_html', $value ) ) : esc_html( (string) $value ); ?>
[] ] ); ?>
'read', 'mark_unread' => 'unread', 'mark_starred' => 'starred' ]; $deleted = 0; foreach ( $ids as $id ) { if ( 'delete' === $action ) { if ( EPPRO_DB::delete_entry( $id ) ) $deleted++; } elseif ( isset( $statuses[ $action ] ) ) { EPPRO_DB::update_entry_status( $id, $statuses[ $action ] ); } } $redirect = admin_url( 'admin.php?page=eppro-form-entries' ); if ( $deleted > 0 ) { $redirect = add_query_arg( 'deleted', $deleted, $redirect ); } wp_safe_redirect( $redirect ); exit; } // ------------------------------------------------------------------------- // CSV Export // ------------------------------------------------------------------------- public static function handle_export(): void { if ( ! current_user_can( 'manage_options' ) ) wp_die( esc_html__( 'Insufficient permissions.', 'admore-widgets-suite' ) ); check_admin_referer( 'eppro_export_entries' ); $form_id = isset( $_GET['form_id'] ) ? sanitize_key( wp_unslash( $_GET['form_id'] ) ) : ''; $status = isset( $_GET['status'] ) ? sanitize_key( wp_unslash( $_GET['status'] ) ) : ''; // get_entries() returns at most 100 rows per page, so walk every page. $entries = []; $paged = 1; do { $result = EPPRO_DB::get_entries( [ 'form_id' => $form_id, 'status' => $status, 'per_page' => 100, 'paged' => $paged ] ); $batch = $result['entries']; $entries = array_merge( $entries, $batch ); $paged++; } while ( count( $batch ) === 100 && $paged <= 2000 ); if ( empty( $entries ) ) { wp_safe_redirect( admin_url( 'admin.php?page=eppro-form-entries' ) ); exit; } $filename = 'eppro-form-entries-' . gmdate( 'Y-m-d' ) . '.csv'; header( 'Content-Type: text/csv; charset=UTF-8' ); header( 'Content-Disposition: attachment; filename="' . $filename . '"' ); header( 'Pragma: no-cache' ); header( 'Expires: 0' ); $out = fopen( 'php://output', 'w' ); // UTF-8 BOM for Excel compatibility. Echoed as a hardcoded literal // (no fwrite()); php://output is the response body, so this writes // straight to the download stream before the CSV rows below. echo "\xEF\xBB\xBF"; // Header row — collect all possible field labels from all entries $all_labels = []; $decoded = []; foreach ( $entries as $entry ) { $fields = json_decode( (string) $entry['fields'], true ); $fields = is_array( $fields ) ? $fields : []; $decoded[] = $fields; $all_labels = array_unique( array_merge( $all_labels, array_map( 'strval', array_keys( $fields ) ) ) ); } $meta_headers = [ 'ID', 'Form', 'Status', 'IP Address', 'Page URL', 'Submitted At' ]; fputcsv( $out, array_map( [ __CLASS__, 'csv_cell' ], array_merge( $meta_headers, $all_labels ) ), ',', '"', '' ); foreach ( $entries as $i => $entry ) { $row = [ $entry['id'], $entry['form_name'] ?: $entry['form_id'], $entry['status'], $entry['ip_address'], $entry['page_url'], $entry['submitted_at'], ]; $fields = $decoded[ $i ]; foreach ( $all_labels as $label ) { $value = $fields[ $label ] ?? ''; $row[] = is_scalar( $value ) ? $value : wp_json_encode( $value ); } fputcsv( $out, array_map( [ __CLASS__, 'csv_cell' ], $row ), ',', '"', '' ); } // No fclose() needed — php://output is a passthrough stream and PHP // flushes and closes it automatically on exit. exit; } /** * Neutralise spreadsheet formulas. Submissions come from the public, and a * cell starting with = + - @ (or a tab/CR) would be executed by Excel or * Google Sheets when the export is opened. */ public static function csv_cell( $value ): string { $value = (string) $value; if ( '' !== $value && in_array( $value[0], [ '=', '+', '-', '@', "\t", "\r" ], true ) ) { $value = "'" . $value; } return $value; } // ------------------------------------------------------------------------- // AJAX: update single entry status // ------------------------------------------------------------------------- public static function ajax_update_status(): void { check_ajax_referer( 'eppro_entries_nonce', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( [], 403 ); } $id = absint( $_POST['entry_id'] ?? 0 ); $status = sanitize_key( wp_unslash( $_POST['status'] ?? '' ) ); if ( ! $id || ! $status ) { wp_send_json_error( [ 'message' => 'Invalid parameters.' ] ); } $ok = EPPRO_DB::update_entry_status( $id, $status ); $ok ? wp_send_json_success() : wp_send_json_error( [ 'message' => 'Update failed.' ] ); } // ------------------------------------------------------------------------- // Helpers // ------------------------------------------------------------------------- private static function status_label( string $status ): string { $map = [ 'unread' => __( 'Unread', 'admore-widgets-suite' ), 'read' => __( 'Read', 'admore-widgets-suite' ), 'starred' => __( 'Starred', 'admore-widgets-suite' ), 'trash' => __( 'Trash', 'admore-widgets-suite' ), ]; return $map[ $status ] ?? ucfirst( $status ); } }