From 7660ea1ae424b13c368fe9287e8f62a12eac2676 Mon Sep 17 00:00:00 2001 From: Jake Jackson Date: Fri, 30 Aug 2024 16:07:19 +1000 Subject: [PATCH 1/2] Add PDF Download endpoint --- src/Rest/Rest_Download_Pdf.php | 259 ++++++++++++++++++++++++++++++++ src/Rest/Rest_Form_Settings.php | 14 +- src/Rest/Rest_Pdf_Preview.php | 4 +- src/bootstrap.php | 4 + 4 files changed, 273 insertions(+), 8 deletions(-) create mode 100644 src/Rest/Rest_Download_Pdf.php diff --git a/src/Rest/Rest_Download_Pdf.php b/src/Rest/Rest_Download_Pdf.php new file mode 100644 index 000000000..fbca0c974 --- /dev/null +++ b/src/Rest/Rest_Download_Pdf.php @@ -0,0 +1,259 @@ + self::API_BASE . '/(?P[\d]+)/(?P[a-fA-F0-9]{13})', + ]; + + /** + * @var Helper_Form + * @since 6.12 + */ + protected $gform; + + /** + * @param Helper_Form $gform + * + * @since 6.12 + */ + public function __construct( $gform ) { + $this->gform = $gform; + } + + /** + * @return void + * @since 6.12 + */ + public function init() { + add_action( 'rest_api_init', [ $this, 'register_routes' ] ); + } + + /** + * Register the routes for the API + * + * @return void + * @since 6.12 + */ + public function register_routes() { + + /* + * Routes to list all form PDF settings, or create a new one + */ + register_rest_route( + static::NAMESPACE, + static::$endpoints['download-pdf'], + [ + 'args' => [ + 'entry' => [ + 'description' => __( 'The unique identifier for the Gravity Forms entry.', 'gravity-forms-pdf-extended' ), + 'type' => 'integer', + 'required' => true, + 'validate_callback' => function( $param, $request ) { + $entry = $this->gform->get_entry( $param ); + + if ( ! is_wp_error( $entry ) ) { + return true; + } + + return new WP_Error( + 'rest_invalid_param', + sprintf( __( 'Invalid entry ID %d provided.', 'gravity-forms-pdf-extended' ), $param ), + [ 'status' => 400 ] + ); + }, + ], + + 'pdf' => [ + 'description' => __( 'The identifier for the PDF', 'gravity-forms-pdf-extended' ), + 'type' => 'string', + 'required' => true, + 'validate_callback' => function( $param, $request ) { + + /* Get all active PDFs for the entry */ + $pdfs = GPDFAPI::get_entry_pdfs( $request->get_param( 'entry' ) ); + + /* PDF ID is valid if in $pdfs array */ + if ( isset( $pdfs[ $param ] ) ) { + return true; + } + + /* translators: 1: Parameter, 2: List of valid values. */ + + return new WP_Error( 'rest_not_in_enum', wp_sprintf( __( '%1$s is not one of %2$l.', 'default' ), $param, ! is_wp_error( $pdfs ) ? array_keys( $pdfs ) : '""' ) ); + }, + ], + ], + + [ + 'methods' => WP_REST_Server::CREATABLE, + 'callback' => [ $this, 'get_item' ], + 'permission_callback' => [ $this, 'get_item_permissions_check' ], + ], + ], + ); + } + + /** + * Generate and return PDF + * + * @param WP_REST_Request $request + * + * @return WP_Error|WP_REST_Response + */ + public function get_item( $request ) { + $path_to_pdf = \GPDFAPI::create_pdf( $request->get_param( 'entry' ), $request->get_param( 'pdf' ) ); + + if ( is_wp_error( $path_to_pdf ) ) { + /* + * Both entry and PDF IDs have been validated by this point. + * If an error occurred it was in the PDF generator, and why the 500 server error. + */ + $path_to_pdf->add_data( [ 'status' => 500 ] ); + + return $path_to_pdf; + } + + $response = new WP_REST_Response( + [ + 'filename' => wp_basename( $path_to_pdf ), + 'size' => filesize( $path_to_pdf ), + 'data' => base64_encode( file_get_contents( $path_to_pdf ) ), //phpcs:ignore + ] + ); + + $response->add_links( $this->prepare_links( $request ) ); + + return $response; + } + + /** + * Add links for PDF + * + * @param WP_REST_Request $request + * + * @return array[] + * + * @since 6.12.0 + */ + protected function prepare_links( $request ) { + + $pdf_id = $data['id'] ?? $request->get_param( 'pdf' ); + $entry_id = $data['entry'] ?? $request->get_param( 'entry' ); + + $entry = $this->gform->get_entry( $entry_id ); + $form_id = $entry['form_id']; + + $links = [ + 'self' => [ + 'href' => rest_url( sprintf( '%s/%d/%s', static::get_route_basepath(), $entry_id, $pdf_id ) ), + ], + + 'pdf' => [ + 'href' => rest_url( sprintf( '%s/%d/%s', Rest_Form_Settings::get_route_basepath(), $form_id, $pdf_id ) ), + 'embeddable' => true, + ], + ]; + + if ( ! class_exists( 'GFWebAPI' ) ) { + return $links; + } + + /* If GF REST API enabled, include direct links to form endpoint */ + $gfwebapi = \GFWebAPI::get_instance(); + if ( ! $gfwebapi->is_v2_enabled( $gfwebapi->get_plugin_settings() ) ) { + return $links; + } + + $links['form'] = [ + 'href' => rest_url( sprintf( 'gf/v2/forms/%d', $form_id ) ), + 'embeddable' => true, + ]; + + if ( ! empty( $entry_id ) ) { + $links['entry'] = [ + 'href' => rest_url( sprintf( 'gf/v2/entries/%d', $entry_id ) ), + 'embeddable' => true, + ]; + } + + return $links; + } + + /** + * @return string + * + * @since 6.12 + */ + public static function get_route_basepath() { + return static::NAMESPACE . static::API_BASE; + } + + /** + * Ensure current user is allowed to access PDF + * + * @param WP_REST_Request $request + * + * @return bool + */ + public function get_item_permissions_check( $request ) { + /* Check if the current user has appropriate capability to view document */ + /** @var Model_PDF $model_pdf */ + $model_pdf = \GPDFAPI::get_pdf_class( 'model' ); + if ( $model_pdf->can_user_view_pdf_with_capabilities() ) { + return true; + } + + /* check if the current user is the owner and owner access is not disabled in the PDF settings */ + $entry = $this->gform->get_entry( $request->get_param( 'entry' ) ); + $pdf = \GPDFAPI::get_pdf( $entry['form_id'] ?? 0, $request->get_param( 'pdf' ) ); + + $is_owner_restricted = $pdf['restrict_owner'] ?? 'No'; + if ( $is_owner_restricted !== 'Yes' && (int) $entry['created_by'] === get_current_user_id() ) { + return true; + } + + return false; + } +} diff --git a/src/Rest/Rest_Form_Settings.php b/src/Rest/Rest_Form_Settings.php index cb75f2f18..c15c25107 100644 --- a/src/Rest/Rest_Form_Settings.php +++ b/src/Rest/Rest_Form_Settings.php @@ -176,15 +176,17 @@ public function register_routes() { 'validate_callback' => function( $param, $request ) { $pdf = GPDFAPI::get_pdf( $request->get_param( 'form' ), $param ); + /* PDF found, PDF ID is valid */ if ( ! is_wp_error( $pdf ) ) { return true; } + /* Get list of valid IDs and include in error */ $pdfs = GPDFAPI::get_form_pdfs( $request->get_param( 'form' ) ); /* translators: 1: Parameter, 2: List of valid values. */ - return new WP_Error( 'rest_not_in_enum', wp_sprintf( __( '%1$s is not one of %2$l.', 'default' ), $param, ! is_wp_error( $pdfs ) ? array_keys( $pdfs ) : '' ) ); + return new WP_Error( 'rest_not_in_enum', wp_sprintf( __( '%1$s is not one of %2$l.', 'default' ), $param, ! is_wp_error( $pdfs ) ? array_keys( $pdfs ) : '""' ) ); }, ], ], @@ -371,7 +373,7 @@ public function check_form_is_valid( $form_id ) { * @since 6.12.0 */ public function check_entry_is_valid( $entry_id, $request ) { - $entry = \GFAPI::get_entry( $entry_id ); + $entry = $this->gform->get_entry( $entry_id ); if ( is_wp_error( $entry ) ) { return new WP_Error( 'rest_invalid_param', @@ -890,7 +892,7 @@ public function get_item_schema() { 'id' => [ 'description' => __( 'Unique identifier for the PDF.', 'gravity-forms-pdf-extended' ), 'type' => 'string', - 'context' => [ 'edit' ], + 'context' => [ 'edit', 'embed' ], 'readonly' => true, 'pattern' => '[a-fA-F0-9]{13}', ], @@ -898,7 +900,7 @@ public function get_item_schema() { 'form' => [ 'description' => __( 'The Gravity Forms ID the PDF is configured on.', 'gravity-forms-pdf-extended' ), 'type' => 'integer', - 'context' => [ 'edit' ], + 'context' => [ 'edit', 'embed' ], 'readonly' => true, ], @@ -906,7 +908,7 @@ public function get_item_schema() { 'description' => __( 'The current state of the PDF.', 'gravity-forms-pdf-extended' ), 'type' => 'boolean', 'default' => true, - 'context' => [ 'edit' ], + 'context' => [ 'edit', 'embed' ], ], ], ]; @@ -1014,7 +1016,7 @@ protected function get_section_schema( $settings, $group ) { 'description' => ! empty( $value['desc'] ) ? wp_strip_all_tags( $value['desc'] ) : $generic_description, 'type' => 'string', 'default' => $default, - 'context' => [ 'edit', $group ], + 'context' => [ 'edit', 'embed', $group ], 'arg_options' => [ 'sanitize_callback' => function( $param, $request, $key ) { return is_array( $param ) ? array_map( 'sanitize_text_field', $param ) : sanitize_text_field( $param ); diff --git a/src/Rest/Rest_Pdf_Preview.php b/src/Rest/Rest_Pdf_Preview.php index 9bf6bffa6..987018b20 100644 --- a/src/Rest/Rest_Pdf_Preview.php +++ b/src/Rest/Rest_Pdf_Preview.php @@ -80,7 +80,7 @@ public function register_routes() { * @since 6.12 */ public function create_item( $request ) { - $form = \GFAPI::get_form( $request->get_param( 'form' ) ); + $form = $this->gform->get_form( $request->get_param( 'form' ) ); $entry = $this->get_entry( $request, $form ); /* Prepare request for previewing */ @@ -114,7 +114,7 @@ public function create_item( $request ) { protected function get_entry( $request, $form ) { /* user requested a specific entry to preview */ if ( $request->get_param( 'entry' ) ) { - return \GFAPI::get_entry( $request->get_param( 'entry' ) ); + return $this->gform->get_entry( $request->get_param( 'entry' ) ); } /* try to get the last form submission */ diff --git a/src/bootstrap.php b/src/bootstrap.php index c78969758..70ab29a83 100644 --- a/src/bootstrap.php +++ b/src/bootstrap.php @@ -702,9 +702,13 @@ public function rest_api() { $pdf_preview_controller = new Rest\Rest_Pdf_Preview( $this->options, $this->gform, $this->misc, $this->templates ); $pdf_preview_controller->init(); + $download_pdf_controller = new Rest\Rest_Download_Pdf( $this->gform ); + $download_pdf_controller->init(); + /* Add to our singleton controller */ $this->singleton->add_class( $form_setting_controller ); $this->singleton->add_class( $pdf_preview_controller ); + $this->singleton->add_class( $download_pdf_controller ); /* Log any errors for PDF endpoints */ $rest_request_after_callback = function( $response, $handle, $request ) { From aba940a6b8ba31c8a1c5f89fdd715bbec7b994ad Mon Sep 17 00:00:00 2001 From: Jake Jackson Date: Fri, 30 Aug 2024 16:11:13 +1000 Subject: [PATCH 2/2] no message --- src/Rest/Rest_Download_Pdf.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Rest/Rest_Download_Pdf.php b/src/Rest/Rest_Download_Pdf.php index fbca0c974..aa3e8936f 100644 --- a/src/Rest/Rest_Download_Pdf.php +++ b/src/Rest/Rest_Download_Pdf.php @@ -155,6 +155,8 @@ public function get_item( $request ) { return $path_to_pdf; } + /* @TODO - add "type" with enum "url" or "base64". If "url" generate standard signed PDF URL with 1 hour expiration and return as "url" */ + $response = new WP_REST_Response( [ 'filename' => wp_basename( $path_to_pdf ),