PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.3.7
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.3.7
1.3.7 1.3.6 1.3.5 1.3.4 1.3.3 1.3.2 1.3.1 1.3.0 1.2.4 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 All 33 releases
← All changes | includes/modules/Cdn/CdnModule.php +47 -10 1.1.4 → 1.3.7 View file →
@@ -24,11 +24,12 @@
24 24 public const VERSION = '1.0.0';
25 25
26 26 public function ui_metadata(): array {
27 27 return array(
28 - 'label' => 'CDN',
28 + 'label' => __( 'CDN', 'xspeed' ),
29 29 'icon' => 'Globe',
30 - 'description' => 'Serve static assets (images, fonts, CSS, JS) from a pull-zone CDN host like BunnyCDN, KeyCDN, or your own.',
30 + 'description' => __( 'Serve images, fonts, CSS and JS from a CDN such as BunnyCDN or KeyCDN.', 'xspeed' ),
31 + 'group' => 'network',
31 32 );
32 33 }
33 34
34 35 public function settings_schema(): array {
@@ -35,16 +36,16 @@
35 36 return array(
36 37 'enabled' => array(
37 38 'type' => 'bool',
38 39 'default' => false,
39 - 'label' => 'Enable CDN',
40 - 'description' => 'Rewrite static asset URLs to the CDN hostname below. Your CDN must be a pull-zone configured to fetch from this site.',
40 + 'label' => __( 'Enable CDN', 'xspeed' ),
41 + 'description' => __( 'Load static files from the CDN address below. Set up the CDN to pull files from this site first.', 'xspeed' ),
41 42 ),
42 43 'cdn_url' => array(
43 44 'type' => 'string',
44 45 'default' => '',
45 - 'label' => 'CDN URL',
46 - 'description' => 'CDN hostname, e.g. cdn.example.com. https:// and trailing slashes are stripped automatically.',
46 + 'label' => __( 'CDN URL', 'xspeed' ),
47 + 'description' => __( 'The CDN address, for example cdn.example.com. xSpeed removes https:// and any trailing slash.', 'xspeed' ),
47 48 'dependsOn' => array( 'field' => 'enabled' ),
48 49 ),
49 50 'included_extensions' => array(
50 51 'type' => 'list',
@@ -49,10 +50,10 @@
49 50 'included_extensions' => array(
50 51 'type' => 'list',
51 52 'default' => Cdn_Rewriter::DEFAULT_EXTENSIONS,
52 53 'item_type' => 'string',
53 - 'label' => 'Included File Extensions',
54 - 'description' => 'Only URLs ending in these extensions are rewritten. Defaults cover images, fonts, CSS, JS, and common media.',
54 + 'label' => __( 'File types to serve', 'xspeed' ),
55 + 'description' => __( 'Only files with these extensions load from the CDN. The defaults cover images, fonts, CSS, JS and common media.', 'xspeed' ),
55 56 'dependsOn' => array( 'field' => 'enabled' ),
56 57 ),
57 58 'excluded_patterns' => array(
58 59 'type' => 'list',
@@ -57,10 +58,10 @@
57 58 'excluded_patterns' => array(
58 59 'type' => 'list',
59 60 'default' => array(),
60 61 'item_type' => 'string',
61 - 'label' => 'Excluded Patterns',
62 - 'description' => 'Glob patterns matched against the URL path. Matching URLs stay on the origin. Examples: /wp-admin/*, *.pdf, /private/*',
62 + 'label' => __( 'Excluded paths', 'xspeed' ),
63 + 'description' => __( 'Files whose path matches a pattern load from your server, not the CDN. Use * as a wildcard, for example /private/* or *.pdf.', 'xspeed' ),
63 64 'dependsOn' => array( 'field' => 'enabled' ),
64 65 ),
65 66 );
66 67 }
@@ -76,8 +77,27 @@
76 77 );
77 78 }
78 79
79 80 public function boot(): void {
81 + /*
82 + * Deferred to `init`. This module reads its own settings to decide
83 + * what to hook, and reading settings builds settings_schema(), whose
84 + * labels are declared through __(). boot() runs on `plugins_loaded`,
85 + * before `after_setup_theme` — the point WordPress 6.7+ treats as the
86 + * earliest safe moment to translate — so doing that here fires
87 + * _load_textdomain_just_in_time on every request AND resolves the
88 + * labels against a domain that is not loaded yet.
89 + *
90 + * Everything below hooks actions that fire after `init`, so running
91 + * one hook later is equivalent.
92 + */
93 + add_action( 'init', array( $this, 'boot_on_init' ) );
94 + }
95 +
96 + /**
97 + * The real boot body — see boot() for why it runs on `init`.
98 + */
99 + public function boot_on_init(): void {
80 100 // Always-on: normalize cdn_url on save (admin context too).
81 101 add_filter( 'pre_update_option_xspeed_module_cdn', array( $this, 'normalize_on_save' ), 10, 1 );
82 102
83 103 // CDN URLs are baked into cached HTML, so a settings change that
@@ -89,8 +109,14 @@
89 109
90 110 if ( is_admin() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) || ( defined( 'DOING_CRON' ) && DOING_CRON ) || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) {
91 111 return;
92 112 }
113 +
114 + // Rewriting asset hosts under a builder editor sends the editor's own
115 + // scripts to the CDN, where the copy can be stale or absent. (#281)
116 + if ( \XSpeed\Builder_Editor::is_active() ) {
117 + return;
118 + }
93 119 $opts = $this->get_settings();
94 120 if ( empty( $opts['enabled'] ) || empty( $opts['cdn_url'] ) ) {
95 121 return;
96 122 }
@@ -237,8 +263,18 @@
237 263 public function on_settings_change(): void {
238 264 $this->sync_font_cors();
239 265 if ( class_exists( '\\XSpeed\\Cache' ) ) {
240 266 \XSpeed\Cache::purge_all( 'cdn settings change' );
267 + // purge_all() only reaches what we wrote. The attachment-URL
268 + // filter below runs DURING render, so a page builder that caches
269 + // rendered output has already stored the old host — Elementor
270 + // keeps it in `_elementor_element_cache` for 24 h and in
271 + // `uploads/elementor/css/post-<id>.css` with no expiry at all.
272 + // Without this, turning the CDN OFF keeps serving the dead host
273 + // (images 404 once the pull zone lapses) and turning it ON leaves
274 + // the LCP hero on the origin — both for a day or more, both after
275 + // a purge the user watched succeed.
276 + \XSpeed\Cache::purge_render_caches( 'cdn settings change' );
241 277 }
242 278 }
243 279
244 280 /**
@@ -379,8 +415,9 @@
379 415 array(
380 416 'name' => 'xspeed cdn',
381 417 'callback' => array( $this, 'cli_handler' ),
382 418 'shortdesc' => 'Show CDN settings + test rewriting a URL.',
419 + 'ai_hint' => 'Is a CDN configured, and does URL rewriting work? Use to check whether assets are served from the CDN, or to test what a given URL rewrites to before trusting the setting.',
383 420 'synopsis' => array(
384 421 array(
385 422 'type' => 'positional',
386 423 'name' => 'action',