PluginProbe
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN / 1.3.3
xSpeed Cache: AI-Powered Performance Hub with MCP, Caching & CDN v1.3.3
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 1.1.5 1.1.6 1.1.7 1.1.8 All 29 releases
← All changes | includes/class-onboarding.php +108 -7 1.1.11.3.3 View file →
@@ -125,27 +125,50 @@
125 125 $asset_js = XSPEED_DIR . 'assets/admin.js';
126 126 $asset_css = XSPEED_DIR . 'assets/admin.css';
127 127
128 128 if ( file_exists( $asset_js ) ) {
129 + // filemtime() cache-busts on every rebuild (matches Admin::enqueue)
130 + // so a stable version between releases never serves a stale bundle
131 + // on the wizard — the connected/login state always reflects the
132 + // current build.
129 133 wp_enqueue_script(
130 134 'xspeed-admin',
131 135 XSPEED_URL . 'assets/admin.js',
132 - array( 'wp-api-fetch' ),
133 - XSPEED_VERSION,
136 + array( 'wp-api-fetch', 'wp-i18n' ),
137 + XSPEED_VERSION . '.' . filemtime( $asset_js ),
134 138 true
135 139 );
140 + // Load .mo translations into window.wp.i18n so the wizard's React
141 + // __() calls resolve (mirrors Admin::enqueue). Without this the
142 + // onboarding strings render untranslated even when a locale exists.
143 + if ( function_exists( 'wp_set_script_translations' ) ) {
144 + wp_set_script_translations( 'xspeed-admin', 'xspeed', XSPEED_DIR . 'languages' );
145 + }
136 146 }
147 + // Redesign v2 tokens + fonts (see Admin::enqueue) — the wizard shares
148 + // them so it matches the dashboard identity.
149 + $theme_css = XSPEED_DIR . 'assets/theme.css';
150 + if ( file_exists( $theme_css ) ) {
151 + wp_enqueue_style(
152 + 'xspeed-theme',
153 + XSPEED_URL . 'assets/theme.css',
154 + array(),
155 + XSPEED_VERSION . '.' . filemtime( $theme_css )
156 + );
157 + }
137 158 if ( file_exists( $asset_css ) ) {
138 159 wp_enqueue_style(
139 160 'xspeed-admin',
140 161 XSPEED_URL . 'assets/admin.css',
141 - array(),
142 - XSPEED_VERSION
162 + array( 'xspeed-theme' ),
163 + XSPEED_VERSION . '.' . filemtime( $asset_css )
143 164 );
144 165 }
145 166
146 - wp_localize_script(
147 - 'xspeed-admin',
167 + // Same type-preserving path as the dashboard — see
168 + // Admin::print_config(). wp_localize_script() would stringify every
169 + // scalar in this payload too. (#105)
170 + Admin::print_config(
148 171 'XSpeedConfig',
149 172 array(
150 173 'mode' => 'onboarding',
151 174 'restUrl' => esc_url_raw( rest_url( Rest_Api::NAMESPACE_V1 ) ),
@@ -157,9 +180,18 @@
157 180 'branding' => Admin::branding(),
158 181 'dashboardUrl' => admin_url( 'admin.php?page=' . Admin::PAGE_SLUG ),
159 182 'bootstrap' => array(
160 183 'settings' => Settings::get(),
184 + // Live values for every wizard toggle, so re-running the
185 + // wizard reflects the site instead of overwriting it.
186 + 'current' => self::current_choices(),
161 187 'env' => self::env_payload(),
188 + // xSpeed Hub connection snapshot for the wizard's first
189 + // "Connect your account" step. Guarded so core onboarding
190 + // never hard-depends on the MCP module — if it's absent the
191 + // step falls back to its own /mcp/hub fetch (and simply shows
192 + // the not-connected invite). See DESIGN.md §24.x.
193 + 'hub' => self::hub_payload(),
162 194 ),
163 195 )
164 196 );
165 197 }
@@ -164,8 +196,50 @@
164 196 );
165 197 }
166 198
167 199 /**
200 + * The site's CURRENT values for every toggle the wizard can write,
201 + * in the same shape as the wizard's OnboardingChoices.
202 + *
203 + * The wizard used to seed its toggles from hard-coded preset constants,
204 + * so re-running it on a configured site showed a fiction: options the
205 + * admin had deliberately switched on rendered as off, and Apply wrote
206 + * that fiction back, silently undoing their configuration. Nothing
207 + * warned them, and the completion screen still reported success.
208 + *
209 + * The wizard couldn't have done better on its own — the bootstrap only
210 + * carried Settings::get(), which is just cache_enabled. Every other
211 + * toggle lives in a per-module option the payload never included, so
212 + * this method is what makes "show the site as it actually is" possible.
213 + *
214 + * Pure read. Mirrors the keys apply() writes, so the two stay in step.
215 + *
216 + * @return array<string,bool|int>
217 + */
218 + public static function current_choices() {
219 + $minify = Settings_Manager::get( 'minify' );
220 + $gzip = Settings_Manager::get( 'gzip' );
221 + $cache = Settings_Manager::get( 'cache' );
222 + $lazy = Settings_Manager::get( 'lazy' );
223 + $browser = Settings_Manager::get( 'browser-cache' );
224 + $hints = Settings_Manager::get( 'resource-hints' );
225 + $settings = Settings::get();
226 +
227 + return array(
228 + 'cache_enabled' => ! empty( $settings['cache_enabled'] ),
229 + 'minify_html' => ! empty( $minify['minify_html'] ),
230 + 'minify_css' => ! empty( $minify['minify_css'] ),
231 + 'minify_js' => ! empty( $minify['minify_js'] ),
232 + 'defer_js' => ! empty( $minify['defer_js'] ),
233 + 'gzip_enabled' => ! empty( $gzip['gzip_enabled'] ),
234 + 'lazy_images' => ! empty( $lazy['lazy_images'] ),
235 + 'browser_cache' => ! empty( $browser['enabled'] ),
236 + 'resource_hints' => ! empty( $hints['enabled'] ),
237 + 'cache_expiry' => isset( $cache['cache_expiry'] ) ? absint( $cache['cache_expiry'] ) : \XSpeed\Modules\Cache\CacheModule::DEFAULT_EXPIRY_HOURS,
238 + );
239 + }
240 +
241 + /**
168 242 * Environment snapshot rendered as Step 1's health rows. Pure read —
169 243 * never writes to disk, never makes outbound requests.
170 244 */
171 245 public static function env_payload() {
@@ -174,8 +248,32 @@
174 248 // module's dashboard panel consume from there.
175 249 return Health::env_payload();
176 250 }
177 251
252 + /**
253 + * xSpeed Hub connection snapshot for the wizard's first step. Identical
254 + * shape to the MCP panel's `GET /mcp/hub` response so the Connect step and
255 + * the panel's Hub card share one contract. Returns null when the MCP module
256 + * is unavailable — the step then renders its own not-connected invite and
257 + * re-fetches live from /mcp/hub if the route exists.
258 + *
259 + * @return array<string,mixed>|null
260 + */
261 + public static function hub_payload() {
262 + if ( ! class_exists( '\XSpeed\Modules\Mcp\Mcp_Hub' ) ) {
263 + return null;
264 + }
265 + $status = \XSpeed\Modules\Mcp\Mcp_Hub::public_status();
266 + // Override attach_url so the Hub returns the user to the WIZARD (mid-flow)
267 + // after they approve — not the default dashboard. The `xspeed_connected`
268 + // marker lets the wizard show the connected state + auto-advance on
269 + // return. Requires the Hub to honor return_url; if it doesn't yet, the
270 + // tab-return reconcile in useHubConnect is the graceful fallback.
271 + $return = admin_url( 'admin.php?page=' . self::PAGE_SLUG . '&xspeed_connected=1' );
272 + $status['attach_url'] = \XSpeed\Modules\Mcp\Mcp_Hub::attach_url( $return );
273 + return $status;
274 + }
275 +
178 276 public function register_routes() {
179 277 register_rest_route(
180 278 Rest_Api::NAMESPACE_V1,
181 279 '/onboarding/apply',
@@ -272,8 +370,12 @@
272 370 // Opt-in usage analytics. Only acted on when the key is present in the
273 371 // payload (legacy onboarding-complete sites are left untouched). The
274 372 // consent toggle defaults OFF in the wizard, so the common path is
275 373 // usage_tracking=false → tracker stays dormant, no outbound HTTP.
374 + // (True since #437; before that the toggle shipped pre-checked and
375 + // this comment described an intent the UI did not implement. The
376 + // same consent is now also writable from Settings → Privacy & usage
377 + // data, via PrivacyModule, which routes through the same opt_in().)
276 378 if ( array_key_exists( 'usage_tracking', $params ) ) {
277 379 $tracker = Plugin::instance()->usage_tracker();
278 380 if ( $tracker ) {
279 381 $tracker->opt_in( ! empty( $params['usage_tracking'] ) );
@@ -280,9 +382,8 @@
280 382 }
281 383 }
282 384
283 385 $install_state = Cache::toggle( $want_cache );
284 - Settings::update( array( 'cache_enabled' => $install_state['enabled'] ) );
285 386
286 387 // Recompute the unified nginx block AFTER cache_enabled is persisted.
287 388 // Cache::toggle() computes it inline, before the Settings::update()
288 389 // above writes cache_enabled — so the block in $install_state reflects