PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 10.5.3
Jetpack – WP Security, Backup, Speed, & Growth v10.5.3
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 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 All 501 releases
jetpack / class.jetpack-plan.php

class.jetpack-plan.php in Jetpack – WP Security, Backup, Speed, & Growth 10.5.3, at class.jetpack-plan.php

355 lines 8.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php //phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * Handles fetching of the site's plan and products from WordPress.com and caching values locally.
4 *
5 * Not to be confused with the `Jetpack_Plans` class (in `_inc/lib/plans.php`), which
6 * fetches general information about all available plans from WordPress.com, side-effect free.
7 *
8 * @package automattic/jetpack
9 */
10
11 use Automattic\Jetpack\Connection\Client;
12
13 /**
14 * Provides methods methods for fetching the site's plan and products from WordPress.com.
15 */
16 class Jetpack_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 * The name of the option that will store the site's plan.
26 *
27 * @var string
28 */
29 const PLAN_OPTION = 'jetpack_active_plan';
30
31 /**
32 * The name of the option that will store the site's products.
33 *
34 * @var string
35 */
36 const SITE_PRODUCTS_OPTION = 'jetpack_site_products';
37
38 const PLAN_DATA = array(
39 'free' => array(
40 'plans' => array(
41 'jetpack_free',
42 ),
43 'supports' => array(
44 'opentable',
45 'calendly',
46 'send-a-message',
47 'whatsapp-button',
48 'social-previews',
49 'videopress',
50
51 'core/video',
52 'core/cover',
53 'core/audio',
54 ),
55 ),
56 'personal' => array(
57 'plans' => array(
58 'jetpack_personal',
59 'jetpack_personal_monthly',
60 'personal-bundle',
61 'personal-bundle-monthly',
62 'personal-bundle-2y',
63 ),
64 'supports' => array(
65 'akismet',
66 'recurring-payments',
67 'premium-content/container',
68 'videopress',
69 ),
70 ),
71 'premium' => array(
72 'plans' => array(
73 'jetpack_premium',
74 'jetpack_premium_monthly',
75 'value_bundle',
76 'value_bundle-monthly',
77 'value_bundle-2y',
78 ),
79 'supports' => array(
80 'donations',
81 'simple-payments',
82 'vaultpress',
83 'videopress',
84 'republicize',
85 ),
86 ),
87 'security' => array(
88 'plans' => array(
89 'jetpack_security_daily',
90 'jetpack_security_daily_monthly',
91 'jetpack_security_realtime',
92 'jetpack_security_realtime_monthly',
93 ),
94 'supports' => array(),
95 ),
96 'business' => array(
97 'plans' => array(
98 'jetpack_business',
99 'jetpack_business_monthly',
100 'business-bundle',
101 'business-bundle-monthly',
102 'business-bundle-2y',
103 'ecommerce-bundle',
104 'ecommerce-bundle-monthly',
105 'ecommerce-bundle-2y',
106 ),
107 'supports' => array(),
108 ),
109
110 'complete' => array(
111 'plans' => array(
112 'jetpack_complete',
113 'jetpack_complete_monthly',
114 'vip',
115 ),
116 'supports' => array(),
117 ),
118 );
119
120 /**
121 * Given a response to the `/sites/%d` endpoint, will parse the response and attempt to set the
122 * site's plan and products from the response.
123 *
124 * @param array $response The response from `/sites/%d`.
125 * @return bool Was the plan successfully updated?
126 */
127 public static function update_from_sites_response( $response ) {
128 // Bail if there was an error or malformed response.
129 if ( is_wp_error( $response ) || ! is_array( $response ) || ! isset( $response['body'] ) ) {
130 return false;
131 }
132
133 $body = wp_remote_retrieve_body( $response );
134 if ( is_wp_error( $body ) ) {
135 return false;
136 }
137
138 // Decode the results.
139 $results = json_decode( $body, true );
140
141 if ( ! is_array( $results ) ) {
142 return false;
143 }
144
145 if ( isset( $results['products'] ) ) {
146 // Store the site's products in an option and return true if updated.
147 self::store_data_in_option( self::SITE_PRODUCTS_OPTION, $results['products'] );
148 }
149
150 if ( ! isset( $results['plan'] ) ) {
151 return false;
152 }
153
154 $current_plan = get_option( self::PLAN_OPTION, array() );
155
156 if ( ! empty( $current_plan ) && $current_plan === $results['plan'] ) {
157 // Bail if the plans array hasn't changed.
158 return false;
159 }
160
161 // Store the new plan in an option and return true if updated.
162 $result = self::store_data_in_option( self::PLAN_OPTION, $results['plan'] );
163
164 if ( $result ) {
165 // Reset the cache since we've just updated the plan.
166 self::$active_plan_cache = null;
167 }
168
169 return $result;
170 }
171
172 /**
173 * Store data in an option.
174 *
175 * @param string $option The name of the option that will store the data.
176 * @param array $data Data to be store in an option.
177 * @return bool Were the subscriptions successfully updated?
178 */
179 private static function store_data_in_option( $option, $data ) {
180 $result = update_option( $option, $data, true );
181
182 // If something goes wrong with the update, so delete the current option and then update it.
183 if ( ! $result ) {
184 delete_option( $option );
185 $result = update_option( $option, $data, true );
186 }
187
188 return $result;
189 }
190
191 /**
192 * Make an API call to WordPress.com for plan status
193 *
194 * @uses Jetpack_Options::get_option()
195 * @uses Client::wpcom_json_api_request_as_blog()
196 * @uses update_option()
197 *
198 * @access public
199 * @static
200 *
201 * @return bool True if plan is updated, false if no update
202 */
203 public static function refresh_from_wpcom() {
204 // Make the API request.
205 $request = sprintf( '/sites/%d', Jetpack_Options::get_option( 'id' ) );
206 $response = Client::wpcom_json_api_request_as_blog( $request, '1.1' );
207
208 return self::update_from_sites_response( $response );
209 }
210
211 /**
212 * Get the plan that this Jetpack site is currently using.
213 *
214 * @uses get_option()
215 *
216 * @access public
217 * @static
218 *
219 * @return array Active Jetpack plan details
220 */
221 public static function get() {
222 // this can be expensive to compute so we cache for the duration of a request.
223 if ( is_array( self::$active_plan_cache ) && ! empty( self::$active_plan_cache ) ) {
224 return self::$active_plan_cache;
225 }
226
227 $plan = get_option( self::PLAN_OPTION, array() );
228
229 // Set the default options.
230 $plan = wp_parse_args(
231 $plan,
232 array(
233 'product_slug' => 'jetpack_free',
234 'class' => 'free',
235 'features' => array(
236 'active' => array(),
237 ),
238 )
239 );
240
241 list( $plan['class'], $supports ) = self::get_class_and_features( $plan['product_slug'] );
242
243 // get available features.
244 foreach ( Jetpack::get_available_modules() as $module_slug ) {
245 $module = Jetpack::get_module( $module_slug );
246 if ( ! isset( $module ) || ! is_array( $module ) ) {
247 continue;
248 }
249 if ( in_array( 'free', $module['plan_classes'], true ) || in_array( $plan['class'], $module['plan_classes'], true ) ) {
250 $supports[] = $module_slug;
251 }
252 }
253
254 $plan['supports'] = $supports;
255
256 self::$active_plan_cache = $plan;
257
258 return $plan;
259 }
260
261 /**
262 * Get the site's products.
263 *
264 * @uses get_option()
265 *
266 * @access public
267 * @static
268 *
269 * @return array Active Jetpack products
270 */
271 public static function get_products() {
272 return get_option( self::SITE_PRODUCTS_OPTION, array() );
273 }
274
275 /**
276 * Get the class of plan and a list of features it supports
277 *
278 * @param string $plan_slug The plan that we're interested in.
279 * @return array Two item array, the plan class and the an array of features.
280 */
281 private static function get_class_and_features( $plan_slug ) {
282 $features = array();
283 foreach ( self::PLAN_DATA as $class => $details ) {
284 $features = array_merge( $features, $details['supports'] );
285 if ( in_array( $plan_slug, $details['plans'], true ) ) {
286 return array( $class, $features );
287 }
288 }
289 return array( 'free', self::PLAN_DATA['free']['supports'] );
290 }
291
292 /**
293 * Gets the minimum plan slug that supports the given feature
294 *
295 * @param string $feature The name of the feature.
296 * @return string|bool The slug for the minimum plan that supports.
297 * the feature or false if not found
298 */
299 public static function get_minimum_plan_for_feature( $feature ) {
300 foreach ( self::PLAN_DATA as $details ) {
301 if ( in_array( $feature, $details['supports'], true ) ) {
302 return $details['plans'][0];
303 }
304 }
305 return false;
306 }
307
308 /**
309 * Determine whether the active plan supports a particular feature
310 *
311 * @uses Jetpack_Plan::get()
312 *
313 * @access public
314 * @static
315 *
316 * @param string $feature The module or feature to check.
317 *
318 * @return bool True if plan supports feature, false if not
319 */
320 public static function supports( $feature ) {
321 // Search product bypasses plan feature check.
322 if ( 'search' === $feature && (bool) get_option( 'has_jetpack_search_product' ) ) {
323 return true;
324 }
325
326 // As of Q3 2021 - a videopress free tier is available to all plans.
327 if ( 'videopress' === $feature ) {
328 return true;
329 }
330
331 $plan = self::get();
332
333 // Manually mapping WordPress.com features to Jetpack module slugs.
334 foreach ( $plan['features']['active'] as $wpcom_feature ) {
335 switch ( $wpcom_feature ) {
336 case 'wordads-jetpack':
337 // WordAds are supported for this site.
338 if ( 'wordads' === $feature ) {
339 return true;
340 }
341 break;
342 }
343 }
344
345 if (
346 in_array( $feature, $plan['supports'], true )
347 || in_array( $feature, $plan['features']['active'], true )
348 ) {
349 return true;
350 }
351
352 return false;
353 }
354 }
355