PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.1-beta.3
Jetpack – WP Security, Backup, Speed, & Growth v16.1-beta.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 / jetpack_vendor / automattic / jetpack-podcast / src / class-podcast-gate.php
jetpack / jetpack_vendor / automattic / jetpack-podcast / src Last commit date
admin-pages 1 month ago blocks 1 month ago endpoints 1 month ago feed 1 month ago class-admin-page.php 1 month ago class-create-ai-podcast-page.php 1 month ago class-new-episode-prefill.php 1 month ago class-podcast-gate.php 1 month ago class-podcast.php 3 weeks ago class-settings.php 1 month ago class-tracks.php 1 month ago
class-podcast-gate.php
149 lines
1 <?php
2 /**
3 * Podcast product-access gate.
4 *
5 * @package automattic/jetpack-podcast
6 */
7
8 namespace Automattic\Jetpack\Podcast;
9
10 use Automattic\Jetpack\Connection\Client;
11 use Automattic\Jetpack\Current_Plan;
12 use Automattic\Jetpack\Status\Host;
13 use Jetpack_Options;
14
15 /**
16 * Premium podcast feature gate (dashboard, stats, episode block). Two paths:
17 *
18 * - WordPress.com (Simple/WoA): the `podcasting` plan feature via
19 * `Current_Plan::supports`, plus the launch-day grandfather rule. Request-
20 * scoped, so gating another blog needs `switch_to_blog` first.
21 * - Self-hosted Jetpack: a Growth/Complete purchase over the connection
22 * (PODS-123). Only consulted in admin/editor contexts.
23 */
24 class Podcast_Gate {
25
26 const FEATURE_SLUG = 'podcasting';
27
28 /**
29 * Launch-day cutoff for the paying-blog grandfather rule. Paid blogs
30 * registered before this date keep Premium podcast features without
31 * needing the `podcasting` plan feature. WordPress.com only.
32 */
33 const GRANDFATHER_CUTOFF_DATE = '2026-05-18';
34
35 /**
36 * Transient holding the cached `/upgrades` response. Short-lived (30s): dedupes
37 * the lookup across the successive editor/admin loads that consult the gate,
38 * without a synchronous WPCOM request on each one.
39 */
40 const PURCHASES_TRANSIENT = 'jetpack_podcast_site_purchases';
41
42 /**
43 * Whether the current site can use the paid podcast surfaces.
44 *
45 * @return bool
46 */
47 public static function has_product_access(): bool {
48 if ( ! ( new Host() )->is_wpcom_platform() ) {
49 return self::self_hosted_has_paid_plan();
50 }
51
52 $blog_id = get_current_blog_id();
53 if ( $blog_id <= 0 ) {
54 return false;
55 }
56
57 if ( self::is_grandfathered( $blog_id ) ) {
58 return true;
59 }
60
61 return (bool) Current_Plan::supports( self::FEATURE_SLUG );
62 }
63
64 /**
65 * The minimum plan slug to upsell: WordPress.com Premium (`value_bundle`) or
66 * Jetpack Growth (`jetpack_growth_yearly`).
67 *
68 * @return string
69 */
70 public static function get_required_plan_slug(): string {
71 return ( new Host() )->is_wpcom_platform() ? 'value_bundle' : 'jetpack_growth_yearly';
72 }
73
74 /**
75 * Whether a self-hosted site owns a Growth/Complete plan. Matches purchased
76 * product slugs, not the `podcasting` feature (which is true for every Jetpack
77 * site on WordPress.com and can't tell free from paid here).
78 */
79 private static function self_hosted_has_paid_plan(): bool {
80 foreach ( self::get_site_current_purchases() as $purchase ) {
81 $slug = is_array( $purchase ) && isset( $purchase['product_slug'] ) ? $purchase['product_slug'] : '';
82
83 // Prefix match so every Growth/Complete billing term counts.
84 foreach ( array( 'jetpack_growth', 'jetpack_complete' ) as $prefix ) {
85 if ( is_string( $slug ) && 0 === strpos( $slug, $prefix ) ) {
86 return true;
87 }
88 }
89 }
90
91 return false;
92 }
93
94 /**
95 * The site's current purchases from WordPress.com (`/upgrades`). Cached in a
96 * short transient so successive gate checks don't each fire a WPCOM request.
97 * Fails closed to an empty list on any error, without caching it, so the next
98 * request retries rather than serving a stale empty.
99 *
100 * @return array Purchase entries; empty on failure.
101 */
102 private static function get_site_current_purchases(): array {
103 $cached = get_transient( self::PURCHASES_TRANSIENT );
104 if ( is_array( $cached ) ) {
105 return $cached;
106 }
107
108 $response = Client::wpcom_json_api_request_as_blog(
109 sprintf( '/upgrades?site=%d', (int) Jetpack_Options::get_option( 'id' ) ),
110 '1.2',
111 array( 'method' => 'GET' )
112 );
113
114 if ( is_wp_error( $response ) || 200 !== (int) wp_remote_retrieve_response_code( $response ) ) {
115 return array();
116 }
117
118 $decoded = json_decode( wp_remote_retrieve_body( $response ), true );
119 if ( ! is_array( $decoded ) ) {
120 return array();
121 }
122
123 set_transient( self::PURCHASES_TRANSIENT, $decoded, 30 );
124 return $decoded;
125 }
126
127 /**
128 * Whether the blog is grandfathered: registered before the cutoff AND on a paid plan.
129 *
130 * @param int $blog_id Blog ID.
131 */
132 protected static function is_grandfathered( int $blog_id ): bool {
133 if ( ! function_exists( 'get_blog_details' ) ) {
134 return false;
135 }
136 $details = get_blog_details( $blog_id );
137 if ( ! $details || empty( $details->registered ) ) {
138 return false;
139 }
140 $registered_ts = strtotime( $details->registered );
141 if ( false === $registered_ts || $registered_ts >= strtotime( self::GRANDFATHER_CUTOFF_DATE ) ) {
142 return false;
143 }
144
145 $plan = Current_Plan::get();
146 return ! empty( $plan['class'] ) && 'free' !== $plan['class'];
147 }
148 }
149