diff --git a/.changeset/tidy-toys-bake.md b/.changeset/tidy-toys-bake.md
new file mode 100644
index 000000000..05c249552
--- /dev/null
+++ b/.changeset/tidy-toys-bake.md
@@ -0,0 +1,14 @@
+---
+'@faustwp/wordpress-plugin': minor
+---
+
+### Fixes
+
+- Fixes various issues with content replacement callback functions and replacing the site_url and media_urls
+- Fixed an issue with content replacement when media replacement was disabled and rewrites enabled, it was overwriting and updating the media URL to the frontend URL rather than leaving it as the original site URL
+
+
+### Added
+
+- Added 6.6 and 6.7 to Github Actions
+- Added 2 new filters for `faustwp_get_wp_site_urls` and `faustwp_get_wp_site_media_urls` to allow users add/remove/edit site and media URLS for the content replacement.
diff --git a/.github/workflows/unit-test-plugin.yml b/.github/workflows/unit-test-plugin.yml
index 1b16961e5..489e293f8 100644
--- a/.github/workflows/unit-test-plugin.yml
+++ b/.github/workflows/unit-test-plugin.yml
@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-22.04
strategy:
matrix:
- wordpress: [ '6.5', '6.4', '6.3', '6.2', '6.1' ]
+ wordpress: [ '6.7', '6.6', '6.5', '6.4', '6.3', '6.2', '6.1' ]
steps:
- name: Checkout
uses: actions/checkout@v4
diff --git a/.gitignore b/.gitignore
index 0ecd42389..6e9c3a229 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,9 @@ process.yml
!.env.local.sample
build/
faustjs.code-workspace
+plugins/faustwp/.docker/plugins/akismet/
+plugins/faustwp/.docker/plugins/hello.php
+plugins/faustwp/.docker/plugins/index.php
# Ignore the WordPress source where used by various development environments
wordpress/
diff --git a/plugins/faustwp/docker-compose.yml b/plugins/faustwp/docker-compose.yml
index 1fd9e57ca..1bfef8b2d 100644
--- a/plugins/faustwp/docker-compose.yml
+++ b/plugins/faustwp/docker-compose.yml
@@ -11,7 +11,7 @@ services:
links:
- db
environment:
- WP_VERSION: ${WP_VERSION:-6.5}
+ WP_VERSION: ${WP_VERSION:-6.7}
WORDPRESS_DB_HOST: db
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: root
diff --git a/plugins/faustwp/includes/replacement/callbacks.php b/plugins/faustwp/includes/replacement/callbacks.php
index de4b84cec..84f11ca01 100644
--- a/plugins/faustwp/includes/replacement/callbacks.php
+++ b/plugins/faustwp/includes/replacement/callbacks.php
@@ -31,39 +31,40 @@
*
* @return string The post content.
*/
-function content_replacement( $content ) {
- $use_wp_domain_for_permalinks = ! domain_replacement_enabled();
- $use_wp_domain_for_media = use_wp_domain_for_media();
+function content_replacement( string $content ): string {
- if ( $use_wp_domain_for_permalinks && $use_wp_domain_for_media ) {
- return $content;
- }
-
- $replacement = faustwp_get_setting( 'frontend_uri' );
- if ( ! $replacement ) {
- $replacement = '/';
+ if ( ! $content ) {
+ return '';
}
- $site_url = site_url();
- $media_dir = str_replace( $site_url, '', wp_upload_dir()['baseurl'] );
- $media_url = $site_url . $media_dir;
+ $replace_content_urls = domain_replacement_enabled();
+ $replace_media_urls = ! use_wp_domain_for_media();
- if ( $use_wp_domain_for_permalinks && ! $use_wp_domain_for_media ) {
- $content = str_replace( $media_url, $replacement . $media_dir, $content );
+ if ( ! $replace_content_urls && ! $replace_media_urls ) {
return $content;
}
- if ( ! $use_wp_domain_for_permalinks && ! $use_wp_domain_for_media ) {
- $content = str_replace( $site_url, $replacement, $content );
+ $wp_site_urls = faustwp_get_wp_site_urls( site_url() );
+ if ( empty( $wp_site_urls ) ) {
return $content;
}
- if ( ! $use_wp_domain_for_permalinks && $use_wp_domain_for_media ) {
- $content = preg_replace( "#{$site_url}(?!{$media_dir})#", "{$replacement}", $content );
- return $content;
+ $relative_upload_url = faustwp_get_relative_upload_url( $wp_site_urls, wp_upload_dir()['baseurl'] );
+ $wp_media_urls = faustwp_get_wp_media_urls( $wp_site_urls, $relative_upload_url );
+ $frontend_uri = (string) faustwp_get_setting( 'frontend_uri' ) ?? '/';
+
+ if ( $replace_content_urls && $replace_media_urls ) {
+ return str_replace( $wp_site_urls, $frontend_uri, $content );
+ }
+
+ if ( $replace_media_urls ) {
+ return str_replace( $wp_media_urls, $frontend_uri . $relative_upload_url, $content );
}
- return $content;
+ $site_urls_pattern = implode( '|', array_map( 'preg_quote', $wp_site_urls ) );
+ $pattern = '#(' . $site_urls_pattern . ')(?!' . $relative_upload_url . '(\/|$))#';
+
+ return preg_replace( $pattern, $frontend_uri, $content );
}
/**
@@ -86,49 +87,58 @@ function image_source_replacement( $content ) {
"#src=\"{$frontend_uri}/#",
'#src="/#',
);
+
return preg_replace( $patterns, "src=\"{$site_url}/", $content );
}
add_filter( 'wp_calculate_image_srcset', __NAMESPACE__ . '\\image_source_srcset_replacement' );
/**
- * Callback for WordPress 'the_content' filter to replace paths to media.
+ * Callback for WordPress 'wp_calculate_image_srcset' filter to replace paths when generating a srcset
*
- * @param array $sources One or more arrays of source data to include in the 'srcset'.
+ * @link https://developer.wordpress.org/reference/functions/wp_calculate_image_srcset/
*
- * @return string One or more arrays of source data.
+ * @param array $sources One or more arrays of source data to include in the 'srcset'.
+ *
+ * @return array One or more arrays of source data.
*/
function image_source_srcset_replacement( $sources ) {
- $use_wp_domain_for_media = use_wp_domain_for_media();
- $frontend_uri = faustwp_get_setting( 'frontend_uri' );
- $site_url = site_url();
- /**
- * For urls with no domain or the frontend domain, replace with the WP site_url.
- * This was the default replacement pattern until Faust 1.2, at which point this
- * was adjusted to correct replacement bugs.
- */
- $patterns = array(
- "#^{$site_url}/#",
+ if ( ! is_array( $sources ) || empty( $sources ) ) {
+ return $sources;
+ }
+
+ $wp_site_urls = faustwp_get_wp_site_urls( site_url() );
+ if ( empty( $wp_site_urls ) ) {
+ return $sources;
+ }
+
+ $replace_media_urls = ! use_wp_domain_for_media();
+ $relative_upload_url = faustwp_get_relative_upload_url( $wp_site_urls, wp_upload_dir()['baseurl'] );
+ $wp_media_urls = faustwp_get_wp_media_urls( $wp_site_urls, $relative_upload_url );
+ $frontend_uri = (string) faustwp_get_setting( 'frontend_uri' );
+ $site_url = site_url() . '/';
+
+ $wp_media_site_url = $frontend_uri . $relative_upload_url;
+ $patterns = array(
+ "#^{$frontend_uri}/#",
'#^/#',
);
- $replacement = $frontend_uri;
-
/**
- * If using WP domain for media and a frontend URL is encountered, rewrite it to WP URL.
+ * Update each source with the correct replacement URL
*/
- if ( $use_wp_domain_for_media ) {
- $patterns = array(
- "#^{$frontend_uri}/#",
- '#^/#',
- );
- $replacement = $site_url;
- }
-
- if ( is_array( $sources ) ) {
- foreach ( $sources as $width => $source ) {
- $sources[ $width ]['url'] = preg_replace( $patterns, "$replacement/", $source['url'] );
+ foreach ( $sources as $width => $source ) {
+ $url = $source['url'];
+
+ if ( $replace_media_urls ) {
+ $sources[ $width ]['url'] = ( strpos( $url, $relative_upload_url ) === 0 )
+ ? $frontend_uri . $url
+ : str_replace( $wp_media_urls, $wp_media_site_url, $source['url'] );
+ continue;
}
+
+ // We need to make sure that the frontend URL or relative URL (legacy) is updated with the site url.
+ $sources[ $width ]['url'] = preg_replace( $patterns, $site_url, $source['url'] );
}
return $sources;
@@ -240,7 +250,15 @@ function post_preview_link( $link, $post ) {
*/
function post_link( $link ) {
global $pagenow;
- $target_pages = array( 'admin-ajax.php', 'index.php', 'edit.php', 'post.php', 'post-new.php', 'upload.php', 'media-new.php' );
+ $target_pages = array(
+ 'admin-ajax.php',
+ 'index.php',
+ 'edit.php',
+ 'post.php',
+ 'post-new.php',
+ 'upload.php',
+ 'media-new.php',
+ );
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified in `is_ajax_generate_permalink_request()` and `is_wp_link_ajax_request()`.
if ( empty( $_POST ) && 'post-new.php' === $pagenow ) {
@@ -251,7 +269,7 @@ function post_link( $link ) {
if ( in_array( $pagenow, $target_pages, true )
&& is_ajax_generate_permalink_request()
) {
- return $link;
+ return $link;
}
if (
diff --git a/plugins/faustwp/includes/replacement/functions.php b/plugins/faustwp/includes/replacement/functions.php
index da5f69382..645aa1eb2 100644
--- a/plugins/faustwp/includes/replacement/functions.php
+++ b/plugins/faustwp/includes/replacement/functions.php
@@ -126,8 +126,70 @@ function is_ajax_generate_permalink_request(): bool {
*/
function is_wp_link_ajax_request(): bool {
return ( wp_doing_ajax()
- && ! empty( $_POST['_ajax_linking_nonce'] )
- && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_ajax_linking_nonce'] ) ), 'internal-linking' )
- && ! empty( $_POST['action'] )
- && 'wp-link-ajax' === $_POST['action'] );
+ && ! empty( $_POST['_ajax_linking_nonce'] )
+ && wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_ajax_linking_nonce'] ) ), 'internal-linking' )
+ && ! empty( $_POST['action'] )
+ && 'wp-link-ajax' === $_POST['action'] );
+}
+
+
+/**
+ * Get all site URLs for each possible HTTP protocol
+ *
+ * @param string $site_url The site url.
+ *
+ * @return array An array of site urls.
+ */
+function faustwp_get_wp_site_urls( string $site_url ): array {
+
+ $host_url = wp_parse_url( $site_url, PHP_URL_HOST );
+
+ $is_https = strpos( $site_url, 0, 6 ) === 'https:';
+
+ return apply_filters(
+ 'faustwp_get_wp_site_urls',
+ array(
+ $is_https ? "https://$host_url" : "http://$host_url",
+ $is_https ? "http://$host_url" : "https://$host_url",
+ "//$host_url",
+ )
+ );
+}
+
+/**
+ * Get all media urls based off the available site urls
+ *
+ * @param array $wp_site_urls The array of potential site urls.
+ * @param string $relative_upload_url The relative upload url.
+ *
+ * @return array The array of media Urls
+ */
+function faustwp_get_wp_media_urls( array $wp_site_urls, string $relative_upload_url ) {
+
+ $media_urls = array();
+ foreach ( $wp_site_urls as $site_url ) {
+ $media_urls[] = $site_url . $relative_upload_url;
+ }
+
+ return apply_filters( 'faustwp_get_wp_site_media_urls', $media_urls );
+}
+
+
+/**
+ * Gets the relative wp-content upload URL.
+ *
+ * @param array $site_urls An array of site URLs.
+ * @param string $upload_url An array of site URLs.
+ *
+ * @return string The relative upload URL.
+ */
+function faustwp_get_relative_upload_url( array $site_urls, string $upload_url = '' ): string {
+
+ foreach ( $site_urls as $site_url ) {
+ if ( strpos( $upload_url, $site_url ) === 0 ) {
+ return (string) str_replace( $site_url, '', $upload_url );
+ }
+ }
+
+ return '';
}
diff --git a/plugins/faustwp/tests/integration/ReplacementCallbacksTests.php b/plugins/faustwp/tests/integration/ReplacementCallbacksTests.php
index 69afb2531..1e8728d44 100644
--- a/plugins/faustwp/tests/integration/ReplacementCallbacksTests.php
+++ b/plugins/faustwp/tests/integration/ReplacementCallbacksTests.php
@@ -7,17 +7,19 @@
namespace WPE\FaustWP\Tests\Integration;
-use function WPE\FaustWP\Replacement\{
- content_replacement,
+use function WPE\FaustWP\Replacement\{content_replacement,
+ faustwp_get_wp_site_urls,
post_preview_link,
preview_link_in_rest_response,
image_source_replacement,
image_source_srcset_replacement,
- post_link,
-};
+ post_link};
use function WPE\FaustWP\Settings\faustwp_update_setting;
-use WP_REST_Response;
+/**
+ * @group replacement-callback
+ * @group callback
+ */
class ReplacementCallbacksTests extends \WP_UnitTestCase {
protected $post_id;
protected $draft_post_id;
@@ -35,6 +37,7 @@ public function setUp(): void {
'post_content' => 'Hi',
'post_status' => 'draft',
] );
+
}
public function test_the_content_filter() {
@@ -64,7 +67,7 @@ public function test_page_link_filter() {
public function test_term_link_filter() {
$this->assertSame( 1000, has_action( 'term_link', 'WPE\FaustWP\Replacement\term_link' ) );
}
-
+
public function test_post_type_link_filter() {
$this->assertSame( 1000, has_filter( 'post_type_link', 'WPE\FaustWP\Replacement\post_link' ) );
}
@@ -396,4 +399,237 @@ public function get_home_url( $url ) {
return "https://example.com/";
}
+ /**
+ * Test to make sure that site url is replaced for both content and media
+ */
+ public function test_content_replacement_for_content_and_media_urls_for_different_http_protocols() {
+
+ $frontend_uri = 'http://localhost:3000';
+ $site_url = site_url();
+ $site_url_secure = str_replace('http:', 'https:', $site_url);
+
+
+ faustwp_update_setting( 'frontend_uri', $frontend_uri );
+ faustwp_update_setting( 'enable_rewrites', '1' );
+ faustwp_update_setting( 'enable_image_source', '0' );
+
+ $contentExample1 = <<
+
+ Hello World
+
+HTML;
+
+ $contentExample2 = <<
+
+ Hello World
+
+HTML;
+
+ $expected_content = <<
+
+ Hello World
+
+HTML;
+
+ $this->assertSame( $expected_content, content_replacement( $contentExample1 ) );
+ $this->assertSame( $expected_content, content_replacement( $contentExample2 ) );
+ }
+
+
+ /**
+ * Test to make sure that site url is replaced for just media and not content
+ */
+ public function test_content_replacement_for_just_media_urls_for_different_http_protocols() {
+
+ $frontend_uri = 'http://localhost:3000';
+ $site_url = site_url();
+ $site_url_secure = str_replace('http:', 'https:', $site_url);
+
+ faustwp_update_setting( 'frontend_uri', $frontend_uri );
+ faustwp_update_setting( 'enable_rewrites', '0' );
+ faustwp_update_setting( 'enable_image_source', '0' );
+
+ $content = <<
+
+ Hello World
+
+HTML;
+
+ $expected_content = <<
+
+ Hello World
+
+HTML;
+
+ $this->assertSame( $expected_content, content_replacement( $content ) );
+
+
+ $content = <<
+
+ Hello World
+
+HTML;
+
+ $expected_content = <<
+
+ Hello World
+
+HTML;
+ $this->assertSame( $expected_content, content_replacement( $content ) );
+ }
+
+
+ /**
+ * Test to make sure that site url is replaced for just content and not media
+ */
+ public function test_content_replacement_for_just_content_urls_for_different_http_protocols() {
+
+ $frontend_uri = 'http://localhost:3000';
+ $site_url = site_url();
+ $site_url_secure = str_replace('http:', 'https:', $site_url);
+
+ faustwp_update_setting( 'frontend_uri', $frontend_uri );
+ faustwp_update_setting( 'enable_rewrites', '1' );
+ faustwp_update_setting( 'enable_image_source', '1' );
+
+ $content = <<
+
+ Hello World
+
+HTML;
+
+ $expected_content = <<
+
+ Hello World
+
+HTML;
+
+ $this->assertSame( $expected_content, content_replacement( $content ) );
+
+ $content = <<
+
+ Hello World
+
+HTML;
+
+ $expected_content = <<
+
+ Hello World
+
+HTML;
+
+ $this->assertSame( $expected_content, content_replacement( $content ) );
+
+ }
+
+
+ public function test_content_replacement_for_content_and_media_urls_and_add_site_to_available_site_urls() {
+ $frontend_uri = 'http://localhost:3000';
+ $site_url = site_url();
+ $site_url_secure = str_replace('http:', 'https:', $site_url);
+ $additional_site_url = 'https://subdomain.example.org';
+
+
+ faustwp_update_setting( 'frontend_uri', $frontend_uri );
+ faustwp_update_setting( 'enable_rewrites', '1' );
+ faustwp_update_setting( 'enable_image_source', '0' );
+
+ add_filter( 'faustwp_get_wp_site_urls', function ( $site_urls ) {
+ return array_merge( $site_urls, [ 'https://subdomain.example.org' ] );
+ });
+
+ $this->assertSame(faustwp_get_wp_site_urls($site_url), [
+ $site_url,
+ $site_url_secure,
+ '//example.org',
+ $additional_site_url
+ ] );
+
+ $content = <<
+
+ Hello World
+
+Find more information about us here
+
+
+
+HTML;
+
+ $expected_content = <<
+
+ Hello World
+
+Find more information about us here
+
+
+
+HTML;
+ $this->assertSame( $expected_content, content_replacement( $content ) );
+ }
+
+
+ public function test_image_sourceset_replacement_for_different_http_protocols_with_media_replacement_enabled() {
+
+ $frontend_uri = 'http://localhost:3000';
+ $site_url = site_url();
+ $site_url_secure = str_replace('http:', 'https:', $site_url);
+
+ faustwp_update_setting( 'frontend_uri', $frontend_uri );
+ faustwp_update_setting( 'enable_rewrites', '1' );
+ faustwp_update_setting( 'enable_image_source', '0' );
+
+ $sources = array (
+ 100 => array('url' => $site_url . '/wp-content/uploads/sites/2/2024/12/WP-Engine-Blue-888x459-1-300x155.webp'),
+ 300 => array('url' => $site_url_secure . '/wp-content/uploads/sites/2/2024/12/WP-Engine-Blue-888x459-1-300x155.webp'),
+ 400 => array('url' => '/wp-content/uploads/sites/2/2024/12/WP-Engine-Blue-888x459-1.webp'),
+ );
+
+ $expected = array (
+ 100 => array('url' => $frontend_uri . '/wp-content/uploads/sites/2/2024/12/WP-Engine-Blue-888x459-1-300x155.webp'),
+ 300 => array('url' => $frontend_uri . '/wp-content/uploads/sites/2/2024/12/WP-Engine-Blue-888x459-1-300x155.webp'),
+ 400 => array('url' => $frontend_uri . '/wp-content/uploads/sites/2/2024/12/WP-Engine-Blue-888x459-1.webp'),
+ );
+
+ $this->assertSame( $expected, image_source_srcset_replacement( $sources ) );
+ }
+
+
+ public function test_image_sourceset_replacement_for_different_http_protocols_with_media_replacement_disabled() {
+
+ $frontend_uri = 'http://localhost:3000';
+ $site_url = site_url();
+ $site_url_secure = str_replace('http:', 'https:', $site_url);
+
+ faustwp_update_setting( 'frontend_uri', $frontend_uri );
+ faustwp_update_setting( 'enable_rewrites', '1' );
+ faustwp_update_setting( 'enable_image_source', '1' );
+
+ $sources = array (
+ 100 => array('url' => $site_url . '/wp-content/uploads/sites/2/2024/12/WP-Engine-Blue-888x459-1-300x155.webp'),
+ 300 => array('url' => $site_url_secure . '/wp-content/uploads/sites/2/2024/12/WP-Engine-Blue-888x459-1-300x155.webp'),
+ 400 => array('url' => '/wp-content/uploads/sites/2/2024/12/WP-Engine-Blue-888x459-1.webp'),
+ );
+
+ $expected = array (
+ 100 => array('url' => $site_url . '/wp-content/uploads/sites/2/2024/12/WP-Engine-Blue-888x459-1-300x155.webp'),
+ 300 => array('url' => $site_url_secure . '/wp-content/uploads/sites/2/2024/12/WP-Engine-Blue-888x459-1-300x155.webp'),
+ 400 => array('url' => $site_url . '/wp-content/uploads/sites/2/2024/12/WP-Engine-Blue-888x459-1.webp'),
+ );
+
+ $this->assertSame( $expected, image_source_srcset_replacement( $sources ) );
+ }
+
}