PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.7
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.7
16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 All 506 releases
← All changes | _inc/lib/class-jetpack-ai-settings.php +113 -8 16.3-a.1 → 16.3-a.7 View file →
@@ -19,9 +19,11 @@
19 19 *
20 20 * @package automattic/jetpack
21 21 */
22 22
23 +use Automattic\Jetpack\Connection\Manager;
23 24 use Automattic\Jetpack\Modules;
25 +use Automattic\Jetpack\Status;
24 26 use Automattic\Jetpack\Status\Host;
25 27
26 28 if ( ! defined( 'ABSPATH' ) ) {
27 29 exit( 0 );
@@ -53,8 +55,32 @@
53 55 */
54 56 const AI_MODULE = 'ai';
55 57
56 58 /**
59 + * The `jetpack_ai_enabled` route custom code can take to hold AI off, as
60 + * reported by {@see self::get_master_forced_off_route()}. Each route names a
61 + * different hook, so each needs its own documentation link.
62 + *
63 + * @var string
64 + */
65 + const FORCED_OFF_ROUTE_FILTER = 'filter';
66 +
67 + /**
68 + * The module-filter route; see {@see self::FORCED_OFF_ROUTE_FILTER}.
69 + *
70 + * @var string
71 + */
72 + const FORCED_OFF_ROUTE_MODULES = 'modules';
73 +
74 + /**
75 + * The filter route on VIP, which documents this filter as its own supported
76 + * off switch and so owns the page to send the reader to.
77 + *
78 + * @var string
79 + */
80 + const FORCED_OFF_ROUTE_FILTER_VIP = 'filter-vip';
81 +
82 + /**
57 83 * Feature key => option name for every toggle on the AI settings page.
58 84 *
59 85 * `ai_search` reuses an option owned by the Search surface; the rest are
60 86 * registered by this class. The automatic-generation option is deliberately
@@ -99,8 +125,16 @@
99 125 */
100 126 private static $initialized = false;
101 127
102 128 /**
129 + * Whether apply_master_gates() should step aside; see
130 + * {@see self::get_master_forced_off_route()}.
131 + *
132 + * @var bool
133 + */
134 + private static $probing_third_party = false;
135 +
136 + /**
103 137 * Hook everything up. Must run on every request (front-end, editor, REST):
104 138 * the filters attached here gate feature loading.
105 139 *
106 140 * @return void
@@ -201,8 +235,14 @@
201 235 * @param bool $enabled The value the call site computed so far.
202 236 * @return bool
203 237 */
204 238 public static function apply_master_gates( $enabled ) {
239 + // Stand aside while get_master_forced_off_route() asks the chain what
240 + // everyone else says; our own verdict would drown theirs out.
241 + if ( self::$probing_third_party ) {
242 + return (bool) $enabled;
243 + }
244 +
205 245 return (bool) $enabled
206 246 && self::host_allows_ai()
207 247 && ( ! self::should_enforce_ai_controls() || self::is_master_enabled() );
208 248 }
@@ -287,8 +327,79 @@
287 327 return ( new Modules() )->is_active( self::AI_MODULE );
288 328 }
289 329
290 330 /**
331 + * Whether the site's WordPress.com connection can carry AI. Offline mode
332 + * counts as disconnected even while the site holds its tokens, and Simple
333 + * sites are always connected.
334 + *
335 + * @return bool
336 + */
337 + public static function site_is_connected() {
338 + return ( new Host() )->is_wpcom_simple()
339 + || ( ( new Manager( 'jetpack' ) )->has_connected_owner()
340 + && ! ( new Status() )->is_offline_mode() );
341 + }
342 +
343 + /**
344 + * Whether the current user's own account is connected. Surfaces that proxy
345 + * as the requesting user need this on top of {@see self::site_is_connected()}.
346 + *
347 + * @return bool
348 + */
349 + public static function user_is_connected() {
350 + return ( new Host() )->is_wpcom_simple()
351 + || ( new Manager( 'jetpack' ) )->is_user_connected();
352 + }
353 +
354 + /**
355 + * Which hook custom code used to hold AI off, so the notice can link to the
356 + * matching documentation. Always empty on WordPress.com Simple, which runs
357 + * no modules.
358 + *
359 + * @return string One of the FORCED_OFF_ROUTE_* constants, or '' when nothing
360 + * holds AI off.
361 + */
362 + public static function get_master_forced_off_route() {
363 + $host = new Host();
364 +
365 + if ( $host->is_wpcom_simple() ) {
366 + return '';
367 + }
368 +
369 + // Ask the chain with our own gates stood down, so a deactivated module
370 + // cannot mask a filter that would keep AI off however the module is set.
371 + $third_party_off = false;
372 + self::$probing_third_party = true;
373 + try {
374 + $third_party_off = ! apply_filters( 'jetpack_ai_enabled', true );
375 + } finally {
376 + self::$probing_third_party = false;
377 + }
378 +
379 + if ( $third_party_off ) {
380 + return $host->is_vip_site()
381 + ? self::FORCED_OFF_ROUTE_FILTER_VIP
382 + : self::FORCED_OFF_ROUTE_FILTER;
383 + }
384 +
385 + if ( self::is_master_enabled() ) {
386 + return '';
387 + }
388 +
389 + // Removed from the available list by `jetpack_get_available_modules`.
390 + if ( ! in_array( self::AI_MODULE, ( new Modules() )->get_available(), true ) ) {
391 + return self::FORCED_OFF_ROUTE_MODULES;
392 + }
393 +
394 + // Forced off through `option_jetpack_active_modules` or `jetpack_active_modules`.
395 + $overridden = class_exists( 'Jetpack_Modules_Overrides' )
396 + && 'inactive' === Jetpack_Modules_Overrides::instance()->get_module_override( self::AI_MODULE );
397 +
398 + return $overridden ? self::FORCED_OFF_ROUTE_MODULES : '';
399 + }
400 +
401 + /**
291 402 * Set the site-wide AI master switch, writing to whichever store backs it on
292 403 * this platform (see {@see self::is_master_enabled()}).
293 404 *
294 405 * On WordPress.com Simple the `jetpack_ai_enabled` option is the master, so
@@ -347,16 +458,10 @@
347 458 return (bool) get_option( $option, self::FEATURE_DEFAULTS[ $feature ] );
348 459 }
349 460
350 461 /**
351 - * Whether the AI SEO feature (metadata generation, manual and automatic)
352 - * is effectively enabled: its own toggle (gate 4) through the filter, with
353 - * the host and master gates ANDed after the chain so no late-priority
354 - * callback can turn the feature back on — same finality as is_ai_enabled().
462 + * Whether AI SEO is enabled after its feature filter and the site-wide AI checks.
355 463 *
356 - * Not {@see self::is_feature_enabled()} with `ai_seo`, which is the stored
357 - * toggle alone. This is the one load points and payloads should read.
358 - *
359 464 * @since 16.2
360 465 *
361 466 * @return bool
362 467 */
@@ -369,9 +474,9 @@
369 474 * @param bool $enabled Whether the SEO feature toggle is on.
370 475 */
371 476 $enabled = (bool) apply_filters( 'jetpack_ai_seo_enabled', self::is_feature_enabled( 'ai_seo' ) );
372 477
373 - return self::apply_master_gates( $enabled );
478 + return $enabled && self::is_ai_enabled();
374 479 }
375 480 }
376 481
377 482 // Self-initialize on load. The consuming AI extension files require this file