From 62ed042a7f5c6901cad689eca9d3d78c9caf738a Mon Sep 17 00:00:00 2001 From: John Shaffer Date: Sat, 18 Sep 2021 21:13:52 -0500 Subject: [PATCH] Add hostsToRewrite option and advanced-options page. --- CHANGELOG.md | 3 ++ phpstan.neon | 2 +- src/Controller.php | 12 ++++++ src/CoreOptions.php | 72 ++++++++++++++++++++++++++++++- src/SimpleRewriter.php | 37 ++++++++++------ src/ViewRenderer.php | 13 ++++++ src/WordPressAdmin.php | 7 +++ tests/unit/SimpleRewriterTest.php | 48 +++++++++++++++++++-- views/advanced-options-page.php | 50 +++++++++++++++++++++ 9 files changed, 225 insertions(+), 19 deletions(-) create mode 100644 views/advanced-options-page.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 36c1542fd..41a09353f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ - accept self-signed certs during sitemap crawling @john-shaffer - detect dead jobs and mark as failed @john-shaffer - mark duplicated waiting jobs as skipped on jobs page @john-shaffer + - add an option to process the queue immediately #794 @john-shaffer + - add ability to rewrite hosts specified on a new advanced options page @john-shaffer + - as part of this, changed the host replacement function to use strtr instead of str_replace to avoid replacing things that we just replaced ## WP2Static 7.1.7 diff --git a/phpstan.neon b/phpstan.neon index d2b43a3e9..6c2b2b5a1 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -22,7 +22,7 @@ parameters: count: 5 - message: '#^In method "WP2Static\\\S+::\S+", you should not use the \$_(GET|POST) superglobal#' path: src/CoreOptions.php - count: 19 + count: 20 - message: '#^In method "WP2Static\\\S+::\S+", you should not use the \$_(GET|POST) superglobal#' path: src/ViewRenderer.php count: 32 diff --git a/src/Controller.php b/src/Controller.php index 135a2b1e9..08079fd29 100755 --- a/src/Controller.php +++ b/src/Controller.php @@ -190,6 +190,7 @@ public static function registerOptionsPage() : void { 'diagnostics' => [ ViewRenderer::class, 'renderDiagnosticsPage' ], 'logs' => [ ViewRenderer::class, 'renderLogsPage' ], 'addons' => [ ViewRenderer::class, 'renderAddonsPage' ], + 'advanced' => [ ViewRenderer::class, 'renderAdvancedOptionsPage' ], ]; foreach ( $submenu_pages as $slug => $method ) { @@ -471,6 +472,17 @@ public static function wp2staticTrashedPostHandler() : void { } } + public static function wp2staticUISaveAdvancedOptions() : void { + CoreOptions::savePosted( 'advanced' ); + + do_action( 'wp2static_addon_ui_save_advanced_options' ); + + check_admin_referer( 'wp2static-ui-advanced-options' ); + + wp_safe_redirect( admin_url( 'admin.php?page=wp2static-advanced' ) ); + exit; + } + public static function wp2staticEnqueueJobs() : void { // check each of these in order we want to enqueue $job_types = [ diff --git a/src/CoreOptions.php b/src/CoreOptions.php index 216ccbea6..68ad81d24 100755 --- a/src/CoreOptions.php +++ b/src/CoreOptions.php @@ -30,6 +30,7 @@ public static function createTable() : void { id mediumint(9) NOT NULL AUTO_INCREMENT, name VARCHAR(191) NOT NULL, value VARCHAR(249) NOT NULL, + blob_value BLOB, label VARCHAR(249) NULL, description VARCHAR(249) NULL, PRIMARY KEY (id) @@ -59,6 +60,10 @@ public static function seedOptions() : void { "INSERT IGNORE INTO $table_name (name, value, label, description) VALUES (%s, %s, %s, %s);"; + $blob_query_string = + "INSERT IGNORE INTO $table_name (name, value, label, description, blob_value) + VALUES (%s, %s, %s, %s, %s);"; + $queries[] = $wpdb->prepare( $query_string, 'detectCustomPostTypes', @@ -203,6 +208,17 @@ public static function seedOptions() : void { 'How to send completion webhook payload (GET|POST).' ); + // Advanced options + + $queries[] = $wpdb->prepare( + $blob_query_string, + 'hostsToRewrite', + '1', + 'Hosts to Rewrite', + 'Hosts to rewrite to the deployment URL.', + 'localhost' + ); + foreach ( $queries as $query ) { $wpdb->query( $query ); } @@ -246,6 +262,47 @@ public static function getValue( string $name ) : string { return $option_value; } + /** + * Get option BLOB value + * + * @throws WP2StaticException + * @return string option BLOB value + */ + public static function getBlobValue( string $name ) : string { + global $wpdb; + + $table_name = $wpdb->prefix . self::$table_name; + + $sql = $wpdb->prepare( + "SELECT blob_value FROM $table_name WHERE" . ' name = %s LIMIT 1', + $name + ); + + $option_value = $wpdb->get_var( $sql ); + + if ( ! is_string( $option_value ) ) { + return ''; + } + + return $option_value; + } + + /** + * @return array + */ + public static function getLineDelimitedBlobValue( string $name ) : array { + $vals = preg_split( + '/\r\n|\r|\n/', + self::getBlobValue( $name ) + ); + + if ( ! $vals ) { + return []; + } + + return $vals; + } + /** * Get option (value, description, label, etc) * @@ -257,7 +314,8 @@ public static function get( string $name ) { $table_name = $wpdb->prefix . self::$table_name; $sql = $wpdb->prepare( - "SELECT name, value, label, description FROM $table_name WHERE" . ' name = %s LIMIT 1', + "SELECT name, value, label, description, blob_value + FROM $table_name WHERE" . ' name = %s LIMIT 1', $name ); @@ -477,6 +535,18 @@ public static function savePosted( string $screen = 'core' ) : void { [ 'name' => 'autoJobQueueDeployment' ] ); + break; + case 'advanced': + $hosts_to_rewrite = preg_replace( + '/^\s+|\s+$/m', + '', + $_POST['hostsToRewrite'] + ); + $wpdb->update( + $table_name, + [ 'blob_value' => $hosts_to_rewrite ], + [ 'name' => 'hostsToRewrite' ] + ); break; } } diff --git a/src/SimpleRewriter.php b/src/SimpleRewriter.php index dc7f91977..f8d859b7c 100644 --- a/src/SimpleRewriter.php +++ b/src/SimpleRewriter.php @@ -53,22 +53,33 @@ public static function rewriteFileContents( string $file_contents ) : string $wordpress_site_url = untrailingslashit( $wordpress_site_url ); $destination_url = untrailingslashit( $destination_url ); + $destination_url_rel = URLHelper::getProtocolRelativeURL( $destination_url ); + $destination_url_rel_cslashes = addcslashes( $destination_url_rel, '/' ); - $search_patterns = [ - $wordpress_site_url, - URLHelper::getProtocolRelativeURL( $wordpress_site_url ), - addcslashes( URLHelper::getProtocolRelativeURL( $wordpress_site_url ), '/' ), - ]; - $replace_patterns = [ - $destination_url, - URLHelper::getProtocolRelativeURL( $destination_url ), - addcslashes( URLHelper::getProtocolRelativeURL( $destination_url ), '/' ), + $replace_pairs = [ + $wordpress_site_url => $destination_url, + URLHelper::getProtocolRelativeURL( $wordpress_site_url ) => + URLHelper::getProtocolRelativeURL( $destination_url ), + addcslashes( URLHelper::getProtocolRelativeURL( $wordpress_site_url ), '/' ) => + addcslashes( URLHelper::getProtocolRelativeURL( $destination_url ), '/' ), ]; - $rewritten_contents = str_replace( - $search_patterns, - $replace_patterns, - $file_contents + $hosts = CoreOptions::getLineDelimitedBlobValue( 'hostsToRewrite' ); + + foreach ( $hosts as $host ) { + if ( $host ) { + $host_rel = URLHelper::getProtocolRelativeURL( 'http://' . $host ); + + $replace_pairs[ 'http:' . $host_rel ] = $destination_url; + $replace_pairs[ 'https:' . $host_rel ] = $destination_url; + $replace_pairs[ $host_rel ] = $destination_url_rel; + $replace_pairs[ addcslashes( $host_rel, '/' ) ] = $destination_url_rel_cslashes; + } + } + + $rewritten_contents = strtr( + $file_contents, + $replace_pairs ); return $rewritten_contents; diff --git a/src/ViewRenderer.php b/src/ViewRenderer.php index c5ac0f29b..c6a70a302 100644 --- a/src/ViewRenderer.php +++ b/src/ViewRenderer.php @@ -27,6 +27,19 @@ public static function renderOptionsPage() : void { require_once WP2STATIC_PATH . 'views/options-page.php'; } + public static function renderAdvancedOptionsPage() : void { + CoreOptions::init(); + + $view = []; + $view['nonce_action'] = 'wp2static-ui-advanced-options'; + + $view['coreOptions'] = [ + 'hostsToRewrite' => CoreOptions::get( 'hostsToRewrite' ), + ]; + + require_once WP2STATIC_PATH . 'views/advanced-options-page.php'; + } + public static function renderDiagnosticsPage() : void { $view = []; $view['memoryLimit'] = ini_get( 'memory_limit' ); diff --git a/src/WordPressAdmin.php b/src/WordPressAdmin.php index c9769a3f8..d58d28564 100755 --- a/src/WordPressAdmin.php +++ b/src/WordPressAdmin.php @@ -197,6 +197,13 @@ public static function registerHooks( string $bootstrap_file ) : void { 0 ); + add_action( + 'admin_post_wp2static_ui_save_advanced_options', + [ Controller::class, 'wp2staticUISaveAdvancedOptions' ], + 10, + 0 + ); + add_action( 'admin_post_wp2static_manually_enqueue_jobs', [ Controller::class, 'wp2staticManuallyEnqueueJobs' ], diff --git a/tests/unit/SimpleRewriterTest.php b/tests/unit/SimpleRewriterTest.php index a27fdd5e1..55a80f5e2 100644 --- a/tests/unit/SimpleRewriterTest.php +++ b/tests/unit/SimpleRewriterTest.php @@ -41,7 +41,10 @@ public function testRewrite() { Mockery::mock( 'overload:\WP2Static\CoreOptions' ) ->shouldreceive( 'getValue' ) ->withArgs( [ 'deploymentURL' ] ) - ->andReturn( 'https://bar.com' ); + ->andReturn( 'https://bar.com' ) + ->shouldreceive( 'getLineDelimitedBlobValue' ) + ->withArgs( [ 'hostsToRewrite' ] ) + ->andReturn( [ 'localhost' ] ); Mockery::mock( 'overload:\WP2Static\SiteInfo' ) ->shouldreceive( 'getUrl' ) ->withArgs( [ 'site' ] ) @@ -104,7 +107,10 @@ public function testRewriteFileContents( $raw_html, $expected ) { Mockery::mock( 'overload:\WP2Static\CoreOptions' ) ->shouldreceive( 'getValue' ) ->withArgs( [ 'deploymentURL' ] ) - ->andReturn( 'https://bar.com' ); + ->andReturn( 'https://bar.com' ) + ->shouldreceive( 'getLineDelimitedBlobValue' ) + ->withArgs( [ 'hostsToRewrite' ] ) + ->andReturn( [ 'localhost' ] ); Mockery::mock( 'overload:\WP2Static\SiteInfo' ) ->shouldreceive( 'getUrl' ) ->withArgs( [ 'site' ] ) @@ -124,6 +130,9 @@ public function testRewriteFileContentsHttpToHttps() { ->shouldreceive( 'getValue' ) ->withArgs( [ 'deploymentURL' ] ) ->andReturn( 'https://bar.com' ) + ->shouldreceive( 'getLineDelimitedBlobValue' ) + ->withArgs( [ 'hostsToRewrite' ] ) + ->andReturn( [ 'localhost' ] ) ->getMock(); Mockery::mock( 'overload:\WP2Static\SiteInfo' ) ->shouldreceive( 'getUrl' ) @@ -143,6 +152,9 @@ public function testRewriteFileContentsHttpsToHttp() { ->shouldreceive( 'getValue' ) ->withArgs( [ 'deploymentURL' ] ) ->andReturn( 'http://bar.com' ) + ->shouldreceive( 'getLineDelimitedBlobValue' ) + ->withArgs( [ 'hostsToRewrite' ] ) + ->andReturn( [ 'localhost' ] ) ->getMock(); Mockery::mock( 'overload:\WP2Static\SiteInfo' ) ->shouldreceive( 'getUrl' ) @@ -156,6 +168,28 @@ public function testRewriteFileContentsHttpsToHttp() { $this->assertEquals( $expected, $actual ); } + public function testRewriteFileContentsHostsToRewrite() { + // Mock the methods and functions used by SimpleRewriter + Mockery::mock( 'overload:\WP2Static\CoreOptions' ) + ->shouldreceive( 'getValue' ) + ->withArgs( [ 'deploymentURL' ] ) + ->andReturn( 'http://bar.com' ) + ->shouldreceive( 'getLineDelimitedBlobValue' ) + ->withArgs( [ 'hostsToRewrite' ] ) + ->andReturn( [ 'localhost' ] ) + ->getMock(); + Mockery::mock( 'overload:\WP2Static\SiteInfo' ) + ->shouldreceive( 'getUrl' ) + ->withArgs( [ 'site' ] ) + ->andReturn( 'https://foo.com/' ) + ->getMock(); + + // localhost -> bar.com + $expected = 'http://bar.com/somepath'; + $actual = SimpleRewriter::rewriteFileContents( 'https://localhost/somepath' ); + $this->assertEquals( $expected, $actual ); + } + /** * @dataProvider rewriteFileContentsProvider */ @@ -164,7 +198,10 @@ public function testRewriteFileContentsDestinationUrlFilter( $raw_html, $expecte Mockery::mock( 'overload:\WP2Static\CoreOptions' ) ->shouldreceive( 'getValue' ) ->withArgs( [ 'deploymentURL' ] ) - ->andReturn( 'https://bar.com' ); + ->andReturn( 'https://bar.com' ) + ->shouldreceive( 'getLineDelimitedBlobValue' ) + ->withArgs( [ 'hostsToRewrite' ] ) + ->andReturn( [ 'localhost' ] ); Mockery::mock( 'overload:\WP2Static\SiteInfo' ) ->shouldreceive( 'getUrl' ) ->withArgs( [ 'site' ] ) @@ -193,7 +230,10 @@ public function testRewriteFileContentsSiteUrlFilter( $raw_html, $expected ) { Mockery::mock( 'overload:\WP2Static\CoreOptions' ) ->shouldreceive( 'getValue' ) ->withArgs( [ 'deploymentURL' ] ) - ->andReturn( 'https://bar.com' ); + ->andReturn( 'https://bar.com' ) + ->shouldreceive( 'getLineDelimitedBlobValue' ) + ->withArgs( [ 'hostsToRewrite' ] ) + ->andReturn( [ 'localhost' ] ); Mockery::mock( 'overload:\WP2Static\SiteInfo' ) ->shouldreceive( 'getUrl' ) ->withArgs( [ 'site' ] ) diff --git a/views/advanced-options-page.php b/views/advanced-options-page.php new file mode 100644 index 000000000..439b0b1fd --- /dev/null +++ b/views/advanced-options-page.php @@ -0,0 +1,50 @@ + + +
+
+ +

Advanced Options

+ +

Post-processing Options

+ + + + + + + + +
+ +
description; ?> +
+ +
+ +

+ + + + + + +

+