diff --git a/api.php b/api.php index 58b9bd13a..f24f0d6eb 100644 --- a/api.php +++ b/api.php @@ -448,19 +448,21 @@ public static function delete_plugin_option( $key ) { } /** - * When provided the Gravity Form entry ID and PDF ID, this method will correctly generate the PDF, save it to disk, - * trigger appropriate actions and return the absolute path to the PDF. + * Generate a PDF, save it to disk, and return the absolute path to the document * * See https://docs.gravitypdf.com/v6/developers/api/create_pdf/ for more information about this method * - * @param integer $entry_id The Gravity Form entry ID - * @param string $pdf_id The Gravity PDF ID number (the pid number in the URL when viewing a setting in the admin area) + * @param int $entry_id The Gravity Form entry ID + * @param string $pdf_id The Gravity PDF ID number (the pid number in the URL when viewing a setting in the admin area) + * @param bool $bypass_cache Force a new PDF to be generated * - * @return mixed Return the full path to the PDF, or a WP_Error on failure + * @return string|WP_Error Return the full path to the PDF, or a WP_Error on failure * * @since 4.0 + * @since 6.12 All PDFs are cached on disk for ~1 hour, but are auto-purged if the form, entry, or PDF settings change + * Re-running the method will return the cached PDF if it exists, unless $bypass_cache = true */ - public static function create_pdf( $entry_id, $pdf_id ) { + public static function create_pdf( $entry_id, $pdf_id, $bypass_cache = false ) { $form_class = static::get_form_class(); @@ -478,16 +480,19 @@ public static function create_pdf( $entry_id, $pdf_id ) { return new WP_Error( 'invalid_pdf_setting', esc_html__( 'Could not located the PDF Settings. Ensure you pass in a valid PDF ID.', 'gravity-forms-pdf-extended' ) ); } - $pdf = static::get_mvc_class( 'Model_PDF' ); - $form = $form_class->get_form( $entry['form_id'] ); + if ( $bypass_cache ) { + add_filter( 'gfpdf_override_pdf_bypass', '__return_true', 9999 ); + } + + /** @var \GFPDF\Model\Model_PDF $pdf */ + $pdf = static::get_mvc_class( 'Model_PDF' ); + $path_to_pdf = $pdf->generate_and_save_pdf( $entry, $setting ); - add_filter( 'gfpdf_override_pdf_bypass', '__return_true' ); - do_action( 'gfpdf_pre_generate_and_save_pdf', $form, $entry, $setting ); - $filename = $pdf->generate_and_save_pdf( $entry, $setting ); - do_action( 'gfpdf_post_generate_and_save_pdf', $form, $entry, $setting ); - remove_filter( 'gfpdf_override_pdf_bypass', '__return_true' ); + if ( $bypass_cache ) { + remove_filter( 'gfpdf_override_pdf_bypass', '__return_true', 9999 ); + } - return $filename; + return $path_to_pdf; } /** diff --git a/pdf.php b/pdf.php index 4425247e3..c0a620821 100644 --- a/pdf.php +++ b/pdf.php @@ -1,7 +1,7 @@ add_actions(); $this->add_filters(); /* Add scheduled tasks */ if ( ! wp_next_scheduled( 'gfpdf_cleanup_tmp_dir' ) ) { - wp_schedule_event( time(), 'twicedaily', 'gfpdf_cleanup_tmp_dir' ); + wp_schedule_event( time(), 'hourly', 'gfpdf_cleanup_tmp_dir' ); } } /** - * Apply any actions needed for the settings page - * * @return void * @since 4.0 - * */ public function add_actions() { /* Process PDF if needed */ add_action( 'parse_request', [ $this, 'process_legacy_pdf_endpoint' ] ); /* legacy PDF endpoint */ add_action( 'parse_request', [ $this, 'process_pdf_endpoint' ] ); /* new PDF endpoint */ - /* Allow custom PDF tags / CSS */ + /* Set up pre- and post-generation PDF hooks */ add_action( 'gfpdf_pre_pdf_generation', [ $this, 'add_pre_pdf_hooks' ] ); add_action( 'gfpdf_post_pdf_generation', [ $this, 'remove_pre_pdf_hooks' ] ); + /* Set up pre generation hooks when streaming PDF to the browser */ + $add_pre_view_or_download_pdf_hooks = function( $form, $entry, $settings ) { + $this->add_pre_view_or_download_pdf_hooks( $form, $entry, $settings ); + }; + + add_action( 'gfpdf_view_or_download_pdf', $add_pre_view_or_download_pdf_hooks, 10, 3 ); + /* Display PDF links in Gravity Forms Admin Area */ add_action( 'gform_entries_first_column_actions', [ $this->model, 'view_pdf_entry_list' ], 10, 4 ); add_action( 'gravityflow_workflow_detail_sidebar', [ $this->model, 'view_pdf_gravityflow_inbox' ], 10, 4 ); - /* Add save PDF actions */ + /* Add hooks to save PDF to disk, or run right after a PDF is saved to disk */ add_action( 'gform_after_submission', [ $this->model, 'maybe_save_pdf' ], 10, 2 ); add_action( 'gfpdf_post_pdf_generation', [ $this->model, 'trigger_post_save_pdf' ], 10, 4 ); - /* Clean-up actions */ - add_action( 'gform_after_submission', [ $this->model, 'cleanup_pdf' ], 9999, 2 ); - add_action( 'gform_after_update_entry', [ $this->model, 'cleanup_pdf_after_submission' ], 9999, 2 ); + /* Scheduled clean-up actions */ add_action( 'gfpdf_cleanup_tmp_dir', [ $this->model, 'cleanup_tmp_dir' ] ); - /* Add Gravity Perk Population Anything Support */ - if ( function_exists( 'gp_populate_anything' ) ) { - add_action( 'gfpdf_pre_pdf_generation', [ $this->model, 'enable_gp_populate_anything' ] ); - add_action( 'gfpdf_pre_pdf_generation_output', [ $this->model, 'disable_gp_populate_anything' ] ); - - /* register preferred hydration method */ - add_filter( 'gfpdf_current_form_object', [ $this->model, 'gp_populate_anything_hydrate_form' ], 5, 2 ); - - /* remove legacy filters */ - if ( class_exists( '\GPPA_Compatibility_GravityPDF' ) ) { - $gp_pdf_compat = \GPPA_Compatibility_GravityPDF::get_instance(); - remove_action( 'gfpdf_pre_view_or_download_pdf', [ $gp_pdf_compat, 'hydrate_form_hook_for_pdf_view_or_download' ] ); - remove_action( 'gfpdf_pre_generate_and_save_pdf_notification', [ $gp_pdf_compat, 'hydrate_form_hook' ] ); - remove_action( 'gfpdf_pre_generate_and_save_pdf', [ $gp_pdf_compat, 'hydrate_form_hook' ] ); - } + /* Remove legacy Gravity Perk Population Anything Support */ + if ( class_exists( '\GPPA_Compatibility_GravityPDF' ) ) { + $gp_pdf_compat = \GPPA_Compatibility_GravityPDF::get_instance(); + remove_action( 'gfpdf_pre_view_or_download_pdf', [ $gp_pdf_compat, 'hydrate_form_hook_for_pdf_view_or_download' ] ); + remove_action( 'gfpdf_pre_generate_and_save_pdf_notification', [ $gp_pdf_compat, 'hydrate_form_hook' ] ); + remove_action( 'gfpdf_pre_generate_and_save_pdf', [ $gp_pdf_compat, 'hydrate_form_hook' ] ); + } + + /* Gravity Wiz Nested Forms support */ + if ( function_exists( 'gp_nested_forms' ) ) { + $included_nested_forms_in_cache_hash = function( $data, $form, $entry, $pdf_settings ) { + return $this->included_nested_forms_in_cache_hash( $data, $form, $entry, $pdf_settings ); + }; + + add_filter( 'gfpdf_cache_hash_array', $included_nested_forms_in_cache_hash, 10, 4 ); } /* Add Legal Signature support */ @@ -163,11 +161,8 @@ public function add_actions() { } /** - * Apply any filters needed for the settings page - * * @return void * @since 4.0 - * */ public function add_filters() { /* PDF authentication middleware */ @@ -188,16 +183,8 @@ public function add_filters() { add_filter( 'gfpdf_field_middleware', [ $this->model, 'field_middle_page' ], 10, 5 ); add_filter( 'gfpdf_field_middleware', [ $this->model, 'field_middle_blacklist' ], 10, 7 ); - /* Tap into GF notifications */ - add_filter( - 'gform_notification', - [ - $this->model, - 'notifications', - ], - 9999, - 3 - ); /* ensure Gravity PDF is one of the last filters to be applied */ + /* Gravity Forms PDF Attachments */ + add_filter( 'gform_notification', [ $this->model, 'notifications' ], 9999, 3 ); /* Change mPDF settings */ add_filter( 'mpdf_font_data', [ $this->model, 'register_custom_font_data_with_mPDF' ] ); @@ -205,10 +192,16 @@ public function add_filters() { add_filter( 'gfpdf_mpdf_init_class', [ $this->model, 'set_watermark_font' ], 10, 4 ); /* Process mergetags and shortcodes in PDF */ + add_filter( 'gfpdf_pdf_core_template_html_output', [ $this->gform, 'process_tags' ], 10, 3 ); add_filter( 'gfpdf_pdf_html_output', [ $this->gform, 'process_tags' ], 10, 3 ); add_filter( 'gfpdf_pdf_html_output', 'do_shortcode' ); - add_filter( 'gfpdf_pdf_core_template_html_output', [ $this->gform, 'process_tags' ], 10, 3 ); + /* Add support for ?html=1 helper parameter */ + $add_view_html_debugger = function( $html, $form, $entry, $pdf_settings, $helper_pdf ) { + return $this->add_view_html_debugger( $html, $form, $entry, $pdf_settings, $helper_pdf ); + }; + + add_filter( 'gfpdf_pdf_html_output', $add_view_html_debugger, 9999, 5 ); /* Backwards compatibility for our Tier 2 plugin */ add_filter( 'gfpdfe_pre_load_template', [ 'PDFRender', 'prepare_ids' ], 1, 8 ); @@ -217,33 +210,42 @@ public function add_filters() { add_filter( 'gfpdf_template_args', [ $this->model, 'preprocess_template_arguments' ] ); add_filter( 'gfpdf_pdf_html_output', [ $this->view, 'autoprocess_core_template_options' ], 5, 4 ); - /* Cleanup filters */ - add_filter( 'gform_before_resend_notifications', [ $this->model, 'resend_notification_pdf_cleanup' ], 10, 2 ); - - /* Third Party Conflict Fixes */ - add_filter( 'gfpdf_pre_view_or_download_pdf', [ $this, 'sgoptimizer_html_minification_fix' ] ); - add_filter( 'gfpdf_legacy_pre_view_or_download_pdf', [ $this, 'sgoptimizer_html_minification_fix' ] ); - add_filter( - 'gfpdf_pre_pdf_generation_output', - function() { - add_filter( 'weglot_active_translation', '__return_false' ); - } - ); - /* Meta boxes */ add_filter( 'gform_entry_detail_meta_boxes', [ $this->model, 'register_pdf_meta_box' ], 10, 3 ); - /* Page field support */ - add_filter( 'gfpdf_current_form_object', [ $this->model, 'register_page_fields' ] ); + /* Manipulate the form object (array) when generating PDFs */ + $add_current_form_object_hooks = function( $form, $entry, $source ) { + return $this->add_current_form_object_hooks( $form, $entry, $source ); + }; + + add_filter( 'gfpdf_current_form_object', $add_current_form_object_hooks, 10, 3 ); + + /* Manipulate the PDF settings object (array) when generating PDFs */ + $add_current_pdf_settings_object_hooks = function( $pdf_settings, $form, $entry ) { + return $this->add_current_pdf_settings_object_hooks( $pdf_settings, $form, $entry ); + }; + + add_filter( 'gfpdf_current_pdf_settings_object', $add_current_pdf_settings_object_hooks, 10, 3 ); } /** - * Determines if we should process the PDF at this stage - * Fires just before the main WP_Query is executed (we don't need it) + * Processes the View/Download PDF Endpoint + * + * Endpoint URLs: + * + * example format -> https://example.com/pdf/{pdfId}/{entryId}/ + * + * view -> https://example.com/pdf/66307560bcdf4/2403/ + * download -> https://example.com/pdf/66307560bcdf4/2403/download/ + * add print dialog -> https://example.com/pdf/66307560bcdf4/2403/?print=1 + * + * Recommend you generate the URL with a shortcode or merge tag + * See https://docs.gravitypdf.com/v6/users/shortcodes-and-mergetags + * + * This method runs just before the main WP_Query class is executed * * @return void * @since 4.0 - * */ public function process_pdf_endpoint() { @@ -252,8 +254,6 @@ public function process_pdf_endpoint() { return null; } - $this->prevent_index(); - $pid = $GLOBALS['wp']->query_vars['pid']; $lid = (int) $GLOBALS['wp']->query_vars['lid']; $action = ( ( isset( $GLOBALS['wp']->query_vars['action'] ) ) && $GLOBALS['wp']->query_vars['action'] === 'download' ) ? 'download' : 'view'; @@ -283,7 +283,7 @@ public function process_pdf_endpoint() { * * @return void * @since 4.0 - * + * @deprecated 4.0 Added for backwards compatibility with v3 PDF links, but ideally should not be used */ public function process_legacy_pdf_endpoint() { @@ -292,7 +292,7 @@ public function process_legacy_pdf_endpoint() { return null; } - $this->prevent_index(); + _doing_it_wrong( __METHOD__, 'Legacy PDF URLs are deprecated. Replace with the [gravitypdf] shortcode or PDF merge tags. See https://docs.gravitypdf.com/v6/users/shortcodes-and-mergetags for usage instructions.', '4.0' ); $config = [ 'lid' => (int) explode( ',', $_GET['lid'] )[0], @@ -303,13 +303,6 @@ public function process_legacy_pdf_endpoint() { ]; /* phpcs:enable */ - $this->log->notice( - 'Processing Legacy PDF endpoint.', - [ - 'config' => $config, - ] - ); - /* Attempt to find a valid config */ $pid = $this->model->get_legacy_config( $config ); @@ -322,6 +315,16 @@ public function process_legacy_pdf_endpoint() { $GLOBALS['wp']->query_vars['pid'] = $pid; $GLOBALS['wp']->query_vars['lid'] = $config['lid']; + $this->log->notice( + 'Processing Legacy PDF endpoint.', + [ + 'config' => $config, + 'pid' => $pid, + ] + ); + + $this->log->warning( 'Legacy PDF URLs are deprecated. Replace with the [gravitypdf] shortcode or PDF merge tags. See https://docs.gravitypdf.com/v6/users/shortcodes-and-mergetags for usage instructions.' ); + /* Send to our model to handle validation / authentication */ do_action( 'gfpdf_legacy_pre_view_or_download_pdf', $config['lid'], $pid, $config['action'] ); $results = $this->model->process_pdf( $pid, $config['lid'], $config['action'] ); @@ -338,6 +341,13 @@ public function process_legacy_pdf_endpoint() { public function add_pre_pdf_hooks() { add_filter( 'wp_kses_allowed_html', [ $this->view, 'allow_pdf_html' ] ); add_filter( 'safe_style_css', [ $this->view, 'allow_pdf_css' ] ); + + $this->misc->maybe_load_gf_entry_detail_class(); /* Backwards compatible for legacy templates */ + + /* Gravity Wiz Populate Anything support */ + if ( function_exists( 'gp_populate_anything' ) ) { + $this->model->enable_gp_populate_anything(); + } } /** @@ -346,16 +356,38 @@ public function add_pre_pdf_hooks() { public function remove_pre_pdf_hooks() { remove_filter( 'wp_kses_allowed_html', [ $this->view, 'allow_pdf_html' ] ); remove_filter( 'safe_style_css', [ $this->view, 'allow_pdf_css' ] ); + + /* Gravity Wiz Populate Anything support */ + if ( function_exists( 'gp_populate_anything' ) ) { + $this->model->disable_gp_populate_anything(); + } } /** - * Prevent the PDF Endpoints being indexed + * Actions / hooks to run prior to streaming PDF to the browser + * These hooks will not be run when sending notifications, using GPDFAPI::create_pdf(), * - * @since 5.2 + * @return void + * + * @since 6.12 */ - public function prevent_index() { - if ( ! headers_sent() ) { - header( 'X-Robots-Tag: noindex, nofollow', true ); + protected function add_pre_view_or_download_pdf_hooks( $form, $entry, $settings ) { + $this->prevent_index(); + + /* + * Support ?data=1 helper parameter + * See https://docs.gravitypdf.com/v6/developers/helper-parameters#data1 + */ + if ( $this->view->maybe_view_form_data() ) { + $this->view->view_form_data( \GPDFAPI::get_form_data( $entry['id'] ) ); + } + + /* + * Support ?html=1 helper parameter + * See https://docs.gravitypdf.com/v6/developers/helper-parameters#html1 + */ + if ( rgget( 'html' ) && Debug::is_enabled_and_can_view() ) { + add_filter( 'gfpdf_override_pdf_bypass', '__return_true' ); } } @@ -363,30 +395,131 @@ public function prevent_index() { * Disables the Siteground HTML Minifier when generating PDFs for the browser * * @since 5.1.5 - * - * @see https://github.com/GravityPDF/gravity-pdf/issues/863 + * @see https://github.com/GravityPDF/gravity-pdf/issues/863 + * @deprecated 6.12 All buffers are auto-closed before a PDF is sent to the browser */ public function sgoptimizer_html_minification_fix() { - if ( class_exists( '\SiteGround_Optimizer\Minifier\Minifier' ) ) { + _doing_it_wrong( __METHOD__, 'This method has been removed and no alternative is available.', '6.12' ); + } + + /** + * Modify the form object specifically for the PDF request + * + * @param array $form + * @param array $entry + * @param string $source + * + * @return array + * + * @since 6.12 + */ + protected function add_current_form_object_hooks( $form, $entry, $source ) { + if ( ! isset( $form['id'] ) ) { + return $form; + } - /* Remove the shutdown buffer and manually close an open buffers */ - $minifier = Minifier::get_instance(); - remove_action( 'shutdown', [ $minifier, 'end_html_minifier_buffer' ] ); + /* Make Page fields first class citizens in the form object */ + $form = $this->model->register_page_fields( $form ); - while ( ob_get_level() > 0 ) { - ob_end_clean(); - } + /* Gravity Perks Conditional Logic Date Fields support */ + if ( method_exists( 'GWConditionalLogicDateFields', 'convert_conditional_logic_date_field_values' ) ) { + $form = \GWConditionalLogicDateFields::convert_conditional_logic_date_field_values( $form ); + } + + /* Gravity Perks Populate Anything support */ + if ( function_exists( 'gp_populate_anything' ) ) { + $form = $this->model->gp_populate_anything_hydrate_form( $form, $entry ); } + + return $form; } /** - * Output PDF error to user + * Modify the PDF settings specifically for the PDF request + * + * @param array $pdf_settings + * @param array $form + * @param array $entry * - * @param Object $error The WP_Error object + * @return array + * + * @since 6.12 + */ + protected function add_current_pdf_settings_object_hooks( $pdf_settings, $form, $entry ) { + $pdf_settings = $this->model->apply_backwards_compatibility_filters( $pdf_settings, $entry ); + + return $pdf_settings; + } + + /** + * A debugging tool that will output the HTML mark-up for the PDF to the browser + * + * Use ?html=1 to active when the website is in development/staging mode and the current logged-in + * user has appropriate capabilities. To easily see the raw source code on screen use ?html=1&raw=1 + * + * @param string $html + * @param array $form + * @param array $entry + * @param array $pdf_settings + * @param Helper_PDF $helper_pdf + * + * @return string + * + * @since 6.12 + * + * @internal was originally included in \GFPDF\Helper\Helper_PDF::maybe_display_raw_html() + * @link https://docs.gravitypdf.com/v6/developers/helper-parameters#html1 + */ + protected function add_view_html_debugger( $html, $form, $entry, $pdf_settings, $helper_pdf ) { + if ( ! is_string( $html ) ) { + return $html; + } + + if ( ! rgget( 'html' ) ) { + return $html; + } + + if ( ! Debug::is_enabled_and_can_view() ) { + return $html; + } + + $html = apply_filters( 'gfpdf_pre_html_browser_output', $html, $pdf_settings, $entry, $form, $helper_pdf ); + + if ( rgget( 'raw' ) ) { + echo '
';
+ echo htmlspecialchars( $html ); /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */
+ echo '
';
+ } else {
+ echo $html; /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */
+ }
+
+ exit;
+ }
+
+ /**
+ * Try to prevent the PDF being indexed or cached by the web server
+ *
+ * @since 5.2
+ * @since 6.12 Set DONOTCACHEPAGE constant (brought forward in the request cycle)
+ */
+ public function prevent_index() {
+ if ( ! headers_sent() ) {
+ header( 'X-Robots-Tag: noindex, nofollow', true );
+ }
+
+ if ( ! defined( 'DONOTCACHEPAGE' ) ) {
+ define( 'DONOTCACHEPAGE', true );
+ }
+ }
+
+ /**
+ * Display appropriate error to user when PDF cannot be display
+ *
+ * @param \WP_Error $error The WP_Error object
*
* @since 4.0
*/
- private function pdf_error( $error ) {
+ protected function pdf_error( $error ) {
$this->log->error(
'PDF Generation Error.',
@@ -421,7 +554,41 @@ private function pdf_error( $error ) {
if ( $this->gform->has_capability( 'gravityforms_view_settings' ) || in_array( $error->get_error_code(), $whitelist_errors, true ) ) {
wp_die( esc_html( $error->get_error_message() ), $status_code ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} else {
- wp_die( esc_html__( 'There was a problem generating your PDF', 'gravity-forms-pdf-extended' ), $status_code ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ wp_die( esc_html__( 'There was a problem creating the PDF', 'gravity-forms-pdf-extended' ), $status_code ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
}
+
+ /**
+ * If Nested Form fields are included in the form, include the child entries in the cache hash.
+ * This will auto-invalidate the parent PDF when the child entry is modified
+ *
+ * @param array $data Data to hash
+ * @param array $form Form object
+ * @param array $entry Entry object
+ * @param array $pdf_settings PDF object
+ *
+ * @return array
+ *
+ * @since 6.12
+ */
+ protected function included_nested_forms_in_cache_hash( $data, $form, $entry, $pdf_settings ) {
+ if ( empty( $entry['id'] ) ) {
+ return $data;
+ }
+
+ if ( ! class_exists( '\GPNF_Entry' ) ) {
+ return $data;
+ }
+
+ $parent_entry = new \GPNF_Entry( $entry );
+ $child_entries = $parent_entry->get_child_entries();
+
+ if ( empty( $child_entries ) ) {
+ return $data;
+ }
+
+ $data['nested_form_entries'] = $child_entries;
+
+ return $data;
+ }
}
diff --git a/src/Controller/Controller_Pdf_Queue.php b/src/Controller/Controller_Pdf_Queue.php
index cadd993b1..ceb7fcabe 100644
--- a/src/Controller/Controller_Pdf_Queue.php
+++ b/src/Controller/Controller_Pdf_Queue.php
@@ -210,11 +210,6 @@ public function do_we_disable_notification( $default, $notification, $form, $ent
*/
public function queue_async_form_submission_tasks( $entry, $form ) {
$this->queue_async_tasks( $form, $entry );
-
- if ( count( $this->queue->get_data() ) > 0 ) {
- $this->queue_cleanup_task( $form, $entry );
- }
-
$this->dispatch_queue();
}
@@ -231,7 +226,9 @@ public function queue_dispatch_resend_notification_tasks( $form, $entry ) {
}
/**
- * Push tasks to the queue for requested notifications
+ * Push tasks to the queue for requested PDFs/Notifications
+ *
+ * Even if a PDF isn't attached to a notification, it may still need to be generated and saved to disk
*
* @param array $form
* @param array $entry
@@ -241,8 +238,10 @@ public function queue_dispatch_resend_notification_tasks( $form, $entry ) {
* @since 6.11.0
*/
public function queue_async_tasks( $form, $entry ) {
- foreach ( $this->form_async_notifications as $notification ) {
- $this->queue->push_to_queue( $this->get_queue_tasks( $entry, $form, [ $notification ] ) );
+ $tasks = $this->get_queue_tasks( $entry, $form, $this->form_async_notifications );
+
+ if ( count( $tasks ) > 0 ) {
+ $this->queue->push_to_queue( $tasks );
}
}
@@ -255,17 +254,10 @@ public function queue_async_tasks( $form, $entry ) {
* @return void
*
* @since 6.11.0
+ * @deprecated 6.12.0 Caching layer + auto-purge added
*/
public function queue_cleanup_task( $form, $entry ) {
- $this->queue->push_to_queue(
- [
- [
- 'id' => sprintf( 'cleanup-pdf-%d-%d', $form['id'], $entry['id'] ),
- 'func' => '\GFPDF\Statics\Queue_Callbacks::cleanup_pdfs',
- 'args' => [ $form['id'], $entry['id'] ],
- ],
- ]
- );
+ _doing_it_wrong( __METHOD__, 'This method is deprecated and no alternative is available. The temporary cache is automatically cleaned every hour using the WP Cron.', '6.12' );
}
/**
@@ -342,7 +334,7 @@ protected function queue_pdfs( $notifications, $pdfs, $form, $entry ) {
$pdf_queue_data = [
'id' => $this->get_queue_id( $form, $entry, $pdf ),
'func' => '\GFPDF\Statics\Queue_Callbacks::create_pdf',
- 'args' => [ $entry['id'], $pdf['id'] ],
+ 'args' => [ $entry['id'], $pdf['id'], get_current_user_id() ],
'unrecoverable' => true,
];
@@ -390,7 +382,7 @@ protected function queue_notifications( $notifications, $pdfs, $form, $entry ) {
$queue_data[] = [
'id' => $this->get_queue_id( $form, $entry, $pdf ) . '-' . $notification['id'],
'func' => '\GFPDF\Statics\Queue_Callbacks::send_notification',
- 'args' => [ $form['id'], $entry['id'], $notification ],
+ 'args' => [ $form['id'], $entry['id'], $notification, get_current_user_id() ],
];
/* Only queue each notification once */
@@ -421,7 +413,7 @@ protected function is_notification_enabled( $notification_id, $form, $entry ) {
}
$notification = $form['notifications'][ $notification_id ];
- if ( empty( $notification['isActive'] ) ) {
+ if ( isset( $notification['isActive'] ) && ! $notification['isActive'] ) {
return false;
}
@@ -477,6 +469,6 @@ public function reset_queue() {
* @deprecated 6.11
*/
public function queue_async_resend_notification_tasks( $notification, $form, $entry ) {
- _doing_it_wrong( esc_html( 'queue_async_resend_notification_tasks() was removed in Gravity PDF 6.11' ) );
+ _doing_it_wrong( __METHOD__, 'This method has been removed and no alternative is available.', '6.11' );
}
}
diff --git a/src/Controller/Controller_Upgrade_Routines.php b/src/Controller/Controller_Upgrade_Routines.php
index 0d5db43ae..a34f67a2e 100644
--- a/src/Controller/Controller_Upgrade_Routines.php
+++ b/src/Controller/Controller_Upgrade_Routines.php
@@ -54,6 +54,11 @@ public function maybe_run_upgrade( string $old_version, string $current_version
$this->update_background_processing_values();
$this->upgrade_custom_fonts();
}
+
+ /* Remove scheduled event(s) so the event can be reregistered with a new frequency */
+ if ( version_compare( $current_version, '6.12.0', '>=' ) && version_compare( $old_version, '6.12.0', '<' ) ) {
+ wp_clear_scheduled_hook( 'gfpdf_cleanup_tmp_dir' );
+ }
}
/**
diff --git a/src/Helper/Helper_Misc.php b/src/Helper/Helper_Misc.php
index 14296161f..04aa5f10e 100644
--- a/src/Helper/Helper_Misc.php
+++ b/src/Helper/Helper_Misc.php
@@ -721,12 +721,11 @@ public function get_legacy_ids( $entry_id, $settings ) {
* @return void
*
* @since 4.0
+ *
+ * @deprecated 6.12 compatibility code no longer required
*/
public function maybe_add_multicurrency_support() {
- if ( class_exists( 'GFMultiCurrency' ) && method_exists( 'GFMultiCurrency', 'admin_pre_render' ) ) {
- $currency = GFMultiCurrency::init();
- add_filter( 'gform_form_post_get_meta', [ $currency, 'admin_pre_render' ] );
- }
+ _doing_it_wrong( __METHOD__, 'This method has been removed and no alternative is available.', '6.12' );
}
/**
diff --git a/src/Helper/Helper_PDF.php b/src/Helper/Helper_PDF.php
index ca842f365..28412fb42 100644
--- a/src/Helper/Helper_PDF.php
+++ b/src/Helper/Helper_PDF.php
@@ -3,6 +3,7 @@
namespace GFPDF\Helper;
use Exception;
+use GFPDF\Statics\Cache;
use GFPDF_Vendor\Mpdf\Config\FontVariables;
use GFPDF_Vendor\Mpdf\Mpdf;
use GFPDF_Vendor\Mpdf\MpdfException;
@@ -178,7 +179,6 @@ class Helper_PDF {
*
* @param array $entry The Gravity Form Entry to be processed
* @param array $settings The Gravity PDF Settings Array
- *
* @param Helper_Abstract_Form $gform
* @param Helper_Data $data
* @param Helper_Misc $misc
@@ -200,6 +200,7 @@ public function __construct( $entry, $settings, Helper_Abstract_Form $gform, Hel
$this->form = apply_filters( 'gfpdf_current_form_object', $this->gform->get_form( $entry['form_id'] ), $entry, 'initialize_pdf_class' );
$this->set_path();
+ $this->set_print_dialog( ! empty( $settings['print'] ) );
}
/**
@@ -250,7 +251,7 @@ public function render_html( $args = [], $html = '' ) {
$form = $this->form;
- /* Allow this method to be short circuited */
+ /* Allow this method to be short-circuited */
if ( apply_filters( 'gfpdf_skip_pdf_html_render', false, $args, $this ) ) {
do_action( 'gfpdf_skipped_html_render', $args, $this );
@@ -270,9 +271,6 @@ public function render_html( $args = [], $html = '' ) {
$html = apply_filters( 'gfpdf_pdf_html_output', $html, $form, $this->entry, $args['settings'], $this );
$html = apply_filters( 'gfpdf_pdf_html_output_' . $form['id'], $html, $this->gform, $this->entry, $args['settings'], $this );
- /* Check if we should output the HTML to the browser, for debugging */
- $this->maybe_display_raw_html( $html );
-
/* Write the HTML to mPDF */
$this->mpdf->WriteHTML( $html );
}
@@ -284,6 +282,7 @@ public function render_html( $args = [], $html = '' ) {
*
* @throws MpdfException
* @since 4.0
+ * @since 6.12 All PDF requests have been standardized to use the functions/methods in \GPDFAPI::create_pdf(), and the DISPLAY/DOWNLOAD options are no longer used by core
*/
public function generate() {
@@ -343,6 +342,8 @@ public function save_pdf( $raw_pdf_string ) {
if ( ! wp_mkdir_p( $this->path ) ) {
throw new Exception( sprintf( 'Could not create directory: %s', esc_html( $this->path ) ) );
}
+
+ file_put_contents( $this->path . 'index.html', '' );
}
/* save our PDF */
@@ -519,7 +520,7 @@ public function set_JS( $js ) {
/**
*
- * Get the current Gravity Form Entry
+ * Get the current Gravity Forms Entry
*
* @return array
* @since 4.0
@@ -539,6 +540,16 @@ public function get_settings() {
return $this->settings;
}
+ /**
+ * Get the current Gravity Forms form object
+ *
+ * @return array
+ * @since 6.12
+ */
+ public function get_form() {
+ return $this->form;
+ }
+
/**
* Get the current PDF Name
*
@@ -584,16 +595,10 @@ public function get_path() {
public function set_path( $path = '' ) {
if ( empty( $path ) ) {
- /* build our PDF path location */
- $path = $this->data->template_tmp_location . $this->entry['form_id'] . $this->entry['id'] . $this->settings['id'] . '/';
- } else {
- /* ensure the path ends with a forward slash */
- if ( substr( $path, -1 ) !== '/' ) {
- $path .= '/';
- }
+ $path = Cache::get_path( $this->form, $this->entry, $this->settings );
}
- $this->path = $path;
+ $this->path = trailingslashit( $path );
}
/**
@@ -862,45 +867,6 @@ protected function load_html( $args = [] ) {
return ob_get_clean();
}
-
- /**
- * Allow site admins to view the RAW HTML if needed
- *
- * @param string $html The HTML that should be output to the browser
- *
- * @return void
- *
- * @since 4.0
- */
- protected function maybe_display_raw_html( $html ) {
-
- $options = \GPDFAPI::get_options_class();
-
- /* Disregard if PDF is being saved */
- if ( $this->output === 'SAVE' ) {
- return;
- }
-
- /* Disregard if `?html` URL parameter doesn't exist */
- if ( ! rgget( 'html' ) ) {
- return;
- }
-
- /* Disregard if PDF Debug Mode off AND the environment is production */
- if ( $options->get_option( 'debug_mode', 'No' ) === 'No' && ( ! function_exists( 'wp_get_environment_type' ) || wp_get_environment_type() === 'production' ) ) {
- return;
- }
-
- /* Check if user has permission to view info */
- if ( ! $this->gform->has_capability( 'gravityforms_edit_forms' ) ) {
- return;
- }
-
- /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped */
- echo apply_filters( 'gfpdf_pre_html_browser_output', $html, $this->settings, $this->entry, $this->gform, $this );
- exit;
- }
-
/**
* Prompt the print dialog box
*
@@ -914,17 +880,6 @@ protected function show_print_dialog() {
}
}
- /**
- * Sets the image DPI in the PDF
- *
- * @return void
- *
- * @since 4.0
- */
- protected function set_image_dpi() {
- _doing_it_wrong( __METHOD__, esc_html__( 'This method has been removed because mPDF no longer supports setting the image DPI after the class is initialised.', 'gravity-forms-pdf-extended' ), '5.2' );
- }
-
/**
* Sets the text direction in the PDF (RTL support)
*
diff --git a/src/Model/Model_Install.php b/src/Model/Model_Install.php
index 8e24eda39..c871c8532 100644
--- a/src/Model/Model_Install.php
+++ b/src/Model/Model_Install.php
@@ -303,23 +303,20 @@ public function create_folder_structures() {
$this->notices->add_error( sprintf( esc_html__( 'Gravity PDF does not have write permission to the %s directory. Contact your web hosting provider to fix the issue.', 'gravity-forms-pdf-extended' ), '' . $this->misc->relative_path( $dir ) . '
' ) );
}
}
- }
- /* create blank index file in all folders to prevent web servers listing the entire directory */
- if ( is_dir( $this->data->template_location ) && ! is_file( $this->data->template_location . 'index.html' ) ) {
- GFCommon::recursive_add_index_file( $this->data->template_location );
+ /* create blank index file in all folders to prevent web servers listing the entire directory */
+ if ( ! is_file( trailingslashit( $dir ) . 'index.html' ) ) {
+ file_put_contents( trailingslashit( $dir ) . 'index.html', '' );
+ }
}
/* create deny htaccess file to prevent direct access to files */
- if ( is_dir( $this->data->template_tmp_location ) ) {
- if ( ! is_file( $this->data->template_tmp_location . 'index.html' ) ) {
- GFCommon::recursive_add_index_file( $this->data->template_tmp_location );
- }
-
- if ( ! is_file( $this->data->template_tmp_location . '.htaccess' ) ) {
- $this->log->notice( 'Create Apache .htaccess Security file' );
- file_put_contents( $this->data->template_tmp_location . '.htaccess', 'deny from all' );
- }
+ if (
+ is_dir( $this->data->template_tmp_location ) &&
+ ! is_file( $this->data->template_tmp_location . '.htaccess' )
+ ) {
+ $this->log->notice( 'Create Apache .htaccess Security file' );
+ file_put_contents( $this->data->template_tmp_location . '.htaccess', 'deny from all' );
}
}
diff --git a/src/Model/Model_PDF.php b/src/Model/Model_PDF.php
index 89fd0c3a0..a4be20034 100644
--- a/src/Model/Model_PDF.php
+++ b/src/Model/Model_PDF.php
@@ -6,6 +6,7 @@
use GF_Field;
use GFCommon;
use GFFormsModel;
+use GFPDF\Controller\Controller_PDF;
use GFPDF\Helper\Fields\Field_Default;
use GFPDF\Helper\Fields\Field_Products;
use GFPDF\Helper\Helper_Abstract_Field_Products;
@@ -49,6 +50,8 @@
* Handles all the PDF display logic
*
* @since 4.0
+ *
+ * @method Controller_PDF getController
*/
class Model_PDF extends Helper_Abstract_Model {
@@ -155,7 +158,7 @@ public function __construct( Helper_Abstract_Form $gform, LoggerInterface $log,
}
/**
- * Our Middleware used to handle the authentication process
+ * Authentication request then generate and display PDF
*
* @param string $pid The Gravity Form PDF Settings ID
* @param integer $lid The Gravity Form Entry ID
@@ -163,34 +166,30 @@ public function __construct( Helper_Abstract_Form $gform, LoggerInterface $log,
*
* @return WP_Error
* @since 4.0
- *
+ * @since 6.12 View/Download PDF creation workflow standardized with Save PDF workflow
*/
public function process_pdf( $pid, $lid, $action = 'view' ) {
- /**
- * Check if we have a valid Gravity Form Entry and PDF Settings ID
- */
+ /* Get entry */
$entry = $this->gform->get_entry( $lid );
-
- /* not a valid entry */
if ( is_wp_error( $entry ) ) {
$this->log->error(
- 'Invalid Entry.',
+ 'Invalid Entry',
[
- 'entry' => $entry,
+ 'entry_id' => $lid,
+ 'WP_Error_Message' => $entry->get_error_message(),
+ 'WP_Error_Code' => $entry->get_error_code(),
]
);
return $entry; /* return error */
}
+ /* Get PDF setting */
$settings = $this->options->get_pdf( $entry['form_id'], $pid );
-
- /* Not valid settings */
if ( is_wp_error( $settings ) ) {
-
$this->log->error(
- 'Invalid PDF Settings.',
+ 'Invalid PDF Settings',
[
'entry' => $entry,
'WP_Error_Message' => $settings->get_error_message(),
@@ -201,23 +200,33 @@ public function process_pdf( $pid, $lid, $action = 'view' ) {
return $settings; /* return error */
}
- /* Add our download setting */
+ /*
+ * Prior to 6.12 this action was saved to the PDF settings, passed to Helper_PDF, and used to
+ * stream the document to the client correctly. Since 6.12, we no longer need to pass this value
+ * to the underlying PDF generator. For backwards compatibility we've included this in case any
+ * user-land code makes use of it in their custom middleware.
+ */
$settings['pdf_action'] = $action;
- /**
- * Our middleware authenticator
- * Allow users to tap into our middleware and add or remove additional authentication layers
+ /*
+ * Authenticate the request to prevent unauthorized access to the PDF
*
- * Default middleware includes 'middle_public_access', 'middle_active', 'middle_conditional', 'middle_owner_restriction', 'middle_logged_out_timeout', 'middle_auth_logged_out_user', 'middle_user_capability'
- * If WP_Error is returned the PDF won't be parsed
+ * Default middleware filters include:
+ * - middle_public_access
+ * - middle_signed_url_access
+ * - middle_active
+ * - middle_conditional
+ * - middle_owner_restriction
+ * - middle_logged_out_timeout
+ * - middle_auth_logged_out_user
+ * - middle_user_capability
*
- * See https://docs.gravitypdf.com/v6/developers/filters/gfpdf_pdf_middleware/ for more details about this filter
+ * If any of the filters return a WP_Error object the request will not be fulfilled
+ *
+ * Refer to https://docs.gravitypdf.com/v6/developers/filters/gfpdf_pdf_middleware/
*/
$middleware = apply_filters( 'gfpdf_pdf_middleware', false, $entry, $settings );
-
- /* Throw error */
if ( is_wp_error( $middleware ) ) {
-
$this->log->error(
'PDF Authentication Failure.',
[
@@ -231,17 +240,101 @@ public function process_pdf( $pid, $lid, $action = 'view' ) {
return $middleware;
}
- /* Add backwards compatibility support for certain settings */
- $settings = $this->apply_backwards_compatibility_filters( $settings, $entry );
+ /*
+ * Normalize the PDF action
+ * The PDF cache introduced in 6.12 relies on a hash generated from the form, entry, and pdf settings
+ * To prevent cache misses we need to ensure we don't unnecessarily modify the settings array
+ */
+ unset( $settings['pdf_action'] );
+ $action = apply_filters( 'gfpdfe_pdf_output_type', $action ); /* Backwards compat */
+ $action = in_array( $action, [ 'view', 'download' ], true ) ? $action : 'view';
- /* Ensure Gravity Forms dependency loaded */
- $this->misc->maybe_load_gf_entry_detail_class();
+ /* Get the PDF document for the request */
+ $form = apply_filters( 'gfpdf_current_form_object', $this->gform->get_form( $entry['form_id'] ), $entry, __FUNCTION__ );
- /* If we are here we can generate our PDF */
- $controller = $this->getController();
- $controller->view->generate_pdf( $entry, $settings );
+ do_action( 'gfpdf_view_or_download_pdf', $form, $entry, $settings );
+
+ /*
+ * Support the print dialog option
+ * The PDF document embeds this preference directly in the source code so we need to
+ * force the cache to be bypassed.
+ */
+ if ( rgget( 'print' ) === '1' ) {
+ $settings['print'] = true;
+ }
+
+ $path_to_pdf = $this->generate_and_save_pdf( $entry, $settings );
+
+ /* Send error upstream for logging and output */
+ if ( is_wp_error( $path_to_pdf ) ) {
+ return $path_to_pdf;
+ }
- return null;
+ /* Verify the PDF can be sent to the client */
+ if ( headers_sent( $filename, $linenumber ) ) {
+ $this->log->error(
+ 'Server headers already sent',
+ [
+ 'filename' => $filename,
+ 'linenumber' => $linenumber,
+ ]
+ );
+
+ return new WP_Error( 'headers_sent', __( 'The PDF cannot be displayed because the server headers have already been sent.', 'gravity-forms-pdf-extended' ) );
+ }
+
+ /* Force any active buffers to close and delete its content */
+ while ( ob_get_level() > 0 ) {
+ ob_end_clean();
+ }
+
+ do_action( 'gfpdf_post_view_or_download_pdf', $path_to_pdf, $form, $entry, $settings, $action );
+
+ /* Send the PDF to the client */
+ header( 'Content-Type: application/pdf' );
+
+ /*
+ * Set the filename, supporting the new utf-8 syntax + backwards compatibility
+ * Refer to RFC 8187 https://www.rfc-editor.org/rfc/rfc8187.html
+ */
+ header(
+ sprintf(
+ 'Content-Disposition: %1$s; filename="%2$s"; filename*=utf-8\'\'%2$s',
+ $action === 'view' ? 'inline' : 'attachment',
+ rawurlencode( basename( $path_to_pdf ) ),
+ )
+ );
+
+ /* only add the length if the server is not using compression */
+ if ( empty( $_SERVER['HTTP_ACCEPT_ENCODING'] ) ) {
+ header( sprintf( 'Content-Length: %d', filesize( $path_to_pdf ) ) );
+ }
+
+ /* Tell client to download the file */
+ if ( $action === 'download' ) {
+ header( 'Content-Description: File Transfer' );
+ header( 'Content-Transfer-Encoding: binary' );
+ }
+
+ /* Set appropriate headers for local browser caching */
+ $last_modified_time = filemtime( $path_to_pdf );
+ $etag = md5( $path_to_pdf ); /* the file path includes a unique hash that automatically changes when a PDF does */
+
+ header( sprintf( 'Last-Modified: %s GMT', gmdate( 'D, d M Y H:i:s', $last_modified_time ) ) );
+ header( sprintf( 'Etag: %s', $etag ) );
+ header( 'Cache-Control: no-cache, private' );
+ header( 'Pragma: no-cache' );
+ header( 'Expires: 0' );
+
+ /* Tell client they can display the PDF from the local cache if it is still current */
+ if ( ! empty( $_SERVER['HTTP_IF_NONE_MATCH'] ) && $_SERVER['HTTP_IF_NONE_MATCH'] === $etag ) {
+ header( 'HTTP/1.1 304 Not Modified' );
+ exit;
+ }
+
+ readfile( $path_to_pdf ); /* phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_readfile */
+
+ exit;
}
/**
@@ -1143,139 +1236,136 @@ public function maybe_attach_to_notification( $notification, $settings, $entry =
/**
* Generate and save the PDF to disk
*
- * @param array $entry The Gravity Form entry array (usually passed in as a filter or pulled using GFAPI::get_entry( $id ) )
- * @param array $settings The PDF configuration settings for the particular entry / form being processed
+ * @param array $entry The Gravity Forms entry (from \GFAPI::get_entry)
+ * @param array $pdf_settings The Gravity PDF settings (from GPDFAPI::get_pdf())
*
- * @return string|WP_Error Return the full path to the PDF, or a WP_Error on failure
+ * @return string|WP_Error Return the full path to the PDF, or a WP_Error on failure
*
- * @throws Exception
* @since 4.0
+ * @since 6.12 The view/download endpoints route through this method
+ *
+ * @see \GPDFAPI::create_pdf() We recommend third-party developers use the API to generate PDFs
*/
- public function generate_and_save_pdf( $entry, $settings ) {
+ public function generate_and_save_pdf( $entry, $pdf_settings ) {
+
+ $form = apply_filters( 'gfpdf_current_form_object', $this->gform->get_form( $entry['form_id'] ), $entry, __FUNCTION__ );
+ $entry = apply_filters( 'gfpdf_current_entry_object', $entry, $form, $pdf_settings, __FUNCTION__ );
+ $pdf_settings = apply_filters( 'gfpdf_current_pdf_settings_object', $pdf_settings, $form, $entry, __FUNCTION__ );
+ $filename = $this->get_pdf_name( $pdf_settings, $entry );
- $pdf_generator = new Helper_PDF( $entry, $settings, $this->gform, $this->data, $this->misc, $this->templates, $this->log );
- $pdf_generator->set_filename( $this->get_pdf_name( $settings, $entry ) );
+ do_action( 'gfpdf_pre_generate_and_save_pdf', $form, $entry, $pdf_settings );
+
+ $pdf_generator = new Helper_PDF( $entry, $pdf_settings, $this->gform, $this->data, $this->misc, $this->templates, $this->log );
+ $pdf_generator->set_filename( $filename );
$pdf_generator = apply_filters( 'gfpdf_pdf_generator_pre_processing', $pdf_generator );
- if ( $this->process_and_save_pdf( $pdf_generator ) ) {
- $pdf_path = $pdf_generator->get_full_pdf_path();
- if ( is_file( $pdf_path ) ) {
- return $pdf_path;
- }
+ if ( ! $this->process_and_save_pdf( $pdf_generator ) ) {
+ return new WP_Error( 'pdf_generation_failure', esc_html__( 'There was a problem creating the PDF', 'gravity-forms-pdf-extended' ) );
}
- return new WP_Error( 'pdf_generation_failure', esc_html__( 'The PDF could not be saved.', 'gravity-forms-pdf-extended' ) );
+ do_action( 'gfpdf_post_generate_and_save_pdf', $form, $entry, $pdf_settings );
+ return $pdf_generator->get_full_pdf_path();
}
/**
* Generate and save PDF to disk
*
- * @param Helper_PDF $pdf The Helper_PDF object
+ * @param Helper_PDF $pdf_generator The Helper_PDF object
*
- * @return boolean
+ * @return bool
*
- * @throws Exception
* @since 4.0
*/
- public function process_and_save_pdf( Helper_PDF $pdf ) {
+ public function process_and_save_pdf( Helper_PDF $pdf_generator ) {
/**
* See https://docs.gravitypdf.com/v6/developers/filters/gfpdf_override_pdf_bypass/ for usage
*
* @since 4.2
*/
- $pdf_override = apply_filters( 'gfpdf_override_pdf_bypass', false, $pdf );
+ $pdf_override = apply_filters( 'gfpdf_override_pdf_bypass', false, $pdf_generator );
- /* Check that the PDF hasn't already been created this session */
- if ( $pdf_override || ! $this->does_pdf_exist( $pdf ) ) {
-
- /* Ensure Gravity Forms dependency loaded */
- $this->misc->maybe_load_gf_entry_detail_class();
+ /* If cached PDF already exists then return early */
+ if ( ! $pdf_override && $this->does_pdf_exist( $pdf_generator ) ) {
+ return true;
+ }
- /* Enable Multicurrency support */
- $this->misc->maybe_add_multicurrency_support();
+ /* Get required parameters */
+ $entry = $pdf_generator->get_entry();
+ $settings = $pdf_generator->get_settings();
+ $form = $pdf_generator->get_form();
- /* Get required parameters */
- $entry = $pdf->get_entry();
- $settings = $pdf->get_settings();
- $form = apply_filters( 'gfpdf_current_form_object', $this->gform->get_form( $entry['form_id'] ), $entry, __FUNCTION__ );
+ do_action( 'gfpdf_pre_pdf_generation', $form, $entry, $settings, $pdf_generator );
- do_action( 'gfpdf_pre_pdf_generation', $form, $entry, $settings, $pdf );
-
- /**
- * Load our arguments that should be accessed by our PDF template
- *
- * @var array
- */
- $args = $this->templates->get_template_arguments(
- $form,
- $this->misc->get_fields_sorted_by_id( $form['id'] ),
- $entry,
- $this->get_form_data( $entry ),
- $settings,
- $this->templates->get_config_class( $settings['template'] ),
- $this->misc->get_legacy_ids( $entry['id'], $settings )
- );
+ /*
+ * Load our arguments that should be accessed by our PDF template
+ */
+ $args = $this->templates->get_template_arguments(
+ $form,
+ $this->misc->get_fields_sorted_by_id( $form['id'] ),
+ $entry,
+ $this->get_form_data( $entry ),
+ $settings,
+ $this->templates->get_config_class( $settings['template'] ),
+ $this->misc->get_legacy_ids( $entry['id'], $settings )
+ );
- /* Add backwards compatibility support */
- $GLOBALS['wp']->query_vars['pid'] = $settings['id'];
- $GLOBALS['wp']->query_vars['lid'] = $entry['id'];
+ /* Add backwards compatibility support */
+ $GLOBALS['wp']->query_vars['pid'] = $settings['id'];
+ $GLOBALS['wp']->query_vars['lid'] = $entry['id'];
- try {
+ try {
- /* Initialise our PDF helper class */
- $pdf->init();
- $pdf->set_template();
- $pdf->set_output_type( 'save' );
+ /* Initialise our PDF helper class */
+ $pdf_generator->init();
+ $pdf_generator->set_template();
+ $pdf_generator->set_output_type( 'save' );
- /* Add Backwards compatibility support for our v3 Tier 2 Add-on */
- if ( isset( $settings['advanced_template'] ) && strtolower( $settings['advanced_template'] ) === 'yes' ) {
+ /* Add Backwards compatibility support for our v3 Tier 2 Add-on */
+ if ( isset( $settings['advanced_template'] ) && strtolower( $settings['advanced_template'] ) === 'yes' ) {
- /* Check if we should process this document using our legacy system */
- if ( $this->handle_legacy_tier_2_processing( $pdf, $entry, $settings, $args ) ) {
- return true;
- }
+ /* Check if we should process this document using our legacy system */
+ if ( $this->handle_legacy_tier_2_processing( $pdf_generator, $entry, $settings, $args ) ) {
+ return true;
}
+ }
- /* Render the PDF template HTML */
- $pdf->render_html( $args );
+ /* Render the PDF template HTML */
+ $pdf_generator->render_html( $args );
- /* Generate and save the PDF */
- $pdf->save_pdf( $pdf->generate() );
+ /* Generate and save the PDF */
+ $pdf_generator->save_pdf( $pdf_generator->generate() );
- do_action( 'gfpdf_post_pdf_generation', $form, $entry, $settings, $pdf );
+ do_action( 'gfpdf_post_pdf_generation', $form, $entry, $settings, $pdf_generator );
- return true;
- } catch ( Exception $e ) {
+ return true;
+ } catch ( Exception $e ) {
- $this->log->error(
- 'PDF Generation Error',
- [
- 'pdf' => $pdf,
- 'exception' => $e->getMessage(),
- ]
- );
+ $this->log->error(
+ 'PDF Generation Error',
+ [
+ 'pdf' => $pdf_generator,
+ 'exception' => $e->getMessage(),
+ ]
+ );
- return false;
- }
+ return false;
}
-
- return true;
}
/**
* Check if the current PDF to be processed already exists on disk
*
- * @param Helper_PDF $pdf The Helper_PDF Object
+ * @param Helper_PDF $pdf_generator The Helper_PDF Object
*
* @return boolean
*
* @since 4.0
*/
- public function does_pdf_exist( Helper_PDF $pdf ) {
+ public function does_pdf_exist( Helper_PDF $pdf_generator ) {
- if ( is_file( $pdf->get_full_pdf_path() ) ) {
+ if ( is_file( $pdf_generator->get_full_pdf_path() ) ) {
return true;
}
@@ -1408,16 +1498,16 @@ public function get_form_data( $entry ) {
/**
* Handles the loading and running of our legacy Tier 2 PDF templates
*
- * @param Helper_PDF $pdf The Helper_PDF object
- * @param array $entry The Gravity Forms raw entry data
- * @param array $settings The Gravity PDF settings
- * @param array $args The data that should be passed directly to a PDF template
+ * @param Helper_PDF $pdf_generator The Helper_PDF object
+ * @param array $entry The Gravity Forms raw entry data
+ * @param array $settings The Gravity PDF settings
+ * @param array $args The data that should be passed directly to a PDF template
*
* @return bool
*
* @since 4.0
*/
- public function handle_legacy_tier_2_processing( Helper_PDF $pdf, $entry, $settings, $args ) {
+ public function handle_legacy_tier_2_processing( Helper_PDF $pdf_generator, $entry, $settings, $args ) {
$form = apply_filters( 'gfpdf_current_form_object', $this->gform->get_form( $entry['form_id'] ), $entry, __FUNCTION__ );
@@ -1425,10 +1515,10 @@ public function handle_legacy_tier_2_processing( Helper_PDF $pdf, $entry, $setti
'gfpdfe_pre_load_template',
$form['id'],
$entry['id'],
- basename( $pdf->get_template_path() ),
+ basename( $pdf_generator->get_template_path() ),
$form['id'] . $entry['id'],
- $this->misc->backwards_compat_output( $pdf->get_output_type() ),
- $pdf->get_filename(),
+ $this->misc->backwards_compat_output( $pdf_generator->get_output_type() ),
+ $pdf_generator->get_filename(),
$this->misc->backwards_compat_conversion( $settings, $form, $entry ),
$args
); /* Backwards Compatibility */
@@ -1954,35 +2044,36 @@ public function trigger_post_save_pdf( $form, $entry, $settings, $pdf ) {
* @since 4.0
*/
public function cleanup_tmp_dir() {
- $max_file_age = time() - 12 * 3600; /* Max age is 12 hours old */
+ $max_file_age = time() - 3600; /* Max age is 1 hour old */
$tmp_directory = $this->data->template_tmp_location;
- if ( is_dir( $tmp_directory ) ) {
+ if ( ! is_dir( $tmp_directory ) ) {
+ return;
+ }
- try {
- $directory_list = new RecursiveIteratorIterator(
- new RecursiveDirectoryIterator( $tmp_directory, RecursiveDirectoryIterator::SKIP_DOTS ),
- RecursiveIteratorIterator::CHILD_FIRST
- );
+ try {
+ $directory_list = new RecursiveIteratorIterator(
+ new RecursiveDirectoryIterator( $tmp_directory, RecursiveDirectoryIterator::SKIP_DOTS ),
+ RecursiveIteratorIterator::CHILD_FIRST
+ );
- foreach ( $directory_list as $file ) {
- if ( in_array( $file->getFilename(), [ '.htaccess', 'index.html' ], true ) || strpos( realpath( $file->getPathname() ), realpath( $this->data->mpdf_tmp_location ) ) !== false ) {
- continue;
- }
+ foreach ( $directory_list as $file ) {
+ if ( in_array( $file->getFilename(), [ '.htaccess', 'index.html' ], true ) || strpos( realpath( $file->getPathname() ), realpath( $this->data->mpdf_tmp_location ) ) !== false ) {
+ continue;
+ }
- if ( $file->isReadable() && $file->getMTime() < $max_file_age ) {
- ( $file->isDir() ) ? $this->misc->rmdir( $file->getPathName() ) : unlink( $file->getPathName() );
- }
+ if ( $file->isReadable() && $file->getMTime() < $max_file_age ) {
+ ( $file->isDir() ) ? $this->misc->rmdir( $file->getPathName() ) : unlink( $file->getPathName() );
}
- } catch ( Exception $e ) {
- $this->log->error(
- 'Filesystem Delete Error',
- [
- 'dir' => $tmp_directory,
- 'exception' => $e->getMessage(),
- ]
- );
}
+ } catch ( Exception $e ) {
+ $this->log->error(
+ 'Filesystem Delete Error',
+ [
+ 'dir' => $tmp_directory,
+ 'exception' => $e->getMessage(),
+ ]
+ );
}
}
@@ -1991,8 +2082,12 @@ public function cleanup_tmp_dir() {
*
* @param array $form
* @param int $entry_id
+ *
+ * @deprecated 6.12 Caching layer + auto-purge added
*/
public function cleanup_pdf_after_submission( $form, $entry_id ) {
+ _doing_it_wrong( __METHOD__, 'This method is deprecated and no alternative is available. The temporary cache is automatically cleaned every hour using the WP Cron.', '6.12' );
+
/* Exit if background processing is enabled */
if ( $this->options->get_option( 'background_processing', 'No' ) === 'Yes' ) {
return;
@@ -2017,11 +2112,13 @@ public function cleanup_pdf_after_submission( $form, $entry_id ) {
*
* @return void
*
- * @internal In future we may give the option to cache PDFs to save on processing power
+ * @since 4.0
*
- * @since 4.0
+ * @deprecated 6.12 Caching layer + auto-purge added
*/
public function cleanup_pdf( $entry, $form ) {
+ _doing_it_wrong( __METHOD__, 'This method is deprecated and no alternative is available. The temporary cache is automatically cleaned every hour using the WP Cron.', '6.12' );
+
$pdfs = $this->get_active_pdfs( $form['gfpdf_form_settings'] ?? [], $entry );
if ( count( $pdfs ) === 0 ) {
@@ -2053,9 +2150,11 @@ public function cleanup_pdf( $entry, $form ) {
*
* @return array We tapped into a filter so we need to return the form object
* @since 4.0
- *
+ * @deprecated 6.12 Caching layer + auto-purge added
*/
public function resend_notification_pdf_cleanup( $form, $entries ) {
+ _doing_it_wrong( __METHOD__, 'This method is deprecated and no alternative is available. The temporary cache is automatically cleaned every hour using the WP Cron.', '6.12' );
+
foreach ( $entries as $entry_id ) {
$entry = $this->gform->get_entry( $entry_id );
$this->cleanup_pdf( $entry, $form );
@@ -2166,8 +2265,10 @@ function( $val ) use ( &$flattened_fonts_array ) {
* @return mixed
*
* @since 4.0
+ * @deprecated 4.0 Added for backwards compatibility, but ideally should not be used
*/
public function get_legacy_config( $config ) {
+ _doing_it_wrong( __METHOD__, 'Legacy PDF URLs are deprecated. Replace with the [gravitypdf] shortcode or PDF merge tags. See https://docs.gravitypdf.com/v6/users/shortcodes-and-mergetags for usage instructions.', '4.0' );
/* Get the form settings */
$pdfs = $this->options->get_form_pdfs( $config['fid'] );
@@ -2412,6 +2513,10 @@ public function set_watermark_font( $mpdf, $form, $entry, $settings ) {
* @since 5.3
*/
public function process_gp_populate_anything( $text, $form, $entry ) {
+ if ( ! class_exists( 'GP_Populate_Anything_Live_Merge_Tags' ) ) {
+ return $text;
+ }
+
$gp = GP_Populate_Anything_Live_Merge_Tags::get_instance();
$this->disable_gp_populate_anything();
diff --git a/src/Model/Model_Shortcodes.php b/src/Model/Model_Shortcodes.php
index af951cf8c..0c51f04da 100644
--- a/src/Model/Model_Shortcodes.php
+++ b/src/Model/Model_Shortcodes.php
@@ -44,10 +44,10 @@ class Model_Shortcodes extends Helper_Abstract_Pdf_Shortcode {
*
* @since 4.0
*
- * @internal Deprecated in 5.2. Use self::process()
+ * @internal Deprecated in 5.2. Use Model_Shortcodes::process()
*/
public function gravitypdf( $attributes ) {
- _doing_it_wrong( __METHOD__, esc_html__( 'This method has been superseded by self::process()', 'gravity-forms-pdf-extended' ), '5.2' );
+ _doing_it_wrong( __METHOD__, 'This method has been replaced by Model_Shortcodes::process()', '5.2' );
return $this->process( $attributes );
}
diff --git a/src/Statics/Cache.php b/src/Statics/Cache.php
new file mode 100644
index 000000000..8a4ebbdfa
--- /dev/null
+++ b/src/Statics/Cache.php
@@ -0,0 +1,182 @@
+template_tmp_location;
+ if ( is_multisite() ) {
+ $base_path .= get_current_blog_id();
+ }
+
+ static::$template_tmp_location = trailingslashit( $base_path ) . 'cache/';
+
+ /* Create directory on disk if it does not exist */
+ if ( ! is_dir( static::$template_tmp_location ) ) {
+ if ( wp_mkdir_p( static::$template_tmp_location ) ) {
+ file_put_contents( static::$template_tmp_location . 'index.html', '' );
+ }
+ }
+
+ return static::$template_tmp_location;
+ }
+
+ /**
+ * Calculate a unique hash based on the form/entry/pdf objects
+ *
+ * @param array $form The form object
+ * @param array $entry The entry object
+ * @param array $pdf_settings The PDF object/settings
+ *
+ * @return string
+ *
+ * @internal if $form, $entry, $pdf_settings, user ID, site ID, or template files are changed a new hash and PDF will be generated
+ *
+ * @since 6.12.0
+ */
+ public static function get_hash( $form, $entry, $pdf_settings ) {
+
+ /*
+ * Standardize field properties that may be added dynamically when fields are processed
+ * for the first run of a PDF. When Gravity Forms accesses properties that don't exist
+ * in a GF_Field object, it adds the value automatically and sets it to an empty string.
+ */
+ array_map(
+ function( $field ) {
+ /** @var \GF_Field $field */
+ /* Set when accessing \GFCommon::selection_display() */
+ if ( in_array( $field->get_input_type(), [ 'checkbox', 'radio', 'select' ], true ) ) {
+ $field->enablePrice;
+ }
+
+ /* Set when accessing \GF_Fields::get_allowable_tags() */
+ if ( $field->get_input_type() === 'section' ) {
+ $field->form_id;
+ }
+
+ /* Set the `use_admin_label` context for all fields */
+ $field->set_context_property( 'use_admin_label', $field->get_context_property( 'use_admin_label' ) );
+ },
+ $form['fields']
+ );
+
+ /*
+ * Ignore specific entry meta that is considered unimportant to PDFs
+ */
+ $ignored_entry_meta = apply_filters( 'gfpdf_cache_hash_ignored_entry_meta', [ 'is_read', 'is_starred', 'is_approved', 'status', 'source_url', 'user_agent' ], $form, $entry, $pdf_settings );
+ foreach ( $ignored_entry_meta as $meta ) {
+ unset( $entry[ $meta ] );
+ }
+
+ /* Add last modified date of template files to hash */
+ $template = \GPDFAPI::get_templates_class();
+ $template_id = $pdf_settings['template'] ?? '';
+
+ try {
+ $template_path = $template->get_template_path_by_id( $template_id );
+ $template_timestamps = filemtime( $template_path );
+ } catch ( \Exception $e ) {
+ $template_timestamps = 0;
+ }
+
+ /* Include config template timestamp if it exists */
+ try {
+ $template_config_path = $template->get_config_path_by_id( $template_id );
+ $template_timestamps .= filemtime( $template_config_path );
+ } catch ( \Exception $e ) {
+ /* do nothing */
+ }
+
+ /* Build an array of unique data relevant to the current PDF */
+ $unique_array = apply_filters(
+ 'gfpdf_cache_hash_array',
+ [
+ 'site_id' => get_current_blog_id(),
+ 'user_id' => get_current_user_id(),
+ 'fields' => $form['fields'],
+ 'entry' => $entry,
+ 'pdf_settings' => $pdf_settings,
+ 'template_last_updated' => $template_timestamps,
+ ],
+ $form,
+ $entry,
+ $pdf_settings
+ );
+
+ /* Generate the hash based on that unique data */
+ $hash_prefix = static::get_hash_prefix( $form, $entry, $pdf_settings );
+ $hash = wp_hash( wp_json_encode( $unique_array ) );
+
+ return sprintf( '%s-%s', $hash_prefix, $hash );
+ }
+
+ /**
+ * Gets the easily-identifiable prefix to add before the hash
+ *
+ * @param array $form The form object
+ * @param array $entry The entry object
+ * @param array $pdf_settings The PDF object/settings
+ *
+ * @return string
+ *
+ * @since 6.12
+ */
+ protected static function get_hash_prefix( $form, $entry, $pdf_settings ) {
+ return sprintf(
+ 's%1$d-f%2$d-e%3$d-p%4$s',
+ get_current_blog_id(),
+ $form['id'] ?? 0,
+ $entry['id'] ?? 0,
+ $pdf_settings['id'] ?? '',
+ );
+ }
+}
diff --git a/src/Statics/Debug.php b/src/Statics/Debug.php
new file mode 100644
index 000000000..861c8a7e4
--- /dev/null
+++ b/src/Statics/Debug.php
@@ -0,0 +1,64 @@
+get_option( 'debug_mode', 'No' ) === 'Yes';
+ $wp_production_environment = ! function_exists( 'wp_get_environment_type' ) || wp_get_environment_type() === 'production';
+
+ return $pdf_debug_mode || ! $wp_production_environment;
+ }
+
+ /**
+ * Check the logged-in user has a specific capability which can view the log info
+ *
+ * @return bool
+ *
+ * @since 6.12
+ */
+ public static function can_view(): bool {
+ $gform = \GPDFAPI::get_form_class();
+
+ return $gform->has_capability( 'gravityforms_logging' );
+
+ }
+
+ /**
+ * Verify debug mode is enabled and the user can view the log info
+ *
+ * @return bool
+ *
+ * @since 6.12
+ */
+ public static function is_enabled_and_can_view(): bool {
+ return static::is_enabled() && static::can_view();
+ }
+}
diff --git a/src/Statics/Queue_Callbacks.php b/src/Statics/Queue_Callbacks.php
index eb89b1b45..b2a4c7a30 100644
--- a/src/Statics/Queue_Callbacks.php
+++ b/src/Statics/Queue_Callbacks.php
@@ -31,20 +31,26 @@ class Queue_Callbacks {
/**
* Generate and save a PDF to disk
*
- * @param $entry_id
- * @param $pdf_id
+ * @param int $entry_id Entry ID to process
+ * @param string $pdf_id PDF ID to process
+ * @param int $user_id User ID who triggered the queue
*
* @throws Exception
*
* @since 5.0
*/
- public static function create_pdf( $entry_id, $pdf_id ) {
+ public static function create_pdf( $entry_id, $pdf_id, $user_id = 0 ) {
$log = GPDFAPI::get_log_class();
+ /* Masquerade as the user ID who scheduled the queue so caching and the {user} merge tag works correctly */
+ $backup_user_id = get_current_user_id();
+ wp_set_current_user( $user_id );
+
/* For performance, only generate the PDF if it does not currently exist on disk */
- add_filter( 'gfpdf_override_pdf_bypass', '__return_false', 20 );
$pdf = GPDFAPI::create_pdf( $entry_id, $pdf_id );
- remove_filter( 'gfpdf_override_pdf_bypass', '__return_false', 20 );
+
+ /* Reset existing user */
+ wp_set_current_user( $backup_user_id );
if ( is_wp_error( $pdf ) ) {
$log->error(
@@ -64,14 +70,15 @@ public static function create_pdf( $entry_id, $pdf_id ) {
/**
* Send a Gravity Forms notification
*
- * @param int $form_id
- * @param int $entry_id
- * @param array $notification
+ * @param int $entry_id Entry ID to process
+ * @param string $pdf_id PDF ID to process
+ * @param array $notification Gravity Forms Notification to send
+ * @param int $user_id User ID who triggered the queue
*
* @throws Exception
* @since 5.0
*/
- public static function send_notification( $form_id, $entry_id, $notification ) {
+ public static function send_notification( $form_id, $entry_id, $notification, $user_id = 0 ) {
$log = GPDFAPI::get_log_class();
$gform = GPDFAPI::get_form_class();
@@ -96,7 +103,14 @@ public static function send_notification( $form_id, $entry_id, $notification ) {
throw new Exception();
}
+ /* Masquerade as the user ID who scheduled the queue so caching and the {user} merge tag works correctly */
+ $backup_user_id = get_current_user_id();
+ wp_set_current_user( $user_id );
+
GFCommon::send_notification( $notification, $form, $entry );
+
+ /* Reset existing user */
+ wp_set_current_user( $backup_user_id );
}
/**
@@ -108,13 +122,13 @@ public static function send_notification( $form_id, $entry_id, $notification ) {
* @throws Exception
*
* @since 5.0
+ * @deprecated 6.12 Caching layer + auto-purge added
*/
public static function cleanup_pdfs( $form_id, $entry_id ) {
- $gform = GPDFAPI::get_form_class();
- $data = GPDFAPI::get_data_class();
- $misc = GPDFAPI::get_misc_class();
- $templates = GPDFAPI::get_templates_class();
- $log = GPDFAPI::get_log_class();
+ _doing_it_wrong( __METHOD__, 'This method is deprecated and no alternative is available. The temporary cache is automatically cleaned every hour using the WP Cron.', '6.12' );
+
+ $gform = GPDFAPI::get_form_class();
+ $log = GPDFAPI::get_log_class();
/** @var Model_PDF $model_pdf */
$model_pdf = GPDFAPI::get_mvc_class( 'Model_PDF' );
diff --git a/src/View/View_PDF.php b/src/View/View_PDF.php
index c3f20c963..e70d83700 100644
--- a/src/View/View_PDF.php
+++ b/src/View/View_PDF.php
@@ -5,8 +5,8 @@
use Exception;
use GF_Field;
use GFCommon;
+use GFPDF\Controller\Controller_PDF;
use GFPDF\Helper\Fields\Field_Products;
-use GFPDF\Helper\Helper_Abstract_Fields;
use GFPDF\Helper\Helper_Abstract_Form;
use GFPDF\Helper\Helper_Abstract_Model;
use GFPDF\Helper\Helper_Abstract_Options;
@@ -19,9 +19,9 @@
use GFPDF\Helper\Helper_Misc;
use GFPDF\Helper\Helper_PDF;
use GFPDF\Helper\Helper_Templates;
+use GFPDF\Statics\Debug;
use GFPDF\Statics\Kses;
use GFPDFEntryDetail;
-use GWConditionalLogicDateFields;
use Psr\Log\LoggerInterface;
use WP_Error;
@@ -42,6 +42,8 @@
* A general class for PDF display
*
* @since 4.0
+ *
+ * @method Controller_PDF getController
*/
class View_PDF extends Helper_Abstract_View {
@@ -139,31 +141,32 @@ public function __construct( array $data_cache, Helper_Abstract_Form $gform, Log
}
/**
- * Our PDF Generator
+ * Legacy view/download PDF generator
*
* @param array $entry The Gravity Forms Entry to process
* @param array $settings The Gravity Form PDF Settings
*
* @return void
- *
- * @since 4.0
+ * @since 4.0
+ * @deprecated 6.12.0 Use \GPDFAPI::create_pdf() to generate PDFs
*/
public function generate_pdf( $entry, $settings ) {
+ _doing_it_wrong( __METHOD__, 'Use \GPDFAPI::create_pdf() to generate PDFs', '6.12' );
$controller = $this->getController();
$model = $controller->model;
$form = apply_filters( 'gfpdf_current_form_object', $this->gform->get_form( $entry['form_id'] ), $entry, __FUNCTION__ );
- $form = $this->add_gravity_perk_conditional_logic_date_support( $form );
- /**
- * Set out our PDF abstraction class
- */
- $pdf = new Helper_PDF( $entry, $settings, $this->gform, $this->data, $this->misc, $this->templates, $this->log );
- $pdf->set_filename( $model->get_pdf_name( $settings, $entry ) );
+ do_action( 'gfpdf_view_or_download_pdf', $form, $entry, $settings );
+
+ $settings['pdf_action'] = apply_filters( 'gfpdfe_pdf_output_type', $settings['pdf_action'] ?? 'download' ); /* Backwards compat */
- $this->fix_wp_external_links_plugin_conflict();
+ /* Setup the PDF that will be generated */
+ $pdf_generator = new Helper_PDF( $entry, $settings, $this->gform, $this->data, $this->misc, $this->templates, $this->log );
+ $pdf_generator->set_filename( $model->get_pdf_name( $settings, $entry ) );
+ $pdf_generator = apply_filters( 'gfpdf_pdf_generator_pre_processing', $pdf_generator );
- do_action( 'gfpdf_pre_pdf_generation', $form, $entry, $settings, $pdf );
+ do_action( 'gfpdf_pre_pdf_generation', $form, $entry, $settings, $pdf_generator );
/**
* Load our arguments that should be accessed by our PDF template
@@ -181,43 +184,40 @@ public function generate_pdf( $entry, $settings ) {
);
/* Show $form_data array if requested */
- $this->maybe_view_form_data( $args['form_data'] ?? [] );
-
- /* Enable Multicurrency support */
- $this->misc->maybe_add_multicurrency_support();
+ if ( $this->maybe_view_form_data() ) {
+ $this->view_form_data( $args['form_data'] ?? [] );
+ }
try {
/* Initialise our PDF helper class */
- $pdf->init();
- $pdf->set_template();
+ $pdf_generator->init();
+ $pdf_generator->set_template();
/* Set display type and allow user to override the behaviour */
- $settings['pdf_action'] = apply_filters( 'gfpdfe_pdf_output_type', $settings['pdf_action'] ); /* Backwards compat */
if ( $settings['pdf_action'] === 'download' ) {
- $pdf->set_output_type( 'download' );
+ $pdf_generator->set_output_type( 'download' );
}
/* Add Backwards compatibility support for our v3 Tier 2 Add-on */
if ( isset( $settings['advanced_template'] ) && strtolower( $settings['advanced_template'] ) === 'yes' ) {
/* Check if we should process this document using our legacy system */
- if ( $model->handle_legacy_tier_2_processing( $pdf, $entry, $settings, $args ) ) {
+ if ( $model->handle_legacy_tier_2_processing( $pdf_generator, $entry, $settings, $args ) ) {
return;
}
}
/* Determine if we should show the print dialog box */
- /* phpcs:ignore WordPress.Security.NonceVerification.Recommended */
- if ( isset( $_GET['print'] ) ) {
- $pdf->set_print_dialog();
+ if ( rgget( 'print' ) ) {
+ $pdf_generator->set_print_dialog();
}
/* Render the PDF template HTML */
- $pdf->render_html( $args );
+ $pdf_generator->render_html( $args );
/* Generate PDF */
- $pdf->generate();
+ $pdf_generator->generate();
} catch ( Exception $e ) {
@@ -245,43 +245,6 @@ public function generate_pdf( $entry, $settings ) {
}
}
- /**
- * The WP External Links plugin conflicts with Gravity PDF when trying to display the PDF
- * This method disables the \WPEL_Front::scan() method which was causes the problem
- *
- * See https://github.com/GravityPDF/gravity-pdf/issues/386
- *
- * @since 4.1
- */
- private function fix_wp_external_links_plugin_conflict() {
- if ( function_exists( 'wpel_init' ) ) {
- add_filter(
- 'wpel_apply_settings',
- function() {
- return false;
- }
- );
- }
- }
-
- /**
- * Add Gravity Perk Conditional Logic Date Field support, if required
- *
- * @Internal Fixed an intermittent issue with the Product table not functioning correctly
- *
- * @param array $form
- *
- * @return array
- * @since 4.5
- */
- private function add_gravity_perk_conditional_logic_date_support( $form ) {
- if ( method_exists( 'GWConditionalLogicDateFields', 'convert_conditional_logic_date_field_values' ) ) {
- $form = GWConditionalLogicDateFields::convert_conditional_logic_date_field_values( $form );
- }
-
- return $form;
- }
-
/**
* Ensure a PHP extension is added to the end of the template name
*
@@ -290,13 +253,12 @@ private function add_gravity_perk_conditional_logic_date_support( $form ) {
* @return string
*
* @since 4.0
+ * @deprecated 4.1
*/
public function get_template_filename( $name ) {
- if ( substr( $name, -4 ) !== '.php' ) {
- $name = $name . '.php';
- }
+ _doing_it_wrong( __METHOD__, 'This method has been replaced by Helper_Misc::get_file_with_extension().', '4.1' );
- return $name;
+ return $this->misc->get_file_with_extension( $name, '.php' );
}
/**
@@ -661,33 +623,35 @@ public function allow_pdf_css( $styles ) {
}
/**
+ * Check if the form data should be viewed during this request
+ *
* @param array $form_data
*
- * @return void
+ * @return bool
*
* @since 6.4.0
*/
- public function maybe_view_form_data( $form_data ) {
-
- /* phpcs:ignore WordPress.Security.NonceVerification.Recommended */
- if ( ! isset( $_GET['data'] ) ) {
- return;
- }
-
- /* Disable if PDF Debug Mode off AND the environment is production */
- if ( $this->options->get_option( 'debug_mode', 'No' ) === 'No' && ( ! function_exists( 'wp_get_environment_type' ) || wp_get_environment_type() === 'production' ) ) {
- return;
- }
-
- /* Check if user has permission to view info */
- if ( ! $this->gform->has_capability( 'gravityforms_view_settings' ) ) {
- return;
- }
+ public function maybe_view_form_data( $form_data = [] ) {
+ return rgget( 'data' ) && Debug::is_enabled_and_can_view();
+ }
- print ''; + /** + * A debugging tool that will display the processed Gravity Forms entry array (aka $form_data) in the browser + * + * Use ?data=1 to active when the website is in development/staging mode and the current logged-in + * user has appropriate capabilities + * + * @param array $form_data + * + * @since 6.11.0 + * + * @link https://docs.gravitypdf.com/v6/developers/helper-parameters#data1 + */ + public function view_form_data( $form_data ) { + echo ''; exit; } diff --git a/src/deprecated.php b/src/deprecated.php index 8e709aabf..df07f3494 100644 --- a/src/deprecated.php +++ b/src/deprecated.php @@ -680,7 +680,7 @@ class GFPDF_Core_Model extends GFPDF_Deprecated_Abstract { * @since 3.0 */ public static function gfpdfe_save_pdf( $entry, $form ) { - $pdfs = GPDFAPI::get_form_pdfs( $form['id'] ); + $pdfs = GPDFAPI::get_entry_pdfs( $entry['id'] ); if ( ! is_wp_error( $pdfs ) ) { foreach ( $pdfs as $pdf ) { diff --git a/tests/e2e/advanced-checks/confirmation-shortcode.test.js b/tests/e2e/advanced-checks/confirmation-shortcode.test.js index df73cdc7e..63bb3459c 100644 --- a/tests/e2e/advanced-checks/confirmation-shortcode.test.js +++ b/tests/e2e/advanced-checks/confirmation-shortcode.test.js @@ -41,7 +41,7 @@ test('should check shortcode confirmation type TEXT is working correctly', async // Assertions await t - .expect(downloadLogger.contains(r => r.response.headers['content-disposition'] === 'attachment; filename="Sample.pdf"')).ok() + .expect(downloadLogger.contains(r => r.response.headers['content-disposition'] === 'attachment; filename="Sample.pdf"; filename*=utf-8\'\'Sample.pdf')).ok() .expect(downloadLogger.contains(r => r.response.headers['content-type'] === 'application/pdf')).ok() }) @@ -96,7 +96,7 @@ test('should check if the shortcode confirmation type PAGE is working correctly' // Assertions await t - .expect(downloadLogger.contains(r => r.response.headers['content-disposition'] === 'attachment; filename="Sample.pdf"')).ok() + .expect(downloadLogger.contains(r => r.response.headers['content-disposition'] === 'attachment; filename="Sample.pdf"; filename*=utf-8\'\'Sample.pdf')).ok() .expect(downloadLogger.contains(r => r.response.headers['content-type'] === 'application/pdf')).ok() }) @@ -125,7 +125,7 @@ test('should check if the shortcode confirmation type REDIRECT download is worki // Assertions await t - .expect(downloadLogger.contains(r => r.response.headers['content-disposition'] === 'attachment; filename="Sample.pdf"')).ok() + .expect(downloadLogger.contains(r => r.response.headers['content-disposition'] === 'attachment; filename="Sample.pdf"; filename*=utf-8\'\'Sample.pdf')).ok() .expect(downloadLogger.contains(r => r.response.headers['content-type'] === 'application/pdf')).ok() }) diff --git a/tests/e2e/utilities/page-model/helpers/page.js b/tests/e2e/utilities/page-model/helpers/page.js index 2f30e40d9..f4dfc8c1c 100644 --- a/tests/e2e/utilities/page-model/helpers/page.js +++ b/tests/e2e/utilities/page-model/helpers/page.js @@ -36,7 +36,7 @@ class Page { .click(this.addBlockIcon) .typeText(this.searchBlock, 'paragraph', { paste: true }) .click(this.paragraphButton) - .typeText(Selector('p.is-selected'), 'Content', { paste: true }) + .typeText(Selector('.is-root-container p:last-of-type'), 'Content', { paste: true }) .click(this.publishButton) .click(this.confirmPublishButton) } diff --git a/tests/phpunit/bootstrap.php b/tests/phpunit/bootstrap.php index 6216c343e..88e4ccc27 100644 --- a/tests/phpunit/bootstrap.php +++ b/tests/phpunit/bootstrap.php @@ -7,6 +7,9 @@ xdebug_disable(); } +/* Use different DB tables for unit tests, so we don't mess with the E2E environment */ +putenv( 'WORDPRESS_TABLE_PREFIX=phpunit_' ); + /** * Override certain pluggable functions so we can unit test them correctly * diff --git a/tests/phpunit/unit-tests/Controller/Test_Controller_Pdf_Queue.php b/tests/phpunit/unit-tests/Controller/Test_Controller_Pdf_Queue.php index 103921f40..399bdf8d3 100644 --- a/tests/phpunit/unit-tests/Controller/Test_Controller_Pdf_Queue.php +++ b/tests/phpunit/unit-tests/Controller/Test_Controller_Pdf_Queue.php @@ -5,6 +5,7 @@ use Exception; use GFPDF\Controller\Controller_Pdf_Queue; use GFPDF\Helper\Helper_Pdf_Queue; +use GFPDF\Statics\Cache; use GFPDF\Statics\Queue_Callbacks; use WP_UnitTestCase; @@ -278,28 +279,26 @@ public function test_queue_async_form_submission_tasks() { $results = $this->create_form_and_entries(); $entry = $results['entry']; $form = $results['form']; - $form['notifications']['1254123223'] = $form['notifications']['54bca349732b8']; + + /* Set up active and inactive notification */ + $form['notifications']['1254123223'] = [ 'id' => '1254123223', 'isActive' => true, 'event' => 'form_submission' ]; + $form['notifications']['2222222222'] = [ 'id' => '2222222222', 'isActive' => false, 'event' => 'form_submission' ]; + $form['gfpdf_form_settings']['556690c67856b']['notification'][] = '1254123223'; + $form['notifications']['54bca349732b8']['isActive'] = true; - foreach( $form['notifications'] as $notification ) { + foreach ( $form['notifications'] as $notification ) { $this->controller->maybe_disable_submission_notifications( false, $notification, $form, $entry ); } - + $this->controller->queue_async_form_submission_tasks( $entry, $form ); $queue = $this->queue_mock->get_data(); - $this->assertCount( 3, $queue[0] ); - $this->assertCount( 3, $queue[1] ); - $this->assertCount( 1, $queue[2] ); - - $actions = [ 'create_pdf', 'create_pdf', 'send_notification' ]; - for ( $i = 0; $i < 3; $i++ ) { - $this->assertStringContainsString( $actions[ $i ], $queue[0][ $i ]['func'] ); - $this->assertStringContainsString( $actions[ $i ], $queue[1][ $i ]['func'] ); - } - - $this->assertStringContainsString( 'cleanup_pdfs', $queue[2][0]['func'] ); + $this->assertStringContainsString( 'create_pdf', $queue[0][0]['func'] ); + $this->assertStringContainsString( 'create_pdf', $queue[0][1]['func'] ); + $this->assertStringContainsString( 'send_notification', $queue[0][2]['func'] ); + $this->assertStringContainsString( 'send_notification', $queue[0][3]['func'] ); } /** @@ -322,14 +321,11 @@ public function test_queue_async_resend_notification_tasks() { $queue = $this->queue_mock->get_data(); $this->assertCount( 3, $queue[0] ); - $this->assertCount( 1, $queue[1] ); $actions = [ 'create_pdf', 'create_pdf', 'send_notification' ]; for ( $i = 0; $i < 3; $i++ ) { $this->assertStringContainsString( $actions[ $i ], $queue[0][ $i ]['func'] ); } - - $this->assertStringContainsString( 'cleanup_pdfs', $queue[1][0]['func'] ); } /** @@ -343,12 +339,13 @@ public function test_queue_dispatch_resend_notification_tasks() { ->method( 'dispatch' ) ->willReturn( $this->queue_mock ); - $this->controller->queue_dispatch_resend_notification_tasks( [ 'id' => 0 ], [ 'id' => 0 ] ); + + $this->controller->queue_dispatch_resend_notification_tasks( [ 'id' => 0 ], [ 'id' => 0, 'form_id' => 0 ] ); $this->assertSame( 0, $spy->getInvocationCount() ); $this->queue_mock->push_to_queue( 'item' ); - $this->controller->queue_dispatch_resend_notification_tasks( [ 'id' => 0 ], [ 'id' => 0 ]); + $this->controller->queue_dispatch_resend_notification_tasks( [ 'id' => 0 ], [ 'id' => 0, 'form_id' => 0 ]); $this->assertSame( 1, $spy->getInvocationCount() ); } @@ -359,21 +356,26 @@ public function test_queue_dispatch_resend_notification_tasks() { * @since 5.0 */ public function test_cleanup_pdfs() { - global $gfpdf; + $this->setExpectedIncorrectUsage( 'GFPDF\Statics\Queue_Callbacks::cleanup_pdfs'); + $this->setExpectedIncorrectUsage( 'GFPDF\Model\Model_PDF::cleanup_pdf'); + + $form_class = \GPDFAPI::get_form_class(); $results = $this->create_form_and_entries(); $entry = $results['entry']; - $form = $results['form']; + $form = $form_class->get_form( $results['form']['id'] ); /* get from the database so the date created is accurate */ + + $path = Cache::get_path( $form, $entry, $form['gfpdf_form_settings']['556690c67856b'] ); + $file = "test-{$form['id']}.pdf"; - $path = $gfpdf->data->template_tmp_location . $entry['form_id'] . $entry['id'] . '556690c67856b/'; wp_mkdir_p( $path ); - $test_file = $path . 'file'; - touch( $test_file ); - $this->assertFileExists( $test_file ); + touch( $path . $file ); + + $this->assertFileExists( $path . $file ); Queue_Callbacks::cleanup_pdfs( $form['id'], $entry['id'] ); - $this->assertFileDoesNotExist( $test_file ); + $this->assertFileDoesNotExist( $path . $file ); $this->assertFileDoesNotExist( $path ); } } diff --git a/tests/phpunit/unit-tests/Statics/Test_Cache.php b/tests/phpunit/unit-tests/Statics/Test_Cache.php new file mode 100644 index 000000000..a1029b360 --- /dev/null +++ b/tests/phpunit/unit-tests/Statics/Test_Cache.php @@ -0,0 +1,74 @@ +create_form_and_entries(); + + $form = $results['form']; + $entry = $results['entry']; + + $pdf_settings = $form['gfpdf_form_settings']['555ad84787d7e']; + $pdf_settings['template'] = 'zadani'; + + /* Verify the hash is the same when called multiple times with the same inputs */ + $hash1 = Cache::get_hash( $form, $entry, $pdf_settings ); + $hash2 = Cache::get_hash( $form, $entry, $pdf_settings ); + + $this->assertEquals( $hash1, $hash2 ); + + /* Verify the hash changes when the input changes */ + $pdf_settings['active'] = false; + + $hash3 = Cache::get_hash( $form, $entry, $pdf_settings ); + + $this->assertNotEquals( $hash3, $hash2 ); + } + + protected function create_form_and_entries() { + global $gfpdf; + + $form = $GLOBALS['GFPDF_Test']->form['all-form-fields']; + $entry = $GLOBALS['GFPDF_Test']->entries['all-form-fields'][0]; + + $gfpdf->data->form_settings = []; + $gfpdf->data->form_settings[ $form['id'] ] = $form['gfpdf_form_settings']; + + return [ + 'form' => $form, + 'entry' => $entry, + ]; + } + + public function test_get_path() { + $results = $this->create_form_and_entries(); + + $form = $results['form']; + $entry = $results['entry']; + + $pdf_settings = $form['gfpdf_form_settings']['555ad84787d7e']; + $pdf_settings['template'] = 'zadani'; + + $hash1 = Cache::get_hash( $form, $entry, $pdf_settings ); + $path = Cache::get_path( $form, $entry, $pdf_settings ); + + $this->assertStringEndsWith( '/' . $hash1 . '/', $path ); + $this->assertStringStartsWith( ABSPATH, $path ); + } + +} diff --git a/tests/phpunit/unit-tests/test-pdf.php b/tests/phpunit/unit-tests/test-pdf.php index 0b1c3c0e2..59f1b6d71 100644 --- a/tests/phpunit/unit-tests/test-pdf.php +++ b/tests/phpunit/unit-tests/test-pdf.php @@ -13,6 +13,7 @@ use GFPDF\Helper\Helper_Url_Signer; use GFPDF\Model\Model_PDF; use GFPDF\Plugins\DeveloperToolkit\Loader\Helper; +use GFPDF\Statics\Cache; use GFPDF\View\View_PDF; use GPDFAPI; use ReflectionMethod; @@ -121,17 +122,6 @@ public function test_actions() { ) ); $this->assertSame( 10, has_action( 'gform_after_submission', [ $this->model, 'maybe_save_pdf' ] ) ); - $this->assertSame( 9999, has_action( 'gform_after_submission', [ $this->model, 'cleanup_pdf' ] ) ); - $this->assertSame( - 9999, - has_action( - 'gform_after_update_entry', - [ - $this->model, - 'cleanup_pdf_after_submission', - ] - ) - ); $this->assertSame( 10, has_action( 'gfpdf_cleanup_tmp_dir', [ $this->model, 'cleanup_tmp_dir' ] ) ); } @@ -183,16 +173,6 @@ public function test_filters() { /* Backwards compatibility */ $this->assertSame( 1, has_filter( 'gfpdfe_pre_load_template', [ 'PDFRender', 'prepare_ids' ] ) ); - $this->assertSame( - 10, - has_filter( - 'gform_before_resend_notifications', - [ - $this->model, - 'resend_notification_pdf_cleanup', - ] - ) - ); } /** @@ -222,7 +202,7 @@ public function test_process_pdf_endpoint() { try { $this->controller->process_pdf_endpoint(); } catch ( Exception $e ) { - $this->assertEquals( 'There was a problem generating your PDF', $e->getMessage() ); + $this->assertEquals( 'There was a problem creating the PDF', $e->getMessage() ); return; } @@ -236,6 +216,8 @@ public function test_process_pdf_endpoint() { * @since 4.0 */ public function test_process_legacy_pdf_endpoint() { + $this->setExpectedIncorrectUsage( 'GFPDF\Controller\Controller_PDF::process_legacy_pdf_endpoint'); + $this->setExpectedIncorrectUsage( 'GFPDF\Model\Model_PDF::get_legacy_config'); /* Force a failure */ $this->assertNull( $this->controller->process_legacy_pdf_endpoint() ); @@ -249,7 +231,7 @@ public function test_process_legacy_pdf_endpoint() { try { $results = $this->controller->process_legacy_pdf_endpoint(); } catch ( Exception $e ) { - $this->assertEquals( 'There was a problem generating your PDF', $e->getMessage() ); + $this->assertEquals( 'There was a problem creating the PDF', $e->getMessage() ); return; } @@ -300,7 +282,7 @@ public function test_pdf_error() { /* Do nothing here */ } - $this->assertEquals( 'There was a problem generating your PDF', $e->getMessage() ); + $this->assertEquals( 'There was a problem creating the PDF', $e->getMessage() ); /* Authorise the current user and check the message is displayed correctly */ $user_id = $this->factory->user->create( [ 'role' => 'administrator' ] ); @@ -1051,17 +1033,15 @@ public function provider_get_active_pdfs() { * @since 4.0 */ public function test_notifications() { - global $gfpdf; + $form_class = \GPDFAPI::get_form_class(); /* Setup some test data */ $results = $this->create_form_and_entries(); $entry = $results['entry']; - $form = $results['form']; - $form['gfpdf_form_settings'] = [ $form['gfpdf_form_settings']['556690c67856b'] ]; + $form = $form_class->get_form( $results['form']['id'] ); /* get from the database so the date created is accurate */ /* Create PDF file so it isn't recreated */ - $folder = $form['id'] . $entry['id'] . '556690c67856b'; - $path = $gfpdf->data->template_tmp_location . "$folder/"; + $path = Cache::get_path( $form, $entry, $form['gfpdf_form_settings']['556690c67856b'] ); $file = "test-{$form['id']}.pdf"; wp_mkdir_p( $path ); @@ -1070,7 +1050,7 @@ public function test_notifications() { $notifications = $this->model->notifications( $form['notifications']['54bca349732b8'], $form, $entry ); /* Check the results are successful */ - $this->assertStringContainsString( "PDF_EXTENDED_TEMPLATES/tmp/$folder/$file", $notifications['attachments'][0] ); + $this->assertEquals( $path . $file, $notifications['attachments'][0] ); /* Clean up */ unlink( $notifications['attachments'][0] ); @@ -1266,12 +1246,12 @@ public function test_cleanup_tmp_dir() { /* Create our files to test */ $files = [ 'test' => time(), - 'test1' => time() - ( 11.5 * 3600 ), - 'test2' => time() - ( 12.01 * 3600 ), - 'test3' => time() - ( 12.5 * 3600 ), + 'test1' => time() - ( 1 * 3600 ), + 'test2' => time() - ( 1.01 * 3600 ), + 'test3' => time() - ( 1.1 * 3600 ), 'test4' => time() - ( 25 * 3600 ), 'test5' => time() - ( 15 * 3600 ), - 'test6' => time() - ( 5 * 3600 ), + 'test6' => time() - ( 0.25 * 3600 ), '.htaccess' => time() - ( 48 * 3600 ), 'mpdf/test' => time() - ( 25 * 3600 ), /* normally deleted, but excluded */ ]; @@ -1280,7 +1260,7 @@ public function test_cleanup_tmp_dir() { touch( $tmp . $file, $modified ); } - /* Run our cleanup function and test the out put */ + /* Run our cleanup function and test the output */ $this->model->cleanup_tmp_dir(); $this->assertFileExists( $tmp . 'test' ); @@ -1305,22 +1285,26 @@ public function test_cleanup_tmp_dir() { * @since 4.0 */ public function test_cleanup_pdf() { - global $gfpdf; + $this->setExpectedIncorrectUsage('GFPDF\Model\Model_PDF::cleanup_pdf'); + + $form_class = \GPDFAPI::get_form_class(); /* Setup some test data */ $results = $this->create_form_and_entries(); $entry = $results['entry']; - $form = $results['form']; - $file = $gfpdf->data->template_tmp_location . "{$form['id']}{$entry['id']}556690c67856b/test-{$form['id']}.pdf"; + $form = $form_class->get_form( $results['form']['id'] ); /* get from the database so the date created is accurate */ + + $path = Cache::get_path( $form, $entry, $form['gfpdf_form_settings']['556690c67856b'] ); + $file = "test-{$form['id']}.pdf"; - wp_mkdir_p( dirname( $file ) ); - touch( $file ); + wp_mkdir_p( $path ); + touch( $path . $file ); - $this->assertFileExists( $file ); + $this->assertFileExists( $path . $file ); $this->model->cleanup_pdf( $entry, $form ); - $this->assertFileDoesNotExist( $file ); + $this->assertFileDoesNotExist( $path . $file ); } /** @@ -1520,6 +1504,7 @@ public function test_get_field_class() { * @since 4.0 */ public function test_get_legacy_config() { + $this->setExpectedIncorrectUsage('GFPDF\Model\Model_PDF::get_legacy_config'); /* Setup some test data */ $results = $this->create_form_and_entries(); @@ -1550,6 +1535,7 @@ public function test_get_legacy_config() { * @dataProvider provider_get_template_filename */ public function test_get_template_filename( $expected, $template ) { + $this->setExpectedIncorrectUsage('GFPDF\View\View_PDF::get_template_filename'); $this->assertEquals( $expected, $this->view->get_template_filename( $template ) ); } diff --git a/tests/phpunit/unit-tests/test-slow-pdf-processes.php b/tests/phpunit/unit-tests/test-slow-pdf-processes.php index cd576e195..f33cc3d4e 100644 --- a/tests/phpunit/unit-tests/test-slow-pdf-processes.php +++ b/tests/phpunit/unit-tests/test-slow-pdf-processes.php @@ -7,6 +7,7 @@ use GFPDF\Helper\Helper_PDF; use GFPDF\Helper\Helper_Url_Signer; use GFPDF\Model\Model_PDF; +use GFPDF\Statics\Cache; use GFPDF\View\View_PDF; use GFPDF_Core_Model; use GPDFAPI; @@ -125,6 +126,77 @@ private function create_form_and_entries() { ]; } + /** + * Test the deprecated legacy PDF endpoint is secured and will generate a PDF successfully + */ + public function test_process_legacy_pdf_endpoint() { + $this->setExpectedIncorrectUsage( 'GFPDF\Controller\Controller_PDF::process_legacy_pdf_endpoint' ); + $this->setExpectedIncorrectUsage( 'GFPDF\Model\Model_PDF::get_legacy_config' ); + + /* Test our endpoint is firing correctly */ + $results = $this->create_form_and_entries(); + + $_GET['gf_pdf'] = 1; + $_GET['fid'] = $results['form']['id']; + $_GET['lid'] = $results['entry']['id']; + $_GET['template'] = 'zadani.php'; + + /* Check middleware security is applied */ + try { + wp_set_current_user( 0 ); + $this->controller->process_legacy_pdf_endpoint(); + } catch ( Exception $e ) { + $this->assertEquals( 'Redirecting', $e->getMessage() ); + } + + /* Check pdf successfully generated */ + try { + $user_id = $this->factory->user->create( [ 'role' => 'administrator' ] ); + wp_set_current_user( $user_id ); + $this->controller->process_legacy_pdf_endpoint(); + } catch ( Exception $e ) { + $this->assertEquals( 'The PDF cannot be displayed because the server headers have already been sent.', $e->getMessage() ); + + return; + } + + $this->fail( 'This test did not successfully complete' ); + } + + /** + * Test the DF endpoint is secured and will generate a PDF successfully + */ + public function test_process_pdf_endpoint() { + + /* Test our endpoint is firing correctly */ + $results = $this->create_form_and_entries(); + + $GLOBALS['wp']->query_vars['gpdf'] = 1; + $GLOBALS['wp']->query_vars['lid'] = $results['entry']['id']; + $GLOBALS['wp']->query_vars['pid'] = '556690c67856b'; + + /* Check middleware security is applied */ + try { + wp_set_current_user( 0 ); + $this->controller->process_pdf_endpoint(); + } catch ( Exception $e ) { + $this->assertEquals( 'Redirecting', $e->getMessage() ); + } + + /* Check pdf successfully generated */ + try { + $user_id = $this->factory->user->create( [ 'role' => 'administrator' ] ); + wp_set_current_user( $user_id ); + $this->controller->process_pdf_endpoint(); + } catch ( Exception $e ) { + $this->assertEquals( 'The PDF cannot be displayed because the server headers have already been sent.', $e->getMessage() ); + + return; + } + + $this->fail( 'This test did not successfully complete' ); + } + /** * Test our PDF generator function works as expected * This function prepares all the details for generating a PDF and is our authentication layer @@ -155,15 +227,20 @@ public function test_process_pdf() { /* Disable all middleware and check if PDF generation begins */ remove_all_filters( 'gfpdf_pdf_middleware' ); - try { - $this->model->process_pdf( $pid, $lid ); - } catch ( Exception $e ) { - $this->assertEquals( 'There was a problem generating your PDF', $e->getMessage() ); - - return; + /* Verify the PDF generation begins and then fails as expected */ + $results = $this->model->process_pdf( $pid, $lid ); + if ( ! is_wp_error( $results ) ) { + $this->fail( 'This test did not fail as expected' ); } - $this->fail( 'This test did not fail as expected' ); + /* + * Prior to 6.12 $this->model->process_pdf() would call $this->view->generate_pdf() + * and any errors would be output via wp_die(), which could be caught as an exception + * in PHPUnit. Now that process_pdf() runs through $this->model->generate_and_save_pdf() + * any errors are returned back up the chain for $this->controller->process_pdf_endpoint() to handle. + * This is the reason this unit test was modified to explicitly check is_wp_error(). + */ + $this->assertEquals( 'pdf_generation_failure', $results->get_error_code() ); } /** @@ -201,25 +278,29 @@ public function test_process_and_save_pdf() { public function test_maybe_save_pdf() { global $gfpdf; + $form_class = \GPDFAPI::get_form_class(); + /* Setup some test data */ $results = $this->create_form_and_entries(); $entry = $results['entry']; - $form = $results['form']; - $file = $gfpdf->data->template_tmp_location . "{$form['id']}{$entry['id']}556690c67856b/test-{$form['id']}.pdf"; + $form = $form_class->get_form( $results['form']['id'] ); /* get from the database so the date created is accurate */ + + $path = Cache::get_path( $form, $entry, $form['gfpdf_form_settings']['556690c67856b'] ); + $file = "test-{$form['id']}.pdf"; $this->model->maybe_save_pdf( $entry, $form ); /* Check the results are successful */ - $this->assertFileExists( $file ); + $this->assertFileExists( $path . $file ); /* Clean up */ - unlink( $file ); + unlink( $path . $file ); /* Ensure function doesn't run when background processing enabled */ $gfpdf->options->update_option( 'background_processing', 'Yes' ); $this->model->maybe_save_pdf( $entry, $form ); - $this->assertFileDoesNotExist( $file ); + $this->assertFileDoesNotExist( $path . $file ); } /** @@ -230,6 +311,8 @@ public function test_maybe_save_pdf() { * @since 4.0 */ public function test_generate_pdf() { + $this->setExpectedIncorrectUsage( 'GFPDF\View\View_PDF::generate_pdf'); + global $gfpdf; /* Setup our form and entries */ @@ -407,21 +490,27 @@ function( $settings ) { * works as expected. */ public function test_deprecated_save_pdf() { - global $gfpdf; + $form_class = \GPDFAPI::get_form_class(); $results = $this->create_form_and_entries(); $entry = $results['entry']; - $form = $results['form']; + $form = $form_class->get_form( $results['form']['id'] ); /* get from the database so the date created is accurate */ - $filename = $gfpdf->data->template_tmp_location . "11556690c67856b/test-{$form['id']}.pdf"; - - if ( is_file( $filename ) ) { - unlink( $filename ); - } + $filename = "test-{$form['id']}.pdf"; GFPDF_Core_Model::gfpdfe_save_pdf( $entry, $form ); - $this->assertTrue( is_file( $filename ) ); - unlink( $filename ); + $pdfs = GPDFAPI::get_entry_pdfs( $entry['id'] ); + foreach ( $pdfs as $pdf ) { + /* Skip non-core PDFs */ + if ( ! in_array( $pdf['template'], [ 'zadani', 'focus-gravity', 'rubix', 'blank-slate' ], true ) ) { + continue; + } + + /* Get PDF directory path from cache */ + $path = Cache::get_path( $form, $entry, $pdf ); + $this->assertFileExists( $path . $filename ); + unlink( $path . $filename ); + } } }'; /* phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r */ print_r( $form_data ); - print ''; + echo '