Skip to content

Fix embedded PDF preview by streaming inline response#21

Merged
erseco merged 1 commit intomainfrom
feature/locate-embedded-pdf-display-issue
Oct 14, 2025
Merged

Fix embedded PDF preview by streaming inline response#21
erseco merged 1 commit intomainfrom
feature/locate-embedded-pdf-display-issue

Conversation

@erseco
Copy link
Copy Markdown
Collaborator

@erseco erseco commented Oct 14, 2025

Summary

  • add an admin-post endpoint that streams generated PDFs with an inline content disposition for previews
  • cache the generated preview filename per user and point the iframe to the streaming endpoint
  • fall back to regenerating the PDF if the cached file is missing so previews remain reliable

Testing

  • composer test (fails: WordPress bootstrap is not available in the test environment)

https://chatgpt.com/codex/tasks/task_e_68eddd4f38588322a049ca41434f06d0

@erseco erseco merged commit 2672040 into main Oct 14, 2025
2 of 3 checks passed
Comment on lines +290 to +355
public function handle_preview_stream() {
$post_id = isset( $_GET['post_id'] ) ? intval( $_GET['post_id'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended

if ( ! $post_id || ! current_user_can( 'edit_post', $post_id ) ) {
wp_die( esc_html__( 'Permisos insuficientes.', 'resolate' ) );
}

if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'resolate_preview_stream_' . $post_id ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
wp_die( esc_html__( 'Nonce no válido.', 'resolate' ) );
}

$user_id = get_current_user_id();
if ( $user_id <= 0 ) {
wp_die( esc_html__( 'Usuario no autenticado.', 'resolate' ) );
}

$key = $this->get_preview_stream_transient_key( $post_id, $user_id );
$filename = get_transient( $key );

if ( false === $filename || '' === $filename ) {
$this->ensure_document_generator();
$result = Resolate_Document_Generator::generate_pdf( $post_id );
if ( is_wp_error( $result ) ) {
wp_die( esc_html__( 'No se pudo generar el PDF para la vista previa.', 'resolate' ) );
}

$filename = basename( $result );
$this->remember_preview_stream_file( $post_id, $filename );
}

$filename = sanitize_file_name( (string) $filename );
if ( '' === $filename ) {
wp_die( esc_html__( 'Archivo de vista previa no disponible.', 'resolate' ) );
}

$upload_dir = wp_upload_dir();
$path = trailingslashit( $upload_dir['basedir'] ) . 'resolate/' . $filename;

if ( ! file_exists( $path ) || ! is_readable( $path ) ) {
wp_die( esc_html__( 'No se pudo acceder al archivo PDF generado.', 'resolate' ) );
}

$filesize = filesize( $path );
$download_name = wp_basename( $filename );
$encoded_name = rawurlencode( $download_name );
$disposition = 'inline; filename="' . $download_name . '"; filename*=UTF-8\'\'' . $encoded_name;

status_header( 200 );
nocache_headers();
header( 'Content-Type: application/pdf' );
header( 'Content-Disposition: ' . $disposition );
if ( $filesize > 0 ) {
header( 'Content-Length: ' . $filesize );
}

$handle = fopen( $path, 'rb' );
if ( false === $handle ) {
wp_die( esc_html__( 'No se pudo leer el archivo PDF.', 'resolate' ) );
}

while ( ! feof( $handle ) ) {
echo fread( $handle, 8192 );
}
fclose( $handle );
exit;
}

Check warning

Code scanning / PHPMD

Code Size Rules: CyclomaticComplexity Warning

The method handle_preview_stream() has a Cyclomatic Complexity of 16. The configured cyclomatic complexity threshold is 10.
Comment on lines +290 to +355
public function handle_preview_stream() {
$post_id = isset( $_GET['post_id'] ) ? intval( $_GET['post_id'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended

if ( ! $post_id || ! current_user_can( 'edit_post', $post_id ) ) {
wp_die( esc_html__( 'Permisos insuficientes.', 'resolate' ) );
}

if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'resolate_preview_stream_' . $post_id ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
wp_die( esc_html__( 'Nonce no válido.', 'resolate' ) );
}

$user_id = get_current_user_id();
if ( $user_id <= 0 ) {
wp_die( esc_html__( 'Usuario no autenticado.', 'resolate' ) );
}

$key = $this->get_preview_stream_transient_key( $post_id, $user_id );
$filename = get_transient( $key );

if ( false === $filename || '' === $filename ) {
$this->ensure_document_generator();
$result = Resolate_Document_Generator::generate_pdf( $post_id );
if ( is_wp_error( $result ) ) {
wp_die( esc_html__( 'No se pudo generar el PDF para la vista previa.', 'resolate' ) );
}

$filename = basename( $result );
$this->remember_preview_stream_file( $post_id, $filename );
}

$filename = sanitize_file_name( (string) $filename );
if ( '' === $filename ) {
wp_die( esc_html__( 'Archivo de vista previa no disponible.', 'resolate' ) );
}

$upload_dir = wp_upload_dir();
$path = trailingslashit( $upload_dir['basedir'] ) . 'resolate/' . $filename;

if ( ! file_exists( $path ) || ! is_readable( $path ) ) {
wp_die( esc_html__( 'No se pudo acceder al archivo PDF generado.', 'resolate' ) );
}

$filesize = filesize( $path );
$download_name = wp_basename( $filename );
$encoded_name = rawurlencode( $download_name );
$disposition = 'inline; filename="' . $download_name . '"; filename*=UTF-8\'\'' . $encoded_name;

status_header( 200 );
nocache_headers();
header( 'Content-Type: application/pdf' );
header( 'Content-Disposition: ' . $disposition );
if ( $filesize > 0 ) {
header( 'Content-Length: ' . $filesize );
}

$handle = fopen( $path, 'rb' );
if ( false === $handle ) {
wp_die( esc_html__( 'No se pudo leer el archivo PDF.', 'resolate' ) );
}

while ( ! feof( $handle ) ) {
echo fread( $handle, 8192 );
}
fclose( $handle );
exit;
}

Check warning

Code scanning / PHPMD

Code Size Rules: NPathComplexity Warning

The method handle_preview_stream() has an NPath complexity of 6912. The configured NPath complexity threshold is 200.
@erseco erseco deleted the feature/locate-embedded-pdf-display-issue branch November 30, 2025 18:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants