PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.2-a.1
Jetpack – WP Security, Backup, Speed, & Growth v16.2-a.1
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 days ago class-plans.php 8 months ago
class-current-plan.php
453 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 return self::update_from_site_record( json_decode( $body, true ) );
177 }
178
179 /**
180 * Given a decoded `/sites/%d` record, attempt to set the site's plan and products from it.
181 *
182 * @since 0.12.0
183 *
184 * @param array $record The decoded site record from the WordPress.com `/sites/%d` endpoint.
185 * @return bool Was the plan successfully updated?
186 */
187 public static function update_from_site_record( $record ) {
188 if ( ! is_array( $record ) ) {
189 return false;
190 }
191
192 if ( isset( $record['products'] ) ) {
193 // Store the site's products in an option and return true if updated.
194 self::store_data_in_option( self::SITE_PRODUCTS_OPTION, $record['products'] );
195 }
196
197 if ( ! isset( $record['plan'] ) ) {
198 return false;
199 }
200
201 $current_plan = get_option( self::PLAN_OPTION, array() );
202
203 if ( ! empty( $current_plan ) && $current_plan === $record['plan'] ) {
204 // Bail if the plans array hasn't changed.
205 return false;
206 }
207
208 // Store the new plan in an option and return true if updated.
209 $result = self::store_data_in_option( self::PLAN_OPTION, $record['plan'] );
210
211 if ( $result ) {
212 // Reset the cache since we've just updated the plan.
213 self::$active_plan_cache = null;
214 }
215
216 return $result;
217 }
218
219 /**
220 * Store data in an option.
221 *
222 * @param string $option The name of the option that will store the data.
223 * @param array $data Data to be store in an option.
224 * @return bool Were the subscriptions successfully updated?
225 */
226 private static function store_data_in_option( $option, $data ) {
227 $result = update_option( $option, $data, true );
228
229 // If something goes wrong with the update, so delete the current option and then update it.
230 if ( ! $result ) {
231 delete_option( $option );
232 $result = update_option( $option, $data, true );
233 }
234
235 return $result;
236 }
237
238 /**
239 * Make an API call to WordPress.com for plan status
240 *
241 * @uses Jetpack_Options::get_option()
242 * @uses Client::wpcom_json_api_request_as_blog()
243 * @uses update_option()
244 *
245 * @access public
246 * @static
247 *
248 * @return bool True if plan is updated, false if no update
249 */
250 public static function refresh_from_wpcom() {
251 $site_id = Manager::get_site_id();
252 if ( is_wp_error( $site_id ) ) {
253 return false;
254 }
255
256 // Make the API request.
257
258 $response = Client::wpcom_json_api_request_as_blog(
259 sprintf( '/sites/%d?force=wpcom', $site_id ),
260 '1.1'
261 );
262
263 return self::update_from_sites_response( $response );
264 }
265
266 /**
267 * Get the plan that this Jetpack site is currently using.
268 *
269 * @uses get_option()
270 *
271 * @access public
272 * @static
273 *
274 * @return array Active Jetpack plan details
275 */
276 public static function get() {
277 // this can be expensive to compute so we cache for the duration of a request.
278 if ( is_array( self::$active_plan_cache ) && ! empty( self::$active_plan_cache ) ) {
279 return self::$active_plan_cache;
280 }
281
282 $plan = get_option( self::PLAN_OPTION, array() );
283
284 // Set the default options.
285 $plan = wp_parse_args(
286 $plan,
287 array(
288 'product_slug' => 'jetpack_free',
289 'class' => 'free',
290 'features' => array(
291 'active' => array(),
292 ),
293 )
294 );
295
296 list( $plan['class'], $supports ) = self::get_class_and_features( $plan['product_slug'] );
297
298 $modules = new Modules();
299 foreach ( $modules->get_available() as $module_slug ) {
300 $module = $modules->get( $module_slug );
301 if ( ! isset( $module ) || ! is_array( $module ) ) {
302 continue;
303 }
304 if ( in_array( 'free', $module['plan_classes'], true ) || in_array( $plan['class'], $module['plan_classes'], true ) ) {
305 $supports[] = $module_slug;
306 }
307 }
308
309 $plan['supports'] = $supports;
310
311 self::$active_plan_cache = $plan;
312
313 return $plan;
314 }
315
316 /**
317 * Get the site's products.
318 *
319 * @uses get_option()
320 *
321 * @access public
322 * @static
323 *
324 * @return array Active Jetpack products
325 */
326 public static function get_products() {
327 return get_option( self::SITE_PRODUCTS_OPTION, array() );
328 }
329
330 /**
331 * Get the class of plan and a list of features it supports
332 *
333 * @param string $plan_slug The plan that we're interested in.
334 * @return array Two item array, the plan class and the an array of features.
335 */
336 private static function get_class_and_features( $plan_slug ) {
337 $features = array();
338 foreach ( self::PLAN_DATA as $class => $details ) {
339 $features = array_merge( $features, $details['supports'] );
340 if ( in_array( $plan_slug, $details['plans'], true ) ) {
341 return array( $class, $features );
342 }
343 }
344 return array( 'free', self::PLAN_DATA['free']['supports'] );
345 }
346
347 /**
348 * Gets the minimum plan slug that supports the given feature
349 *
350 * @param string $feature The name of the feature.
351 * @return string|bool The slug for the minimum plan that supports.
352 * the feature or false if not found
353 */
354 public static function get_minimum_plan_for_feature( $feature ) {
355 foreach ( self::PLAN_DATA as $details ) {
356 if ( in_array( $feature, $details['supports'], true ) ) {
357 return $details['plans'][0];
358 }
359 }
360 return false;
361 }
362
363 /**
364 * Determine whether the active plan supports a particular feature
365 *
366 * @uses self::get()
367 *
368 * @access public
369 * @static
370 *
371 * @param string $feature The module or feature to check.
372 * @param bool $refresh_from_wpcom Refresh the local plan cache from wpcom.
373 *
374 * @return bool True if plan supports feature, false if not
375 */
376 public static function supports( $feature, $refresh_from_wpcom = false ) {
377 if ( $refresh_from_wpcom ) {
378 self::refresh_from_wpcom();
379 }
380
381 // Hijack the feature eligibility check on WordPress.com sites since they are gated differently.
382 $should_wpcom_gate_feature = (
383 function_exists( 'wpcom_site_has_feature' ) &&
384 function_exists( 'wpcom_feature_exists' ) &&
385 wpcom_feature_exists( $feature )
386 );
387 if ( $should_wpcom_gate_feature ) {
388 return wpcom_site_has_feature( $feature );
389 }
390
391 // Search product bypasses plan feature check.
392 if ( 'search' === $feature && (bool) get_option( 'has_jetpack_search_product' ) ) {
393 return true;
394 }
395
396 // As of Q3 2021 - a videopress free tier is available to all plans.
397 if ( 'videopress' === $feature ) {
398 return true;
399 }
400
401 // As of 05 2023 - all plans support Earn features (minus 'simple-payments').
402 if ( in_array( $feature, array( 'donations', 'recurring-payments', 'premium-content/container' ), true ) ) {
403 return true;
404 }
405
406 $plan = self::get();
407
408 if (
409 in_array( $feature, $plan['supports'], true )
410 || in_array( $feature, $plan['features']['active'], true )
411 ) {
412 return true;
413 }
414
415 return false;
416 }
417
418 /**
419 * Retrieve site-specific features for Simple sites.
420 *
421 * See Jetpack_Gutenberg::get_site_specific_features()
422 *
423 * @return array
424 */
425 public static function get_simple_site_specific_features() {
426 $is_simple_site = defined( 'IS_WPCOM' ) && constant( 'IS_WPCOM' );
427
428 if ( ! $is_simple_site ) {
429 return array(
430 'active' => array(),
431 'available' => array(),
432 );
433 }
434
435 $current_blog_id = get_current_blog_id();
436
437 // Return the cached value if it exists.
438 if ( isset( self::$simple_site_specific_features[ $current_blog_id ] ) ) {
439 return self::$simple_site_specific_features[ $current_blog_id ];
440 }
441
442 if ( ! class_exists( '\Store_Product_List' ) ) {
443 require WP_CONTENT_DIR . '/admin-plugins/wpcom-billing/store-product-list.php';
444 }
445
446 $simple_site_specific_features = \Store_Product_List::get_site_specific_features_data( $current_blog_id );
447
448 self::$simple_site_specific_features[ $current_blog_id ] = $simple_site_specific_features;
449
450 return $simple_site_specific_features;
451 }
452 }
453