PluginProbe
Faust.js / trunk
Faust.js vtrunk
1.8.12 1.8.11 1.4.1 1.8.0 1.8.8 1.8.9 trunk 0.7.0 0.7.1 0.7.10 0.7.11 0.7.3 0.7.4 0.7.5 0.7.6 0.7.7 0.7.8 0.7.9 0.8.0 0.8.1 0.8.3 0.8.4 0.8.5 0.8.6 0.8.7 All 40 releases
faustwp / includes / replacement / callbacks.php

callbacks.php in Faust.js trunk, at includes/replacement/callbacks.php

423 lines 12.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Replacement related callbacks.
4 *
5 * @package FaustWP
6 */
7
8 declare(strict_types=1);
9
10 namespace WPE\FaustWP\Replacement;
11
12 use function WPE\FaustWP\Settings\{
13 faustwp_get_setting,
14 is_image_source_replacement_enabled,
15 is_rewrites_enabled,
16 is_redirects_enabled,
17 use_wp_domain_for_media,
18 };
19 use function WPE\FaustWP\Utilities\{
20 plugin_version,
21 };
22
23 if ( ! defined( 'ABSPATH' ) ) {
24 exit;
25 }
26
27 add_filter( 'the_content', __NAMESPACE__ . '\\content_replacement' );
28 add_filter( 'wpgraphql_content_blocks_resolver_content', __NAMESPACE__ . '\\content_replacement' );
29 /**
30 * Replace the URLs in the post content basesd on two settings if enabled:
31 * 1. Enable Post and Category URL rewrites - rewrite the WordPress internal URL in the content with the Front-end URL
32 * 1.1 If NOT enabled - use the WordPress URL
33 * 2. Use the WordPress domain for media URLs in post content - use WordPress URL for media files (images,csv,pdf)
34 * 2.1 If NOT enabled - use front-end url
35 *
36 * @param ?string $content The post content.
37 *
38 * @return ?string The post content.
39 */
40 function content_replacement( ?string $content ) {
41
42 if ( ! $content ) {
43 return $content;
44 }
45 $replace_content_urls = domain_replacement_enabled();
46 $replace_media_urls = ! use_wp_domain_for_media();
47
48 if ( ! $replace_content_urls && ! $replace_media_urls ) {
49 return $content;
50 }
51 $wp_site_urls = faustwp_get_wp_site_urls( site_url() );
52 if ( empty( $wp_site_urls ) ) {
53 return $content;
54 }
55 $relative_upload_url = faustwp_get_relative_upload_url( $wp_site_urls, wp_upload_dir()['baseurl'] );
56 $wp_media_urls = faustwp_get_wp_media_urls( $wp_site_urls, $relative_upload_url );
57 $frontend_uri = (string) faustwp_get_setting( 'frontend_uri' ) ?? '/';
58
59 /* If "Enable Post and Category URL" is enabled, use front-end URL for internal URLs, but not for media links */
60
61 if ( $replace_content_urls ) {
62
63 // Look for href links.
64 preg_match_all( '#href="([^"]+)"#i', $content, $href_links );
65 if ( is_array( $href_links ) && ! empty( $href_links[1] ) ) {
66 foreach ( $href_links[1] as $i => $url ) {
67 // skip media links.
68 $is_media = array_filter( $wp_media_urls, fn( $media ) => strpos( $url, $media ) === 0 );
69 if ( $is_media ) {
70 continue;
71 }
72
73 $is_wp_url = array_filter( $wp_site_urls, fn( $base ) => strpos( $url, $base ) === 0 );
74 if ( ! $is_wp_url ) {
75 continue;
76 }
77
78 // get relative link.
79 $relative = str_replace( reset( $is_wp_url ), '', $url );
80 $updated = 'href="' . $frontend_uri . $relative . '"';
81
82 $original = $href_links[0][ $i ];
83
84 if ( $original ) {
85 $content = str_replace( $original, $updated, $content );
86 }
87 }
88 }
89 }
90
91 /* If "Use the WordPress domain for media URLs in post content" is NOT enabled, use front-end URL for media URLs */
92
93 if ( $replace_media_urls ) {
94 $content = str_replace( $wp_media_urls, $frontend_uri . $relative_upload_url, $content );
95 }
96
97 return $content;
98 }
99
100 /**
101 * Callback for WordPress 'the_content' filter to replace paths to media.
102 *
103 * @param string $content The post content.
104 *
105 * @return string The post content.
106 */
107 function image_source_replacement( $content ) {
108 if ( ! is_image_source_replacement_enabled() ) {
109 return $content;
110 }
111
112 $frontend_uri = faustwp_get_setting( 'frontend_uri' );
113 $site_url = site_url();
114
115 // For urls with no domain or the frontend domain, replace with the wp site_url.
116 $patterns = array(
117 "#src=\"{$frontend_uri}/#",
118 '#src="/#',
119 );
120
121 return preg_replace( $patterns, "src=\"{$site_url}/", $content );
122 }
123
124 add_filter( 'wp_calculate_image_srcset', __NAMESPACE__ . '\\image_source_srcset_replacement' );
125 /**
126 * Callback for WordPress 'wp_calculate_image_srcset' filter to replace paths when generating a srcset
127 *
128 * @link https://developer.wordpress.org/reference/functions/wp_calculate_image_srcset/
129 *
130 * @param array<string> $sources One or more arrays of source data to include in the 'srcset'.
131 *
132 * @return array One or more arrays of source data.
133 */
134 function image_source_srcset_replacement( $sources ) {
135
136 if ( ! is_array( $sources ) || empty( $sources ) ) {
137 return $sources;
138 }
139
140 $wp_site_urls = faustwp_get_wp_site_urls( site_url() );
141 if ( empty( $wp_site_urls ) ) {
142 return $sources;
143 }
144
145 $replace_media_urls = ! use_wp_domain_for_media();
146 $relative_upload_url = faustwp_get_relative_upload_url( $wp_site_urls, wp_upload_dir()['baseurl'] );
147 $wp_media_urls = faustwp_get_wp_media_urls( $wp_site_urls, $relative_upload_url );
148 $frontend_uri = (string) faustwp_get_setting( 'frontend_uri' );
149 $site_url = site_url() . '/';
150
151 $wp_media_site_url = $frontend_uri . $relative_upload_url;
152 $patterns = array(
153 "#^{$frontend_uri}/#",
154 '#^/#',
155 );
156
157 /**
158 * Update each source with the correct replacement URL
159 */
160 foreach ( $sources as $width => $source ) {
161 $url = $source['url'];
162
163 if ( $replace_media_urls ) {
164 $sources[ $width ]['url'] = ( strpos( $url, $relative_upload_url ) === 0 )
165 ? $frontend_uri . $url
166 : str_replace( $wp_media_urls, $wp_media_site_url, $source['url'] );
167 continue;
168 }
169
170 // We need to make sure that the frontend URL or relative URL (legacy) is updated with the site url.
171 $sources[ $width ]['url'] = preg_replace( $patterns, $site_url, $source['url'] );
172 }
173
174 return $sources;
175 }
176
177 add_filter( 'preview_post_link', __NAMESPACE__ . '\\post_preview_link', 1000, 2 );
178 /**
179 * Callback for WordPress 'preview_post_link' filter.
180 *
181 * Swap the post preview link for headless front-end and to use the API entry to support Next.js preview mode.
182 *
183 * @param string $link URL used for the post preview.
184 * @param WP_Post $post Post object.
185 *
186 * @return string URL used for the post preview.
187 */
188 function post_preview_link( $link, $post ) {
189 // Don't rewrite preview link if redirect is disabled.
190 if ( ! is_redirects_enabled() ) {
191 return $link;
192 }
193 $frontend_uri = faustwp_get_setting( 'frontend_uri' );
194
195 if ( $frontend_uri ) {
196 $home_url = trailingslashit( get_home_url() );
197 $frontend_uri = trailingslashit( $frontend_uri );
198
199 $link = str_replace( $home_url, $frontend_uri, $link );
200
201 // Replace the schemes, if different.
202 $frontend_uri_scheme = wp_parse_url( $frontend_uri, PHP_URL_SCHEME );
203 $link_scheme = wp_parse_url( $link, PHP_URL_SCHEME );
204 if ( $frontend_uri_scheme !== $link_scheme ) {
205 $link = str_replace( $link_scheme . '://', $frontend_uri_scheme . '://', $link );
206 }
207
208 /**
209 * This should already be handled by WPE\FaustWP\Replacement\post_link, but
210 * it's here for verbosity's sake and if the other filter changes for any reason.
211 */
212 $parsed_link_query = wp_parse_url( $link, PHP_URL_QUERY );
213 $args = wp_parse_args( $parsed_link_query );
214 $preview_id = isset( $args['preview_id'] ) ? $args['preview_id'] : $post->ID;
215
216 // Remove ?p=xx&preview=true from link temporarily.
217 $link = remove_query_arg(
218 array_keys( $args ),
219 $link
220 );
221
222 if ( ! isset( $args['previewPathname'] ) ) {
223 $args['previewPathname'] = rawurlencode( wp_make_link_relative( get_permalink( $post ) ) );
224 }
225
226 // Add p=xx if it's missing, which is the case for published posts.
227 if ( ! isset( $args['p'] ) ) {
228 $args['p'] = $preview_id;
229 }
230
231 // Add preview=true in case other plugins have stripped it.
232 if ( ! isset( $args['preview'] ) ) {
233 $args['preview'] = 'true';
234 }
235
236 // Add page_id=xx if it's missing, which is the case for published pages.
237 if ( ! isset( $args['page_id'] ) && 'page' === $post->post_type ) {
238 $args['page_id'] = $preview_id;
239 }
240
241 /**
242 * We use this query param to get the correct preview post type.
243 * This saves us a second request to the GraphQL endpoint, as we first
244 * need to determine the preview node post type before retrieving
245 * the data.
246 */
247 $post_type_object = get_post_type_object( $post->post_type );
248 if (
249 ! isset( $args['typeName'] ) &&
250 isset( $post_type_object ) &&
251 ! empty( $post_type_object->graphql_single_name )
252 ) {
253 $gql_type_name = ucfirst( $post_type_object->graphql_single_name );
254 $args['typeName'] = $gql_type_name;
255 }
256
257 // Add ?p=xx&preview=true to link again.
258 $link = add_query_arg(
259 array(
260 $args,
261 ),
262 $link
263 );
264 }
265
266 return $link;
267 }
268
269 add_filter( 'post_link', __NAMESPACE__ . '\\post_link', 1000 );
270 add_filter( 'page_link', __NAMESPACE__ . '\\post_link', 1000 );
271 add_filter( 'post_type_link', __NAMESPACE__ . '\\post_link', 1000 );
272 /**
273 * Callback for WordPress 'post_link', 'page_link', and `post_type_link` filter.
274 *
275 * Swap post links in admin for headless front-end.
276 *
277 * @param string $link URL used for the post.
278 *
279 * @return string URL used for the post.
280 */
281 function post_link( $link ) {
282 global $pagenow;
283 $target_pages = array(
284 'admin-ajax.php',
285 'index.php',
286 'edit.php',
287 'post.php',
288 'post-new.php',
289 'upload.php',
290 'media-new.php',
291 );
292
293 // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified in `is_ajax_generate_permalink_request()` and `is_wp_link_ajax_request()`.
294 if ( empty( $_POST ) && 'post-new.php' === $pagenow ) {
295 return $link;
296 }
297
298 // Ajax requests to generate permalink.
299 if ( in_array( $pagenow, $target_pages, true )
300 && is_ajax_generate_permalink_request()
301 ) {
302 return $link;
303 }
304
305 if (
306 ! is_rewrites_enabled()
307 || ( function_exists( 'is_graphql_request' ) && is_graphql_request() )
308 // Block editor makes REST requests on these pages to query content.
309 || ( in_array( $pagenow, $target_pages, true ) && current_user_can( 'edit_posts' ) && defined( 'REST_REQUEST' ) && REST_REQUEST )
310 ) {
311 return $link;
312 }
313
314 // Check for wp-link-ajax requests. Used by Classic Editor when linking content.
315 if ( is_wp_link_ajax_request() ) {
316 return $link;
317 }
318
319 return equivalent_frontend_url( $link );
320 }
321
322 add_filter( 'term_link', __NAMESPACE__ . '\\term_link', 1000 );
323 /**
324 * Rewrites term links to point to the specified front-end URL.
325 *
326 * @param string $term_link Term link URL.
327 *
328 * @return string
329 */
330 function term_link( $term_link ) {
331 if (
332 ! is_rewrites_enabled()
333 || ( function_exists( 'is_graphql_request' ) && is_graphql_request() )
334 ) {
335 return $term_link;
336 }
337
338 return equivalent_frontend_url( $term_link );
339 }
340
341 add_action( 'enqueue_block_editor_assets', __NAMESPACE__ . '\\enqueue_preview_scripts' );
342 /**
343 * Adds JavaScript file to the Gutenberg editor page that prepends /preview to the preview link.
344 *
345 * XXX: Please remove this once this issue is resolved: https://github.com/WordPress/gutenberg/issues/13998
346 */
347 function enqueue_preview_scripts() {
348 wp_enqueue_script( 'faustwp-gutenberg-filters', plugins_url( '/previewlinks.js', __FILE__ ), array(), plugin_version(), true );
349 wp_localize_script(
350 'faustwp-gutenberg-filters',
351 '_faustwp_preview_data',
352 array(
353 '_preview_link' => get_preview_post_link(),
354 '_wp_version' => get_bloginfo( 'version' ),
355 )
356 );
357 }
358
359 add_filter( 'rest_api_init', __NAMESPACE__ . '\\register_preview_link_hooks_for_all_draft_post_types' );
360
361 /**
362 * Registers the preview link hooks for all post types.
363 */
364 function register_preview_link_hooks_for_all_draft_post_types() {
365 $post_types = get_post_types(
366 array(
367 'public' => true,
368 )
369 );
370
371 foreach ( $post_types as $post_type ) {
372 add_filter( 'rest_prepare_' . $post_type, __NAMESPACE__ . '\\preview_link_in_rest_response', 10, 2 );
373 }
374 }
375
376 /**
377 * Adds the preview link to rest responses.
378 *
379 * @param WP_REST_Response $response The rest response object.
380 * @param WP_Post $post Post object.
381 *
382 * @return string URL used for the post preview.
383 */
384 function preview_link_in_rest_response( $response, $post ) {
385 if ( 'draft' === $post->post_status ) {
386 $response->data['link'] = get_preview_post_link( $post->ID );
387 }
388
389 return $response;
390 }
391
392 add_filter( 'wp_sitemaps_posts_entry', __NAMESPACE__ . '\\sitemaps_posts_entry' );
393 /**
394 * Filters the sitemap entry for an individual post.
395 *
396 * @param array $sitemap_entry Sitemap entry for the post.
397 */
398 function sitemaps_posts_entry( $sitemap_entry ) {
399 return normalize_sitemap_entry( $sitemap_entry );
400 }
401
402 add_filter( 'wp_sitemaps_taxonomies_entry', __NAMESPACE__ . '\\sitemaps_taxonomies_entry' );
403 /**
404 * Filters the sitemap entry for an individual term.
405 *
406 * @param array $sitemap_entry Sitemap entry for the term.
407 */
408 function sitemaps_taxonomies_entry( $sitemap_entry ) {
409 return normalize_sitemap_entry( $sitemap_entry );
410 }
411
412 add_filter( 'wpseo_xml_sitemap_post_url', __NAMESPACE__ . '\\yoast_sitemap_post_url' );
413 /**
414 * Filter the URL Yoast SEO uses in the XML sitemap.
415 *
416 * Note that only absolute local URLs are allowed as the check after this removes external URLs.
417 *
418 * @param string $url URL to use in the XML sitemap.
419 */
420 function yoast_sitemap_post_url( $url ) {
421 return equivalent_wp_url( $url );
422 }
423