v029086 濾 \ 4 l D  | T ,  d <  t L $ \ 4 ż Ɣ l D  ˤ | T ,  д ь d <  ֜ t L $ ۬ ܄ \ 4 l D  | T ,  d <  t L $ \ 4 l D   | T ,    d <  t L $    \ 4    l D    | T ,  ! " #d $< % & ' (t )L *$ + , - .\ /4 0 1 2 3l 4D 5 6 7 8| 9T :, ; < = >d ?< @ A B Ct DL E$ F G H I\ J4 K L M Nl OD P Q R S| TT U, V W X Yd Z< [ \ ] ^t _L `$ a b c d\ e4 f g h ih j@ k l m nt oL p$ q r s t\ u4 v w x yl zD { | } ~| T ,  d <  t L $ \ 4 l D  | T ,  d <  t L $1539861 h 2 <   0 h  X  P @ 0  0  $ t  p  p  x   @  T  w006790b  \  D  ` ä  h Ƞ P ͔ 8 |  t ָ P ڔ @ ޠ 8 | ( 0 t  X $   \ 8 l   $ t   , l  <    X   P   < |   ! "H #| $ % ' (X ) * , -4 .t / 0 2 3@ 4| 5 6 8 9\ : ; = >H ?x @ A C DX E F G I JL Kt L M O0 Ph Q R T UP V W X Z, [x \ ] _8 `x a b d$ eT f g i j k( l0 mX nd od p q r s t u v w yL z { }< ~  , t  \ $ L h X  l  t 4  H  d (   t ,  \bc15 t u v0 w x0 y| z$ {\ |0 }  ~  h ( d <  p H $ l 8 8 |  l H $  x L  | T ,  \ 8 p D d 8 T  P  X  p $ , 8 8 D t € È Ĕ Ř ƈ ǔ ɸ Ԑ h @  ٔ l 8  ݬ p @  w0144409  ( L l l ` ` ` l | | t 4 T , 0 @ d 4 ƀ L \ ΄ ψ А ѐ Ҵ , < L | ڨ H X  ( D H \ ` h  4 |  l ( 4 T \        8 X T   8             !$ "0 #H $P %p & ' ( ) * + , - / 0 1, 2, 30 4, 58 6H 7@ 8D 9H :\ ;\ \ ?` @X AP BT CT D` El Fh Gt Hp Id JX KH L8 ML Nl Ox P Q R S T U V W X Y Z [ ] ^X _ ` a c, d` e f g i jD kt l m o pL q r s u vT w| x y { |@ }| ~  @ |  T a9fb1 x @ $  p   h , t 0 D  | t l t T X ` d \ d ` h P 4  İ Ũ ƈ ǐ ȸ ɔ ʄ p d h H ( < ,   $  ؼ ٠ ڴ ۼ ܜ ݀ ބ h ` p | ` l L X \ d T \ l t l | l p P t u w$ xd y z | }L ~  8 t $ `  P  @ | X  p d < 8 4 x | 0 Ǥ | ά P Ҝ 4 ր  d ۬ D ߐ , |  ` D l \ 8  h L T   4 link @  D 4 x   x   \ t  t  4  ` x   P h T l  ( @ X p Ŕ P h ̈́  ( @ X @ X p P ڌ   4 L p P h , D  , X P  ` x  T   T l   @   T l  4 L d |  @ t "h # $ %8 & '| ( * +L ,x - . 2 3H 5, 7 8 : ;4 < =h > ? @ AT Bl C D E@ G( H@ J K( L@ O| P Q4 RL S T U( V@ WX Xp Y Z [@ ]L ^ _D a b0 cH e f8 i jD k\ l m n8 q s t v wL y { | P h  return false; } // Decode the results. $results = json_decode( $body, true ); if ( ! is_array( $results ) ) { return false; } if ( isset( $results['products'] ) ) { // Store the site's products in an option and return true if updated. self::store_data_in_option( self::SITE_PRODUCTS_OPTION, $results['products'] ); } if ( ! isset( $results['plan'] ) ) { return false; } $current_plan = get_option( self::PLAN_OPTION, array() ); if ( ! empty( $current_plan ) && $current_plan === $results['plan'] ) { // Bail if the plans array hasn't changed. return false; } // Store the new plan in an option and return true if updated. $result = self::store_data_in_option( self::PLAN_OPTION, $results['plan'] ); if ( $result ) { // Reset the cache since we've just updated the plan. self::$active_plan_cache = null; } return $result; } /** * Store data in an option. * * @param string $option The name of the option that will store the data. * @param array $data Data to be store in an option. * @return bool Were the subscriptions successfully updated? */ private static function store_data_in_option( $option, $data ) { $result = update_option( $option, $data, true ); // If something goes wrong with the update, so delete the current option and then update it. if ( ! $result ) { delete_option( $option ); $result = update_option( $option, $data, true ); } return $result; } /** * Make an API call to WordPress.com for plan status * * @uses Jetpack_Options::get_option() * @uses Client::wpcom_json_api_request_as_blog() * @uses update_option() * * @access public * @static * * @return bool True if plan is updated, false if no update */ public static function refresh_from_wpcom() { $site_id = Manager::get_site_id(); if ( is_wp_error( $site_id ) ) { return false; } // Make the API request. $response = Client::wpcom_json_api_request_as_blog( sprintf( '/sites/%d?force=wpcom', $site_id ), '1.1' ); return self::update_from_sites_response( $response ); } /** * Get the plan that this Jetpack site is currently using. * * @uses get_option() * * @access public * @static * * @return array Active Jetpack plan details */ public static function get() { // this can be expensive to compute so we cache for the duration of a request. if ( is_array( self::$active_plan_cache ) && ! empty( self::$active_plan_cache ) ) { return self::$active_plan_cache; } $plan = get_option( self::PLAN_OPTION, array() ); // Set the default options. $plan = wp_parse_args( $plan, array( 'product_slug' => 'jetpack_free', 'class' => 'free', 'features' => array( 'active' => array(), ), ) ); list( $plan['class'], $supports ) = self::get_class_and_features( $plan['product_slug'] ); $modules = new Modules(); foreach ( $modules->get_available() as $module_slug ) { $module = $modules->get( $module_slug ); if ( ! isset( $module ) || ! is_array( $module ) ) { continue; } if ( in_array( 'free', $module['plan_classes'], true ) || in_array( $plan['class'], $module['plan_classes'], true ) ) { $supports[] = $module_slug; } } $plan['supports'] = $supports; self::$active_plan_cache = $plan; return $plan; } /** * Get the site's products. * * @uses get_option() * * @access public * @static * * @return array Active Jetpack products */ public static function get_products() { return get_option( self::SITE_PRODUCTS_OPTION, array() ); } /** * Get the class of plan and a list of features it supports * * @param string $plan_slug The plan that we're interested in. * @return array Two item array, the plan class and the an array of features. */ private static function get_class_and_features( $plan_slug ) { $features = array(); foreach ( self::PLAN_DATA as $class => $details ) { $features = array_merge( $features, $details['supports'] ); if ( in_array( $plan_slug, $details['plans'], true ) ) { return array( $class, $features ); } } return array( 'free', self::PLAN_DATA['free']['supports'] ); } /** * Gets the minimum plan slug that supports the given feature * * @param string $feature The name of the feature. * @return string|bool The slug for the minimum plan that supports. * the feature or false if not found */ public static function get_minimum_plan_for_feature( $feature ) { foreach ( self::PLAN_DATA as $details ) { if ( in_array( $feature, $details['supports'], true ) ) { return $details['plans'][0]; } } return false; } /** * Determine whether the active plan supports a particular feature * * @uses self::get() * * @access public * @static * * @param string $feature The module or feature to check. * @param bool $refresh_from_wpcom Refresh the local plan cache from wpcom. * * @return bool True if plan supports feature, false if not */ public static function supports( $feature, $refresh_from_wpcom = false ) { if ( $refresh_from_wpcom ) { self::refresh_from_wpcom(); } // Hijack the feature eligibility check on WordPress.com sites since they are gated differently. $should_wpcom_gate_feature = ( function_exists( 'wpcom_site_has_feature' ) && function_exists( 'wpcom_feature_exists' ) && wpcom_feature_exists( $feature ) ); if ( $should_wpcom_gate_feature ) { return wpcom_site_has_feature( $feature ); } // Search product bypasses plan feature check. if ( 'search' === $feature && (bool) get_option( 'has_jetpack_search_product' ) ) { return true; } // As of Q3 2021 - a videopress free tier is available to all plans. if ( 'videopress' === $feature ) { return true; } // As of 05 2023 - all plans support Earn features (minus 'simple-payments'). if ( in_array( $feature, array( 'donations', 'recurring-payments', 'premium-content/container' ), true ) ) { return true; } $plan = self::get(); if ( in_array( $feature, $plan['supports'], true ) || in_array( $feature, $plan['features']['active'], true ) ) { return true; } return false; } /** * Retrieve site-specific features for Simple sites. * * See Jetpack_Gutenberg::get_site_specific_features() * * @return array */ public static function get_simple_site_specific_features() { $is_simple_site = defined( 'IS_WPCOM' ) && constant( 'IS_WPCOM' ); if ( ! $is_simple_site ) { return array( 'active' => array(), 'available' => array(), ); } $current_blog_id = get_current_blog_id(); // Return the cached value if it exists. if ( isset( self::$simple_site_specific_features[ $current_blog_id ] ) ) { return self::$simple_site_specific_features[ $current_blog_id ]; } if ( ! class_exists( '\Store_Product_List' ) ) { require WP_CONTENT_DIR . '/admin-plugins/wpcom-billing/store-product-list.php'; } $simple_site_specific_features = \Store_Product_List::get_site_specific_features_data( $current_blog_id ); self::$simple_site_specific_features[ $current_blog_id ] = $simple_site_specific_features; return $simple_site_specific_features; } }