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