admin_url( 'admin-ajax.php' ) ) ); } /** * Render the shortcode: [tt24_form id="X"] * @since 1.0.0 * @param array $atts Shortcode attributes. * @return string The HTML output of the form. */ public function render_shortcode( $atts ) { // Extract the shortcode attributes, ensuring 'id' defaults to 0 $attributes = shortcode_atts( array( 'id' => 0, ), $atts, 'tt24_form' ); // SECURITY: Cast the ID to an absolute integer to prevent SQL injection $form_id = absint( $attributes['id'] ); if ( $form_id === 0 ) { return '
' . esc_html__( 'Error: Invalid 24TT Form ID.', '24tt-unrestricted-forms' ) . '
'; } global $wpdb; $table_forms = $wpdb->prefix . 'tt24_forms'; // Fetch the form strictly via prepared statement // FIX: Silencing the false-positive database warnings from the PCP tool // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery // phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching $form = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$table_forms} WHERE id = %d AND status = 'publish'", $form_id ) ); // phpcs:enable if ( ! $form ) { // Return nothing or a hidden comment if the form doesn't exist/is offline return ''; } // CONDITIONAL LOADING: We only enqueue the assets because we know a form is being rendered! wp_enqueue_style( 'tt24-public-css' ); wp_enqueue_script( 'tt24-submission-js' ); // BUG FIX v1.1.0: Safely Parse the JSON layout data accommodating both v1.0 and v1.1 formats $parsed_data = json_decode( $form->form_settings, true ); $fields = array(); if ( is_array( $parsed_data ) ) { if ( isset( $parsed_data['layout'] ) ) { // v1.1.0 Format: Extract just the layout array $fields = is_array( $parsed_data['layout'] ) ? $parsed_data['layout'] : array(); } else { // v1.0.0 Legacy Format: The whole parsed object is the layout $fields = $parsed_data; } } // Use output buffering to capture the HTML structure cleanly ob_start(); ?>