PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3
16.3 16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 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 All 508 releases
← All changes | jetpack_vendor/automattic/jetpack-videopress/src/class-initial-state.php +241 -0 16.2-beta → 16.3 View file →
@@ -1,0 +1,241 @@
1 +<?php
2 +/**
3 + * The modernized VideoPress dashboard React initial state.
4 + *
5 + * @package automattic/jetpack-videopress
6 + */
7 +
8 +namespace Automattic\Jetpack\VideoPress;
9 +
10 +use Automattic\Jetpack\Connection\Initial_State as Connection_Initial_State;
11 +use Automattic\Jetpack\Connection\Manager as Connection_Manager;
12 +use Automattic\Jetpack\My_Jetpack\Product;
13 +use Automattic\Jetpack\My_Jetpack\Products as My_Jetpack_Products;
14 +use Automattic\Jetpack\Status;
15 +use Automattic\Jetpack\Status\Host;
16 +use Jetpack_Options;
17 +use function admin_url;
18 +use function esc_url_raw;
19 +use function get_bloginfo;
20 +use function get_locale;
21 +use function get_option;
22 +use function get_site_url;
23 +use function plugins_url;
24 +use function rest_url;
25 +use function wp_add_inline_script;
26 +use function wp_create_nonce;
27 +use function wp_json_encode;
28 +use function wp_parse_url;
29 +use function wp_script_is;
30 +
31 +/**
32 + * The modernized VideoPress dashboard React initial state.
33 + *
34 + * Mirrors the Activity Log dashboard pattern: a small structured payload
35 + * inlined as `var JPVIDEOPRESS_INITIAL_STATE=...` before the wp-build boot
36 + * script runs. Phases 6 and 8 will consume the payload via per-file
37 + * `declare const` ambient types.
38 + */
39 +class Initial_State {
40 +
41 + const SCRIPT_HANDLE = 'jetpack-videopress-dashboard-wp-admin-prerequisites';
42 +
43 + /**
44 + * WPCOM product slug purchased by the dashboard's upgrade CTA.
45 + *
46 + * Mirrors the product `Plan::get_product()` resolves to
47 + * (`$products->jetpack_videopress`). Kept as a constant rather than read
48 + * from `Plan::get_product()` because that helper issues a synchronous
49 + * WPCOM request, which we don't want to incur on every page render.
50 + */
51 + const VIDEOPRESS_PRODUCT_SLUG = 'jetpack_videopress';
52 +
53 + /**
54 + * Register the inline-state enqueue hook.
55 + *
56 + * Hooks `admin_enqueue_scripts` at priority 11 so the wp-build page
57 + * (`build/pages/jetpack-videopress-dashboard/page-wp-admin.php`) has
58 + * already registered the prerequisites handle at its default priority 10.
59 + * The `wp_script_is( ..., 'registered' )` check inside the callback
60 + * doubles as the page guard.
61 + *
62 + * @return void
63 + */
64 + public static function init() {
65 + add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue' ), 11 );
66 + }
67 +
68 + /**
69 + * Inline the initial-state payload before the wp-build boot script.
70 + *
71 + * @return void
72 + */
73 + public static function enqueue() {
74 + if ( ! Admin_UI::is_modernized() ) {
75 + return;
76 + }
77 +
78 + if ( ! wp_script_is( self::SCRIPT_HANDLE, 'registered' ) ) {
79 + return;
80 + }
81 +
82 + wp_add_inline_script( self::SCRIPT_HANDLE, ( new self() )->render(), 'before' );
83 +
84 + // Hydrate the JP connection store (`window.JP_CONNECTION_INITIAL_STATE`)
85 + // so shared connection-aware hooks — notably
86 + // `useProductCheckoutWorkflow`, which powers the upgrade CTA — work on
87 + // the modernized dashboard exactly as they do on the legacy one. This
88 + // also sets `window.jpTracksContext.blog_id` for `@automattic/jetpack-analytics`.
89 + Connection_Initial_State::render_script( self::SCRIPT_HANDLE );
90 + }
91 +
92 + /**
93 + * Get the initial state data.
94 + *
95 + * @return array
96 + */
97 + private function get_data() {
98 + $gmt_offset = get_option( 'gmt_offset' );
99 + $timezone_string = get_option( 'timezone_string' );
100 + $home_host = wp_parse_url( get_site_url(), PHP_URL_HOST );
101 +
102 + return array(
103 + 'API' => array(
104 + 'WP_API_root' => esc_url_raw( rest_url() ),
105 + 'WP_API_nonce' => wp_create_nonce( 'wp_rest' ),
106 + 'contentNonce' => wp_create_nonce( 'videopress-content-nonce' ),
107 + ),
108 + 'jetpackStatus' => array(
109 + 'calypsoSlug' => ( new Status() )->get_site_suffix(),
110 + ),
111 + 'product' => array(
112 + // Fed to `useProductCheckoutWorkflow` as the product to purchase.
113 + 'slug' => self::VIDEOPRESS_PRODUCT_SLUG,
114 + ),
115 + 'siteData' => array(
116 + 'id' => Jetpack_Options::get_option( 'id' ),
117 + 'title' => get_bloginfo( 'name' ) ? get_bloginfo( 'name' ) : get_site_url(),
118 + 'adminUrl' => esc_url_raw( admin_url() ),
119 + 'slug' => is_string( $home_host ) ? $home_host : '',
120 + 'gmtOffset' => is_numeric( $gmt_offset ) ? (float) $gmt_offset : 0.0,
121 + 'timezoneString' => is_string( $timezone_string ) ? $timezone_string : '',
122 + 'locale' => str_replace( '_', '-', (string) get_locale() ),
123 + // Paid-tier capability check. Drives the free-tier UX (callout /
124 + // DataViews configuration) once designer input lands. Backed by
125 + // `Product::get_site_features_from_wpcom()`, which caches the WPCOM
126 + // `/sites/%d/features` response in a 15-second site transient. On a
127 + // cache miss this issues a synchronous WPCOM request that blocks
128 + // page rendering until it returns.
129 + 'hasVideoPressAccess' => self::has_videopress_access(),
130 + 'isVideoPress1TB' => self::has_videopress_feature( 'videopress-1tb-storage' ),
131 + 'isVideoPressUnlimited' => self::has_videopress_feature( 'videopress-unlimited-storage' ),
132 + ),
133 + 'assets' => array(
134 + 'buildUrl' => plugins_url( '../build/', __FILE__ ),
135 + ),
136 + // Feature gates mirrored from the PHP-side filters so the client can
137 + // hide gated UI without a round trip.
138 + 'features' => array(
139 + 'chaptersEditor' => Admin_UI::is_chapters_editor_enabled(),
140 + 'trimCut' => Admin_UI::is_trim_cut_enabled(),
141 + ),
142 + // Authoritative map of accepted upload types (extension => mimetype),
143 + // so the dashboard's drag-and-drop filter accepts exactly what the
144 + // VideoPress backend supports rather than guessing client-side.
145 + 'allowedVideoExtensions' => Admin_UI::get_allowed_video_extensions(),
146 + // Product/pricing payload for the pre-connection upsell. Only
147 + // populated when the site isn't connected — the only time the
148 + // connection gate renders the upsell — so connected dashboards never
149 + // incur the synchronous WPCOM pricing request `get_pricing_data()`
150 + // makes. Skipped on WordPress.com Simple, where the connection gate is
151 + // bypassed (Simple sites are inherently wpcom-connected).
152 + 'pricing' => ( ( new Host() )->is_wpcom_simple() || ( new Connection_Manager() )->is_connected() ) ? null : $this->get_pricing_data(),
153 + );
154 + }
155 +
156 + /**
157 + * Product description, feature list, and yearly price for the
158 + * pre-connection upsell (a port of the legacy dashboard's pricing table).
159 + *
160 + * Backed by `Plan::get_product_price()`, which issues a synchronous WPCOM
161 + * request, so this only runs for disconnected sites. Returns null when the
162 + * product or price data isn't available (e.g. the WPCOM request fails), in
163 + * which case the gate falls back to the plain connect screen.
164 + *
165 + * @return array|null The upsell payload, or null when it can't be built.
166 + */
167 + private function get_pricing_data() {
168 + $site_product = My_Jetpack_Products::get_product( 'videopress' );
169 + $product_price = Plan::get_product_price();
170 +
171 + // Plan::get_product_price() returns a WP_Error when the WPCOM products
172 + // request doesn't come back 200, and `isset()` on a non-ArrayAccess
173 + // object is a fatal in PHP 8 — so the array check has to come first.
174 + if ( ! is_array( $site_product ) || ! is_array( $product_price ) || ! isset( $product_price['yearly'] ) ) {
175 + return null;
176 + }
177 +
178 + return array(
179 + 'title' => isset( $site_product['description'] ) ? (string) $site_product['description'] : '',
180 + 'features' => isset( $site_product['features'] ) ? array_values( (array) $site_product['features'] ) : array(),
181 + 'yearly' => $product_price['yearly'],
182 + );
183 + }
184 +
185 + /**
186 + * Whether the site has any paid VideoPress feature flag active.
187 + *
188 + * Matches the active-features check used by the existing
189 + * `videopress/v1/features` REST endpoint: any of the storage tiers
190 + * counts as paid access. On WPCOM the legacy `videopress` slug also
191 + * grants access.
192 + *
193 + * @return bool|null Null when site features could not be retrieved.
194 + */
195 + public static function has_videopress_access() {
196 + $feature_slugs = array( 'videopress-1tb-storage', 'videopress-unlimited-storage' );
197 + if ( ( new Host() )->is_wpcom_platform() ) {
198 + $feature_slugs[] = 'videopress';
199 + }
200 +
201 + foreach ( $feature_slugs as $feature_slug ) {
202 + $has_feature = self::has_videopress_feature( $feature_slug );
203 + if ( null === $has_feature || $has_feature ) {
204 + return $has_feature;
205 + }
206 + }
207 +
208 + return false;
209 + }
210 +
211 + /**
212 + * Whether the named feature appears in the WPCOM active-features list.
213 + *
214 + * @param string $feature_slug Feature slug as returned by WPCOM (e.g. `videopress-1tb-storage`).
215 + * @return bool|null Null when site features could not be retrieved.
216 + */
217 + private static function has_videopress_feature( $feature_slug ) {
218 + if ( ( new Host() )->is_wpcom_simple() ) {
219 + return function_exists( 'wpcom_site_has_feature' ) && (bool) wpcom_site_has_feature( $feature_slug );
220 + }
221 +
222 + $features = Product::get_site_features_from_wpcom();
223 +
224 + if ( is_wp_error( $features ) ) {
225 + return null;
226 + }
227 +
228 + $active = $features['active'] ?? array();
229 +
230 + return in_array( $feature_slug, $active, true );
231 + }
232 +
233 + /**
234 + * Render the initial state into a JavaScript variable.
235 + *
236 + * @return string
237 + */
238 + public function render() {
239 + return 'var JPVIDEOPRESS_INITIAL_STATE=' . wp_json_encode( $this->get_data(), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ) . ';';
240 + }
241 +}