diff --git a/CHANGELOG.md b/CHANGELOG.md index d9fa34aae..bbdc3936b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ - 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 + - add advanced option to skip URL rewriting @john-shaffer ## WP2Static 7.1.7 diff --git a/phpstan.neon b/phpstan.neon index 098b60892..8f31a44fc 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -22,7 +22,7 @@ parameters: count: 6 - message: '#^In method "WP2Static\\\S+::\S+", you should not use the \$_(GET|POST) superglobal#' path: src/CoreOptions.php - count: 20 + count: 22 - 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 b0725d465..35aca2588 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 734f86574..d29db1349 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', @@ -211,6 +216,26 @@ public static function seedOptions() : void { 'How to send completion webhook payload (GET|POST).' ); + // Advanced options + + $queries[] = $wpdb->prepare( + $query_string, + 'skipURLRewrite', + '0', + 'Skip URL Rewrite', + 'Don\'t rewrite any URLs. This may give a slight speed-up when the' + . ' deployment URL is the same as WordPress\'s URL.' + ); + + $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 ); } @@ -254,6 +279,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) * @@ -265,7 +331,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 ); @@ -492,6 +559,24 @@ public static function savePosted( string $screen = 'core' ) : void { [ 'name' => 'autoJobQueueDeployment' ] ); + break; + case 'advanced': + $wpdb->update( + $table_name, + [ 'value' => isset( $_POST['skipURLRewrite'] ) ? 1 : 0 ], + [ 'name' => 'skipURLRewrite' ] + ); + + $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..f2f6337a6 100644 --- a/src/SimpleRewriter.php +++ b/src/SimpleRewriter.php @@ -41,6 +41,10 @@ public static function rewriteFileContents( string $file_contents ) : string return ''; } + if ( (int) CoreOptions::getValue( 'skipURLRewrite' ) === 1 ) { + return $file_contents; + } + $destination_url = apply_filters( 'wp2static_set_destination_url', CoreOptions::getValue( 'deploymentURL' ) @@ -53,22 +57,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_c = 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 ), '/' ), + $replacement_patterns = [ + $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 ); + + $replacement_patterns[ 'http:' . $host_rel ] = $destination_url; + $replacement_patterns[ 'https:' . $host_rel ] = $destination_url; + $replacement_patterns[ $host_rel ] = $destination_url_rel; + $replacement_patterns[ addcslashes( $host_rel, '/' ) ] = $destination_url_rel_c; + } + } + + $rewritten_contents = strtr( + $file_contents, + $replacement_patterns ); return $rewritten_contents; diff --git a/src/ViewRenderer.php b/src/ViewRenderer.php index 623f805c4..912ccf786 100644 --- a/src/ViewRenderer.php +++ b/src/ViewRenderer.php @@ -27,6 +27,20 @@ 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' ), + 'skipURLRewrite' => CoreOptions::get( 'skipURLRewrite' ), + ]; + + 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 d27262742..6bdc38fd0 100755 --- a/src/WordPressAdmin.php +++ b/src/WordPressAdmin.php @@ -204,6 +204,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..9d5813ada 100644 --- a/tests/unit/SimpleRewriterTest.php +++ b/tests/unit/SimpleRewriterTest.php @@ -29,6 +29,16 @@ public function tearDown() : void Mockery::close(); } + public static function coreOptionsMock() : \Mockery\CompositeExpectation { + return Mockery::mock( 'overload:\WP2Static\CoreOptions' ) + ->shouldReceive( 'getValue' ) + ->withArgs( [ 'skipURLRewrite' ] ) + ->andReturn( '0' ) + ->shouldreceive( 'getLineDelimitedBlobValue' ) + ->withArgs( [ 'hostsToRewrite' ] ) + ->andReturn( [ 'localhost' ] ); + } + /** * Test deleteDirWithFiles method * @@ -38,7 +48,7 @@ public function tearDown() : void */ public function testRewrite() { // Mock the methods and functions used by SimpleRewriter - Mockery::mock( 'overload:\WP2Static\CoreOptions' ) + self::coreOptionsMock() ->shouldreceive( 'getValue' ) ->withArgs( [ 'deploymentURL' ] ) ->andReturn( 'https://bar.com' ); @@ -101,7 +111,7 @@ public function rewriteFileContentsProvider() { */ public function testRewriteFileContents( $raw_html, $expected ) { // Mock the methods and functions used by SimpleRewriter - Mockery::mock( 'overload:\WP2Static\CoreOptions' ) + self::coreOptionsMock() ->shouldreceive( 'getValue' ) ->withArgs( [ 'deploymentURL' ] ) ->andReturn( 'https://bar.com' ); @@ -120,7 +130,7 @@ public function testRewriteFileContents( $raw_html, $expected ) { public function testRewriteFileContentsHttpToHttps() { // Mock the methods and functions used by SimpleRewriter - Mockery::mock( 'overload:\WP2Static\CoreOptions' ) + self::coreOptionsMock() ->shouldreceive( 'getValue' ) ->withArgs( [ 'deploymentURL' ] ) ->andReturn( 'https://bar.com' ) @@ -139,7 +149,7 @@ public function testRewriteFileContentsHttpToHttps() { public function testRewriteFileContentsHttpsToHttp() { // Mock the methods and functions used by SimpleRewriter - Mockery::mock( 'overload:\WP2Static\CoreOptions' ) + self::coreOptionsMock() ->shouldreceive( 'getValue' ) ->withArgs( [ 'deploymentURL' ] ) ->andReturn( 'http://bar.com' ) @@ -156,12 +166,53 @@ public function testRewriteFileContentsHttpsToHttp() { $this->assertEquals( $expected, $actual ); } + public function testRewriteFileContentsSkipURLRewrite() { + // Mock the methods and functions used by SimpleRewriter + Mockery::mock( 'overload:\WP2Static\CoreOptions' ) + ->shouldReceive( 'getValue' ) + ->withArgs( [ 'skipURLRewrite' ] ) + ->andReturn( '1' ) + ->shouldreceive( 'getLineDelimitedBlobValue' ) + ->withArgs( [ 'hostsToRewrite' ] ) + ->andReturn( [ 'localhost' ] ) + ->shouldreceive( 'getValue' ) + ->withArgs( [ 'deploymentURL' ] ) + ->andReturn( 'http://bar.com' ); + Mockery::mock( 'overload:\WP2Static\SiteInfo' ) + ->shouldreceive( 'getUrl' ) + ->withArgs( [ 'site' ] ) + ->andReturn( 'https://foo.com/' ); + + $expected = 'https://foo.com/somepath'; + $actual = SimpleRewriter::rewriteFileContents( $expected ); + $this->assertEquals( $expected, $actual ); + } + + public function testRewriteFileContentsHostsToRewrite() { + // Mock the methods and functions used by SimpleRewriter + self::coreOptionsMock() + ->shouldreceive( 'getValue' ) + ->withArgs( [ 'deploymentURL' ] ) + ->andReturn( 'http://bar.com' ) + ->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 */ public function testRewriteFileContentsDestinationUrlFilter( $raw_html, $expected ) { // Mock the methods and functions used by SimpleRewriter - Mockery::mock( 'overload:\WP2Static\CoreOptions' ) + self::coreOptionsMock() ->shouldreceive( 'getValue' ) ->withArgs( [ 'deploymentURL' ] ) ->andReturn( 'https://bar.com' ); @@ -190,7 +241,7 @@ public function testRewriteFileContentsDestinationUrlFilter( $raw_html, $expecte */ public function testRewriteFileContentsSiteUrlFilter( $raw_html, $expected ) { // Mock the methods and functions used by SimpleRewriter - Mockery::mock( 'overload:\WP2Static\CoreOptions' ) + self::coreOptionsMock() ->shouldreceive( 'getValue' ) ->withArgs( [ 'deploymentURL' ] ) ->andReturn( 'https://bar.com' ); diff --git a/views/advanced-options-page.php b/views/advanced-options-page.php new file mode 100644 index 000000000..e93fe1131 --- /dev/null +++ b/views/advanced-options-page.php @@ -0,0 +1,67 @@ + + +
+
+ +

Advanced Options

+ +

Post-processing Options

+ + + + + + + + + + + + +
+ +
description; ?> +
+ value === 1 ? 'checked' : ''; ?> + /> +
+ +
description; ?> +
+ +
+ +

+ + + + + + +

+