From 6276c80486a745c93fed91e3a33ab7b912720e9a Mon Sep 17 00:00:00 2001
From: Blake Wilson <blake.wilson@wpengine.com>
Date: Tue, 14 Nov 2023 09:56:02 -0600
Subject: [PATCH 1/8] Fix app router build publish (#1651)

* Fix the build step for experimental app router before publish

* Add changeset
---
 .changeset/breezy-dragons-appear.md | 5 +++++
 package.json                        | 4 ++--
 2 files changed, 7 insertions(+), 2 deletions(-)
 create mode 100644 .changeset/breezy-dragons-appear.md

diff --git a/.changeset/breezy-dragons-appear.md b/.changeset/breezy-dragons-appear.md
new file mode 100644
index 000000000..e3bebbc1c
--- /dev/null
+++ b/.changeset/breezy-dragons-appear.md
@@ -0,0 +1,5 @@
+---
+'@faustwp/experimental-app-router': patch
+---
+
+Fix broken build from 0.2.0
diff --git a/package.json b/package.json
index 4ef363c28..90f1d2a3e 100644
--- a/package.json
+++ b/package.json
@@ -30,7 +30,7 @@
     "build:faust-cli": "npm run build --workspace=@faustwp/cli",
     "build:faust-core": "npm run build --workspace=@faustwp/core",
     "build:faust-blocks": "npm run build --workspace=@faustwp/blocks",
-    "build:experimentala-app-router": "npm run build --workspace=@faustwp/experimental-app-router",
+    "build:experimental-app-router": "npm run build --workspace=@faustwp/experimental-app-router",
     "clean": "npm run clean --workspace=@faustjs/core --workspace=@faustjs/react --workspace=@faustjs/next --workspace=@faustwp/cli --workspace=@faustwp/core --workspace=@faustwp/experimental-app-router --workspace=@faustwp/block-editor-utils",
     "clean:examples": "rimraf examples/**/node_modules",
     "format": "npm run format --workspace=@faustjs/core --workspace=@faustjs/react --workspace=@faustjs/next --workspace=@faustwp/cli --workspace=@faustwp/core --workspace=@faustwp/experimental-app-router --workspace=@faustwp/block-editor-utils",
@@ -54,7 +54,7 @@
     "version": "changeset version && node scripts/versionPlugin.js",
     "version:nightly": "changeset version --snapshot && node scripts/versionPlugin.js",
     "version:status": "changeset status",
-    "release": "npm run build && changeset publish",
+    "release": "npm run build && npm run build:experimental-app-router && changeset publish",
     "release:nightly": "npm run build && changeset publish --tag canary",
     "lint": "eslint ./packages --ext js,jsx,ts,tsx --max-warnings=0",
     "lint:fix": "eslint ./packages --ext js,jsx,ts,tsx --max-warnings=0 --fix"

From 77da190bf35133a72cb289f26824969ca82ecc89 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
 <41898282+github-actions[bot]@users.noreply.github.com>
Date: Tue, 14 Nov 2023 10:19:47 -0600
Subject: [PATCH 2/8] Version Packages (#1652)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
---
 .changeset/breezy-dragons-appear.md           | 5 -----
 examples/next/app-router/package.json         | 2 +-
 packages/experimental-app-router/CHANGELOG.md | 6 ++++++
 packages/experimental-app-router/package.json | 2 +-
 4 files changed, 8 insertions(+), 7 deletions(-)
 delete mode 100644 .changeset/breezy-dragons-appear.md

diff --git a/.changeset/breezy-dragons-appear.md b/.changeset/breezy-dragons-appear.md
deleted file mode 100644
index e3bebbc1c..000000000
--- a/.changeset/breezy-dragons-appear.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@faustwp/experimental-app-router': patch
----
-
-Fix broken build from 0.2.0
diff --git a/examples/next/app-router/package.json b/examples/next/app-router/package.json
index a318149cb..b5dc72966 100644
--- a/examples/next/app-router/package.json
+++ b/examples/next/app-router/package.json
@@ -14,7 +14,7 @@
     "@apollo/experimental-nextjs-app-support": "^0.5.1",
     "@faustwp/cli": "^1.2.0",
     "@faustwp/core": "^1.2.0",
-    "@faustwp/experimental-app-router": "^0.2.0",
+    "@faustwp/experimental-app-router": "^0.2.1",
     "graphql": "^16.7.1",
     "next": "^14.0.1",
     "react": "^18.3.0-canary-ce2bc58a9-20231102",
diff --git a/packages/experimental-app-router/CHANGELOG.md b/packages/experimental-app-router/CHANGELOG.md
index 5c52841f2..13eaf40fb 100644
--- a/packages/experimental-app-router/CHANGELOG.md
+++ b/packages/experimental-app-router/CHANGELOG.md
@@ -1,5 +1,11 @@
 # @faustwp/experimental-app-router
 
+## 0.2.1
+
+### Patch Changes
+
+- 6276c80: Fix broken build from 0.2.0
+
 ## 0.2.0
 
 ### Minor Changes
diff --git a/packages/experimental-app-router/package.json b/packages/experimental-app-router/package.json
index a0aa15799..292e8ec1d 100644
--- a/packages/experimental-app-router/package.json
+++ b/packages/experimental-app-router/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@faustwp/experimental-app-router",
-  "version": "0.2.0",
+  "version": "0.2.1",
   "description": "Experimental: A Faust package to support Next.js' App Router",
   "exports": {
     ".": "./dist/index.js",

From 78a061a752214a0a8d2d17696f4f514d2e6af589 Mon Sep 17 00:00:00 2001
From: John Parris <john.parris@wpengine.com>
Date: Wed, 15 Nov 2023 07:57:24 -0500
Subject: [PATCH 3/8] MERL-1228: Fix issue with rewriting links to front end
 (#1624)

* test: confirm content replacement behavior with a mixed content blob

* test: confirm unrelated URLs are untouched

* chore: add `use_wp_domain_for_post_and_category_urls()` function.

Determines if post and category URLs should link to the WP site.

* chore: add `use_wp_domain_for_media()` function.

Determines if WP media URLs should link to the WP site.

* chore: remove callback attached to `graphql_request_results` filter

* chore: use new `use_wp_domain_for_post_and_category_urls()` function inside `domain_replacement_enabled()`

* fix: adjust content replacement logic to avoid rewriting media links incorrectly

* filter block content via `wpgraphql_content_blocks_resolver_content`

* refactor: Adjust logic in `image_source_srcset_replacement`

* chore: add changeset
---
 .changeset/four-books-invent.md               |  5 ++
 .../includes/replacement/callbacks.php        | 68 ++++++++++++++-----
 .../includes/replacement/functions.php        |  7 +-
 .../replacement/graphql-callbacks.php         |  1 -
 .../faustwp/includes/settings/functions.php   | 17 +++++
 .../integration/GraphQLCallbacksTests.php     |  4 --
 .../integration/ReplacementCallbacksTests.php | 49 +++++++++++--
 7 files changed, 121 insertions(+), 30 deletions(-)
 create mode 100644 .changeset/four-books-invent.md

diff --git a/.changeset/four-books-invent.md b/.changeset/four-books-invent.md
new file mode 100644
index 000000000..21f3cec7f
--- /dev/null
+++ b/.changeset/four-books-invent.md
@@ -0,0 +1,5 @@
+---
+'@faustwp/wordpress-plugin': patch
+---
+
+Fixed a bug that caused links to files in wp-content to be rewritten to the Faust Front-end site URL when they should not have been.
diff --git a/plugins/faustwp/includes/replacement/callbacks.php b/plugins/faustwp/includes/replacement/callbacks.php
index 82a271baa..0115469f2 100644
--- a/plugins/faustwp/includes/replacement/callbacks.php
+++ b/plugins/faustwp/includes/replacement/callbacks.php
@@ -10,7 +10,9 @@
 use function WPE\FaustWP\Settings\{
 	faustwp_get_setting,
 	is_image_source_replacement_enabled,
-	is_rewrites_enabled
+	is_rewrites_enabled,
+	use_wp_domain_for_media,
+	use_wp_domain_for_post_and_category_urls,
 };
 use function WPE\FaustWP\Utilities\{
 	plugin_version,
@@ -21,32 +23,49 @@
 }
 
 add_filter( 'the_content', __NAMESPACE__ . '\\content_replacement' );
+add_filter( 'wpgraphql_content_blocks_resolver_content', __NAMESPACE__ . '\\content_replacement' );
 /**
  * Callback for WordPress 'the_content' filter.
  *
  * @param string $content The post content.
  *
  * @return string The post content.
- * @todo Needs work...
  */
 function content_replacement( $content ) {
-	if ( ! domain_replacement_enabled() ) {
+	$use_wp_domain_for_permalinks = ! domain_replacement_enabled();
+	$use_wp_domain_for_media      = use_wp_domain_for_media();
+
+	if ( $use_wp_domain_for_permalinks && $use_wp_domain_for_media ) {
 		return $content;
 	}
 
 	$replacement = faustwp_get_setting( 'frontend_uri' );
-	$site_url    = site_url();
-
 	if ( ! $replacement ) {
 		$replacement = '/';
 	}
 
-	$content = str_replace( "href=\"{$site_url}", "href=\"{$replacement}", $content );
+	$site_url  = site_url();
+	$media_dir = str_replace( $site_url, '', wp_upload_dir()['baseurl'] );
+	$media_url = $site_url . $media_dir;
+
+	if ( $use_wp_domain_for_permalinks && ! $use_wp_domain_for_media ) {
+		$content = str_replace( $media_url, $replacement . $media_dir, $content );
+		return $content;
+	}
+
+	if ( ! $use_wp_domain_for_permalinks && ! $use_wp_domain_for_media ) {
+		$content = str_replace( $site_url, $replacement, $content );
+		return $content;
+	}
+
+	if ( ! $use_wp_domain_for_permalinks && $use_wp_domain_for_media ) {
+		$content = preg_replace( "#{$site_url}(?!{$media_dir})#", "{$replacement}", $content );
+		return $content;
+	}
 
-	return str_replace( 'href="//', 'href="/', $content );
+	return $content;
 }
 
-add_filter( 'the_content', __NAMESPACE__ . '\\image_source_replacement' );
 /**
  * Callback for WordPress 'the_content' filter to replace paths to media.
  *
@@ -79,21 +98,36 @@ function image_source_replacement( $content ) {
  * @return string One or more arrays of source data.
  */
 function image_source_srcset_replacement( $sources ) {
-	if ( ! is_image_source_replacement_enabled() ) {
-		return $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}/#",
+		'#^/#',
+	);
 
-	$frontend_uri = faustwp_get_setting( 'frontend_uri' );
-	$site_url     = site_url();
+	$replacement = $frontend_uri;
 
-	if ( is_array( $sources ) ) {
-		// For urls with no domain or the frontend domain, replace with the wp site_url.
-		$patterns = array(
+	/**
+	 * If using WP domain for media and a frontend URL is encountered, rewrite it to WP 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, "$site_url/", $sources[ $width ]['url'] );
+			$sources[ $width ]['url'] = preg_replace( $patterns, "$replacement/", $source['url'] );
 		}
 	}
 
diff --git a/plugins/faustwp/includes/replacement/functions.php b/plugins/faustwp/includes/replacement/functions.php
index 3a875e207..84768450a 100644
--- a/plugins/faustwp/includes/replacement/functions.php
+++ b/plugins/faustwp/includes/replacement/functions.php
@@ -9,7 +9,8 @@
 
 use function WPE\FaustWP\Settings\{
 	faustwp_get_setting,
-	is_rewrites_enabled
+	is_rewrites_enabled,
+	use_wp_domain_for_post_and_category_urls,
 };
 
 if ( ! defined( 'ABSPATH' ) ) {
@@ -19,8 +20,6 @@
 /**
  * Determine if domain replacement can be done.
  *
- * Enabled if query string parameter 'replace-domain' is present.
- *
  * @return bool True if can proceed with replacement, false if else.
  */
 function domain_replacement_enabled() {
@@ -31,7 +30,7 @@ function domain_replacement_enabled() {
 	 *
 	 * @param bool $enabled True if domain replacement is enabled, false if else.
 	 */
-	return apply_filters( 'faustwp_domain_replacement_enabled', is_rewrites_enabled() );
+	return apply_filters( 'faustwp_domain_replacement_enabled', ! use_wp_domain_for_post_and_category_urls() );
 }
 
 /**
diff --git a/plugins/faustwp/includes/replacement/graphql-callbacks.php b/plugins/faustwp/includes/replacement/graphql-callbacks.php
index 8de262e10..e8397d88e 100644
--- a/plugins/faustwp/includes/replacement/graphql-callbacks.php
+++ b/plugins/faustwp/includes/replacement/graphql-callbacks.php
@@ -14,7 +14,6 @@
 	exit;
 }
 
-add_filter( 'graphql_request_results', __NAMESPACE__ . '\\url_replacement' );
 /**
  * Callback for WP GraphQL 'graphql_request_results' filter.
  *
diff --git a/plugins/faustwp/includes/settings/functions.php b/plugins/faustwp/includes/settings/functions.php
index 56aa42e89..ba2d643c1 100644
--- a/plugins/faustwp/includes/settings/functions.php
+++ b/plugins/faustwp/includes/settings/functions.php
@@ -29,6 +29,15 @@ function is_rewrites_enabled() {
 	return '1' === faustwp_get_setting( 'enable_rewrites' );
 }
 
+/**
+ * Determines if posts and category URLs should link to the WP site.
+ *
+ * @return bool
+ */
+function use_wp_domain_for_post_and_category_urls() {
+	return ! is_rewrites_enabled();
+}
+
 /**
  * Determine if themes are disabled.
  *
@@ -47,6 +56,14 @@ function is_image_source_replacement_enabled() {
 	return '1' === faustwp_get_setting( 'enable_image_source' );
 }
 
+/**
+ * Determine if sourcing images from WP domain is enabled.
+ *
+ * @return bool True if image sources from WP are enabled, false if else.
+ */
+function use_wp_domain_for_media() {
+	return is_image_source_replacement_enabled();
+}
 
 /**
  * Get the secret key setting.
diff --git a/plugins/faustwp/tests/integration/GraphQLCallbacksTests.php b/plugins/faustwp/tests/integration/GraphQLCallbacksTests.php
index 2364fca4a..8e4e57fcc 100644
--- a/plugins/faustwp/tests/integration/GraphQLCallbacksTests.php
+++ b/plugins/faustwp/tests/integration/GraphQLCallbacksTests.php
@@ -115,10 +115,6 @@ public function test_graphql_section_field_value() {
 		$this->assertSame( 10, has_action( 'graphql_get_setting_section_field_value', 'WPE\FaustWP\GraphQL\filter_introspection' ) );
 	}
 
-	public function test_graphql_request_results_filter() {
-		$this->assertSame( 10, has_action( 'graphql_request_results', 'WPE\FaustWP\Replacement\url_replacement' ) );
-	}
-
 	/**
 	 * Tests url_replacement() returns original data when rewrites are not enabled.
 	 */
diff --git a/plugins/faustwp/tests/integration/ReplacementCallbacksTests.php b/plugins/faustwp/tests/integration/ReplacementCallbacksTests.php
index 15cbb5161..de3f4be75 100644
--- a/plugins/faustwp/tests/integration/ReplacementCallbacksTests.php
+++ b/plugins/faustwp/tests/integration/ReplacementCallbacksTests.php
@@ -48,10 +48,6 @@ public function test_term_link_filter() {
 		$this->assertSame( 1000, has_action( 'term_link', 'WPE\FaustWP\Replacement\term_link' ) );
 	}
 
-	public function test_graphql_request_results_filter() {
-		$this->assertSame( 10, has_action( 'graphql_request_results', 'WPE\FaustWP\Replacement\url_replacement' ) );
-	}
-
 	public function test_enqueue_preview_scripts_action() {
 		$this->assertSame( 10, has_action( 'enqueue_block_editor_assets', 'WPE\FaustWP\Replacement\enqueue_preview_scripts' ) );
 	}
@@ -68,6 +64,10 @@ public function test_wpseo_xml_sitemap_post_url_filter() {
 		$this->assertSame( 10, has_action( 'wpseo_xml_sitemap_post_url', 'WPE\FaustWP\Replacement\yoast_sitemap_post_url' ) );
 	}
 
+	public function test_wp_calculate_image_srcset_filter(): void {
+		self::assertSame( 10, has_action( 'wp_calculate_image_srcset', 'WPE\FaustWP\Replacement\image_source_srcset_replacement' ) );
+	}
+
 	/**
 	 * Tests content_replacement() returns original value when content replacement is not enabled.
 	 */
@@ -131,6 +131,9 @@ public function test_image_source_srcset_replacement_filters_content_when_image_
 	public function test_image_source_replacement_filters_content_when_image_replacement_not_enabled() {
 		faustwp_update_setting( 'enable_image_source', '0' );
 		$this->assertSame( '<img src="/image.png">', image_source_replacement( '<img src="/image.png">' ) );
+
+		// Ensure unrelated domains are left alone.
+		$this->assertSame( '<img src="http://fake/image.png">', image_source_replacement( '<img src="http://fake/image.png">' ) );
 	}
 
 	/**
@@ -255,6 +258,44 @@ public function test_term_link_returns_filtered_link_when_rewrite_term_links_ena
 		$this->assertSame( 'http://moo/?cat=' . $term_id, get_term_link( $term_id ) );
 	}
 
+	/**
+	 * Tests content_replacement() handles mixed content blobs properly.
+	 */
+	public function test_content_replacement_properly_handles_a_mixed_content_blob() {
+		faustwp_update_setting( 'frontend_uri', 'http://moo' );
+		faustwp_update_setting( 'enable_image_source', '1' );
+		faustwp_update_setting( 'enable_rewrites', '1' );
+
+		$input = <<<INPUT
+		<p>This is a <a href="http://example.org/hello-world/" data-type="post" data-id="1">post link</a>.</p><p>This is a <a href="http://example.org/wp-content/uploads/2023/10/out-of-the-tar-pit.pdf" data-type="attachment" data-id="12">media link</a> to a PDF file.</p><p>This is a <a href="http://example.org/wp-content/uploads/2023/10/IMG_8963-scaled.jpg" data-type="attachment" data-id="15">media link</a> to an image.</p>
+	INPUT;
+
+		$output = <<<OUTPUT
+		<p>This is a <a href="http://moo/hello-world/" data-type="post" data-id="1">post link</a>.</p><p>This is a <a href="http://example.org/wp-content/uploads/2023/10/out-of-the-tar-pit.pdf" data-type="attachment" data-id="12">media link</a> to a PDF file.</p><p>This is a <a href="http://example.org/wp-content/uploads/2023/10/IMG_8963-scaled.jpg" data-type="attachment" data-id="15">media link</a> to an image.</p>
+	OUTPUT;
+
+		self::assertSame(
+				$output,
+				content_replacement( $input )
+		);
+
+		// Check that media URLs are rewritten when enable_image_source setting is configured to NOT use the WP domain.
+		faustwp_update_setting( 'enable_image_source', '0' );
+
+		$input = <<<INPUT
+		<p>This is a <a href="http://example.org/hello-world/" data-type="post" data-id="1">post link</a>.</p><p>This is a <a href="http://example.org/wp-content/uploads/2023/10/out-of-the-tar-pit.pdf" data-type="attachment" data-id="12">media link</a> to a PDF file.</p><p>This is a <a href="http://example.org/wp-content/uploads/2023/10/IMG_8963-scaled.jpg" data-type="attachment" data-id="15">media link</a> to an image.</p>
+	INPUT;
+
+		$output = <<<OUTPUT
+		<p>This is a <a href="http://moo/hello-world/" data-type="post" data-id="1">post link</a>.</p><p>This is a <a href="http://moo/wp-content/uploads/2023/10/out-of-the-tar-pit.pdf" data-type="attachment" data-id="12">media link</a> to a PDF file.</p><p>This is a <a href="http://moo/wp-content/uploads/2023/10/IMG_8963-scaled.jpg" data-type="attachment" data-id="15">media link</a> to an image.</p>
+	OUTPUT;
+
+		self::assertSame(
+				$output,
+				content_replacement( $input )
+		);
+	}
+
 	private function getCustomPostType() {
 		register_post_type('document', [
 			'public' => true,

From 75f5c806b1cc7bb6da3c5286ec81e99f2c365b9b Mon Sep 17 00:00:00 2001
From: John Parris <john.parris@wpengine.com>
Date: Wed, 15 Nov 2023 11:28:34 -0500
Subject: [PATCH 4/8] MERL-1270: Do not rewrite links in the post editor
 (#1625)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: do not rewrite links in the post editor

When Faust’s “Enable Post and Category URL rewrites” is enabled, this has been causing URLs to be rewritten in the editor to contain the front-end URL instead of the WP URL, which causes them to be saved to the database this way. Instead, we want the WP URL saved to the database.

* fix: don't rewrite links in `wp-link-ajax` ajax requests

* fix: do not rewrite links on permalink generation requests

* test: confirm post_link filtering behavior.

Confirms links are not rewritten on post-new.php pages when $_POST is empty. Also confirms Ajax requests to generate sample permalinks are not rewritten. These checks prevent the incorrect guid from being written to the database.

Confirms `wp-link-ajax` Ajax requests are not rewritten. This is used by the Classic Editor when linking content.

chore: update `enable_rewrites` values in tests to be `1` instead of `true`, for consistency.

* add changeset

* refactor: extract conditional logic to functions
---
 .changeset/yellow-foxes-complain.md           |  5 ++
 .../includes/replacement/callbacks.php        | 22 +++++++++
 .../includes/replacement/functions.php        | 21 ++++++++
 .../integration/ReplacementCallbacksTests.php | 49 +++++++++++++++++--
 4 files changed, 93 insertions(+), 4 deletions(-)
 create mode 100644 .changeset/yellow-foxes-complain.md

diff --git a/.changeset/yellow-foxes-complain.md b/.changeset/yellow-foxes-complain.md
new file mode 100644
index 000000000..98ec986b2
--- /dev/null
+++ b/.changeset/yellow-foxes-complain.md
@@ -0,0 +1,5 @@
+---
+'@faustwp/wordpress-plugin': patch
+---
+
+Fixed a bug where links were rewritten to the Faust Front-end Site URL when using the post editor, resulting in those rewritten links being saved to the post content and guid fields when they shouldn't be. These links are now saved with the URL pointing to the WP site, as they should be. They are still rewritten at runtime to link to the Front-end Site URL when appropriate.
diff --git a/plugins/faustwp/includes/replacement/callbacks.php b/plugins/faustwp/includes/replacement/callbacks.php
index 0115469f2..9cf215124 100644
--- a/plugins/faustwp/includes/replacement/callbacks.php
+++ b/plugins/faustwp/includes/replacement/callbacks.php
@@ -234,13 +234,35 @@ function post_preview_link( $link, $post ) {
  * @return string URL used for the 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' );
+
+	// 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 ) {
+		return $link;
+	}
+
+	// Ajax requests to generate permalink.
+	if ( in_array( $pagenow, $target_pages, true )
+		&& is_ajax_generate_permalink_request()
+	) {
+			return $link;
+	}
+
 	if (
 		! is_rewrites_enabled()
 		|| ( function_exists( 'is_graphql_request' ) && is_graphql_request() )
+		// Block editor makes REST requests on these pages to query content.
+		|| ( in_array( $pagenow, $target_pages, true ) && current_user_can( 'edit_posts' ) && defined( 'REST_REQUEST' ) && REST_REQUEST )
 	) {
 		return $link;
 	}
 
+	// Check for wp-link-ajax requests. Used by Classic Editor when linking content.
+	if ( is_wp_link_ajax_request() ) {
+		return $link;
+	}
+
 	return equivalent_frontend_url( $link );
 }
 
diff --git a/plugins/faustwp/includes/replacement/functions.php b/plugins/faustwp/includes/replacement/functions.php
index 84768450a..056a932b7 100644
--- a/plugins/faustwp/includes/replacement/functions.php
+++ b/plugins/faustwp/includes/replacement/functions.php
@@ -110,3 +110,24 @@ function has_file_extension( $string ) {
 	}
 }
 
+/**
+ * Determines if an AJAX request to generate permalinks is in progress.
+ *
+ * @return boolean
+ */
+function is_ajax_generate_permalink_request(): bool {
+	return ( ! empty( $_POST['samplepermalinknonce'] ) && check_ajax_referer( 'samplepermalink', 'samplepermalinknonce' ) );
+}
+
+/**
+ * Determines if a wp-link-ajax request is in progress.
+ *
+ * @return boolean
+ */
+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'] );
+}
diff --git a/plugins/faustwp/tests/integration/ReplacementCallbacksTests.php b/plugins/faustwp/tests/integration/ReplacementCallbacksTests.php
index de3f4be75..6ad1c77a5 100644
--- a/plugins/faustwp/tests/integration/ReplacementCallbacksTests.php
+++ b/plugins/faustwp/tests/integration/ReplacementCallbacksTests.php
@@ -11,7 +11,8 @@
 	content_replacement,
 	post_preview_link,
 	image_source_replacement,
-	image_source_srcset_replacement
+	image_source_srcset_replacement,
+	post_link
 };
 use function WPE\FaustWP\Settings\faustwp_update_setting;
 
@@ -159,11 +160,51 @@ public function test_post_link_returns_unfiltered_link_when_content_replacement_
 	 */
 	public function test_post_link_returns_filtered_link_when_content_replacement_is_enabled() {
 		faustwp_update_setting( 'frontend_uri', 'http://moo' );
-		faustwp_update_setting( 'enable_rewrites', true );
+		faustwp_update_setting( 'enable_rewrites', '1' );
 
 		$this->assertSame( 'http://moo/?p=' . $this->post_id, get_permalink( $this->post_id ) );
 	}
 
+	public function test_post_link_returns_unfiltered_link_when_on_post_new_page(): void {
+		global $pagenow;
+		$pagenow = 'post-new.php';
+		self::assertSame( 'http://example.org/hello-world', post_link( 'http://example.org/hello-world' ) );
+	}
+
+	public function test_post_link_returns_unfiltered_link_on_ajax_requests_to_generate_permalinks_using_samplepermalinknonce(): void {
+		global $pagenow, $_REQUEST, $_POST;
+		$pagenow = 'admin-ajax.php';
+		wp_set_current_user( 1 );
+		faustwp_update_setting( 'frontend_uri', 'http://moo' );
+		faustwp_update_setting( 'enable_rewrites', '1' );
+		$_REQUEST['samplepermalinknonce'] = wp_create_nonce( 'samplepermalink' );
+		$_POST['samplepermalinknonce'] = $_REQUEST['samplepermalinknonce'];
+
+		self::assertSame( 'http://example.org/hello-world', post_link( 'http://example.org/hello-world' ) );
+
+		unset( $_REQUEST['samplepermalinknonce'], $_POST['samplepermalinknonce'] );
+		unset( $pagenow );
+		wp_set_current_user( null );
+	}
+
+	public function test_post_link_returns_unfiltered_link_on_ajax_requests_to_generate_permalinks_using_ajax_linking_nonce(): void {
+		global $pagenow, $_POST;
+		$pagenow = 'admin-ajax.php';
+		wp_set_current_user( 1 );
+		faustwp_update_setting( 'frontend_uri', 'http://moo' );
+		faustwp_update_setting( 'enable_rewrites', '1' );
+		add_filter( 'wp_doing_ajax', '__return_true' );
+		$_POST['_ajax_linking_nonce'] = wp_create_nonce( 'internal-linking' );
+		$_POST['action'] = 'wp-link-ajax';
+
+		self::assertSame( 'http://example.org/hello-world', post_link( 'http://example.org/hello-world' ) );
+
+		unset( $_POST['_ajax_linking_nonce'], $_POST['action'] );
+		unset( $pagenow );
+		remove_filter( 'wp_doing_ajax', '__return_true' );
+		wp_set_current_user( null );
+	}
+
 	/**
 	 * Tests get_preview_post_link() returns rewritten value.
 	 */
@@ -203,7 +244,7 @@ public function test_post_preview_link_uses_frontend_uri_scheme() {
 	public function test_custom_post_type_post_preview_link_returns_filtered_link_when_content_replacement_is_enabled()
 	{
 		faustwp_update_setting( 'frontend_uri', 'http://moo' );
-		faustwp_update_setting( 'enable_rewrites', true );
+		faustwp_update_setting( 'enable_rewrites', '1' );
 		$post_id = $this->getCustomPostType();
 		$this->assertSame( 'http://moo/?document=' . $post_id . '&preview=true&previewPathname=' . rawurlencode( wp_make_link_relative( get_permalink( $post_id ) ) ) . '&p=' . $post_id . '&typeName=Document', get_preview_post_link( $post_id ) );
 		faustwp_update_setting( 'frontend_uri', null );
@@ -216,7 +257,7 @@ public function test_custom_post_type_post_preview_link_returns_filtered_link_wh
 	public function test_custom_post_type_post_link_returns_unfiltered_link_when_content_replacement_is_enabled()
 	{
 		faustwp_update_setting( 'frontend_uri', 'http://moo' );
-		faustwp_update_setting( 'enable_rewrites', true );
+		faustwp_update_setting( 'enable_rewrites', '1' );
 		$post_id = $this->getCustomPostType();
 		$this->assertSame( 'http://example.org/?document=' . $post_id, get_permalink($post_id) );
 		faustwp_update_setting( 'frontend_uri', null );

From a4190ad05c5acad63738ba6192da576cf73f902b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 16 Nov 2023 14:54:00 -0500
Subject: [PATCH 5/8] chore(deps): Bump sharp from 0.30.7 to 0.32.6 (#1658)

Bumps [sharp](https://github.com/lovell/sharp) from 0.30.7 to 0.32.6.
- [Release notes](https://github.com/lovell/sharp/releases)
- [Changelog](https://github.com/lovell/sharp/blob/main/docs/changelog.md)
- [Commits](https://github.com/lovell/sharp/compare/v0.30.7...v0.32.6)

---
updated-dependencies:
- dependency-name: sharp
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 examples/next/getting-started/package.json |  2 +-
 package-lock.json                          | 36 ++++++++++++++--------
 2 files changed, 25 insertions(+), 13 deletions(-)

diff --git a/examples/next/getting-started/package.json b/examples/next/getting-started/package.json
index b3b37d404..cad50e1b5 100644
--- a/examples/next/getting-started/package.json
+++ b/examples/next/getting-started/package.json
@@ -23,7 +23,7 @@
     "react": "^17.0.2",
     "react-dom": "^17.0.2",
     "sass": "^1.54.0",
-    "sharp": "^0.30.5"
+    "sharp": "^0.32.6"
   },
   "devDependencies": {
     "@gqty/cli": "^3.1.0",
diff --git a/package-lock.json b/package-lock.json
index efb289713..651c4360b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -53,7 +53,7 @@
         "@apollo/experimental-nextjs-app-support": "^0.5.1",
         "@faustwp/cli": "^1.2.0",
         "@faustwp/core": "^1.2.0",
-        "@faustwp/experimental-app-router": "^0.2.0",
+        "@faustwp/experimental-app-router": "^0.2.1",
         "graphql": "^16.7.1",
         "next": "^14.0.1",
         "react": "^18.3.0-canary-ce2bc58a9-20231102",
@@ -736,7 +736,7 @@
         "react": "^17.0.2",
         "react-dom": "^17.0.2",
         "sass": "^1.54.0",
-        "sharp": "^0.30.5"
+        "sharp": "^0.32.6"
       },
       "devDependencies": {
         "@gqty/cli": "^3.1.0",
@@ -18803,8 +18803,9 @@
       }
     },
     "node_modules/node-addon-api": {
-      "version": "5.1.0",
-      "license": "MIT"
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz",
+      "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA=="
     },
     "node_modules/node-fetch": {
       "version": "2.7.0",
@@ -21864,26 +21865,37 @@
       }
     },
     "node_modules/sharp": {
-      "version": "0.30.7",
+      "version": "0.32.6",
+      "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.6.tgz",
+      "integrity": "sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==",
       "hasInstallScript": true,
-      "license": "Apache-2.0",
       "dependencies": {
         "color": "^4.2.3",
-        "detect-libc": "^2.0.1",
-        "node-addon-api": "^5.0.0",
+        "detect-libc": "^2.0.2",
+        "node-addon-api": "^6.1.0",
         "prebuild-install": "^7.1.1",
-        "semver": "^7.3.7",
+        "semver": "^7.5.4",
         "simple-get": "^4.0.1",
-        "tar-fs": "^2.1.1",
+        "tar-fs": "^3.0.4",
         "tunnel-agent": "^0.6.0"
       },
       "engines": {
-        "node": ">=12.13.0"
+        "node": ">=14.15.0"
       },
       "funding": {
         "url": "https://opencollective.com/libvips"
       }
     },
+    "node_modules/sharp/node_modules/tar-fs": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz",
+      "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==",
+      "dependencies": {
+        "mkdirp-classic": "^0.5.2",
+        "pump": "^3.0.0",
+        "tar-stream": "^3.1.5"
+      }
+    },
     "node_modules/shebang-command": {
       "version": "2.0.0",
       "license": "MIT",
@@ -27738,7 +27750,7 @@
     },
     "packages/experimental-app-router": {
       "name": "@faustwp/experimental-app-router",
-      "version": "0.2.0",
+      "version": "0.2.1",
       "license": "MIT",
       "devDependencies": {
         "@apollo/client": "^3.8.0",

From 50a8d08e3fee9216b287090c251a677304e14fe7 Mon Sep 17 00:00:00 2001
From: Theofanis Despoudis <328805+theodesp@users.noreply.github.com>
Date: Fri, 17 Nov 2023 10:53:26 +0000
Subject: [PATCH 6/8] RFC: Set 404 Revalidate timer to 900 seconds. (#1639)

* RFC: Set 404 Revalidate timer to 900 seconds.

* Review: Remove DEFAULT_ISR_REVALIDATE from getSitemapProps

* Refactor: Conditionally Add `revalidate` on non SSR requests.

* Update package.json

* Update package-lock.json
---
 .../next/faustwp-getting-started/package.json |   2 +-
 package-lock.json                             | 119 +++++++++++++++++-
 packages/faustwp-core/src/getProps.ts         |   1 +
 .../faustwp-core/src/getWordPressProps.tsx    |  21 ++--
 packages/faustwp-core/tests/getProps.test.ts  |   2 +-
 5 files changed, 133 insertions(+), 12 deletions(-)

diff --git a/examples/next/faustwp-getting-started/package.json b/examples/next/faustwp-getting-started/package.json
index d2506d851..8cd0f66f4 100644
--- a/examples/next/faustwp-getting-started/package.json
+++ b/examples/next/faustwp-getting-started/package.json
@@ -13,7 +13,7 @@
     "@apollo/client": "^3.6.6",
     "@faustwp/cli": "^1.2.0",
     "@faustwp/core": "^1.2.0",
-    "@wordpress/base-styles": "^4.26.0",
+    "@wordpress/base-styles": "^4.36.0",
     "@wordpress/block-library": "^7.19.0",
     "classnames": "^2.3.1",
     "graphql": "^16.6.0",
diff --git a/package-lock.json b/package-lock.json
index 651c4360b..861415a43 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -711,7 +711,7 @@
         "@apollo/client": "^3.6.6",
         "@faustwp/cli": "^1.2.0",
         "@faustwp/core": "^1.2.0",
-        "@wordpress/base-styles": "^4.26.0",
+        "@wordpress/base-styles": "^4.36.0",
         "@wordpress/block-library": "^7.19.0",
         "classnames": "^2.3.1",
         "graphql": "^16.6.0",
@@ -725,6 +725,11 @@
         "npm": ">=8"
       }
     },
+    "examples/next/faustwp-getting-started/node_modules/@wordpress/base-styles": {
+      "version": "4.36.0",
+      "resolved": "https://registry.npmjs.org/@wordpress/base-styles/-/base-styles-4.36.0.tgz",
+      "integrity": "sha512-N+Hpz6nI06Ji4mmx9vv/YSKR3XCYHnWV6v0mMehQvC9oZVjxBmo3hzWdiMrrjXFxONH9cjJUql0Cht+traFfRA=="
+    },
     "examples/next/getting-started": {
       "name": "@faustjs/next-headless-getting-started",
       "version": "0.1.0",
@@ -27822,6 +27827,118 @@
         "node": ">= 10"
       }
     },
+    "packages/experimental-app-router/node_modules/@next/swc-linux-arm64-gnu": {
+      "version": "12.3.4",
+      "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.3.4.tgz",
+      "integrity": "sha512-kiX0vgJGMZVv+oo1QuObaYulXNvdH/IINmvdZnVzMO/jic/B8EEIGlZ8Bgvw8LCjH3zNVPO3mGrdMvnEEPEhKA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "packages/experimental-app-router/node_modules/@next/swc-linux-arm64-musl": {
+      "version": "12.3.4",
+      "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.3.4.tgz",
+      "integrity": "sha512-EETZPa1juczrKLWk5okoW2hv7D7WvonU+Cf2CgsSoxgsYbUCZ1voOpL4JZTOb6IbKMDo6ja+SbY0vzXZBUMvkQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "packages/experimental-app-router/node_modules/@next/swc-linux-x64-gnu": {
+      "version": "12.3.4",
+      "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.3.4.tgz",
+      "integrity": "sha512-4csPbRbfZbuWOk3ATyWcvVFdD9/Rsdq5YHKvRuEni68OCLkfy4f+4I9OBpyK1SKJ00Cih16NJbHE+k+ljPPpag==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "packages/experimental-app-router/node_modules/@next/swc-linux-x64-musl": {
+      "version": "12.3.4",
+      "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.3.4.tgz",
+      "integrity": "sha512-YeBmI+63Ro75SUiL/QXEVXQ19T++58aI/IINOyhpsRL1LKdyfK/35iilraZEFz9bLQrwy1LYAR5lK200A9Gjbg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "packages/experimental-app-router/node_modules/@next/swc-win32-arm64-msvc": {
+      "version": "12.3.4",
+      "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.3.4.tgz",
+      "integrity": "sha512-Sd0qFUJv8Tj0PukAYbCCDbmXcMkbIuhnTeHm9m4ZGjCf6kt7E/RMs55Pd3R5ePjOkN7dJEuxYBehawTR/aPDSQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "packages/experimental-app-router/node_modules/@next/swc-win32-ia32-msvc": {
+      "version": "12.3.4",
+      "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.3.4.tgz",
+      "integrity": "sha512-rt/vv/vg/ZGGkrkKcuJ0LyliRdbskQU+91bje+PgoYmxTZf/tYs6IfbmgudBJk6gH3QnjHWbkphDdRQrseRefQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "packages/experimental-app-router/node_modules/@next/swc-win32-x64-msvc": {
+      "version": "12.3.4",
+      "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.3.4.tgz",
+      "integrity": "sha512-DQ20JEfTBZAgF8QCjYfJhv2/279M6onxFjdG/+5B0Cyj00/EdBxiWb2eGGFgQhrBbNv/lsvzFbbi0Ptf8Vw/bg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
     "packages/experimental-app-router/node_modules/@swc/helpers": {
       "version": "0.5.2",
       "dev": true,
diff --git a/packages/faustwp-core/src/getProps.ts b/packages/faustwp-core/src/getProps.ts
index b02f841b9..432f030cf 100644
--- a/packages/faustwp-core/src/getProps.ts
+++ b/packages/faustwp-core/src/getProps.ts
@@ -65,6 +65,7 @@ export async function getNextStaticProps<TProps>(
   if (isBoolean(notFound) && notFound === true) {
     return {
       notFound,
+      revalidate: DEFAULT_ISR_REVALIDATE,
     };
   }
 
diff --git a/packages/faustwp-core/src/getWordPressProps.tsx b/packages/faustwp-core/src/getWordPressProps.tsx
index c35ea4f5f..aad6aae30 100644
--- a/packages/faustwp-core/src/getWordPressProps.tsx
+++ b/packages/faustwp-core/src/getWordPressProps.tsx
@@ -16,6 +16,14 @@ function isSSR(
   return (ctx as GetServerSidePropsContext).req !== undefined;
 }
 
+const createNotFound = (
+  ctx: GetStaticPropsContext,
+  revalidate?: number | boolean,
+) => ({
+  notFound: true as const,
+  ...(!isSSR(ctx) && { revalidate: revalidate ?? DEFAULT_ISR_REVALIDATE }),
+});
+
 export type WordPressTemplate = React.FC & {
   query?: DocumentNode;
   variables?: (
@@ -54,6 +62,7 @@ export async function getWordPressProps(
     }
   | {
       notFound: true;
+      revalidate?: number | boolean | undefined;
     }
 > {
   const { templates } = getConfig();
@@ -84,9 +93,7 @@ export async function getWordPressProps(
   }) as string | null;
 
   if (!resolvedUrl) {
-    return {
-      notFound: true,
-    };
+    return createNotFound(ctx, revalidate);
   }
 
   const seedQuery = hooks.applyFilters('seedQueryDocumentNode', SEED_QUERY, {
@@ -104,9 +111,7 @@ export async function getWordPressProps(
   debugLog(`Seed Node for resolved url: "${resolvedUrl}": `, seedNode);
 
   if (!seedNode) {
-    return {
-      notFound: true,
-    };
+    return createNotFound(ctx, revalidate);
   }
 
   infoLog(
@@ -117,9 +122,7 @@ export async function getWordPressProps(
   const template = getTemplate(seedNode, templates);
 
   if (!template) {
-    return {
-      notFound: true,
-    };
+    return createNotFound(ctx, revalidate);
   }
 
   let templateQueryRes;
diff --git a/packages/faustwp-core/tests/getProps.test.ts b/packages/faustwp-core/tests/getProps.test.ts
index 0b0cc076f..0034a73ba 100644
--- a/packages/faustwp-core/tests/getProps.test.ts
+++ b/packages/faustwp-core/tests/getProps.test.ts
@@ -29,7 +29,7 @@ describe('getProps', () => {
       expect.assertions(1);
       expect(
         await getNextStaticProps({}, { Page: {}, notFound: true }),
-      ).toStrictEqual({ notFound: true });
+      ).toStrictEqual({ notFound: true, revalidate: 900 });
     });
 
     test('getNextStaticProps() handles `redirect`', async () => {

From 42cbe14baf5ec681eadba4a9fc18d93bd2dbad7f Mon Sep 17 00:00:00 2001
From: Matthew Wright <1815200+matthewguywright@users.noreply.github.com>
Date: Fri, 17 Nov 2023 13:43:05 -0600
Subject: [PATCH 7/8] MERL-1185: Redirect Off Doesn't Rewrite Post Preview Link
 (#1641)

* init commit

* added test for redirects being off

* Update ReplacementCallbacksTests.php

* Update callbacks.php

* setting restored after test

* added setting for redirects to be true for previous tests

* Update ReplacementCallbacksTests.php

* Refactor unit test

* Update callbacks.php

* added settings update back in

---------

Co-authored-by: Joe Fusco <josephfusco@users.noreply.github.com>
Co-authored-by: Blake Wilson <blake.wilson@wpengine.com>
Co-authored-by: John Parris <john.parris@wpengine.com>
---
 .../includes/replacement/callbacks.php        |  6 +++++-
 .../integration/ReplacementCallbacksTests.php | 19 ++++++++++++++++++-
 2 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/plugins/faustwp/includes/replacement/callbacks.php b/plugins/faustwp/includes/replacement/callbacks.php
index 9cf215124..efbf33315 100644
--- a/plugins/faustwp/includes/replacement/callbacks.php
+++ b/plugins/faustwp/includes/replacement/callbacks.php
@@ -11,8 +11,8 @@
 	faustwp_get_setting,
 	is_image_source_replacement_enabled,
 	is_rewrites_enabled,
+	is_redirects_enabled,
 	use_wp_domain_for_media,
-	use_wp_domain_for_post_and_category_urls,
 };
 use function WPE\FaustWP\Utilities\{
 	plugin_version,
@@ -146,6 +146,10 @@ function image_source_srcset_replacement( $sources ) {
  * @return string URL used for the post preview.
  */
 function post_preview_link( $link, $post ) {
+	// Don't rewrite preview link if redirect is disabled.
+	if ( ! is_redirects_enabled() ) {
+		return $link;
+	}
 	$frontend_uri = faustwp_get_setting( 'frontend_uri' );
 
 	if ( $frontend_uri ) {
diff --git a/plugins/faustwp/tests/integration/ReplacementCallbacksTests.php b/plugins/faustwp/tests/integration/ReplacementCallbacksTests.php
index 6ad1c77a5..d078cbdcd 100644
--- a/plugins/faustwp/tests/integration/ReplacementCallbacksTests.php
+++ b/plugins/faustwp/tests/integration/ReplacementCallbacksTests.php
@@ -210,6 +210,7 @@ public function test_post_link_returns_unfiltered_link_on_ajax_requests_to_gener
 	 */
 	public function test_post_preview_link_returns_filtered_link() {
 		faustwp_update_setting( 'frontend_uri', 'http://moo' );
+		faustwp_update_setting( 'enable_redirects', true );
 
 		$this->assertSame( 'http://moo/?p=' . $this->post_id . '&preview=true&previewPathname=' . rawurlencode( wp_make_link_relative( get_permalink( $this->post_id ) ) ) . '&typeName=Post', get_preview_post_link( $this->post_id ) );
 	}
@@ -219,12 +220,26 @@ public function test_post_preview_link_returns_filtered_link() {
 	 */
 	public function test_post_preview_link_adds_preview_true_query_param() {
 		faustwp_update_setting( 'frontend_uri', 'http://moo' );
+		faustwp_update_setting( 'enable_redirects', true );
 
 		$link = post_preview_link( 'http://moo/', get_post( $this->post_id ) );
 
 		$this->assertSame( 'http://moo/?previewPathname=' . rawurlencode( wp_make_link_relative( get_permalink( $this->post_id ) ) ) . '&p=' . $this->post_id . '&preview=true&typeName=Post', $link );
 	}
 
+	/**
+	 * Tests post_preview_link() doesn't rewrite link if enable redirects is false.
+	 */
+	public function test_post_preview_doesnt_rewrite_link_with_redirect_off() {
+		faustwp_update_setting( 'enable_redirects', false );
+		$expected = 'http://moo/?p=' . $this->post_id;
+		$link     = post_preview_link( $expected, get_post( $this->post_id ) );
+
+		$this->assertSame( $expected, $link );
+
+		faustwp_update_setting( 'enable_redirects', true );
+	}
+
 	/**
 	 * Tests post_preview_link() uses frontend_uri scheme if different than home_url scheme.
 	 */
@@ -244,7 +259,8 @@ public function test_post_preview_link_uses_frontend_uri_scheme() {
 	public function test_custom_post_type_post_preview_link_returns_filtered_link_when_content_replacement_is_enabled()
 	{
 		faustwp_update_setting( 'frontend_uri', 'http://moo' );
-		faustwp_update_setting( 'enable_rewrites', '1' );
+		faustwp_update_setting( 'enable_rewrites', true );
+		faustwp_update_setting( 'enable_redirects', true );
 		$post_id = $this->getCustomPostType();
 		$this->assertSame( 'http://moo/?document=' . $post_id . '&preview=true&previewPathname=' . rawurlencode( wp_make_link_relative( get_permalink( $post_id ) ) ) . '&p=' . $post_id . '&typeName=Document', get_preview_post_link( $post_id ) );
 		faustwp_update_setting( 'frontend_uri', null );
@@ -269,6 +285,7 @@ public function test_custom_post_type_post_link_returns_unfiltered_link_when_con
 	 */
 	public function test_post_preview_link_filters_link_for_posts_not_registered_with_wpgraphql() {
 		faustwp_update_setting( 'frontend_uri', 'http://moo' );
+		faustwp_update_setting( 'enable_redirects', true );
 
 		register_post_type('notgraphql', ['public' => true]);
 

From 2559958bacdc6809bce8e323af443388a593170d Mon Sep 17 00:00:00 2001
From: Theofanis Despoudis <328805+theodesp@users.noreply.github.com>
Date: Mon, 27 Nov 2023 17:01:10 +0000
Subject: [PATCH 8/8] [Merl 1256] Bug fix. Generate Autosave is missing from
 Post/Page Previews (#1644)

* Fix: (faustwp) Fix autosave in preview links.

* Chore: Changeset

* Chore: Update deps

* Security: Fix Axios Advisory

* WIP

* Add rest_prepare_post and rest_prepare_page callbacks to fix autoupdate.

* Lint: PHPCS update

* Lint: Revert phpcs update

* Testing: Attempt to update codeception.

* WIP: Revert last commit

* Build: Revert package-lock and composer.lock

* Tests: Add unit tests for filters.

* Update ReplacementCallbacksTests.php

* Chore: Revert composer.json

* WIP: Debug testing CI/CD

* WIP: Debugging CI/CD

* WIP: CI/CD debug

* WIP: Debug CI/CD

* WIP: Cleanup risky test
---
 .changeset/smooth-cooks-warn.md               |  5 ++++
 .../faustwp/includes/graphql/callbacks.php    | 12 ++++----
 .../includes/replacement/callbacks.php        | 18 ++++++++++++
 .../includes/replacement/previewlinks.js      | 28 ++++++++++---------
 .../integration/ReplacementCallbacksTests.php | 18 +++++++++++-
 5 files changed, 61 insertions(+), 20 deletions(-)
 create mode 100644 .changeset/smooth-cooks-warn.md

diff --git a/.changeset/smooth-cooks-warn.md b/.changeset/smooth-cooks-warn.md
new file mode 100644
index 000000000..907c5fe71
--- /dev/null
+++ b/.changeset/smooth-cooks-warn.md
@@ -0,0 +1,5 @@
+---
+'@faustwp/wordpress-plugin': patch
+---
+
+Bug Fix: Fixed missing call to autosave when using Post/Page previews.
diff --git a/plugins/faustwp/includes/graphql/callbacks.php b/plugins/faustwp/includes/graphql/callbacks.php
index 0cc956d8e..7c0cf1fdc 100644
--- a/plugins/faustwp/includes/graphql/callbacks.php
+++ b/plugins/faustwp/includes/graphql/callbacks.php
@@ -7,10 +7,10 @@
 
 namespace WPE\FaustWP\GraphQL;
 
-use function WPE\FaustWP\Auth\generate_authorization_code;
-use function WPE\FaustWP\Settings\get_secret_key;
 use GraphQL\Type\Definition\ResolveInfo;
 use WPGraphQL\AppContext;
+use function WPE\FaustWP\Auth\generate_authorization_code;
+use function WPE\FaustWP\Settings\get_secret_key;
 
 if ( ! defined( 'ABSPATH' ) ) {
 	exit;
@@ -64,7 +64,7 @@ function filter_introspection( $value, $default_value, $option_name, $section_fi
 	// check header for faust secret key.
 	if ( ! isset( $_SERVER['HTTP_X_FAUST_SECRET'] ) ) {
 		return $value;
-	};
+	}
 
 	$secret_key = get_secret_key();
 	if ( $secret_key !== $_SERVER['HTTP_X_FAUST_SECRET'] ) {
@@ -92,7 +92,7 @@ function register_faust_toolbar_field() {
 		'shouldShowFaustToolbar',
 		array(
 			'type'    => 'Boolean',
-			'resolve' => function() {
+			'resolve' => function () {
 				$user                    = wp_get_current_user();
 				$toolbar_preference_meta = get_user_meta( $user->ID, 'show_admin_bar_front', true );
 
@@ -149,7 +149,7 @@ function register_global_stylesheet_field() {
 				),
 			),
 			'description' => __( 'Returns the stylesheet resulting of merging core, theme, and user data.', 'faustwp' ),
-			'resolve'     => function( $root, $args, $context, $info ) {
+			'resolve'     => function ( $root, $args, $context, $info ) {
 				$types = $args['types'] ?? null;
 
 				return wp_get_global_stylesheet( $types );
@@ -339,7 +339,7 @@ function register_generate_ac_mutation() {
 					'description' => __( 'Error encountered during user authentication, if any', 'faustwp' ),
 				),
 			),
-			'mutateAndGetPayload' => function( $input, $context, $info ) {
+			'mutateAndGetPayload' => function ( $input, $context, $info ) {
 				$is_email = isset( $input['email'] ) ? true : false;
 				$username = isset( $input['username'] ) ? $input['username'] : null;
 				$email = isset( $input['email'] ) ? $input['email'] : null;
diff --git a/plugins/faustwp/includes/replacement/callbacks.php b/plugins/faustwp/includes/replacement/callbacks.php
index efbf33315..6f72e8857 100644
--- a/plugins/faustwp/includes/replacement/callbacks.php
+++ b/plugins/faustwp/includes/replacement/callbacks.php
@@ -304,6 +304,24 @@ function enqueue_preview_scripts() {
 	);
 }
 
+add_filter( 'rest_prepare_post', __NAMESPACE__ . '\\preview_link_in_rest_response', 10, 2 );
+add_filter( 'rest_prepare_page', __NAMESPACE__ . '\\preview_link_in_rest_response', 10, 2 );
+/**
+ * Adds the preview link to rest responses.
+ *
+ * @param WP_REST_Response $response The rest response object.
+ * @param WP_Post          $post Post object.
+ *
+ * @return string URL used for the post preview.
+ */
+function preview_link_in_rest_response( $response, $post ) {
+	if ( 'draft' === $post->post_status ) {
+		$response->data['link'] = get_preview_post_link( $post->ID );
+	}
+
+	return $response;
+}
+
 add_filter( 'wp_sitemaps_posts_entry', __NAMESPACE__ . '\\sitemaps_posts_entry' );
 /**
  * Filters the sitemap entry for an individual post.
diff --git a/plugins/faustwp/includes/replacement/previewlinks.js b/plugins/faustwp/includes/replacement/previewlinks.js
index ac48faf44..13af970b4 100644
--- a/plugins/faustwp/includes/replacement/previewlinks.js
+++ b/plugins/faustwp/includes/replacement/previewlinks.js
@@ -2,7 +2,7 @@
  * XXX: Please remove this once this issue is resolved: https://github.com/WordPress/gutenberg/issues/13998
  */
 
-document.addEventListener('DOMContentLoaded', function() {
+document.addEventListener('DOMContentLoaded', function () {
   // Get the preview data via wp_localize_script
   const faustPreviewData = window._faustwp_preview_data;
 
@@ -19,12 +19,10 @@ document.addEventListener('DOMContentLoaded', function() {
 
   function debounce(func, wait) {
     let timeout;
-    return function() {
-      const context = this;
-      const args = arguments;
+    return function (...args) {
       clearTimeout(timeout);
-      timeout = setTimeout(function() {
-        func.apply(context, args);
+      timeout = setTimeout(function () {
+        func(args);
       }, wait);
     };
   }
@@ -34,8 +32,12 @@ document.addEventListener('DOMContentLoaded', function() {
     switch (version) {
       default:
         return {
-          headerLink: document.querySelector('.edit-post-header-preview__grouping-external a'),
-          snackbarLink: document.querySelector('.components-snackbar__content a'),
+          headerLink: document.querySelector(
+            '.edit-post-header-preview__grouping-external a',
+          ),
+          snackbarLink: document.querySelector(
+            '.components-snackbar__content a',
+          ),
         };
     }
   }
@@ -43,14 +45,14 @@ document.addEventListener('DOMContentLoaded', function() {
   function updateUIElements() {
     const { headerLink, snackbarLink } = getPreviewLinksByVersion(wpVersion);
 
-    // Clone & replace the original link in order to clear pre-existing events.
     if (headerLink && headerLink.getAttribute('href') !== faustPreviewLink) {
-      const clonedHeaderLink = headerLink.cloneNode(true);
-      headerLink.parentNode.replaceChild(clonedHeaderLink, headerLink);
-      if (clonedHeaderLink) clonedHeaderLink.setAttribute('href', faustPreviewLink);
+      headerLink.setAttribute('href', faustPreviewLink);
     }
 
-    if (snackbarLink && snackbarLink.getAttribute('href') !== faustPreviewLink) {
+    if (
+      snackbarLink &&
+      snackbarLink.getAttribute('href') !== faustPreviewLink
+    ) {
       snackbarLink.setAttribute('href', faustPreviewLink);
     }
   }
diff --git a/plugins/faustwp/tests/integration/ReplacementCallbacksTests.php b/plugins/faustwp/tests/integration/ReplacementCallbacksTests.php
index d078cbdcd..3ccbfb7a9 100644
--- a/plugins/faustwp/tests/integration/ReplacementCallbacksTests.php
+++ b/plugins/faustwp/tests/integration/ReplacementCallbacksTests.php
@@ -10,14 +10,17 @@
 use function WPE\FaustWP\Replacement\{
 	content_replacement,
 	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;
 
 class ReplacementCallbacksTests extends \WP_UnitTestCase {
 	protected $post_id;
+	protected $draft_post_id;
 
 	public function setUp(): void {
 		parent::setUp();
@@ -27,6 +30,11 @@ public function setUp(): void {
 			'post_content' => 'Hi',
 			'post_status'  => 'publish',
 		] );
+		$this->draft_post_id = wp_insert_post( [
+			'title'        => 'Hello',
+			'post_content' => 'Hi',
+			'post_status'  => 'draft',
+		] );
 	}
 
 	public function test_the_content_filter() {
@@ -37,6 +45,14 @@ public function test_preview_post_link_filter() {
 		$this->assertSame( 1000, has_action( 'preview_post_link', 'WPE\FaustWP\Replacement\post_preview_link' ) );
 	}
 
+	public function test_preview_rest_prepare_post_filter() {
+		$this->assertSame( 10, has_action( 'rest_prepare_post', 'WPE\FaustWP\Replacement\preview_link_in_rest_response' ) );
+	}
+
+	public function test_preview_rest_prepare_page_filter() {
+		$this->assertSame( 10, has_action( 'rest_prepare_page', 'WPE\FaustWP\Replacement\preview_link_in_rest_response' ) );
+	}
+
 	public function test_post_link_filter() {
 		$this->assertSame( 1000, has_action( 'post_link', 'WPE\FaustWP\Replacement\post_link' ) );
 	}