Skip to content

Implement ZetaJS PDF exports and warn on unknown document fields#4

Merged
erseco merged 1 commit intomainfrom
feature/configure-custom-post-type-document
Oct 13, 2025
Merged

Implement ZetaJS PDF exports and warn on unknown document fields#4
erseco merged 1 commit intomainfrom
feature/configure-custom-post-type-document

Conversation

@erseco
Copy link
Copy Markdown
Collaborator

@erseco erseco commented Oct 13, 2025

Summary

  • warn when stored document fields are not part of the selected taxonomy schema and surface them in the Gutenberg content composer
  • add a ZetaJS CLI wrapper and use it to generate downloadable PDF files alongside existing DOCX/ODT exports
  • refresh the document edit actions and preview page to leverage the new PDF conversion flow

Testing

  • php -l includes/custom-post-types/class-resolate-documents.php
  • php -l includes/class-resolate-document-generator.php
  • php -l includes/class-resolate-admin.php
  • php -l includes/class-resolate-zetajs.php

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

Comment on lines +48 to +105
public static function convert( $input_path, $output_path, $output_format = '', $input_format = '' ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
if ( ! file_exists( $input_path ) ) {
return new WP_Error( 'resolate_zetajs_input_missing', __( 'El fichero origen para la conversión no existe.', 'resolate' ) );
}

if ( ! self::is_available() ) {
return new WP_Error( 'resolate_zetajs_not_available', __( 'Configura la ruta del ejecutable de ZetaJS en RESOLATE_ZETAJS_BIN.', 'resolate' ) );
}

$cli = self::get_cli_path();
$dir = dirname( $output_path );
if ( ! is_dir( $dir ) ) {
wp_mkdir_p( $dir );
}

if ( file_exists( $output_path ) ) {
@unlink( $output_path ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
}

$descriptor = array(
0 => array( 'pipe', 'r' ),
1 => array( 'pipe', 'w' ),
2 => array( 'pipe', 'w' ),
);

$command = array( $cli, 'convert', $input_path, $output_path );

$process = proc_open( $command, $descriptor, $pipes, null, null, array( 'bypass_shell' => true ) );
if ( ! is_resource( $process ) ) {
return new WP_Error( 'resolate_zetajs_proc', __( 'No se pudo iniciar el proceso de conversión con ZetaJS.', 'resolate' ) );
}

// Close STDIN as we don't need to feed data.
fclose( $pipes[0] );
$stdout = stream_get_contents( $pipes[1] );
fclose( $pipes[1] );
$stderr = stream_get_contents( $pipes[2] );
fclose( $pipes[2] );

$status = proc_close( $process );
if ( 0 !== $status ) {
$message = trim( $stderr );
if ( '' === $message ) {
$message = trim( $stdout );
}
if ( '' === $message ) {
$message = sprintf( __( 'El proceso de ZetaJS finalizó con código %d.', 'resolate' ), $status );
}
return new WP_Error( 'resolate_zetajs_failed', $message );
}

if ( ! file_exists( $output_path ) ) {
$context = $stderr ? $stderr : $stdout;
return new WP_Error( 'resolate_zetajs_output_missing', sprintf( __( 'La conversión finalizó pero no se generó el archivo de salida. Detalles: %s', 'resolate' ), $context ) );
}

return $output_path;
}

Check warning

Code scanning / PHPMD

Code Size Rules: CyclomaticComplexity Warning

The method convert() has a Cyclomatic Complexity of 12. The configured cyclomatic complexity threshold is 10.
Comment on lines +48 to +105
public static function convert( $input_path, $output_path, $output_format = '', $input_format = '' ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
if ( ! file_exists( $input_path ) ) {
return new WP_Error( 'resolate_zetajs_input_missing', __( 'El fichero origen para la conversión no existe.', 'resolate' ) );
}

if ( ! self::is_available() ) {
return new WP_Error( 'resolate_zetajs_not_available', __( 'Configura la ruta del ejecutable de ZetaJS en RESOLATE_ZETAJS_BIN.', 'resolate' ) );
}

$cli = self::get_cli_path();
$dir = dirname( $output_path );
if ( ! is_dir( $dir ) ) {
wp_mkdir_p( $dir );
}

if ( file_exists( $output_path ) ) {
@unlink( $output_path ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
}

$descriptor = array(
0 => array( 'pipe', 'r' ),
1 => array( 'pipe', 'w' ),
2 => array( 'pipe', 'w' ),
);

$command = array( $cli, 'convert', $input_path, $output_path );

$process = proc_open( $command, $descriptor, $pipes, null, null, array( 'bypass_shell' => true ) );
if ( ! is_resource( $process ) ) {
return new WP_Error( 'resolate_zetajs_proc', __( 'No se pudo iniciar el proceso de conversión con ZetaJS.', 'resolate' ) );
}

// Close STDIN as we don't need to feed data.
fclose( $pipes[0] );
$stdout = stream_get_contents( $pipes[1] );
fclose( $pipes[1] );
$stderr = stream_get_contents( $pipes[2] );
fclose( $pipes[2] );

$status = proc_close( $process );
if ( 0 !== $status ) {
$message = trim( $stderr );
if ( '' === $message ) {
$message = trim( $stdout );
}
if ( '' === $message ) {
$message = sprintf( __( 'El proceso de ZetaJS finalizó con código %d.', 'resolate' ), $status );
}
return new WP_Error( 'resolate_zetajs_failed', $message );
}

if ( ! file_exists( $output_path ) ) {
$context = $stderr ? $stderr : $stdout;
return new WP_Error( 'resolate_zetajs_output_missing', sprintf( __( 'La conversión finalizó pero no se generó el archivo de salida. Detalles: %s', 'resolate' ), $context ) );
}

return $output_path;
}

Check warning

Code scanning / PHPMD

Code Size Rules: NPathComplexity Warning

The method convert() has an NPath complexity of 960. The configured NPath complexity threshold is 200.
Comment on lines +1059 to +1106
private function collect_unknown_dynamic_fields( $post_id, $known_meta_keys ) {
$known_lookup = array();
if ( ! empty( $known_meta_keys ) ) {
foreach ( $known_meta_keys as $meta_key ) {
$known_lookup[ $meta_key ] = true;
}
}

$unknown = array();
$prefix = 'resolate_field_';

foreach ( $_POST as $key => $value ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
if ( ! is_string( $key ) || 0 !== strpos( $key, $prefix ) ) {
continue;
}
if ( isset( $known_lookup[ $key ] ) ) {
continue;
}
if ( is_array( $value ) ) {
continue;
}
$unknown[ $key ] = array(
'value' => wp_unslash( $value ), // phpcs:ignore WordPress.Security.NonceVerification.Missing
'source' => 'post',
);
}

if ( $post_id > 0 ) {
$all_meta = get_post_meta( $post_id );
foreach ( $all_meta as $meta_key => $values ) {
if ( 0 !== strpos( $meta_key, $prefix ) ) {
continue;
}
if ( isset( $known_lookup[ $meta_key ] ) ) {
continue;
}
if ( isset( $unknown[ $meta_key ] ) ) {
continue;
}
$unknown[ $meta_key ] = array(
'value' => get_post_meta( $post_id, $meta_key, true ),
'source' => 'meta',
);
}
}

return $unknown;
}

Check warning

Code scanning / PHPMD

Code Size Rules: CyclomaticComplexity Warning

The method collect_unknown_dynamic_fields() has a Cyclomatic Complexity of 14. The configured cyclomatic complexity threshold is 10.
Comment on lines +1059 to +1106
private function collect_unknown_dynamic_fields( $post_id, $known_meta_keys ) {
$known_lookup = array();
if ( ! empty( $known_meta_keys ) ) {
foreach ( $known_meta_keys as $meta_key ) {
$known_lookup[ $meta_key ] = true;
}
}

$unknown = array();
$prefix = 'resolate_field_';

foreach ( $_POST as $key => $value ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
if ( ! is_string( $key ) || 0 !== strpos( $key, $prefix ) ) {
continue;
}
if ( isset( $known_lookup[ $key ] ) ) {
continue;
}
if ( is_array( $value ) ) {
continue;
}
$unknown[ $key ] = array(
'value' => wp_unslash( $value ), // phpcs:ignore WordPress.Security.NonceVerification.Missing
'source' => 'post',
);
}

if ( $post_id > 0 ) {
$all_meta = get_post_meta( $post_id );
foreach ( $all_meta as $meta_key => $values ) {
if ( 0 !== strpos( $meta_key, $prefix ) ) {
continue;
}
if ( isset( $known_lookup[ $meta_key ] ) ) {
continue;
}
if ( isset( $unknown[ $meta_key ] ) ) {
continue;
}
$unknown[ $meta_key ] = array(
'value' => get_post_meta( $post_id, $meta_key, true ),
'source' => 'meta',
);
}
}

return $unknown;
}

Check warning

Code scanning / PHPMD

Code Size Rules: NPathComplexity Warning

The method collect_unknown_dynamic_fields() has an NPath complexity of 351. The configured NPath complexity threshold is 200.
@erseco erseco merged commit 29e5b9d into main Oct 13, 2025
3 of 4 checks passed
@erseco erseco deleted the feature/configure-custom-post-type-document branch February 26, 2026 16:26
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