PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3
16.3 16.3-beta 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 All 508 releases
jetpack / jetpack_vendor / automattic / jetpack-search / src / class-ai-answers.php

class-ai-answers.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3, at jetpack_vendor/automattic/jetpack-search/src/class-ai-answers.php

216 lines 6.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * AI Answers feature — behavior meta and enabled flag.
4 *
5 * @package automattic/jetpack-search
6 */
7
8 namespace Automattic\Jetpack\Search;
9
10 use Automattic\Jetpack\Constants;
11 use Automattic\Jetpack\Modules;
12 use Automattic\Jetpack\Status\Host;
13
14 /**
15 * Registers behavior meta on the Gutenberg Guidelines CPT and exposes the
16 * jetpack_search_ai_answers_enabled option.
17 */
18 class AI_Answers {
19 const BEHAVIOR_META_KEY = '_guideline_block_jetpack_search-ai-summary';
20 const BEHAVIOR_OPTION_KEY = 'jetpack_search_ai_behavior_instructions';
21 const AI_MODULE = 'ai';
22 const AI_MASTER_OPTION = 'jetpack_ai_enabled';
23 const ENABLED_OPTION = 'jetpack_search_ai_answers_enabled';
24
25 /**
26 * Hook up meta/setting registration.
27 */
28 public function init() {
29 add_action( 'rest_api_init', array( $this, 'register_behavior_meta' ) );
30 }
31
32 /**
33 * Register the behavior instructions storage for the REST API.
34 *
35 * When the Gutenberg Guidelines CPT is present, registers the block-specific
36 * meta key on it. Otherwise registers a site option exposed via /wp/v2/settings.
37 */
38 public function register_behavior_meta() {
39 if ( post_type_exists( 'wp_guideline' ) ) {
40 register_post_meta(
41 'wp_guideline',
42 self::BEHAVIOR_META_KEY,
43 array(
44 'single' => true,
45 'type' => 'string',
46 'show_in_rest' => true,
47 'default' => '',
48 'sanitize_callback' => 'sanitize_textarea_field',
49 'auth_callback' => function () {
50 return current_user_can( 'manage_options' );
51 },
52 )
53 );
54 return;
55 }
56
57 register_setting(
58 'options',
59 self::BEHAVIOR_OPTION_KEY,
60 array(
61 'type' => 'string',
62 'default' => '',
63 'sanitize_callback' => 'sanitize_textarea_field',
64 'show_in_rest' => true,
65 )
66 );
67 }
68
69 /**
70 * Retrieve the behavior instructions.
71 *
72 * Reads from the Gutenberg Guidelines CPT when available, otherwise falls
73 * back to the site option.
74 *
75 * @return string Behavior instructions, or empty string if none saved.
76 */
77 public static function get_behavior_instructions() {
78 if ( post_type_exists( 'wp_guideline' ) ) {
79 $posts = get_posts(
80 array(
81 'post_type' => 'wp_guideline',
82 'posts_per_page' => 1,
83 'post_status' => 'publish',
84 )
85 );
86 if ( ! empty( $posts ) ) {
87 $guidelines = get_post_meta( $posts[0]->ID, self::BEHAVIOR_META_KEY, true );
88 return is_string( $guidelines ) ? $guidelines : '';
89 }
90 }
91 return (string) get_option( self::BEHAVIOR_OPTION_KEY, '' );
92 }
93
94 /**
95 * Whether the site-wide AI checks allow AI Answers, regardless of its saved setting.
96 *
97 * Probe the feature filter for additional restrictions from the Jetpack plugin.
98 *
99 * @since 8.0.0
100 *
101 * @return bool
102 */
103 public static function is_master_enabled() {
104 if ( ! self::should_enforce_master() ) {
105 return false;
106 }
107
108 // Ignore the saved master switch where its controls have not launched.
109 if ( ! self::is_master_rollout_active() ) {
110 return true;
111 }
112
113 return (bool) apply_filters( 'jetpack_search_ai_answers_enabled', true );
114 }
115
116 /**
117 * Whether master enforcement has rolled out here.
118 *
119 * Simple keeps its existing option contract, self-hosted sites use the
120 * Jetpack module, and Atomic remains limited to internal testing.
121 *
122 * @return bool
123 */
124 private static function is_master_rollout_active() {
125 $host = new Host();
126 if ( $host->is_wpcom_simple() ) {
127 return true;
128 }
129
130 return ! $host->is_woa_site()
131 || ( function_exists( 'jetpack_is_internal_testing_environment' ) && jetpack_is_internal_testing_environment() );
132 }
133
134 /**
135 * Whether the AI filter and the site's master switch allow AI Answers.
136 *
137 * Mirrors Jetpack_AI_Settings without requiring the Jetpack plugin on standalone Search sites.
138 *
139 * @since 8.0.0
140 *
141 * @return bool Whether site-wide AI restrictions allow AI Answers.
142 */
143 public static function should_enforce_master() {
144 /** This filter is documented in projects/plugins/jetpack/_inc/lib/class-jetpack-ai-settings.php */
145 if ( ! apply_filters( 'jetpack_ai_enabled', true ) ) {
146 return false;
147 }
148
149 if ( ! self::is_master_rollout_active() ) {
150 return true;
151 }
152
153 if ( ( new Host() )->is_wpcom_simple() ) {
154 return (bool) get_option( self::AI_MASTER_OPTION, true );
155 }
156
157 $modules = new Modules();
158
159 // Without the Jetpack plugin — a standalone Jetpack Search install — the
160 // `ai` module is not registered, so is_active() would report false for a
161 // master switch that was never installed. Don't gate those sites.
162 if ( ! in_array( self::AI_MODULE, $modules->get_available(), true ) ) {
163 return true;
164 }
165
166 // Availability is already proven above, so skip is_active()'s repeat intersect.
167 return $modules->is_active( self::AI_MODULE, false );
168 }
169
170 /**
171 * The stored AI Answers choice, ignoring every gate.
172 *
173 * The dashboard shows this while the master switch is off, so a saved choice
174 * isn't misreported back to the user as off.
175 *
176 * @since 8.0.0
177 *
178 * @return bool
179 */
180 public static function is_saved_on() {
181 return (bool) get_option( self::ENABLED_OPTION, false );
182 }
183
184 /**
185 * Whether AI Answers is enabled for the current site.
186 *
187 * Paid-plan eligibility is applied after the filter chain alongside the
188 * master gate so neither can be filtered back on.
189 */
190 public static function is_enabled() {
191 $enabled = (bool) apply_filters( 'jetpack_search_ai_answers_enabled', self::is_saved_on() );
192
193 // The master gate is applied after the filter chain so it cannot be
194 // filtered back on, matching `Jetpack_AI_Settings::is_ai_enabled()`.
195 return $enabled && self::should_enforce_master() && Search_Blocks::supports_paid_search();
196 }
197
198 /**
199 * Whether the host allows AI at all — core's wp_supports_ai(), falling back
200 * to the WP_AI_SUPPORT constant on WordPress versions that predate it.
201 * Mirrors the Jetpack plugin's Jetpack_AI_Settings::host_allows_ai().
202 *
203 * @since 8.0.0
204 *
205 * @return bool
206 */
207 public static function host_allows_ai() {
208 if ( function_exists( 'wp_supports_ai' ) ) {
209 return wp_supports_ai();
210 }
211
212 // WordPress versions predating wp_supports_ai() only have the constant.
213 return ! Constants::is_defined( 'WP_AI_SUPPORT' ) || (bool) Constants::get_constant( 'WP_AI_SUPPORT' );
214 }
215 }
216