PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.1-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.1-beta
16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / vendor / automattic / jetpack-plans / src / class-current-plan.php
jetpack / vendor / automattic / jetpack-plans / src Last commit date
class-current-plan.php 2 months ago class-plans.php 9 months ago
class-current-plan.php
444 lines
1 <?php
2 /**
3 * Handles fetching of the site's plan and products from WordPress.com and caching values locally.
4 *
5 * @package automattic/jetpack-plans
6 */
7
8 namespace Automattic\Jetpack;
9
10 use Automattic\Jetpack\Connection\Client;
11 use Automattic\Jetpack\Connection\Manager;
12
13 /**
14 * Provides methods methods for fetching the site's plan and products from WordPress.com.
15 */
16 class Current_Plan {
17 /**
18 * A cache variable to hold the active plan for the current request.
19 *
20 * @var array
21 */
22 private static $active_plan_cache;
23
24 /**
25 * Simple Site-specific features available.
26 * Their calculation can be expensive and slow, so we're caching it for the request.
27 *
28 * @var array Site-specific features
29 */
30 private static $simple_site_specific_features = array();
31
32 /**
33 * The name of the option that will store the site's plan.
34 *
35 * @var string
36 */
37 const PLAN_OPTION = 'jetpack_active_plan';
38
39 /**
40 * The name of the option that will store the site's products.
41 *
42 * @var string
43 */
44 const SITE_PRODUCTS_OPTION = 'jetpack_site_products';
45
46 const PLAN_DATA = array(
47 'free' => array(
48 'plans' => array(
49 'jetpack_free',
50 ),
51 'supports' => array(
52 'advanced-seo',
53 'opentable',
54 'calendly',
55 'send-a-message',
56 'sharing-block',
57 'whatsapp-button',
58 'social-previews',
59 'videopress',
60 'videopress/video',
61 'v6-video-frame-poster',
62
63 'core/video',
64 'core/cover',
65 'core/audio',
66 'multistep-form',
67 'form-webhooks',
68 ),
69 ),
70 'personal' => array(
71 'plans' => array(
72 'jetpack_personal',
73 'jetpack_personal_monthly',
74 'personal-bundle',
75 'personal-bundle-monthly',
76 'personal-bundle-2y',
77 'personal-bundle-3y',
78 'starter-plan',
79 ),
80 'supports' => array(
81 'akismet',
82 'payments',
83 'videopress',
84 ),
85 ),
86 'premium' => array(
87 'plans' => array(
88 'jetpack_premium',
89 'jetpack_premium_monthly',
90 'value_bundle',
91 'value_bundle-monthly',
92 'value_bundle-2y',
93 'value_bundle-3y',
94 'jetpack_creator_yearly',
95 'jetpack_creator_bi_yearly',
96 'jetpack_creator_monthly',
97 ),
98 'supports' => array(
99 'simple-payments',
100 'vaultpress',
101 'videopress',
102 'republicize',
103 ),
104 ),
105 'security' => array(
106 'plans' => array(
107 'jetpack_security_daily',
108 'jetpack_security_daily_monthly',
109 'jetpack_security_realtime',
110 'jetpack_security_realtime_monthly',
111 'jetpack_security_t1_yearly',
112 'jetpack_security_t1_monthly',
113 'jetpack_security_t2_yearly',
114 'jetpack_security_t2_monthly',
115 ),
116 'supports' => array(),
117 ),
118 'business' => array(
119 'plans' => array(
120 'jetpack_business',
121 'jetpack_business_monthly',
122 'business-bundle',
123 'business-bundle-monthly',
124 'business-bundle-2y',
125 'business-bundle-3y',
126 'ecommerce-bundle',
127 'ecommerce-bundle-monthly',
128 'ecommerce-bundle-2y',
129 'ecommerce-bundle-3y',
130 'pro-plan',
131 'wp_bundle_migration_trial_monthly',
132 'wp_bundle_hosting_trial_monthly',
133 'ecommerce-trial-bundle-monthly',
134 'wooexpress-small-bundle-yearly',
135 'wooexpress-small-bundle-monthly',
136 'wooexpress-medium-bundle-yearly',
137 'wooexpress-medium-bundle-monthly',
138 'wp_com_hundred_year_bundle_centennially',
139 ),
140 'supports' => array(
141 'ai-seo-enhancer',
142 ),
143 ),
144
145 'complete' => array(
146 'plans' => array(
147 'jetpack_complete',
148 'jetpack_complete_monthly',
149 'vip',
150 ),
151 'supports' => array(
152 'field-file', // Forms
153 'social-image-generator',
154 ),
155 ),
156 );
157
158 /**
159 * Given a response to the `/sites/%d` endpoint, will parse the response and attempt to set the
160 * site's plan and products from the response.
161 *
162 * @param array $response The response from `/sites/%d`.
163 * @return bool Was the plan successfully updated?
164 */
165 public static function update_from_sites_response( $response ) {
166 // Bail if there was an error or malformed response.
167 if ( is_wp_error( $response ) || ! is_array( $response ) || ! isset( $response['body'] ) ) {
168 return false;
169 }
170
171 $body = wp_remote_retrieve_body( $response );
172 if ( is_wp_error( $body ) ) {
173 return false;
174 }
175
176 // Decode the results.
177 $results = json_decode( $body, true );
178
179 if ( ! is_array( $results ) ) {
180 return false;
181 }
182
183 if ( isset( $results['products'] ) ) {
184 // Store the site's products in an option and return true if updated.
185 self::store_data_in_option( self::SITE_PRODUCTS_OPTION, $results['products'] );
186 }
187
188 if ( ! isset( $results['plan'] ) ) {
189 return false;
190 }
191
192 $current_plan = get_option( self::PLAN_OPTION, array() );
193
194 if ( ! empty( $current_plan ) && $current_plan === $results['plan'] ) {
195 // Bail if the plans array hasn't changed.
196 return false;
197 }
198
199 // Store the new plan in an option and return true if updated.
200 $result = self::store_data_in_option( self::PLAN_OPTION, $results['plan'] );
201
202 if ( $result ) {
203 // Reset the cache since we've just updated the plan.
204 self::$active_plan_cache = null;
205 }
206
207 return $result;
208 }
209
210 /**
211 * Store data in an option.
212 *
213 * @param string $option The name of the option that will store the data.
214 * @param array $data Data to be store in an option.
215 * @return bool Were the subscriptions successfully updated?
216 */
217 private static function store_data_in_option( $option, $data ) {
218 $result = update_option( $option, $data, true );
219
220 // If something goes wrong with the update, so delete the current option and then update it.
221 if ( ! $result ) {
222 delete_option( $option );
223 $result = update_option( $option, $data, true );
224 }
225
226 return $result;
227 }
228
229 /**
230 * Make an API call to WordPress.com for plan status
231 *
232 * @uses Jetpack_Options::get_option()
233 * @uses Client::wpcom_json_api_request_as_blog()
234 * @uses update_option()
235 *
236 * @access public
237 * @static
238 *
239 * @return bool True if plan is updated, false if no update
240 */
241 public static function refresh_from_wpcom() {
242 $site_id = Manager::get_site_id();
243 if ( is_wp_error( $site_id ) ) {
244 return false;
245 }
246
247 // Make the API request.
248
249 $response = Client::wpcom_json_api_request_as_blog(
250 sprintf( '/sites/%d?force=wpcom', $site_id ),
251 '1.1'
252 );
253
254 return self::update_from_sites_response( $response );
255 }
256
257 /**
258 * Get the plan that this Jetpack site is currently using.
259 *
260 * @uses get_option()
261 *
262 * @access public
263 * @static
264 *
265 * @return array Active Jetpack plan details
266 */
267 public static function get() {
268 // this can be expensive to compute so we cache for the duration of a request.
269 if ( is_array( self::$active_plan_cache ) && ! empty( self::$active_plan_cache ) ) {
270 return self::$active_plan_cache;
271 }
272
273 $plan = get_option( self::PLAN_OPTION, array() );
274
275 // Set the default options.
276 $plan = wp_parse_args(
277 $plan,
278 array(
279 'product_slug' => 'jetpack_free',
280 'class' => 'free',
281 'features' => array(
282 'active' => array(),
283 ),
284 )
285 );
286
287 list( $plan['class'], $supports ) = self::get_class_and_features( $plan['product_slug'] );
288
289 $modules = new Modules();
290 foreach ( $modules->get_available() as $module_slug ) {
291 $module = $modules->get( $module_slug );
292 if ( ! isset( $module ) || ! is_array( $module ) ) {
293 continue;
294 }
295 if ( in_array( 'free', $module['plan_classes'], true ) || in_array( $plan['class'], $module['plan_classes'], true ) ) {
296 $supports[] = $module_slug;
297 }
298 }
299
300 $plan['supports'] = $supports;
301
302 self::$active_plan_cache = $plan;
303
304 return $plan;
305 }
306
307 /**
308 * Get the site's products.
309 *
310 * @uses get_option()
311 *
312 * @access public
313 * @static
314 *
315 * @return array Active Jetpack products
316 */
317 public static function get_products() {
318 return get_option( self::SITE_PRODUCTS_OPTION, array() );
319 }
320
321 /**
322 * Get the class of plan and a list of features it supports
323 *
324 * @param string $plan_slug The plan that we're interested in.
325 * @return array Two item array, the plan class and the an array of features.
326 */
327 private static function get_class_and_features( $plan_slug ) {
328 $features = array();
329 foreach ( self::PLAN_DATA as $class => $details ) {
330 $features = array_merge( $features, $details['supports'] );
331 if ( in_array( $plan_slug, $details['plans'], true ) ) {
332 return array( $class, $features );
333 }
334 }
335 return array( 'free', self::PLAN_DATA['free']['supports'] );
336 }
337
338 /**
339 * Gets the minimum plan slug that supports the given feature
340 *
341 * @param string $feature The name of the feature.
342 * @return string|bool The slug for the minimum plan that supports.
343 * the feature or false if not found
344 */
345 public static function get_minimum_plan_for_feature( $feature ) {
346 foreach ( self::PLAN_DATA as $details ) {
347 if ( in_array( $feature, $details['supports'], true ) ) {
348 return $details['plans'][0];
349 }
350 }
351 return false;
352 }
353
354 /**
355 * Determine whether the active plan supports a particular feature
356 *
357 * @uses self::get()
358 *
359 * @access public
360 * @static
361 *
362 * @param string $feature The module or feature to check.
363 * @param bool $refresh_from_wpcom Refresh the local plan cache from wpcom.
364 *
365 * @return bool True if plan supports feature, false if not
366 */
367 public static function supports( $feature, $refresh_from_wpcom = false ) {
368 if ( $refresh_from_wpcom ) {
369 self::refresh_from_wpcom();
370 }
371
372 // Hijack the feature eligibility check on WordPress.com sites since they are gated differently.
373 $should_wpcom_gate_feature = (
374 function_exists( 'wpcom_site_has_feature' ) &&
375 function_exists( 'wpcom_feature_exists' ) &&
376 wpcom_feature_exists( $feature )
377 );
378 if ( $should_wpcom_gate_feature ) {
379 return wpcom_site_has_feature( $feature );
380 }
381
382 // Search product bypasses plan feature check.
383 if ( 'search' === $feature && (bool) get_option( 'has_jetpack_search_product' ) ) {
384 return true;
385 }
386
387 // As of Q3 2021 - a videopress free tier is available to all plans.
388 if ( 'videopress' === $feature ) {
389 return true;
390 }
391
392 // As of 05 2023 - all plans support Earn features (minus 'simple-payments').
393 if ( in_array( $feature, array( 'donations', 'recurring-payments', 'premium-content/container' ), true ) ) {
394 return true;
395 }
396
397 $plan = self::get();
398
399 if (
400 in_array( $feature, $plan['supports'], true )
401 || in_array( $feature, $plan['features']['active'], true )
402 ) {
403 return true;
404 }
405
406 return false;
407 }
408
409 /**
410 * Retrieve site-specific features for Simple sites.
411 *
412 * See Jetpack_Gutenberg::get_site_specific_features()
413 *
414 * @return array
415 */
416 public static function get_simple_site_specific_features() {
417 $is_simple_site = defined( 'IS_WPCOM' ) && constant( 'IS_WPCOM' );
418
419 if ( ! $is_simple_site ) {
420 return array(
421 'active' => array(),
422 'available' => array(),
423 );
424 }
425
426 $current_blog_id = get_current_blog_id();
427
428 // Return the cached value if it exists.
429 if ( isset( self::$simple_site_specific_features[ $current_blog_id ] ) ) {
430 return self::$simple_site_specific_features[ $current_blog_id ];
431 }
432
433 if ( ! class_exists( '\Store_Product_List' ) ) {
434 require WP_CONTENT_DIR . '/admin-plugins/wpcom-billing/store-product-list.php';
435 }
436
437 $simple_site_specific_features = \Store_Product_List::get_site_specific_features_data( $current_blog_id );
438
439 self::$simple_site_specific_features[ $current_blog_id ] = $simple_site_specific_features;
440
441 return $simple_site_specific_features;
442 }
443 }
444