From 8af615150f9bb845fe12ecd926c35cf7b4751c8f Mon Sep 17 00:00:00 2001 From: Jake Jackson Date: Mon, 6 May 2019 11:26:33 +1000 Subject: [PATCH 1/7] Convert AJAX to REST API Endpoints WIP --- src/Api/V1/Base_Api.php | 96 ++++ src/Api/V1/Fonts/Api_Fonts.php | 408 +++++++++++++++ src/Api/V1/Fonts/Core/Api_Fonts_Core.php | 186 +++++++ src/Api/V1/License/Api_License.php | 188 +++++++ .../Migration/Multisite/Api_Migration_v4.php | 216 ++++++++ src/Api/V1/Pdf/Settings/Api_Pdf_Settings.php | 190 +++++++ .../Tmp/Api_Security_Tmp_Directory.php | 193 ++++++++ src/Api/V1/Template/Api_Template.php | 466 ++++++++++++++++++ src/bootstrap.php | 52 ++ .../unit-tests/api/ApiFontsEndpointRoutes.php | 168 +++++++ .../api/ApiLicenseEndpointRoutes.php | 136 +++++ .../api/ApiTemplateEndpointRoutes.php | 211 ++++++++ .../api/FontCoreApiEndpointRoutes.php | 131 +++++ 13 files changed, 2641 insertions(+) create mode 100644 src/Api/V1/Base_Api.php create mode 100644 src/Api/V1/Fonts/Api_Fonts.php create mode 100644 src/Api/V1/Fonts/Core/Api_Fonts_Core.php create mode 100644 src/Api/V1/License/Api_License.php create mode 100644 src/Api/V1/Migration/Multisite/Api_Migration_v4.php create mode 100644 src/Api/V1/Pdf/Settings/Api_Pdf_Settings.php create mode 100644 src/Api/V1/Security/Tmp/Api_Security_Tmp_Directory.php create mode 100644 src/Api/V1/Template/Api_Template.php create mode 100644 tests/phpunit/unit-tests/api/ApiFontsEndpointRoutes.php create mode 100644 tests/phpunit/unit-tests/api/ApiLicenseEndpointRoutes.php create mode 100644 tests/phpunit/unit-tests/api/ApiTemplateEndpointRoutes.php create mode 100644 tests/phpunit/unit-tests/api/FontCoreApiEndpointRoutes.php diff --git a/src/Api/V1/Base_Api.php b/src/Api/V1/Base_Api.php new file mode 100644 index 000000000..8aca54e33 --- /dev/null +++ b/src/Api/V1/Base_Api.php @@ -0,0 +1,96 @@ +has_capability( $capability ); + } + + /** + * Register WordPress REST API endpoint(s) + * + * @return void + * + * @internal Use `register_rest_route()` to register WordPress REST API endpoint(s) + * + * @since 5.2 + */ + abstract public function register(); +} diff --git a/src/Api/V1/Fonts/Api_Fonts.php b/src/Api/V1/Fonts/Api_Fonts.php new file mode 100644 index 000000000..cc5bf322b --- /dev/null +++ b/src/Api/V1/Fonts/Api_Fonts.php @@ -0,0 +1,408 @@ +log = $log; + $this->misc = $misc; + $this->data = $data; + $this->options = $options; + } + + /** + * Register our PDF save font endpoint + * + * @Internal Use this endpoint to save fonts + * + * @since 5.2 + */ + public function register() { + register_rest_route( + self::ENTRYPOINT . '/' . self::VERSION, + '/fonts/', + [ + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => [ $this, 'save_font' ], + 'permission_callback' => function() { + return $this->has_capabilities( 'gravityforms_edit_settings' ); + }, + ] + ); + + register_rest_route( + self::ENTRYPOINT . '/' . self::VERSION, + '/fonts/', + [ + 'methods' => \WP_REST_Server::DELETABLE, + 'callback' => [ $this, 'delete_font' ], + 'permission_callback' => function() { + return $this->has_capabilities( 'gravityforms_edit_settings' ); + }, + ] + ); + } + + /** + * Register our PDF save font endpoint + * + * @param WP_REST_Request $request + * + * @return \WP_REST_Response + * + * @since 5.2 + */ + public function save_font( \WP_REST_Request $request ) { + + /* get the json parameter */ + $params = $request->get_json_params(); + + /* Handle the validation and saving of the font */ + $payload = isset( $params['payload'] ) ? $params : null; + + if (! $payload) { + return new \WP_Error( 'required_fields_missing', 'Required fields have not been included', [ 'status' => 400 ] ); + } + + $results = $this->process_font( $payload ); + + /* There was an issue downloading and saving fonts */ + if ( $results->errors ) { + return $results ; + } + + /* If we reached this point the results were successful so return the new object */ + $this->log->addNotice( + 'AJAX – Successfully Saved Font', + [ + 'results' => $results, + ] + ); + + return [ 'message' => 'Font saved successfully' ]; + + } + + /** + * Validate user input and save as new font + * + * @param array $font The four font fields to be processed + * + * @return array + * + * @since 5.2 + */ + public function process_font( $font ) { + + /* remove any empty fields */ + $font = array_filter( $font ); + + /* Check we have the required data */ + if ( empty( $font['payload']['font_name'] ) || empty( $font['payload']['regular'] ) ) { + + $error = esc_html__( 'Required fields have not been included.', 'gravity-forms-pdf-extended' ); + + $this->log->addWarning( 'Font Validation Failed', (array)$error ); + + return new \WP_Error( 'required_fields_missing', $error, [ 'status' => 400 ] ); + } + + /* Check we have a valid font name */ + $name = $font['payload']['font_name']; + + if ( ! $this->is_font_name_valid( $name ) ) { + $error = esc_html__( 'Font name is not valid. Only alphanumeric characters and spaces are accepted.', 'gravity-forms-pdf-extended' ); + + $this->log->addWarning( 'Font Validation Failed', (array)$error ); + + return new \WP_Error( 'invalid_font_name', $error, [ 'status' => 400 ] ); + } + + /* Check the font name is unique */ + $shortname = $this->options->get_font_short_name( $name ); + + $id = ( isset( $font['id'] ) ) ? $font['id'] : ''; + + if ( ! $this->is_font_name_unique( $shortname, $id ) ) { + $error = esc_html__( 'A font with the same name already exists. Try a different name.', 'gravity-forms-pdf-extended' ); + + $this->log->addWarning( 'Font Validation Failed', (array)$error ); + + return new \WP_Error( 'font_name_exist', $error, [ 'status' => 422 ] ); + } + + /* Move fonts to our Gravity PDF font folder */ + $installation = $this->install_fonts( $font['payload'] ); + + /* If we got here the installation was successful so return the data */ + /* Return whatever is thrown by install_fonts function */ + return $installation; + } + + /** + * Check that the font name passed conforms to our expected nameing convesion + * + * @param string $name The font name to check + * + * @return boolean True on valid, false on failure + * + * @since 5.2 + */ + public function is_font_name_valid( $name ) { + $regex = '^[A-Za-z0-9 ]+$'; + + if ( preg_match( "/$regex/", $name ) ) { + return true; + } + + return false; + } + + /** + * Handles the database updates required to save a new font + * + * @param array $fonts + * + * @return array + * + * @since 5.2 + */ + public function install_fonts( $fonts ) { + $types = [ 'regular', 'bold', 'italics', 'bolditalics' ]; + $errors = []; + + foreach ( $types as $type ) { + + /* Check if a key exists for this type and process */ + if ( isset( $fonts[ $type ] ) ) { + + $path = $this->misc->convert_url_to_path( $fonts[ $type ] ); + + /* Couldn't find file so throw error */ + if ( ! $path ) { + return new \WP_Error( "font_installation_error", "Could not locate font on web server: " . $fonts[ 'font_name' ] . " " . $fonts[ $type ], [ "status" => 404 ] ); + } + + /* Copy font to our fonts folder */ + $filename = basename( $path ); + + if ( ! is_file( $this->data->template_font_location . $filename ) && ! copy( $path, $this->data->template_font_location . $filename ) ) { + + return new \WP_Error( "font_installation_error", "There was a problem installing the font: " . $filename . "Please try again.", [ "status" => 500 ] ); + } + } + } + + /* Insert our font into the database */ + $custom_fonts = $this->options->get_option( 'custom_fonts' ); + + /* Prepare our font data and give it a unique id */ + if ( empty( $fonts['id'] ) ) { + $id = uniqid(); + $fonts['id'] = $id; + } + + $custom_fonts[ $fonts['id'] ] = $fonts; + + /* Update our font database */ + $this->options->update_option( 'custom_fonts', $custom_fonts ); + + /* Cleanup the mPDF tmp directory to prevent font caching issues */ + $this->misc->cleanup_dir( $this->data->mpdf_tmp_location ); + + /* Fonts sucessfully installed so return font data */ + return $fonts; + } + + /** + * Query our custom fonts options table and check if the font name already exists + * + * @param string $name The font name to check + * @param int|string $id The configuration ID (if any) + * + * @return bool True if valid, false on failure + * + * @since 5.2 + */ + public function is_font_name_unique( $name, $id = '' ) { + + /* Get the shortname of the current font */ + $name = $this->options->get_font_short_name( $name ); + + /* Loop through default fonts and check for duplicate */ + $default_fonts = $this->options->get_installed_fonts(); + + unset( $default_fonts[ esc_html__( 'User-Defined Fonts', 'gravity-forms-pdf-extended' ) ] ); + + /* check for exact match */ + foreach ( $default_fonts as $group ) { + if ( isset( $group[ $name ] ) ) { + return false; + } + } + + $custom_fonts = $this->options->get_option( 'custom_fonts' ); + + if ( is_array( $custom_fonts ) ) { + foreach ( $custom_fonts as $font ) { + + /* Skip over itself */ + if ( ! empty( $id ) && $font['id'] == $id ) { + continue; + } + + if ( $name == $this->options->get_font_short_name( $font['font_name'] ) ) { + return false; + } + } + } + + return true; + } + + /** + * Delete custom font + * + * @return \WP_REST_Response + * + * @since 5.2 + */ + public function delete_font( \WP_REST_Request $request ) { + + /* get the json parameter */ + $params = $request->get_json_params(); + + /* Get the required details for deleting fonts */ + $id = ( isset( $params['id'] ) ) ? $params['id'] : ''; + $fonts = $this->options->get_option( 'custom_fonts' ); + + /* Check font actually exists and remove */ + if ( ! isset( $fonts[ $id ] ) || ! $this->remove_font_file( $fonts[ $id ] ) ) { + $error = ['error' => esc_html__( 'Could not delete Gravity PDF font correctly. Please try again.', 'gravity-forms-pdf-extended' ) ]; + + $this->log->addError( 'AJAX Endpoint Error', $error ); + + return new \WP_Error( 'delete_font', $error, [ 'status' => 500 ] ); + } + + unset( $fonts[ $id ] ); + + /* Cleanup the mPDF tmp directory to prevent font caching issues */ + $this->misc->cleanup_dir( $this->data->mpdf_tmp_location ); + + if ( $this->options->update_option( 'custom_fonts', $fonts ) ) { + $this->log->addNotice( 'Successfully Deleted Font' ); + return true; + } + } + + /** + * Removes the current font's TTF files from our font directory + * + * @param array $fonts The font config + * + * @return boolean True on success, false on failure + * + * @since 5.2 + */ + public function remove_font_file( $fonts ) { + $fonts = array_filter( $fonts ); + $types = [ 'regular', 'bold', 'italics', 'bolditalics' ]; + + foreach ( $types as $type ) { + if ( isset( $fonts[ $type ] ) ) { + $filename = basename( $fonts[ $type ] ); + + if ( is_file( $this->data->template_font_location . $filename ) && ! unlink( $this->data->template_font_location . $filename ) ) { + return false; + } + } + } + + return true; + } +} \ No newline at end of file diff --git a/src/Api/V1/Fonts/Core/Api_Fonts_Core.php b/src/Api/V1/Fonts/Core/Api_Fonts_Core.php new file mode 100644 index 000000000..5a005a857 --- /dev/null +++ b/src/Api/V1/Fonts/Core/Api_Fonts_Core.php @@ -0,0 +1,186 @@ +log = $log; + $this->template_font_location = $template_font_location; + } + + /** + * Register WordPress REST API endpoint(s) + * + * @return void + * + * @internal Use `register_rest_route()` to register WordPress REST API endpoint(s) + * + * @since 5.2 + */ + public function register() { + register_rest_route( + self::ENTRYPOINT . '/' . self::VERSION, + '/fonts/core/', + [ + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => [ $this, 'save_core_font' ], + 'permission_callback' => function() { + return $this->has_capabilities( 'gravityforms_edit_settings' ); + }, + ] + ); + + } + + /** + * Processes the rest API endpoint + * + * @param \WP_REST_Request $request + * + * @return array|\WP_Error + * + * @since 5.2 + */ + public function save_core_font( \WP_REST_Request $request ) { + + $params = $request->get_json_params(); + + /* Download and save our font */ + $fontname = isset( $params['font_name'] ) ? $params['font_name'] : ''; + $results = $this->download_and_save_font( $fontname ); + + if ( ! $results ) { + return new \WP_Error( 'download_and_save_font', 'Core Font Download Failed', [ 'status' => 400 ] ); + } + + return [ 'message' => 'Font saved successfully' ]; + } + + /** + * Stream files from remote server and save them locally + * + * @param $fontname + * + * @return bool + * + * @since 5.2 + */ + protected function download_and_save_font( $fontname ) { + + if ( empty( $fontname ) ) { + return false; + } + + /* Only the font name is passed via AJAX. The Repo we download from is fixed (prevent security issues) */ + $response = wp_remote_get( + $this->github_repo . $fontname, + [ + 'timeout' => 60, + 'stream' => true, + 'filename' => $this->template_font_location . $fontname, + ] + ); + + /* Check for errors and log them to file */ + if ( is_wp_error( $response ) ) { + $this->log->addError( + 'Core Font Download Failed', + [ + 'name' => $fontname, + 'WP_Error_Message' => $response->get_error_message(), + 'WP_Error_Code' => $response->get_error_code(), + ] + ); + + return false; + } + + $response_code = wp_remote_retrieve_response_code( $response ); + if ( $response_code !== 200 ) { + $this->log->addError( + 'Core Font API Response Failed', + [ + 'response_code' => $response_code, + ] + ); + + return false; + } + + /* If we got here, the call was successfull */ + return true; + } +} diff --git a/src/Api/V1/License/Api_License.php b/src/Api/V1/License/Api_License.php new file mode 100644 index 000000000..83f2fbde6 --- /dev/null +++ b/src/Api/V1/License/Api_License.php @@ -0,0 +1,188 @@ +log = $log; + $this->data = $data; + } + + /** + * Register our PDF save font endpoint + * + * @Internal Use this endpoint to save fonts + * + * @since 5.2 + */ + public function register() { + register_rest_route( + self::ENTRYPOINT . '/' . self::VERSION, + '/license/(?P\d+)/deactivate', + [ + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => [ $this, 'process_license_deactivation' ], + + 'permission_callback' => function() { + return $this->has_capabilities( 'gravityforms_edit_settings' ); + }, + ] + ); + } + + /** + * Processes the rest API endpoint + * + * @param \WP_REST_Request $request + * + * @return array|\WP_Error + * + * @since 5.2 + */ + public function process_license_deactivation( \WP_REST_Request $request ) { + + $params = $request->get_json_params(); + + /* Get the required details */ + /* do not proceed if these are empty/null */ + if ( empty( $params['addon_name'] ) || empty( $params['license'] ) ) { + return new \WP_Error( 'process_license_deactivation', 'Required Field is missing, please try again', [ 'status' => 400 ] ); + } + + $addon = $this->data->addon( $params['addon_name'] ); + + /* Check add-on currently installed */ + if ( empty( $addon ) ) { + return new \WP_Error( 'process_license_deactivation', 'An error occurred during deactivation, please try again', [ 'status' => 404 ] ); + } + + $was_deactivated = $this->deactivate_license_key( $addon, $params['license'] ); + + if ( ! $was_deactivated ) { + $license_info = $addon->get_license_info(); + return new \WP_Error( 'schedule_license_check', $license_info['message'], [ 'status' => 400 ] ); + } + + $this->log->addNotice( 'Successfully Deactivated License' ); + return [ 'success' => esc_html__( 'License deactivated.', 'gravity-forms-pdf-extended' ) ]; + } + + /** + * Do API call to GravityPDF.com to deactivate add-on license + * + * @param Helper_Abstract_Addon $addon + * @param string $license_key + * + * @return bool + * + * @since 5.2 + */ + public function deactivate_license_key( Helper_Abstract_Addon $addon, $license_key ) { + $response = wp_remote_post( + $this->data->store_url, + [ + 'timeout' => 15, + 'body' => [ + 'edd_action' => 'deactivate_license', + 'license' => $license_key, + 'item_name' => urlencode( $addon->get_short_name() ), // the name of our product in EDD + 'url' => home_url(), + ], + ] + ); + + /* If API error exit early */ + if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { + return false; + } + + /* Get API response and check license is now deactivated */ + $license_data = json_decode( wp_remote_retrieve_body( $response ) ); + if ( ! isset( $license_data->license ) || $license_data->license !== 'deactivated' ) { + return false; + } + + /* Remove license data from database */ + $addon->delete_license_info(); + + $this->log->addNotice( + 'License successfully deactivated', + [ + 'slug' => $addon->get_slug(), + 'license' => $license_key, + ] + ); + + return true; + } +} \ No newline at end of file diff --git a/src/Api/V1/Migration/Multisite/Api_Migration_v4.php b/src/Api/V1/Migration/Multisite/Api_Migration_v4.php new file mode 100644 index 000000000..2b1a89f32 --- /dev/null +++ b/src/Api/V1/Migration/Multisite/Api_Migration_v4.php @@ -0,0 +1,216 @@ +. +*/ + +/** + * Class ApiMigrationv4Endpoint + * + * @package GFPDF\Plugins\GravityPDF\API + */ +class Api_Migration_v4 extends Base_Api { + + /** + * Holds our log class + * + * @var \Monolog\Logger + * + * @since 4.0 + */ + public $log; + + /** + * Holds our Helper_Abstract_Options / Helper_Options_Fields object + * Makes it easy to access global PDF settings and individual form PDF settings + * + * @var \GFPDF\Helper\Helper_Options_Fields + * + * @since 4.0 + */ + protected $options; + + /** + * Holds our Helper_Data object + * which we can autoload with any data needed + * + * @var \GFPDF\Helper\Helper_Data + * + * @since 4.0 + */ + protected $data; + + /** + * Holds our Helper_Data object + * which we can autoload with any data needed + * + * @var \GFPDF\Helper\Helper_Migration + * + * @since 5.2 + */ + protected $migration; + + /** + * Api_Migration_v4 constructor. + * + * @param Helper_Abstract_Options $options + * @param Helper_Data $data + * @param Helper_Migration $migration + * + * @since 5.2 + */ + public function __construct( LoggerInterface $log, Helper_Abstract_Options $options, Helper_Data $data, Helper_Migration $migration ) { + $this->log = $log; + $this->options = $options; + $this->data = $data; + $this->migration = $migration; + } + + /** + * Register our Multisite Migration endpoint + * + * @Internal Use this endpoint register multisite migration + * + * @since 5.2 + */ + public function register() { + register_rest_route( + self::ENTRYPOINT . '/' . self::VERSION, + '/migration/multisite/', + [ + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => [ $this, 'ajax_multisite_v3_migration' ], + + 'permission_callback' => function() { + return $this->has_capabilities( 'manage_sites' ); + }, + ] + ); + } + + /** + * Register our PDF save font endpoint + * + * @param WP_REST_Request $request + * + * @return \WP_REST_Response + * + * @since 5.2 + */ + public function ajax_multisite_v3_migration( \WP_REST_Request $request ) { + $params = $request->get_json_params(); + + /* Ensure multisite website */ + if ( ! is_multisite() ) { + + return new \WP_Error( 'ajax_multisite_v3_migration', 'You are not authorized to perform this action. Please try again.', [ 'status' => 401 ] ); + } + + /* Check there's a configuration file to migrate */ + $blog_id = ( isset( $params['blog_id'] ) ) ? (int) $params['blog_id'] : 0; + + /* Check if we have a config file that should be migrated */ + $path = $this->data->template_location . $blog_id . '/'; + + if ( ! is_file( $path . 'configuration.php' ) ) { + + $this->log->addError( 'Configuration file not found', $path ); + + return new \WP_Error( 'no_configuration_file', 'No configuration file found for site #%s', [ 'status' => 404 ] ); + } + + /* Setup correct migration settings */ + switch_to_blog( $blog_id ); + $this->data->multisite_template_location = $path; + + /* Do migration */ + if ( ! $this->migrate_v3( $path ) ) { + + $this->log->addError( 'AJAX Endpoint Failed' ); + + return new \WP_Error( 'unable_to_connect_to_server', 'Database import problem for site #%s', [ 'status' => 500 ] ); + } + + /* migration successful */ + return [ 'message' => 'Migration completed successfully' ]; + } + + /** + * Does the migration and notice clearing (if unsuccessful) + * + * @param string $path Path to the current site's template directory + * + * @return boolean + * + * @since 5.2 + */ + private function migrate_v3( $path ) { + + /* Start migration */ + if ( $this->migration->begin_migration() ) { + /** + * Migration Successful. + * + * If there was a problem removing the configuration file we'll automatically prevent the migration message displaying again + */ + if ( is_file( $path . 'configuration.php' ) ) { + $this->dismiss_notice( 'migrate_v3_to_v4' ); + } + + return true; + } + + return false; + } + + /** + * Mark the current notice as being dismissed + * + * @param string $type The current notice ID + * + * @return void + * + * @since 5.2 + */ + public function dismiss_notice( $type ) { + + $dismissed_notices = $this->options->get_option( 'action_dismissal', [] ); + $dismissed_notices[ $type ] = $type; + $this->options->update_option( 'action_dismissal', $dismissed_notices ); + } + +} diff --git a/src/Api/V1/Pdf/Settings/Api_Pdf_Settings.php b/src/Api/V1/Pdf/Settings/Api_Pdf_Settings.php new file mode 100644 index 000000000..c442167fb --- /dev/null +++ b/src/Api/V1/Pdf/Settings/Api_Pdf_Settings.php @@ -0,0 +1,190 @@ +. +*/ + +/** + * Class ApiPdfSettingEndpoint + * + * @package GFPDF\Plugins\GravityPDF\API + */ +class Api_Pdf_Settings extends Base_Api { + + /** + * Holds our log class + * + * @var \Monolog\Logger + * + * @since 4.0 + */ + public $log; + + /** + * @var string + * + * @since 5.2 + */ + protected $template_font_location; + + /** + * @var boolean + * + * @since 5.2 + */ + protected $has_access = true; + + /** + * @var string + * + * @since 5.2 + */ + protected $tmp_test_file = 'public_tmp_directory_test.txt'; + + /** + * Holds our Helper_Misc object + * Makes it easy to access common methods throughout the plugin + * + * @var \GFPDF\Helper\Helper_Misc + * + * @since 5.2 + */ + protected $misc; + + /** + * Api_Pdf_Settings constructor. + * + * @param Helper_Misc $misc + * + * @param string $template_font_location The absolute path to the current PDF font directory + * + * @since 5.2 + */ + public function __construct( LoggerInterface $log, Helper_Misc $misc, $template_font_location ) { + $this->log = $log; + $this->misc = $misc; + $this->template_font_location = $template_font_location; + } + + /** + * @since 5.2 + */ + public function register() { + register_rest_route( + self::ENTRYPOINT . '/' . self::VERSION, + '/pdf/settings/', + [ + 'methods' => \WP_REST_Server::READABLE, + 'callback' => [ $this, 'check_tmp_pdf_security' ], + 'permission_callback' => function() { + return $this->has_capabilities( 'gravityforms_view_settings' ); + }, + ] + ); + } + + /** + * Create a file in our tmp directory and check if it is publically accessible (i.e no .htaccess protection) + * + * @param $_POST ['nonce'] + * + * @return WP_REST_Response + * + * @since 5.2 + */ + public function check_tmp_pdf_security( \WP_REST_Request $request ) { + + /* Create our tmp file and do our actual check */ + $result = json_encode( $this->test_public_tmp_directory_access() ); + + if (!$result) { + return new \WP_Error( 'test_public_tmp_directory_access', 'Unable to create tmp Directory', [ 'status' => 401 ] ); + } + + return [ 'message' => 'Tmp file successfully created' ]; + } + + /** + * Create a file in our tmp directory and verify if it's protected from the public + * + * @return boolean + * + * @since 5.2 + */ + public function test_public_tmp_directory_access() { + + /* create our file */ + file_put_contents( $this->template_font_location . $this->tmp_test_file, 'failed-if-read' ); + + /* verify it exists */ + if ( is_file( $this->template_font_location . $this->tmp_test_file ) ) { + + /* Run our test */ + $site_url = $this->misc->convert_path_to_url( $this->template_font_location ); + + if ( $site_url !== false ) { + + $response = wp_remote_get( $site_url . $this->tmp_test_file ); + + if ( ! is_wp_error( $response ) ) { + + /* Check if the web server responded with a OK status code and we can read the contents of our file, then fail our test */ + if ( isset( $response['response']['code'] ) && $response['response']['code'] === 200 && + isset( $response['body'] ) && $response['body'] === 'failed-if-read' + ) { + $response_object = $response['http_response']; + $raw_response = $response_object->get_response_object(); + $this->log->warning( + 'PDF temporary directory not protected', + [ + 'url' => $raw_response->url, + 'status_code' => $raw_response->status_code, + 'response' => $raw_response->raw, + ] + ); + + $this->has_access = false; + } + } + } + } + + /* Cleanup our test file */ + @unlink( $this->template_font_location . $this->tmp_test_file ); + + return $this->has_access; + } + +} diff --git a/src/Api/V1/Security/Tmp/Api_Security_Tmp_Directory.php b/src/Api/V1/Security/Tmp/Api_Security_Tmp_Directory.php new file mode 100644 index 000000000..5ea45b18d --- /dev/null +++ b/src/Api/V1/Security/Tmp/Api_Security_Tmp_Directory.php @@ -0,0 +1,193 @@ +. +*/ + +/** + * Class ApiSecurityTmpDirectoryEndpoint + * + * @package GFPDF\Plugins\GravityPDF\API + */ +class Api_Security_Tmp_Directory extends Base_Api { + + /** + * Holds our log class + * + * @var \Monolog\Logger + * + * @since 4.0 + */ + public $log; + + /** + * @var boolean + * + * @since 5.2 + */ + protected $has_access = true; + + /** + * @var string the temporary text file to write to directory + * + * @since 5.2 + */ + protected $tmp_test_file = 'public_tmp_directory_test.txt'; + + /** + * Holds our Helper_Misc object + * Makes it easy to access common methods throughout the plugin + * + * @var \GFPDF\Helper\Helper_Misc + * + * @since 5.2 + */ + protected $misc; + + /** + * Holds our Helper_Data object + * which we can autoload with any data needed + * + * @var \GFPDF\Helper\Helper_Data + * + * @since 5.2 + */ + protected $data; + + /** + * Api_Security_Tmp_Directory constructor. + * + * @param Helper_Data $data + * + * @since 5.2 + */ + public function __construct( LoggerInterface $log, Helper_Misc $misc, Helper_Data $data ) { + $this->log = $log; + $this->misc = $misc; + $this->data = $data; + + } + + /** + * @since 5.2 + */ + public function register() { + register_rest_route( + self::ENTRYPOINT . '/' . self::VERSION, + '/security/tmp/', + [ + 'methods' => \WP_REST_Server::READABLE, + 'callback' => [ $this, 'check_tmp_pdf_security' ], + 'permission_callback' => function() { + return $this->has_capabilities( 'gravityforms_view_settings' ); + }, + ] + ); + } + + /** + * Create a file in our tmp directory and check if it is publically accessible (i.e no .htaccess protection) + * + * @param $_POST ['nonce'] + * + * @return boolean + * + * @since 5.2 + */ + public function check_tmp_pdf_security( ) { + + /* check if we can access tmp directory */ + $result = $this->test_public_tmp_directory_access(); + + if (!$result) { + + return new \WP_Error( 'check_tmp_pdf_security', 'There was an error creating access to tmp directory', [ 'status' => 500 ] ); + } + + return [ 'message' => 'Tmp Directory Accessible' ]; + } + + /** + * Create a file in our tmp directory and verify if it's protected from the public + * + * @return boolean + * + * @since 5.2 + */ + public function test_public_tmp_directory_access() { + + /* create our file */ + file_put_contents( $this->data->template_tmp_location . $this->tmp_test_file, 'failed-if-read' ); + + /* verify it exists */ + if ( is_file( $this->data->template_tmp_location . $this->tmp_test_file ) ) { + + /* Run our test */ + $site_url = $this->misc->convert_path_to_url( $this->data->template_tmp_location ); + + if ( $site_url !== false ) { + + $response = wp_remote_get( $site_url . $this->tmp_test_file ); + + if ( ! is_wp_error( $response ) ) { + + /* Check if the web server responded with a OK status code and we can read the contents of our file, then fail our test */ + if ( isset( $response['response']['code'] ) && $response['response']['code'] === 200 && + isset( $response['body'] ) && $response['body'] === 'failed-if-read' + ) { + $response_object = $response['http_response']; + $raw_response = $response_object->get_response_object(); + $this->log->warning( + 'PDF temporary directory not protected', + [ + 'url' => $raw_response->url, + 'status_code' => $raw_response->status_code, + 'response' => $raw_response->raw, + ] + ); + + $this->has_access = false; + } + } + } + } + + /* Cleanup our test file */ + @unlink( $this->data->template_tmp_location . $this->tmp_test_file ); + + return $this->has_access; + } +} diff --git a/src/Api/V1/Template/Api_Template.php b/src/Api/V1/Template/Api_Template.php new file mode 100644 index 000000000..c43c6736e --- /dev/null +++ b/src/Api/V1/Template/Api_Template.php @@ -0,0 +1,466 @@ +. +*/ + +/** + * Class ApiTemplateEndpoint + * + * @package GFPDF\Plugins\GravityPDF\API + */ +class Api_Template extends Base_Api { + + /** + * Holds our log class + * + * @var \Monolog\Logger + * + * @since 4.0 + */ + public $log; + + /** + * Holds our Helper_Misc object + * Makes it easy to access common methods throughout the plugin + * + * @var \GFPDF\Helper\Helper_Misc + * + * @since 5.2 + */ + protected $misc; + + /** + * Holds our Helper_Data object + * which we can autoload with any data needed + * + * @var \GFPDF\Helper\Helper_Data + * + * @since 5.2 + */ + protected $data; + + /** + * Holds our Helper_Abstract_Options / Helper_Options_Fields object + * Makes it easy to access global PDF settings and individual form PDF settings + * + * @var \GFPDF\Helper\Helper_Options_Fields + * + * @since 5.2 + */ + protected $options; + + /** + * Holds our Helper_Templates object + * used to ease access to our PDF templates + * + * @var \GFPDF\Helper\Helper_Templates + * + * @since 5.2 + */ + public $templates; + + /** + * Api_Pdf_Settings constructor. + * + * @param Helper_Misc $misc + * + * @param Helper_Data $data + * + * @since 5.2 + */ + public function __construct( LoggerInterface $log, Helper_Misc $misc, Helper_Data $data, Helper_Abstract_Options $options, Helper_Templates $templates ) { + $this->log = $log; + $this->misc = $misc; + $this->data = $data; + $this->options = $options; + $this->templates = $templates; + } + + /** + * Register our PDF save font endpoint + * + * @Internal Use this endpoint to save fonts + * + * @since 5.2 + */ + public function register() { + register_rest_route( + self::ENTRYPOINT . '/' . self::VERSION, + '/template/', + [ + 'methods' => \WP_REST_Server::READABLE, + 'callback' => [ $this, 'ajax_process_build_template_options_html' ], + + 'permission_callback' => function() { + return current_user_can( 'gravityforms_edit_settings' ); + }, + ] + ); + + register_rest_route( + self::ENTRYPOINT . '/' . self::VERSION, + '/template/', + [ + 'methods' => \WP_REST_Server::CREATABLE, + 'callback' => [ $this, 'ajax_process_uploaded_template' ], + + 'permission_callback' => function() { + return current_user_can( 'gravityforms_edit_settings' ); + }, + ] + ); + + register_rest_route( + self::ENTRYPOINT . '/' . self::VERSION, + '/template/', + [ + 'methods' => \WP_REST_Server::DELETABLE, + 'callback' => [ $this, 'ajax_process_delete_template' ], + + 'permission_callback' => function() { + return current_user_can( 'gravityforms_edit_settings' ); + }, + ] + ); + + } + + /** + * AJAX Endpoint for building the template select box options (so we don't have to recreate the logic in React) + * + * @param string $_POST ['nonce'] a valid nonce + * + * @since 5.2 + */ + public function ajax_process_build_template_options_html() { + + $registered_settings = $this->options->get_registered_fields(); + + $template_settings = $registered_settings['form_settings']['template']; + + header( 'Content-Type: application/text' ); + echo $this->options->build_options_for_select( $template_settings['options'], $this->options->get_form_value( $template_settings ) ); + + /* Okay Response */ + return [ 'message' => 'Template options built successfully' ]; + } + + /** + * AJAX Endpoint to handle the uploading of PDF templates + * + * @param string $_POST ['nonce'] a valid nonce + * + * @since 5.2 + */ + public function ajax_process_uploaded_template() { + + /* Validate uploaded file */ + try { + $storage = new FileSystem( $this->data->template_tmp_location ); + $file = new File( 'template', $storage ); + $zip_path = $this->move_template_to_tmp_dir( $file ); + } catch ( Exception $e ) { + $this->log->addWarning( + 'File validation and move failed', + [ + 'file' => $_FILES, + 'error' => $e->getMessage(), + ] + ); + + /* Bad Request */ + return new \WP_Error( 'validate_upload_file', 'File validation and move failed', [ 'status' => 400 ] ); + } + + /* Unzip and check the PDF templates look valid */ + try { + $this->unzip_and_verify_templates( $zip_path ); + } catch ( Exception $e ) { + $this->cleanup_template_files( $zip_path ); + + $this->log->addWarning( + 'File validation and move failed', + [ + 'file' => $_FILES, + 'error' => $e->getMessage(), + ] + ); + + header( 'Content-Type: application/json' ); + $error = json_encode( [ 'error' => $e->getMessage() ] ); + + /* Bad Response */ + return new \WP_Error( 'validate_upload_file', $error, [ 'status' => 400 ] ); + } + + /* Copy all the files to the active PDF working directory */ + $unzipped_dir_name = $this->get_unzipped_dir_name( $zip_path ); + $template_path = $this->templates->get_template_path(); + + $results = $this->misc->copyr( $unzipped_dir_name, $template_path ); + + /* Get the template headers now all the files are in the right location */ + $this->templates->flush_template_transient_cache(); + $headers = $this->get_template_info( glob( $unzipped_dir_name . '*.php' ) ); + + /* Fix template path */ + $headers = array_map( + function( $header ) use ( $unzipped_dir_name, $template_path ) { + $header['path'] = str_replace( $unzipped_dir_name, $template_path, $header['path'] ); + return $header; + }, + $headers + ); + + /* Run PDF template SetUp method if required */ + $this->maybe_run_template_setup( $headers ); + + /* Cleanup tmp uploaded files */ + $this->cleanup_template_files( $zip_path ); + + if ( is_wp_error( $results ) ) { + /* Internal Server Error */ + return new \WP_Error( 'unable_to_copy_files', $results, [ 'status' => 500 ] ); + } + + /* Return newly-installed template headers */ + header( 'Content-Type: application/json' ); + echo json_encode( + [ + 'templates' => $headers, + ] + ); + + /* Okay Response */ + // wp_die( '', 200 ); + return [ 'message' => 'Template uploaded successfully' ]; + } + + /** + * Extracts the zip file, checks there are valid PDF template files found and retreives information about them + * + * @param $zip_path The full path to the zip file + * + * @return array The PDF template headers from the valid files + * + * @throws Exception Thrown if a PDF template file isn't valid + * + * @since 5.2 + */ + public function unzip_and_verify_templates( $zip_path ) { + $this->enable_wp_filesystem(); + + $dir = $this->get_unzipped_dir_name( $zip_path ); + $results = unzip_file( $zip_path, $dir ); + + /* If the unzip failed we'll throw an error */ + if ( is_wp_error( $results ) ) { + return new \WP_Error( 'unzip_failed', $results->get_error_message(), [ 'status' => 500 ] ); + } + + /* Check unziped templates for a valid v4 header, or v3 string pattern */ + $files = glob( $dir . '*.php' ); + + if ( ! is_array( $files ) || sizeof( $files ) === 0 ) { + return new \WP_Error( 'no_valid_template_found', 'No valid PDF template found in Zip archive.', [ 'status' => 404 ] ); + } + + $this->check_for_valid_pdf_templates( $files ); + } + + /** + * Remove the zip file and the unzipped directory + * + * @param $zip_path The full path to the zip file + * + * @since 5.2 + */ + public function cleanup_template_files( $zip_path ) { + $dir = $this->get_unzipped_dir_name( $zip_path ); + + $this->misc->rmdir( $dir ); + unlink( $zip_path ); + } + + /** + * Gets the full path to a new directory which is based on the zip file's unique name + * + * @param string $zip_path The full path to the zip file + * + * @return string + * + * @since 5.2 + */ + public function get_unzipped_dir_name( $zip_path ) { + return dirname( $zip_path ) . '/' . basename( $zip_path, '.zip' ) . '/'; + } + + /** + * Get the PDF template info to pass to our application + * + * @param array $files + * + * @return array + * + * @since 5.2 + */ + public function get_template_info( $files = [] ) { + return array_map( + function( $file ) { + return $this->templates->get_template_info_by_path( $file ); + }, + $files + ); + } + + /** + * Execute the setUp method on any templates that impliment it + * + * @param array $headers Contains the array returned from $this->get_template_info() + * + * @since 5.2 + */ + public function maybe_run_template_setup( $headers = [] ) { + foreach ( $headers as $template ) { + $config = $this->templates->get_config_class( $template['id'] ); + + /* Check if the PDF config impliments our Setup/TearDown interface and run the tear down */ + if ( in_array( 'GFPDF\Helper\Helper_Interface_Setup_TearDown', class_implements( $config ) ) ) { + $config->setUp(); + } + } + } + + /** + * Sniffs the PHP file for signs that it's a valid Gravity PDF tempalte file + * + * @param array $files The full paths to the PDF templates + * + * @return array The PDF template header information + * + * @throws Exception Thrown if file found not to be valid + * + * @since 5.2 + */ + public function check_for_valid_pdf_templates( $files = [] ) { + foreach ( $files as $file ) { + + /* Check if we have a valid v4 template header in the file */ + $info = $this->templates->get_template_info_by_path( $file ); + + if ( ! isset( $info['template'] ) || strlen( $info['template'] ) === 0 ) { + /* Check if it's a v3 template */ + $fp = fopen( $file, 'r' ); + $file_data = fread( $fp, 8192 ); + fclose( $fp ); + + /* Check the first 8kiB contains the string RGForms or GFForms, which signifies our v3 templates */ + if ( strpos( $file_data, 'RGForms' ) === false && strpos( $file_data, 'GFForms' ) === false ) { + // throw new Exception( sprintf( esc_html__( 'The PHP file %s is not a valid PDF Template.', 'gravity-forms-pdf-extended' ), + // basename( $file ) ) ); + return new \WP_Error( 'invalid_template', 'The PHP file is not a valid PDF Template.', [ 'status' => 422 ] ); + } + } + } + } + + + /** + * Register our PDF save font endpoint + * + * @param WP_REST_Request $request + * + * @return \WP_REST_Response + * + * @since 5.2 + */ + public function ajax_process_delete_template( \WP_REST_Request $request ) { + + /* get the json parameter */ + $params = $request->get_json_params(); + + $template_id = ( isset( $params['id'] ) ) ? $params['id'] : ''; + + /* Get all the necessary PDF template files to delete */ + try { + $this->delete_template( $template_id ); + } catch ( Exception $e ) { + /* Bad Request */ + return new \WP_Error( 'process_delete_template', $e->getMessage(), [ 'status' => 400 ] ); + } + + $this->templates->flush_template_transient_cache(); + + // header( 'Content-Type: application/json' ); + // echo json_encode( true ); + + /* Okay Response */ + return [ 'message' => 'Template deleted successfully' ]; + } + + /** + * Delete's a PDF templates files + * + * @param string $template_id + * + * @throws Exception + * + * @since 5.2 + */ + public function delete_template( $template_id ) { + try { + $files = $this->templates->get_template_files_by_id( $template_id ); + $config = $this->templates->get_config_class( $template_id ); + + /* Check if the PDF config impliments our Setup/TearDown interface and run the tear down */ + if ( in_array( 'GFPDF\Helper\Helper_Interface_Setup_TearDown', class_implements( $config ) ) ) { + $config->tearDown(); + } + + /* Remove the PDF template files */ + foreach ( $files as $file ) { + unlink( $file ); + } + } catch ( Exception $e ) { + // throw $e; /* throw further down the chain */ + return new \WP_Error( 'delete_template', $e->getMessage(), [ 'status' => 500 ] ); + } + } + +} diff --git a/src/bootstrap.php b/src/bootstrap.php index cdff420a8..d87fe43a1 100644 --- a/src/bootstrap.php +++ b/src/bootstrap.php @@ -9,6 +9,9 @@ use GFPDF_Core; + +use GFPDF\Plugins\Previewer\API\RegisterPdfViewerAPIEndpoint; + /** * Bootstrap / Router Class * The bootstrap is loaded on WordPress 'plugins_loaded' functionality @@ -140,6 +143,17 @@ class Router implements Helper\Helper_Interface_Actions, Helper\Helper_Interface * * @since 4.0 */ + + /** + * Holds our Helper_Migration object + * which we can autoload with any data needed + * + * @var \GFPDF\Helper\Helper_Migration + * + * @since 5.2 + */ + protected $migration; + public function __call( $name, $arguments ) { trigger_error( sprintf( esc_html__( '"%s" has been deprecated as of Gravity PDF 4.0', 'gravity-forms-pdf-extended' ), $name ), E_USER_DEPRECATED ); } @@ -217,6 +231,17 @@ public function init() { $this->templates ); + /* Set up our migration object */ + $this->migration = new Helper\Helper_Migration( + $this->gform, + $this->log, + $this->data, + $this->options, + $this->misc, + $this->notices, + $this->templates + ); + /* Setup our Singleton object */ $this->singleton = new Helper\Helper_Singleton(); @@ -232,6 +257,7 @@ public function init() { $this->template_manager(); $this->load_core_font_handler(); $this->load_debug(); + $this->api(); /* Add localisation support */ $this->add_localization_support(); @@ -266,6 +292,7 @@ public function add_actions() { /* Cache our Gravity PDF Settings and register our settings fields with the Options API */ add_action( 'init', [ $this, 'init_settings_api' ], 1 ); add_action( 'admin_init', [ $this, 'setup_settings_fields' ], 1 ); + } /** @@ -277,6 +304,7 @@ public function add_actions() { */ public function add_filters() { + /* Automatically handle GF noconflict mode */ add_filter( 'gform_noconflict_scripts', [ $this, 'auto_noconflict_scripts' ] ); add_filter( 'gform_noconflict_styles', [ $this, 'auto_noconflict_styles' ] ); @@ -891,6 +919,30 @@ public function load_debug() { $this->singleton->add_class( $class ); } + public function api() { + $apis = [ + new Api\V1\Fonts\Core\Api_Fonts_Core( $this->log, $this->data->template_font_location ), + new Api\V1\Fonts\Api_Fonts( $this->log, $this->misc, $this->data, $this->options ), + new Api\V1\License\Api_License( $this->log, $this->data ), + new Api\V1\Migration\Multisite\Api_Migration_v4( $this->log, $this->options, $this->data, $this->migration ), + new Api\V1\Pdf\Settings\Api_Pdf_Settings( $this->log, $this->misc, $this->data->template_font_location ), + new Api\V1\Security\Tmp\Api_Security_Tmp_Directory( $this->log, $this->misc, $this->data ), + new Api\V1\Template\Api_Template( $this->log, $this->misc, $this->data, $this->options, $this->templates ), + ]; + + foreach ( $apis as $api ) { + $trait = class_uses( $api ); + if ( isset( $trait['GFPDF\Helper\Helper_Trait_Logger'] ) ) { + $api->set_logger( $this->log ); + } + + $api->init(); + + $this->singleton->add_class( $api ); + } + + } + /** * Initialise our background PDF processing handler * diff --git a/tests/phpunit/unit-tests/api/ApiFontsEndpointRoutes.php b/tests/phpunit/unit-tests/api/ApiFontsEndpointRoutes.php new file mode 100644 index 000000000..6babcad89 --- /dev/null +++ b/tests/phpunit/unit-tests/api/ApiFontsEndpointRoutes.php @@ -0,0 +1,168 @@ +log = GPDFAPI::get_log_class(); + $this->options = GPDFAPI::get_options_class(); + $this->data = GPDFAPI::get_data_class(); + $this->misc = GPDFAPI::get_misc_class(); + + $this->class = new Api_Fonts( $this->log, $this->misc, $this->data, $this->options ); + $this->class->init(); + + parent::setUp(); + } + + /** + * Test our endpoints are registered correctly + * + * @since 5.2 + */ + public function test_rest_api_fonts_endpoints() { + $wp_rest_server = rest_get_server(); + do_action( 'rest_api_init' ); + + $this->assertContains( 'gravity-pdf/v1', $wp_rest_server->get_namespaces() ); + $this->assertArrayHasKey( '/gravity-pdf/v1/fonts', $wp_rest_server->get_routes() ); + } + + /** + * @since 5.2 + */ + public function test_rest_api_save_font() { + + $request = new WP_REST_Request( \WP_REST_Server::CREATABLE, '/gravity-pdf/v1/fonts/save_font' ); + + $request->set_body_params( [ + 'payload' => [], + ] ); + + /* Test empty font name */ + $response = $this->class->save_font( $request ); + + if ( is_wp_error( $response ) ) { + $res = $response->get_error_data( 'required_fields_missing' ); + $this->assertSame( 400, $res['status'] ); + } + + /* Mock remote request and simulate success */ + $request->set_body_params( [ + 'payload' => 'Test', + ] ); + + $api_response = function() { + return new WP_Error(); + }; + + add_filter( 'pre_http_request', $api_response ); + + $response = $this->class->save_font( $request ); + + $this->assertTrue( is_wp_error( $response ) ); + + remove_filter( 'pre_http_request', $api_response ); + + } + + + /** + * @since 5.2 + */ + public function test_rest_api_delete_font() { + + $request = new WP_REST_Request( \WP_REST_Server::DELETABLE, '/gravity-pdf/v1/fonts/delete_font' ); + + $request->set_body_params( [ + 'payload' => '', + ] ); + + /* Test empty font name */ + $response = $this->class->delete_font( $request ); + + if ( is_wp_error( $response ) ) { + $res = $response->get_error_data( 'delete_font' ); + $this->assertSame( 500, $res['status'] ); + } + + /* Mock remote request and simulate success */ + $request->set_body_params( [ + 'payload' => 'Test', + ] ); + + $api_response = function() { + return new WP_Error(); + }; + + add_filter( 'pre_http_request', $api_response ); + + $response = $this->class->delete_font( $request ); + $this->assertTrue( is_wp_error( $response ) ); + + remove_filter( 'pre_http_request', $api_response ); + + } + +} diff --git a/tests/phpunit/unit-tests/api/ApiLicenseEndpointRoutes.php b/tests/phpunit/unit-tests/api/ApiLicenseEndpointRoutes.php new file mode 100644 index 000000000..99316a9b1 --- /dev/null +++ b/tests/phpunit/unit-tests/api/ApiLicenseEndpointRoutes.php @@ -0,0 +1,136 @@ +log = GPDFAPI::get_log_class(); + $this->data = GPDFAPI::get_data_class(); + + $this->class = new Api_License( $this->log, $this->data ); + $this->class->init(); + + parent::setUp(); + } + + /** + * Test our endpoints are registered correctly + * + * @since 5.2 + * + */ + public function test_rest_api_license_endpoints() { + + $wp_rest_server = rest_get_server(); + do_action( 'rest_api_init' ); + + $this->assertContains( 'gravity-pdf/v1', $wp_rest_server->get_namespaces() ); + + $this->assertArrayHasKey( '/gravity-pdf/v1/license/(?P\d+)/deactivate', $wp_rest_server->get_routes() ); + + } + + /** + * @since 5.2 + */ + public function test_rest_api_process_license_deactivation() { + + $request = new WP_REST_Request( \WP_REST_Server::CREATABLE, '/gravity-pdf/v1/license/(?P\d+)/deactivate' ); + + $request->set_body_params( [ + 'addon_name' => '', + 'license' => '' + ] ); + + $res = $request->get_body_params(); + + /* Test empty required parameters */ + $response = $this->class->deactivate_license_key( $this->data->addon($res['addon_name']), $res['license'] ); + + if ( is_wp_error( $response ) ) { + $res = $response->get_error_data( 'required_fields_missing' ); + $this->assertSame( 400, $res['status'] ); + } + + /* Mock remote request and simulate success */ + $request->set_body_params( [ + 'addon_name' => 'Test', + 'license' => '12345678' + ] ); + + $api_response = function() { + return new WP_Error(); + }; + + add_filter( 'pre_http_request', $api_response ); + + $response = $this->class->deactivate_license_key( $request ); + + $this->assertTrue( is_wp_error( $response ) ); + + remove_filter( 'pre_http_request', $api_response ); + + } + +} diff --git a/tests/phpunit/unit-tests/api/ApiTemplateEndpointRoutes.php b/tests/phpunit/unit-tests/api/ApiTemplateEndpointRoutes.php new file mode 100644 index 000000000..27e96a965 --- /dev/null +++ b/tests/phpunit/unit-tests/api/ApiTemplateEndpointRoutes.php @@ -0,0 +1,211 @@ +log = GPDFAPI::get_log_class(); + $this->options = GPDFAPI::get_options_class(); + $this->data = GPDFAPI::get_data_class(); + $this->misc = GPDFAPI::get_misc_class(); + + $this->class = new Api_Template( $this->log, $this->misc, $this->data, $this->options ); + $this->class->init(); + + parent::setUp(); + } + + /** + * Test our endpoints are registered correctly + * + * @since 5.2 + */ + public function test_rest_api_template_endpoints() { + $wp_rest_server = rest_get_server(); + do_action( 'rest_api_init' ); + + $this->assertContains( 'gravity-pdf/v1', $wp_rest_server->get_namespaces() ); + $this->assertArrayHasKey( '/gravity-pdf/v1/template', $wp_rest_server->get_routes() ); + } + + /** + * @since 5.2 + */ + public function test_ajax_process_build_template_options_html() { + + $request = new WP_REST_Request( \WP_REST_Server::CREATABLE, '/gravity-pdf/v1/template' ); + + // $request->set_body_params( [ + // 'font_name' => '', + // ] ); + + /* Test empty font name */ + $response = $this->class->ajax_process_build_template_options_html( $request ); + + if ( is_wp_error( $response ) ) { + $res = $response->get_error_data( 'ajax_process_build_template_options_html' ); + $this->assertSame( 400, $res['status'] ); + } + + /* Mock remote request and simulate success */ + // $request->set_body_params( [ + // 'font_name' => 'Test', + // ] ); + + $api_response = function() { + return new WP_Error(); + }; + + add_filter( 'pre_http_request', $api_response ); + + $response = $this->class->save_core_font( $request ); + $this->assertTrue( is_wp_error( $response ) ); + + remove_filter( 'pre_http_request', $api_response ); + + } + + + /** + * @since 5.2 + */ + public function test_ajax_process_uploaded_template() { + + $request = new WP_REST_Request( \WP_REST_Server::CREATABLE, '/gravity-pdf/v1/template' ); + + // $request->set_body_params( [ + // 'font_name' => '', + // ] ); + + /* Test empty font name */ + $response = $this->class->test_ajax_process_uploaded_template( $request ); + + if ( is_wp_error( $response ) ) { + $res = $response->get_error_data( 'test_ajax_process_uploaded_template' ); + $this->assertSame( 400, $res['status'] ); + } + + /* Mock remote request and simulate success */ + // $request->set_body_params( [ + // 'font_name' => 'Test', + // ] ); + + $api_response = function() { + return new WP_Error(); + }; + + add_filter( 'pre_http_request', $api_response ); + + $response = $this->class->save_core_font( $request ); + $this->assertTrue( is_wp_error( $response ) ); + + remove_filter( 'pre_http_request', $api_response ); + + } + + + /** + * @since 5.2 + */ + public function test_ajax_process_delete_template() { + + $request = new WP_REST_Request( \WP_REST_Server::CREATABLE, '/gravity-pdf/v1/template' ); + + // $request->set_body_params( [ + // 'font_name' => '', + // ] ); + + /* Test empty font name */ + $response = $this->class->ajax_process_delete_template( $request ); + + if ( is_wp_error( $response ) ) { + $res = $response->get_error_data( 'ajax_process_delete_template' ); + $this->assertSame( 400, $res['status'] ); + } + + /* Mock remote request and simulate success */ + // $request->set_body_params( [ + // 'font_name' => 'Test', + // ] ); + + $api_response = function() { + return new WP_Error(); + }; + + add_filter( 'pre_http_request', $api_response ); + + $response = $this->class->save_core_font( $request ); + $this->assertTrue( is_wp_error( $response ) ); + + remove_filter( 'pre_http_request', $api_response ); + + } + + + protected function stub_remote_request( $response ) { + + } + + protected function unstub_remote_request() { + + } + +} diff --git a/tests/phpunit/unit-tests/api/FontCoreApiEndpointRoutes.php b/tests/phpunit/unit-tests/api/FontCoreApiEndpointRoutes.php new file mode 100644 index 000000000..b4ae0639d --- /dev/null +++ b/tests/phpunit/unit-tests/api/FontCoreApiEndpointRoutes.php @@ -0,0 +1,131 @@ +log = GPDFAPI::get_log_class(); + + $this->class = new Api_Fonts_Core( $this->log, '' ); + $this->class->init(); + + parent::setUp(); + } + + /** + * Test our endpoints are registered correctly + * + * @since 5.2 + */ + public function test_rest_api_font_core_endpoints() { + $wp_rest_server = rest_get_server(); + do_action( 'rest_api_init' ); + + $this->assertContains( 'gravity-pdf/v1', $wp_rest_server->get_namespaces() ); + $this->assertArrayHasKey( '/gravity-pdf/v1/fonts/core', $wp_rest_server->get_routes() ); + } + + /** + * @since 5.2 + */ + public function test_save_core_font() { + + $request = new WP_REST_Request( \WP_REST_Server::CREATABLE, '/gravity-pdf/v1/fonts/core' ); + + $request->set_body_params( [ + 'font_name' => '', + ] ); + + /* Test empty font name */ + $response = $this->class->save_core_font( $request ); + + if ( is_wp_error( $response ) ) { + $res = $response->get_error_data( 'download_and_save_font' ); + $this->assertSame( 400, $res['status'] ); + } + + /* Mock remote request and simulate success */ + $request->set_body_params( [ + 'font_name' => 'Test', + ] ); + + $api_response = function() { + return new WP_Error(); + }; + + add_filter( 'pre_http_request', $api_response ); + + $response = $this->class->save_core_font( $request ); + $this->assertTrue( is_wp_error( $response ) ); + + remove_filter( 'pre_http_request', $api_response ); + + } + + protected function stub_remote_request( $response ) { + + } + + protected function unstub_remote_request() { + + } + +} From b0254cc8f6429c3b8295a31c9d5d60ea789d37a1 Mon Sep 17 00:00:00 2001 From: Jake Jackson Date: Mon, 6 May 2019 12:30:32 +1000 Subject: [PATCH 2/7] Re-write unit tests for API License Endpoint, and clean up code --- src/Api/V1/License/Api_License.php | 57 +++---- .../{api => Api}/ApiFontsEndpointRoutes.php | 0 .../ApiTemplateEndpointRoutes.php | 0 .../FontCoreApiEndpointRoutes.php | 0 .../Api/V1/License/Test_Api_License.php | 156 ++++++++++++++++++ .../api/ApiLicenseEndpointRoutes.php | 136 --------------- 6 files changed, 177 insertions(+), 172 deletions(-) rename tests/phpunit/unit-tests/{api => Api}/ApiFontsEndpointRoutes.php (100%) rename tests/phpunit/unit-tests/{api => Api}/ApiTemplateEndpointRoutes.php (100%) rename tests/phpunit/unit-tests/{api => Api}/FontCoreApiEndpointRoutes.php (100%) create mode 100644 tests/phpunit/unit-tests/Api/V1/License/Test_Api_License.php delete mode 100644 tests/phpunit/unit-tests/api/ApiLicenseEndpointRoutes.php diff --git a/src/Api/V1/License/Api_License.php b/src/Api/V1/License/Api_License.php index 83f2fbde6..1e738cd06 100644 --- a/src/Api/V1/License/Api_License.php +++ b/src/Api/V1/License/Api_License.php @@ -5,8 +5,8 @@ use GFPDF\Api\V1\Base_Api; use GFPDF\Helper\Helper_Data; use GFPDF\Helper\Helper_Abstract_Addon; - use Psr\Log\LoggerInterface; +use WP_Error; /** * @package Gravity PDF @@ -48,20 +48,13 @@ class Api_License extends Base_Api { /** - * Holds our log class - * - * @var \Monolog\Logger - * - * @since 4.0 + * @var LoggerInterface + * @since 5.2 */ public $log; /** - * Holds our Helper_Data object - * which we can autoload with any data needed - * * @var \GFPDF\Helper\Helper_Data - * * @since 5.2 */ protected $data; @@ -69,20 +62,17 @@ class Api_License extends Base_Api { /** * Api_License constructor. * - * @param Helper_Data $data + * @param LoggerInterface $log + * @param Helper_Data $data * * @since 5.2 */ public function __construct( LoggerInterface $log, Helper_Data $data ) { - $this->log = $log; + $this->log = $log; $this->data = $data; } /** - * Register our PDF save font endpoint - * - * @Internal Use this endpoint to save fonts - * * @since 5.2 */ public function register() { @@ -101,39 +91,36 @@ public function register() { } /** - * Processes the rest API endpoint + * Processes the REST API endpoint * * @param \WP_REST_Request $request * - * @return array|\WP_Error + * @return array|WP_Error * * @since 5.2 */ public function process_license_deactivation( \WP_REST_Request $request ) { - $params = $request->get_json_params(); - - /* Get the required details */ - /* do not proceed if these are empty/null */ - if ( empty( $params['addon_name'] ) || empty( $params['license'] ) ) { - return new \WP_Error( 'process_license_deactivation', 'Required Field is missing, please try again', [ 'status' => 400 ] ); + if ( ! isset( $params['addon_name'], $params['license'] ) ) { + return new WP_Error( 'license_deactivation_fields_missing', 'Required Field is missing, please try again', [ 'status' => 400 ] ); } - $addon = $this->data->addon( $params['addon_name'] ); - /* Check add-on currently installed */ - if ( empty( $addon ) ) { - return new \WP_Error( 'process_license_deactivation', 'An error occurred during deactivation, please try again', [ 'status' => 404 ] ); + if ( empty( $this->data->addon[ $params['addon_name'] ] ) ) { + return new WP_Error( 'license_deactivation_addon_not_found', 'An error occurred during deactivation, please try again', [ 'status' => 404 ] ); } + $addon = $this->data->addon[ $params['addon_name'] ]; $was_deactivated = $this->deactivate_license_key( $addon, $params['license'] ); - + if ( ! $was_deactivated ) { $license_info = $addon->get_license_info(); - return new \WP_Error( 'schedule_license_check', $license_info['message'], [ 'status' => 400 ] ); + + return new WP_Error( 'license_deactivation_schedule_license_check', $license_info['message'], [ 'status' => 400 ] ); } $this->log->addNotice( 'Successfully Deactivated License' ); + return [ 'success' => esc_html__( 'License deactivated.', 'gravity-forms-pdf-extended' ) ]; } @@ -147,7 +134,7 @@ public function process_license_deactivation( \WP_REST_Request $request ) { * * @since 5.2 */ - public function deactivate_license_key( Helper_Abstract_Addon $addon, $license_key ) { + protected function deactivate_license_key( Helper_Abstract_Addon $addon, $license_key ) { $response = wp_remote_post( $this->data->store_url, [ @@ -161,18 +148,16 @@ public function deactivate_license_key( Helper_Abstract_Addon $addon, $license_k ] ); - /* If API error exit early */ - if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { + if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) !== 200 ) { return false; } - /* Get API response and check license is now deactivated */ + /* Verify license is now deactivated */ $license_data = json_decode( wp_remote_retrieve_body( $response ) ); if ( ! isset( $license_data->license ) || $license_data->license !== 'deactivated' ) { return false; } - /* Remove license data from database */ $addon->delete_license_info(); $this->log->addNotice( @@ -185,4 +170,4 @@ public function deactivate_license_key( Helper_Abstract_Addon $addon, $license_k return true; } -} \ No newline at end of file +} diff --git a/tests/phpunit/unit-tests/api/ApiFontsEndpointRoutes.php b/tests/phpunit/unit-tests/Api/ApiFontsEndpointRoutes.php similarity index 100% rename from tests/phpunit/unit-tests/api/ApiFontsEndpointRoutes.php rename to tests/phpunit/unit-tests/Api/ApiFontsEndpointRoutes.php diff --git a/tests/phpunit/unit-tests/api/ApiTemplateEndpointRoutes.php b/tests/phpunit/unit-tests/Api/ApiTemplateEndpointRoutes.php similarity index 100% rename from tests/phpunit/unit-tests/api/ApiTemplateEndpointRoutes.php rename to tests/phpunit/unit-tests/Api/ApiTemplateEndpointRoutes.php diff --git a/tests/phpunit/unit-tests/api/FontCoreApiEndpointRoutes.php b/tests/phpunit/unit-tests/Api/FontCoreApiEndpointRoutes.php similarity index 100% rename from tests/phpunit/unit-tests/api/FontCoreApiEndpointRoutes.php rename to tests/phpunit/unit-tests/Api/FontCoreApiEndpointRoutes.php diff --git a/tests/phpunit/unit-tests/Api/V1/License/Test_Api_License.php b/tests/phpunit/unit-tests/Api/V1/License/Test_Api_License.php new file mode 100644 index 000000000..d6348751c --- /dev/null +++ b/tests/phpunit/unit-tests/Api/V1/License/Test_Api_License.php @@ -0,0 +1,156 @@ +data = GPDFAPI::get_data_class(); + $this->class = new Api_License( GPDFAPI::get_log_class(), $this->data ); + $this->class->init(); + + parent::setUp(); + } + + /** + * @since 5.2 + */ + public function test_rest_api_license_endpoints() { + $wp_rest_server = rest_get_server(); + do_action( 'rest_api_init' ); + + $this->assertContains( 'gravity-pdf/v1', $wp_rest_server->get_namespaces() ); + $this->assertArrayHasKey( '/gravity-pdf/v1/license/(?P\d+)/deactivate', $wp_rest_server->get_routes() ); + } + + /** + * @param array $data + * + * @return WP_REST_Request + * + * @since 5.2 + */ + protected function get_request( $data ) { + $request = new WP_REST_Request(); + $request->set_body( json_encode( $data ) ); + $request->set_header( 'content-type', 'application/json' ); + $request->get_json_params(); + + return $request; + } + + /** + * @since 5.2 + */ + public function test_deactivate_license_key_validation() { + $request = $this->get_request( [ 'addon_name' => '', 'license' => '' ] ); + $response = $this->class->process_license_deactivation( $request ); + $this->assertSame( 400, $response->get_error_data( 'license_deactivation_fields_missing' )['status'] ); + + /* Test unregistered addon */ + $request = $this->get_request( [ 'addon_name' => 'test', 'license' => '12345' ] ); + $response = $this->class->process_license_deactivation( $request ); + $this->assertSame( 404, $response->get_error_data( 'license_deactivation_addon_not_found' )['status'] ); + } + + /** + * @since 5.2 + */ + public function test_deactivate_license_key_api() { + $request = $this->get_request( [ 'addon_name' => 'test', 'license' => '12345' ] ); + + /* Mock remote request and simulate success */ + $this->data->addon['test'] = new TestAddon( 'test', 'Test', 'Gravity PDF', '1.0', '', $this->data, GPDFAPI::get_options_class(), new Helper_Singleton(), new Helper_Logger( 'test', 'Test' ), GPDFAPI::get_notice_class() ); + + $api_response = function() { + return new \WP_Error(); + }; + + add_filter( 'pre_http_request', $api_response ); + + $response = $this->class->process_license_deactivation( $request ); + $this->assertSame( 400, $response->get_error_data( 'license_deactivation_schedule_license_check' )['status'] ); + + remove_filter( 'pre_http_request', $api_response ); + + /* Get a success test */ + $api_response = function() { + return [ + 'response' => [ 'code' => 200 ], + 'body' => json_encode( [ 'license' => 'deactivated' ] ), + ]; + }; + + add_filter( 'pre_http_request', $api_response ); + $response = $this->class->process_license_deactivation( $request ); + $this->assertArrayHasKey( 'success', $response ); + remove_filter( 'pre_http_request', $api_response ); + } +} + +class TestAddon extends Helper_Abstract_Addon { + public function plugin_updater() { + + } +} diff --git a/tests/phpunit/unit-tests/api/ApiLicenseEndpointRoutes.php b/tests/phpunit/unit-tests/api/ApiLicenseEndpointRoutes.php deleted file mode 100644 index 99316a9b1..000000000 --- a/tests/phpunit/unit-tests/api/ApiLicenseEndpointRoutes.php +++ /dev/null @@ -1,136 +0,0 @@ -log = GPDFAPI::get_log_class(); - $this->data = GPDFAPI::get_data_class(); - - $this->class = new Api_License( $this->log, $this->data ); - $this->class->init(); - - parent::setUp(); - } - - /** - * Test our endpoints are registered correctly - * - * @since 5.2 - * - */ - public function test_rest_api_license_endpoints() { - - $wp_rest_server = rest_get_server(); - do_action( 'rest_api_init' ); - - $this->assertContains( 'gravity-pdf/v1', $wp_rest_server->get_namespaces() ); - - $this->assertArrayHasKey( '/gravity-pdf/v1/license/(?P\d+)/deactivate', $wp_rest_server->get_routes() ); - - } - - /** - * @since 5.2 - */ - public function test_rest_api_process_license_deactivation() { - - $request = new WP_REST_Request( \WP_REST_Server::CREATABLE, '/gravity-pdf/v1/license/(?P\d+)/deactivate' ); - - $request->set_body_params( [ - 'addon_name' => '', - 'license' => '' - ] ); - - $res = $request->get_body_params(); - - /* Test empty required parameters */ - $response = $this->class->deactivate_license_key( $this->data->addon($res['addon_name']), $res['license'] ); - - if ( is_wp_error( $response ) ) { - $res = $response->get_error_data( 'required_fields_missing' ); - $this->assertSame( 400, $res['status'] ); - } - - /* Mock remote request and simulate success */ - $request->set_body_params( [ - 'addon_name' => 'Test', - 'license' => '12345678' - ] ); - - $api_response = function() { - return new WP_Error(); - }; - - add_filter( 'pre_http_request', $api_response ); - - $response = $this->class->deactivate_license_key( $request ); - - $this->assertTrue( is_wp_error( $response ) ); - - remove_filter( 'pre_http_request', $api_response ); - - } - -} From 5be6cac3780b013b98f0e334eadf93e5ba5639a3 Mon Sep 17 00:00:00 2001 From: Sag Gonzales Date: Tue, 7 May 2019 12:46:23 +0800 Subject: [PATCH 3/7] refactor Api Pdf Settings arrange folder structure --- src/Api/V1/Pdf/Settings/Api_Pdf_Settings.php | 88 +++++++------ .../Fonts/Core/Test_Fonts_Core.php} | 4 +- .../Fonts/Test_Api_Fonts.php} | 4 +- .../Api/V1/License/Test_Api_License.php | 4 +- .../V1/Pdf/Settings/Test_Api_Pdf_Settings.php | 116 ++++++++++++++++++ .../Settings/public_tmp_directory_test.txt | 1 + .../Template/Test_Api_Template.php} | 8 +- 7 files changed, 177 insertions(+), 48 deletions(-) rename tests/phpunit/unit-tests/Api/{FontCoreApiEndpointRoutes.php => V1/Fonts/Core/Test_Fonts_Core.php} (97%) rename tests/phpunit/unit-tests/Api/{ApiFontsEndpointRoutes.php => V1/Fonts/Test_Api_Fonts.php} (97%) create mode 100644 tests/phpunit/unit-tests/Api/V1/Pdf/Settings/Test_Api_Pdf_Settings.php create mode 100644 tests/phpunit/unit-tests/Api/V1/Pdf/Settings/public_tmp_directory_test.txt rename tests/phpunit/unit-tests/Api/{ApiTemplateEndpointRoutes.php => V1/Template/Test_Api_Template.php} (97%) diff --git a/src/Api/V1/Pdf/Settings/Api_Pdf_Settings.php b/src/Api/V1/Pdf/Settings/Api_Pdf_Settings.php index c442167fb..2bcecf470 100644 --- a/src/Api/V1/Pdf/Settings/Api_Pdf_Settings.php +++ b/src/Api/V1/Pdf/Settings/Api_Pdf_Settings.php @@ -5,6 +5,7 @@ use GFPDF\Api\V1\Base_Api; use GFPDF\Helper\Helper_Misc; use Psr\Log\LoggerInterface; +use WP_Error; /** * @package Gravity PDF @@ -116,7 +117,7 @@ public function register() { } /** - * Create a file in our tmp directory and check if it is publically accessible (i.e no .htaccess protection) + * Create a file in our tmp directory and check if it is publicly accessible (i.e no .htaccess protection) * * @param $_POST ['nonce'] * @@ -124,44 +125,25 @@ public function register() { * * @since 5.2 */ - public function check_tmp_pdf_security( \WP_REST_Request $request ) { + public function check_tmp_pdf_security() { + /* first create the test file in the tmp directory */ + $this->create_public_tmp_directory_test_file(); - /* Create our tmp file and do our actual check */ - $result = json_encode( $this->test_public_tmp_directory_access() ); - - if (!$result) { - return new \WP_Error( 'test_public_tmp_directory_access', 'Unable to create tmp Directory', [ 'status' => 401 ] ); - } - - return [ 'message' => 'Tmp file successfully created' ]; - } - - /** - * Create a file in our tmp directory and verify if it's protected from the public - * - * @return boolean - * - * @since 5.2 - */ - public function test_public_tmp_directory_access() { - - /* create our file */ - file_put_contents( $this->template_font_location . $this->tmp_test_file, 'failed-if-read' ); - - /* verify it exists */ - if ( is_file( $this->template_font_location . $this->tmp_test_file ) ) { - - /* Run our test */ + /* check if tmp directotyr file is publicly accessible */ + if ( file_exists( $this->template_font_location . $this->tmp_test_file ) ) { $site_url = $this->misc->convert_path_to_url( $this->template_font_location ); - - if ( $site_url !== false ) { - + + /* file found */ + if ( $site_url !== false ) { $response = wp_remote_get( $site_url . $this->tmp_test_file ); - if ( ! is_wp_error( $response ) ) { + /* Cleanup our test file */ + @unlink( $this->template_font_location . $this->tmp_test_file ); + if ( ! is_wp_error( $response ) ) { /* Check if the web server responded with a OK status code and we can read the contents of our file, then fail our test */ - if ( isset( $response['response']['code'] ) && $response['response']['code'] === 200 && + if ( isset( $response['response']['code'] ) && + $response['response']['code'] === 200 && isset( $response['body'] ) && $response['body'] === 'failed-if-read' ) { $response_object = $response['http_response']; @@ -174,17 +156,47 @@ public function test_public_tmp_directory_access() { 'response' => $raw_response->raw, ] ); - - $this->has_access = false; + //@todo which one to return + // success but file is publicly accessible + // return [ 'message' => 'Tmp file successfully created but publicly accessible', 'has_access' => true ]; + return true; } + //@todo which one to return + // success and file is secured + // return [ 'message' => 'Tmp file successfully created and not publicly accessible', 'has_access' => false ]; + return false; } + /* Unable to get url */ + return new WP_Error( 'wp_remote_get_response', 'Response Error', [ 'status' => 400 ] ); } + /* Unable to convert path to url */ + return new WP_Error( 'convert_path_to_url', 'Unable to find path to convert to url', [ 'status' => 404 ] ); } + /* file or directory not created */ + return new WP_Error( 'create_public_tmp_directory_test_file', 'Tmp directory and test file not found', [ 'status' => 404 ] ); + } - /* Cleanup our test file */ - @unlink( $this->template_font_location . $this->tmp_test_file ); + /** + * Create a file in our tmp directory + * + * @param $_POST ['nonce'] + * + * @return Bool + * @return WP_Error + * + * @since 5.2 + */ + public function create_public_tmp_directory_test_file() { + /* create our file */ + file_put_contents( $this->template_font_location . $this->tmp_test_file, 'failed-if-read' ); + + /* verify it exists */ + if ( is_file( $this->template_font_location . $this->tmp_test_file ) ) { + return true; + } - return $this->has_access; + return new \WP_Error( 'test_public_tmp_directory_access', 'Unable to create tmp Directory', [ 'status' => 401 ] ); + } } diff --git a/tests/phpunit/unit-tests/Api/FontCoreApiEndpointRoutes.php b/tests/phpunit/unit-tests/Api/V1/Fonts/Core/Test_Fonts_Core.php similarity index 97% rename from tests/phpunit/unit-tests/Api/FontCoreApiEndpointRoutes.php rename to tests/phpunit/unit-tests/Api/V1/Fonts/Core/Test_Fonts_Core.php index b4ae0639d..13737d63c 100644 --- a/tests/phpunit/unit-tests/Api/FontCoreApiEndpointRoutes.php +++ b/tests/phpunit/unit-tests/Api/V1/Fonts/Core/Test_Fonts_Core.php @@ -41,13 +41,13 @@ */ /** - * Class TestFontCoreApiEndpoint + * Class TestFontCore * * @package GFPDF\Tests\GravityPDF * * @group REST-API */ -class TestFontCoreApiEndpointRoutes extends WP_UnitTestCase { +class TestFontCore extends WP_UnitTestCase { /** * @var $class diff --git a/tests/phpunit/unit-tests/Api/ApiFontsEndpointRoutes.php b/tests/phpunit/unit-tests/Api/V1/Fonts/Test_Api_Fonts.php similarity index 97% rename from tests/phpunit/unit-tests/Api/ApiFontsEndpointRoutes.php rename to tests/phpunit/unit-tests/Api/V1/Fonts/Test_Api_Fonts.php index 6babcad89..66203cb20 100644 --- a/tests/phpunit/unit-tests/Api/ApiFontsEndpointRoutes.php +++ b/tests/phpunit/unit-tests/Api/V1/Fonts/Test_Api_Fonts.php @@ -44,13 +44,13 @@ */ /** - * Class TestApiFontsEndpoint + * Class TestApiFonts * * @package GFPDF\Tests\GravityPDF * * @group REST-API */ -class TestApiFontsEndpointRoutes extends WP_UnitTestCase { +class TestApiFonts extends WP_UnitTestCase { /** * @var $class diff --git a/tests/phpunit/unit-tests/Api/V1/License/Test_Api_License.php b/tests/phpunit/unit-tests/Api/V1/License/Test_Api_License.php index d6348751c..e2ca84e77 100644 --- a/tests/phpunit/unit-tests/Api/V1/License/Test_Api_License.php +++ b/tests/phpunit/unit-tests/Api/V1/License/Test_Api_License.php @@ -43,13 +43,13 @@ */ /** - * Class TestApiLicenseEndpoint + * Class TestApiLicense * * @package GFPDF\Tests\GravityPDF * * @group REST-API */ -class TestApiLicenseEndpointRoutes extends WP_UnitTestCase { +class TestApiLicense extends WP_UnitTestCase { /** * @var Api_License diff --git a/tests/phpunit/unit-tests/Api/V1/Pdf/Settings/Test_Api_Pdf_Settings.php b/tests/phpunit/unit-tests/Api/V1/Pdf/Settings/Test_Api_Pdf_Settings.php new file mode 100644 index 000000000..4ba066a83 --- /dev/null +++ b/tests/phpunit/unit-tests/Api/V1/Pdf/Settings/Test_Api_Pdf_Settings.php @@ -0,0 +1,116 @@ +template_font_location = plugin_dir_path(__FILE__) . 'tmp/gravityforms/fonts/'; + + // $this->data = GPDFAPI::get_data_class(); + $this->class = new Api_Pdf_Settings( GPDFAPI::get_log_class(), GPDFAPI::get_misc_class(), $this->template_font_location ); + $this->class->init(); + + parent::setUp(); + } + + /** + * @since 5.2 + */ + public function test_rest_api_license_endpoints() { + $wp_rest_server = rest_get_server(); + do_action( 'rest_api_init' ); + + $this->assertContains( 'gravity-pdf/v1', $wp_rest_server->get_namespaces() ); + $this->assertArrayHasKey( '/gravity-pdf/v1/pdf/settings', $wp_rest_server->get_routes() ); + } + + /** + * @since 5.2 + */ + public function test_check_tmp_pdf_security() { + + /* Test unable to access directory */ + $response = $this->class->check_tmp_pdf_security(); + + $this->assertSame( 401, $response->get_error_data( 'test_public_tmp_directory_access' )['status'] ); + + /* Test successful access on directory */ + // $request = $this->get_request( [ 'addon_name' => 'test', 'license' => '12345' ] ); + $response = $this->class->test_public_tmp_directory_access(); + $this->assertSame( 200, $response->get_error_data( 'test_public_tmp_directory_access' )['status'] ); + } + + +} diff --git a/tests/phpunit/unit-tests/Api/V1/Pdf/Settings/public_tmp_directory_test.txt b/tests/phpunit/unit-tests/Api/V1/Pdf/Settings/public_tmp_directory_test.txt new file mode 100644 index 000000000..bf4ae67bc --- /dev/null +++ b/tests/phpunit/unit-tests/Api/V1/Pdf/Settings/public_tmp_directory_test.txt @@ -0,0 +1 @@ +failed-if-read \ No newline at end of file diff --git a/tests/phpunit/unit-tests/Api/ApiTemplateEndpointRoutes.php b/tests/phpunit/unit-tests/Api/V1/Template/Test_Api_Template.php similarity index 97% rename from tests/phpunit/unit-tests/Api/ApiTemplateEndpointRoutes.php rename to tests/phpunit/unit-tests/Api/V1/Template/Test_Api_Template.php index 27e96a965..e9201f101 100644 --- a/tests/phpunit/unit-tests/Api/ApiTemplateEndpointRoutes.php +++ b/tests/phpunit/unit-tests/Api/V1/Template/Test_Api_Template.php @@ -1,9 +1,9 @@ Date: Wed, 8 May 2019 07:32:21 +0800 Subject: [PATCH 4/7] add test case for api pdf settings --- src/Api/V1/Pdf/Settings/Api_Pdf_Settings.php | 6 ++-- .../V1/Pdf/Settings/Test_Api_Pdf_Settings.php | 34 +++++++++++++------ .../Settings/public_tmp_directory_test.txt | 1 - 3 files changed, 27 insertions(+), 14 deletions(-) delete mode 100644 tests/phpunit/unit-tests/Api/V1/Pdf/Settings/public_tmp_directory_test.txt diff --git a/src/Api/V1/Pdf/Settings/Api_Pdf_Settings.php b/src/Api/V1/Pdf/Settings/Api_Pdf_Settings.php index 2bcecf470..ebae7c213 100644 --- a/src/Api/V1/Pdf/Settings/Api_Pdf_Settings.php +++ b/src/Api/V1/Pdf/Settings/Api_Pdf_Settings.php @@ -134,7 +134,7 @@ public function check_tmp_pdf_security() { $site_url = $this->misc->convert_path_to_url( $this->template_font_location ); /* file found */ - if ( $site_url !== false ) { + if ( $site_url !== null ) { $response = wp_remote_get( $site_url . $this->tmp_test_file ); /* Cleanup our test file */ @@ -166,7 +166,7 @@ public function check_tmp_pdf_security() { // return [ 'message' => 'Tmp file successfully created and not publicly accessible', 'has_access' => false ]; return false; } - /* Unable to get url */ + /* Unable to access url */ return new WP_Error( 'wp_remote_get_response', 'Response Error', [ 'status' => 400 ] ); } /* Unable to convert path to url */ @@ -195,7 +195,7 @@ public function create_public_tmp_directory_test_file() { return true; } - return new \WP_Error( 'test_public_tmp_directory_access', 'Unable to create tmp Directory', [ 'status' => 401 ] ); + return new \WP_Error( 'test_public_tmp_directortest_public_tmp_directory_accessy_access', 'Unable to create tmp Directory', [ 'status' => 401 ] ); } diff --git a/tests/phpunit/unit-tests/Api/V1/Pdf/Settings/Test_Api_Pdf_Settings.php b/tests/phpunit/unit-tests/Api/V1/Pdf/Settings/Test_Api_Pdf_Settings.php index 4ba066a83..a9ad8a12b 100644 --- a/tests/phpunit/unit-tests/Api/V1/Pdf/Settings/Test_Api_Pdf_Settings.php +++ b/tests/phpunit/unit-tests/Api/V1/Pdf/Settings/Test_Api_Pdf_Settings.php @@ -76,7 +76,7 @@ class TestApiPdfSettings extends WP_UnitTestCase { */ public function setUp() { - $this->template_font_location = plugin_dir_path(__FILE__) . 'tmp/gravityforms/fonts/'; +// $this->template_font_location = 'tmp/gravityforms/fonts/'; // $this->data = GPDFAPI::get_data_class(); $this->class = new Api_Pdf_Settings( GPDFAPI::get_log_class(), GPDFAPI::get_misc_class(), $this->template_font_location ); @@ -96,21 +96,35 @@ public function test_rest_api_license_endpoints() { $this->assertArrayHasKey( '/gravity-pdf/v1/pdf/settings', $wp_rest_server->get_routes() ); } + /** * @since 5.2 */ - public function test_check_tmp_pdf_security() { - - /* Test unable to access directory */ - $response = $this->class->check_tmp_pdf_security(); + public function test_create_public_tmp_directory_test_file() { + /* Test able to write to directory */ + $response = $this->class->create_public_tmp_directory_test_file(); + $this->assertTrue( $response ); - $this->assertSame( 401, $response->get_error_data( 'test_public_tmp_directory_access' )['status'] ); + /* Test unable to write to directory */ + // $this->assertFileExists( $this->template_font_location . $this->tmp_test_file ); - /* Test successful access on directory */ - // $request = $this->get_request( [ 'addon_name' => 'test', 'license' => '12345' ] ); - $response = $this->class->test_public_tmp_directory_access(); - $this->assertSame( 200, $response->get_error_data( 'test_public_tmp_directory_access' )['status'] ); } + /** + * @since 5.2 + */ + public function test_check_tmp_pdf_security( ) { + + $response = $this->class->check_tmp_pdf_security(); + + /* Test unable to conver path to URL */ + $this->assertSame( 404, $response->get_error_data( 'convert_path_to_url' )['status'] ); + + /* Test unable to access generated URL */ + $this->assertSame( 400, $response->get_error_data( 'wp_remote_get_response' )['status'] ); + + // Test able to read the content of file + + } } diff --git a/tests/phpunit/unit-tests/Api/V1/Pdf/Settings/public_tmp_directory_test.txt b/tests/phpunit/unit-tests/Api/V1/Pdf/Settings/public_tmp_directory_test.txt deleted file mode 100644 index bf4ae67bc..000000000 --- a/tests/phpunit/unit-tests/Api/V1/Pdf/Settings/public_tmp_directory_test.txt +++ /dev/null @@ -1 +0,0 @@ -failed-if-read \ No newline at end of file From d459093de49a26ee5f447aec6dac5df7e555cc85 Mon Sep 17 00:00:00 2001 From: Sag Gonzales Date: Wed, 8 May 2019 07:41:15 +0800 Subject: [PATCH 5/7] cleaned up file --- .../Api/V1/Pdf/Settings/Test_Api_Pdf_Settings.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/tests/phpunit/unit-tests/Api/V1/Pdf/Settings/Test_Api_Pdf_Settings.php b/tests/phpunit/unit-tests/Api/V1/Pdf/Settings/Test_Api_Pdf_Settings.php index a9ad8a12b..ab5ecee44 100644 --- a/tests/phpunit/unit-tests/Api/V1/Pdf/Settings/Test_Api_Pdf_Settings.php +++ b/tests/phpunit/unit-tests/Api/V1/Pdf/Settings/Test_Api_Pdf_Settings.php @@ -74,11 +74,7 @@ class TestApiPdfSettings extends WP_UnitTestCase { /** * @since 5.2 */ - public function setUp() { - -// $this->template_font_location = 'tmp/gravityforms/fonts/'; - - // $this->data = GPDFAPI::get_data_class(); + public function setUp() { $this->class = new Api_Pdf_Settings( GPDFAPI::get_log_class(), GPDFAPI::get_misc_class(), $this->template_font_location ); $this->class->init(); @@ -96,7 +92,6 @@ public function test_rest_api_license_endpoints() { $this->assertArrayHasKey( '/gravity-pdf/v1/pdf/settings', $wp_rest_server->get_routes() ); } - /** * @since 5.2 */ @@ -107,7 +102,6 @@ public function test_create_public_tmp_directory_test_file() { /* Test unable to write to directory */ // $this->assertFileExists( $this->template_font_location . $this->tmp_test_file ); - } /** @@ -124,7 +118,6 @@ public function test_check_tmp_pdf_security( ) { $this->assertSame( 400, $response->get_error_data( 'wp_remote_get_response' )['status'] ); // Test able to read the content of file - } } From 22c1b8016a52b9f8136796089f19602f3741456c Mon Sep 17 00:00:00 2001 From: Sag Gonzales Date: Wed, 8 May 2019 09:31:29 +0800 Subject: [PATCH 6/7] adding migration test case --- .../Migration/Multisite/Api_Migration_v4.php | 6 +- .../Multisite/Test_Api_Migration_v4.php | 128 ++++++++++++++++++ .../V1/Pdf/Settings/Test_Api_Pdf_Settings.php | 6 +- 3 files changed, 132 insertions(+), 8 deletions(-) create mode 100644 tests/phpunit/unit-tests/Api/V1/Migration/Multisite/Test_Api_Migration_v4.php diff --git a/src/Api/V1/Migration/Multisite/Api_Migration_v4.php b/src/Api/V1/Migration/Multisite/Api_Migration_v4.php index 2b1a89f32..386fdbaeb 100644 --- a/src/Api/V1/Migration/Multisite/Api_Migration_v4.php +++ b/src/Api/V1/Migration/Multisite/Api_Migration_v4.php @@ -113,7 +113,7 @@ public function register() { '/migration/multisite/', [ 'methods' => \WP_REST_Server::CREATABLE, - 'callback' => [ $this, 'ajax_multisite_v3_migration' ], + 'callback' => [ $this, 'multisite_v3_migration' ], 'permission_callback' => function() { return $this->has_capabilities( 'manage_sites' ); @@ -131,13 +131,13 @@ public function register() { * * @since 5.2 */ - public function ajax_multisite_v3_migration( \WP_REST_Request $request ) { + public function multisite_v3_migration( \WP_REST_Request $request ) { $params = $request->get_json_params(); /* Ensure multisite website */ if ( ! is_multisite() ) { - return new \WP_Error( 'ajax_multisite_v3_migration', 'You are not authorized to perform this action. Please try again.', [ 'status' => 401 ] ); + return new \WP_Error( 'multisite_v3_migration', 'You are not authorized to perform this action. Please try again.', [ 'status' => 401 ] ); } /* Check there's a configuration file to migrate */ diff --git a/tests/phpunit/unit-tests/Api/V1/Migration/Multisite/Test_Api_Migration_v4.php b/tests/phpunit/unit-tests/Api/V1/Migration/Multisite/Test_Api_Migration_v4.php new file mode 100644 index 000000000..7e85634ce --- /dev/null +++ b/tests/phpunit/unit-tests/Api/V1/Migration/Multisite/Test_Api_Migration_v4.php @@ -0,0 +1,128 @@ +data = GPDFAPI::get_data_class(); + $this->class = new Api_Migration_v4( GPDFAPI::get_log_class(), GPDFAPI::get_options_class(), $this->data, GPDFAPI::get_migration_class() ); + + $this->class->init(); + + parent::setUp(); + } + + /** + * @since 5.2 + */ + public function test_rest_api_license_endpoints() { + $wp_rest_server = rest_get_server(); + do_action( 'rest_api_init' ); + + $this->assertContains( 'gravity-pdf/v1', $wp_rest_server->get_namespaces() ); + $this->assertArrayHasKey( '/gravity-pdf/v1/migration/multisite', $wp_rest_server->get_routes() ); + } + + /** + * @param array $data + * + * @return WP_REST_Request + * + * @since 5.2 + */ + protected function get_request( $data ) { + $request = new WP_REST_Request(); + $request->set_body( json_encode( $data ) ); + $request->set_header( 'content-type', 'application/json' ); + $request->get_json_params(); + + return $request; + } + + /** + * @since 5.2 + */ + public function test_multisite_v3_migration() { + + $request = $this->get_request( [ 'addon_name' => '', 'license' => '' ] ); + $response = $this->class->multisite_v3_migration( $request ); + + $this->assertSame( 400, $response->get_error_data( 'license_deactivation_fields_missing' )['status'] ); + + /* Test unregistered addon */ + $request = $this->get_request( [ 'addon_name' => 'test', 'license' => '12345' ] ); + $response = $this->class->process_license_deactivation( $request ); + $this->assertSame( 404, $response->get_error_data( 'license_deactivation_addon_not_found' )['status'] ); + } + + +} + +// class TestAddon extends Helper_Abstract_Addon { +// public function plugin_updater() { + +// } +// } diff --git a/tests/phpunit/unit-tests/Api/V1/Pdf/Settings/Test_Api_Pdf_Settings.php b/tests/phpunit/unit-tests/Api/V1/Pdf/Settings/Test_Api_Pdf_Settings.php index ab5ecee44..a95457737 100644 --- a/tests/phpunit/unit-tests/Api/V1/Pdf/Settings/Test_Api_Pdf_Settings.php +++ b/tests/phpunit/unit-tests/Api/V1/Pdf/Settings/Test_Api_Pdf_Settings.php @@ -2,10 +2,6 @@ namespace GFPDF\Api\V1\Pdf\Settings; -use GFPDF\Helper\Helper_Data; -use GFPDF\Helper\Helper_Abstract_Addon; -use GFPDF\Helper\Helper_Logger; -use GFPDF\Helper\Helper_Singleton; use WP_UnitTestCase; use WP_REST_Request; use GPDFAPI; @@ -115,7 +111,7 @@ public function test_check_tmp_pdf_security( ) { $this->assertSame( 404, $response->get_error_data( 'convert_path_to_url' )['status'] ); /* Test unable to access generated URL */ - $this->assertSame( 400, $response->get_error_data( 'wp_remote_get_response' )['status'] ); + // $this->assertSame( 400, $response->get_error_data( 'wp_remote_get_response' )['status'] ); // Test able to read the content of file } From d467101f375ee72e258b34be89f58291129d38ee Mon Sep 17 00:00:00 2001 From: Jake Jackson Date: Wed, 8 May 2019 12:37:09 +1000 Subject: [PATCH 7/7] Refractor the Api_Pdf_Settings class --- src/Api/V1/Pdf/Settings/Api_Pdf_Settings.php | 155 +++++------------- src/Exceptions/GravityPdfException.php | 38 +++++ src/Exceptions/GravityPdfRuntimeException.php | 38 +++++ src/bootstrap.php | 6 +- 4 files changed, 126 insertions(+), 111 deletions(-) create mode 100644 src/Exceptions/GravityPdfException.php create mode 100644 src/Exceptions/GravityPdfRuntimeException.php diff --git a/src/Api/V1/Pdf/Settings/Api_Pdf_Settings.php b/src/Api/V1/Pdf/Settings/Api_Pdf_Settings.php index ebae7c213..0193f3e72 100644 --- a/src/Api/V1/Pdf/Settings/Api_Pdf_Settings.php +++ b/src/Api/V1/Pdf/Settings/Api_Pdf_Settings.php @@ -3,8 +3,7 @@ namespace GFPDF\Api\V1\Pdf\Settings; use GFPDF\Api\V1\Base_Api; -use GFPDF\Helper\Helper_Misc; -use Psr\Log\LoggerInterface; +use GFPDF\Exceptions\GravityPdfRuntimeException; use WP_Error; /** @@ -44,59 +43,27 @@ */ class Api_Pdf_Settings extends Base_Api { - /** - * Holds our log class - * - * @var \Monolog\Logger - * - * @since 4.0 - */ - public $log; - /** * @var string - * * @since 5.2 */ - protected $template_font_location; - - /** - * @var boolean - * - * @since 5.2 - */ - protected $has_access = true; + protected $test_file_path; /** * @var string - * * @since 5.2 */ - protected $tmp_test_file = 'public_tmp_directory_test.txt'; - - /** - * Holds our Helper_Misc object - * Makes it easy to access common methods throughout the plugin - * - * @var \GFPDF\Helper\Helper_Misc - * - * @since 5.2 - */ - protected $misc; + protected $test_file_url; /** * Api_Pdf_Settings constructor. * - * @param Helper_Misc $misc - * - * @param string $template_font_location The absolute path to the current PDF font directory - * - * @since 5.2 + * @param string $test_file_path + * @param string $test_file_url */ - public function __construct( LoggerInterface $log, Helper_Misc $misc, $template_font_location ) { - $this->log = $log; - $this->misc = $misc; - $this->template_font_location = $template_font_location; + public function __construct( $test_file_path, $test_file_url ) { + $this->test_file_path = $test_file_path; + $this->test_file_url = $test_file_url; } /** @@ -107,96 +74,64 @@ public function register() { self::ENTRYPOINT . '/' . self::VERSION, '/pdf/settings/', [ - 'methods' => \WP_REST_Server::READABLE, - 'callback' => [ $this, 'check_tmp_pdf_security' ], + 'methods' => \WP_REST_Server::READABLE, + 'callback' => [ $this, 'check_temporary_pdf_directory_security' ], 'permission_callback' => function() { - return $this->has_capabilities( 'gravityforms_view_settings' ); + return $this->has_capabilities( 'gravityforms_view_settings' ); }, ] ); } /** - * Create a file in our tmp directory and check if it is publicly accessible (i.e no .htaccess protection) - * - * @param $_POST ['nonce'] + * Create a test file in the temporary folder and try read its content via a remote GET request * - * @return WP_REST_Response + * @return bool|WP_Error * * @since 5.2 */ - public function check_tmp_pdf_security() { - /* first create the test file in the tmp directory */ - $this->create_public_tmp_directory_test_file(); - - /* check if tmp directotyr file is publicly accessible */ - if ( file_exists( $this->template_font_location . $this->tmp_test_file ) ) { - $site_url = $this->misc->convert_path_to_url( $this->template_font_location ); - - /* file found */ - if ( $site_url !== null ) { - $response = wp_remote_get( $site_url . $this->tmp_test_file ); - - /* Cleanup our test file */ - @unlink( $this->template_font_location . $this->tmp_test_file ); - - if ( ! is_wp_error( $response ) ) { - /* Check if the web server responded with a OK status code and we can read the contents of our file, then fail our test */ - if ( isset( $response['response']['code'] ) && - $response['response']['code'] === 200 && - isset( $response['body'] ) && $response['body'] === 'failed-if-read' - ) { - $response_object = $response['http_response']; - $raw_response = $response_object->get_response_object(); - $this->log->warning( - 'PDF temporary directory not protected', - [ - 'url' => $raw_response->url, - 'status_code' => $raw_response->status_code, - 'response' => $raw_response->raw, - ] - ); - //@todo which one to return - // success but file is publicly accessible - // return [ 'message' => 'Tmp file successfully created but publicly accessible', 'has_access' => true ]; - return true; - } - //@todo which one to return - // success and file is secured - // return [ 'message' => 'Tmp file successfully created and not publicly accessible', 'has_access' => false ]; - return false; - } - /* Unable to access url */ - return new WP_Error( 'wp_remote_get_response', 'Response Error', [ 'status' => 400 ] ); + public function check_temporary_pdf_directory_security() { + try { + $this->create_test_file(); + + if ( $this->test_file_url === false ) { + throw new GravityPdfRuntimeException( 'Could not determine the temporary file URL' ); + } + + $response = wp_remote_get( $this->test_file_url ); + + if ( is_wp_error( $response ) ) { + throw new GravityPdfRuntimeException( 'Remote request for temporary file URL failed' ); } - /* Unable to convert path to url */ - return new WP_Error( 'convert_path_to_url', 'Unable to find path to convert to url', [ 'status' => 404 ] ); + + /* File was successfully accessed over public URL. Test failed */ + if ( wp_remote_retrieve_response_code( $response ) === 200 && wp_remote_retrieve_response_message( $response ) === 'failed-if-read' ) { + return false; + } + + return true; + } catch ( GravityPdfRuntimeException $e ) { + return new WP_Error( 'runtime_error', $e->getMessage(), [ 'status' => 500 ] ); } - /* file or directory not created */ - return new WP_Error( 'create_public_tmp_directory_test_file', 'Tmp directory and test file not found', [ 'status' => 404 ] ); } /** - * Create a file in our tmp directory + * Create the temporary test file * - * @param $_POST ['nonce'] + * @return bool + * @throws GravityPdfRuntimeException * - * @return Bool - * @return WP_Error - * * @since 5.2 */ - public function create_public_tmp_directory_test_file() { - /* create our file */ - file_put_contents( $this->template_font_location . $this->tmp_test_file, 'failed-if-read' ); + protected function create_test_file() { + if ( ! is_file( $this->test_file_path ) ) { + file_put_contents( $this->test_file_path, 'failed-if-read' ); - /* verify it exists */ - if ( is_file( $this->template_font_location . $this->tmp_test_file ) ) { - return true; - } + if ( ! is_file( $this->test_file_path ) ) { + throw new GravityPdfRuntimeException( 'Could not create temporary test file' ); + } + } - return new \WP_Error( 'test_public_tmp_directortest_public_tmp_directory_accessy_access', 'Unable to create tmp Directory', [ 'status' => 401 ] ); - + return true; } - } diff --git a/src/Exceptions/GravityPdfException.php b/src/Exceptions/GravityPdfException.php new file mode 100644 index 000000000..d3f4e9ea5 --- /dev/null +++ b/src/Exceptions/GravityPdfException.php @@ -0,0 +1,38 @@ +data->template_font_location . 'public_tmp_directory_test.txt'; + $test_file_url = $this->misc->convert_path_to_url( $test_file_path ); + $apis = [ new Api\V1\Fonts\Core\Api_Fonts_Core( $this->log, $this->data->template_font_location ), new Api\V1\Fonts\Api_Fonts( $this->log, $this->misc, $this->data, $this->options ), new Api\V1\License\Api_License( $this->log, $this->data ), new Api\V1\Migration\Multisite\Api_Migration_v4( $this->log, $this->options, $this->data, $this->migration ), - new Api\V1\Pdf\Settings\Api_Pdf_Settings( $this->log, $this->misc, $this->data->template_font_location ), + new Api\V1\Pdf\Settings\Api_Pdf_Settings( $test_file_path, $test_file_url ), new Api\V1\Security\Tmp\Api_Security_Tmp_Directory( $this->log, $this->misc, $this->data ), new Api\V1\Template\Api_Template( $this->log, $this->misc, $this->data, $this->options, $this->templates ), ];