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-recommendations.php +45 -20 1.1.11.3.3 View file →
@@ -45,12 +45,18 @@
45 45 'id' => 'cache_disabled',
46 46 'priority' => 5,
47 47 'title' => __( 'Enable page caching', 'xspeed' ),
48 48 'detail' => __( 'Caching is off, so every visit renders the full page. Turning it on is the single biggest speed win.', 'xspeed' ),
49 - 'action' => array(
50 - 'type' => 'link',
51 - 'hash' => '#cache',
52 - 'label' => __( 'Go to Cache settings', 'xspeed' ),
49 + 'action' => array_merge(
50 + array( 'type' => 'link' ),
51 + // Lands ON the enable toggle, not merely on the panel
52 + // that contains it (issue #49).
53 + // No `suggest` here: cache_enabled is deliberately outside
54 + // the cache schema (it drives the drop-in install), so the
55 + // Apply strip — which only renders on a schema row — could
56 + // never appear for it. Offering a value nothing can apply
57 + // is worse than offering none.
58 + Deep_Link::action( __( 'Go to Cache settings', 'xspeed' ), 'cache', 'cache_enabled' )
53 59 ),
54 60 );
55 61 // Later rules assume a running cache; report just this one.
56 62 return $out;
@@ -100,12 +106,13 @@
100 106 __( '%1$s sets the "%2$s" cookie on anonymous pages, which makes CDNs skip their edge cache for all HTML.', 'xspeed' ),
101 107 $culprit,
102 108 (string) ( $first['name'] ?? '' )
103 109 ),
104 - 'action' => array(
105 - 'type' => 'link',
106 - 'hash' => '#health',
107 - 'label' => __( 'See details in Health', 'xspeed' ),
110 + 'action' => array_merge(
111 + array( 'type' => 'link' ),
112 + // Health is a read-only panel — there is no control to
113 + // focus, so this one carries the destination only.
114 + Deep_Link::action( __( 'See details in Health', 'xspeed' ), 'health' )
108 115 ),
109 116 );
110 117 }
111 118
@@ -115,12 +122,11 @@
115 122 'id' => 'nginx_snippet_missing',
116 123 'priority' => 20,
117 124 'title' => __( 'Apply the nginx server snippet', 'xspeed' ),
118 125 'detail' => __( 'nginx can serve cache hits directly (~5-15ms TTFB, PHP bypassed) once the snippet is in your server block.', 'xspeed' ),
119 - 'action' => array(
120 - 'type' => 'link',
121 - 'hash' => '#cache',
122 - 'label' => __( 'Get the snippet', 'xspeed' ),
126 + 'action' => array_merge(
127 + array( 'type' => 'link' ),
128 + Deep_Link::action( __( 'Get the snippet', 'xspeed' ), 'cache', 'nginx_snippet' )
123 129 ),
124 130 );
125 131 }
126 132
@@ -150,12 +156,11 @@
150 156 'id' => 'object_cache_degraded',
151 157 'priority' => 30,
152 158 'title' => __( 'Object cache is configured but not persisting', 'xspeed' ),
153 159 'detail' => __( 'The backend is not connected, so every request falls back to the database. Run the connection test to see why.', 'xspeed' ),
154 - 'action' => array(
155 - 'type' => 'link',
156 - 'hash' => '#object-cache',
157 - 'label' => __( 'Test the connection', 'xspeed' ),
160 + 'action' => array_merge(
161 + array( 'type' => 'link' ),
162 + Deep_Link::action( __( 'Test the connection', 'xspeed' ), 'object-cache', 'connection_test' )
158 163 ),
159 164 );
160 165 }
161 166
@@ -166,12 +171,11 @@
166 171 'id' => 'cloudflare_unverified',
167 172 'priority' => 35,
168 173 'title' => __( 'Finish connecting Cloudflare', 'xspeed' ),
169 174 'detail' => __( 'The Cloudflare module is on but has no verified credentials or zone, so edge purges cannot work.', 'xspeed' ),
170 - 'action' => array(
171 - 'type' => 'link',
172 - 'hash' => '#cloudflare',
173 - 'label' => __( 'Open Cloudflare settings', 'xspeed' ),
175 + 'action' => array_merge(
176 + array( 'type' => 'link' ),
177 + Deep_Link::action( __( 'Open Cloudflare settings', 'xspeed' ), 'cloudflare', 'api_token' )
174 178 ),
175 179 );
176 180 }
177 181
@@ -253,8 +257,29 @@
253 257 'applied' => $id,
254 258 'recommendations' => self::all(),
255 259 );
256 260 }
261 + /**
262 + * Last chance to resolve a recommendation id this engine doesn't own.
263 + *
264 + * Free and Pro each ship a recommendation engine with its OWN id
265 + * namespace — Free uses underscores (`nginx_snippet_missing`), Pro
266 + * uses hyphens (`cache-disabled`) — but only Free's ids reached this
267 + * method, so EVERY Pro Apply button returned 404 and the failure was
268 + * swallowed by the UI. This seam lets Pro claim its own ids rather
269 + * than duplicating the endpoint. (#198)
270 + *
271 + * Return an array (the same shape this method returns on success) or
272 + * a WP_Error to claim the id; return null to decline it.
273 + *
274 + * @param array|\WP_Error|null $handled Result from a previous filter, or null.
275 + * @param string $id The recommendation id being applied.
276 + */
277 + $handled = apply_filters( 'xspeed_apply_recommendation', null, $id );
278 + if ( is_array( $handled ) || is_wp_error( $handled ) ) {
279 + return $handled;
280 + }
281 +
257 282 return new \WP_Error(
258 283 'xspeed_rec_unknown',
259 284 __( 'Unknown or no-longer-applicable recommendation.', 'xspeed' ),
260 285 array( 'status' => 404 )