PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.3-beta
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 13.7.2 All 507 releases
jetpack / jetpack_vendor / automattic / jetpack-publicize / src / class-social-admin-page.php

class-social-admin-page.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-beta, at jetpack_vendor/automattic/jetpack-publicize/src/class-social-admin-page.php

300 lines 9.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Social Admin Page class.
4 *
5 * @package automattic/jetpack-publicize
6 */
7
8 namespace Automattic\Jetpack\Publicize;
9
10 use Automattic\Jetpack\Admin_UI\Admin_Menu;
11 use Automattic\Jetpack\Connection\Initial_State as Connection_Initial_State;
12 use Automattic\Jetpack\Connection\Manager as Connection_Manager;
13 use Automattic\Jetpack\Current_Plan;
14 use Automattic\Jetpack\Publicize\Publicize_Utils as Utils;
15 use Automattic\Jetpack\Status\Host;
16
17 /**
18 * The class to handle the Social Admin Page.
19 */
20 class Social_Admin_Page {
21
22 /**
23 * Nonce action used when refreshing plan data.
24 */
25 public const REFRESH_PLAN_NONCE_ACTION = 'jetpack_social_refresh_plan_data';
26
27 /**
28 * The instance of the class.
29 *
30 * @var Social_Admin_Page
31 */
32 private static $instance;
33
34 /**
35 * The screen ID alias_screen_id_for_wp_build() replaced, until it is restored.
36 *
37 * @var string|null
38 */
39 private static $wp_build_original_screen_id = null;
40
41 /**
42 * Initialize the class.
43 *
44 * @return Social_Admin_Page
45 */
46 public static function init() {
47 if ( ! isset( self::$instance ) ) {
48 self::$instance = new self();
49 }
50
51 return self::$instance;
52 }
53
54 /**
55 * The constructor.
56 */
57 private function __construct() {
58 // Load the wp-build dashboard at admin_menu priority 1 so its render
59 // function is defined before `add_menu` (priority 10) registers the page.
60 add_action( 'admin_menu', array( __CLASS__, 'maybe_load_wp_build' ), 1 );
61 add_action( 'admin_menu', array( $this, 'add_menu' ) );
62 }
63
64 /**
65 * Load the wp-build dashboard on the Social admin request.
66 *
67 * Hooked to `admin_menu` priority 1 so the wp-build render function and
68 * enqueue hook are in place before `add_menu()` runs at the default priority.
69 *
70 * @return void
71 */
72 public static function maybe_load_wp_build() {
73 if ( ! self::is_social_admin_request() ) {
74 return;
75 }
76
77 self::load_wp_build_with_screen_alias();
78
79 // wp-build registers standalone modules (e.g. the init module) on
80 // wp_default_scripts, which has already fired by admin_menu. Register them
81 // directly so the init module makes it into the import map.
82 if ( function_exists( 'jetpack_social_register_script_modules' ) ) {
83 jetpack_social_register_script_modules(); // @phan-suppress-current-line PhanUndeclaredFunction -- Checked with function_exists(); defined in the generated build/modules.php, which Phan excludes.
84 }
85 }
86
87 /**
88 * Add the admin menu.
89 */
90 public function add_menu() {
91
92 // Remove the old Social menu item, if it exists.
93 Admin_Menu::remove_menu( 'jetpack-social' );
94
95 // If this isn't an admin (or someone with the capability to change the module status )
96 // and Publicize is inactive, then don't render the admin page.
97 if ( ! current_user_can( 'manage_options' ) && ! Utils::is_publicize_active() ) {
98 return;
99 }
100
101 // We don't need Jetpack connection on WP.com.
102 $needs_site_connection = ! ( new Host() )->is_wpcom_platform() && ! ( new Connection_Manager() )->is_connected();
103
104 /**
105 * If the Jetpack Social plugin is not active,
106 * we want to hide the menu if the site is not connected.
107 */
108 if ( ! defined( 'JETPACK_SOCIAL_PLUGIN_DIR' ) && $needs_site_connection ) {
109 return;
110 }
111
112 $page_suffix = Admin_Menu::add_menu(
113 /** "Jetpack Social" is a product name, do not translate. */
114 'Jetpack Social',
115 'Social',
116 'publish_posts',
117 'jetpack-social',
118 array( $this, 'render' ),
119 null,
120 array(
121 'product' => 'social',
122 'key' => 'jetpack-social',
123 )
124 );
125
126 add_action( 'load-' . $page_suffix, array( $this, 'admin_init' ) );
127 }
128
129 /**
130 * Initialize the admin resources.
131 */
132 public function admin_init() {
133 // Refresh data if coming from purchase to ensure it is up to date
134 // without making API calls on every admin page load.
135 if ( isset( $_GET['refresh_plan_data'] ) ) {
136 check_admin_referer( self::REFRESH_PLAN_NONCE_ACTION );
137 if ( apply_filters( 'jetpack_social_should_refresh_plan_data', true ) ) {
138 Current_Plan::refresh_from_wpcom();
139 }
140 }
141
142 /**
143 * Use priority 20 to ensure that we can dequeue the old Social assets.
144 */
145 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ), 20 );
146
147 // Initialize the media library for the social image generator.
148 wp_enqueue_media();
149 }
150
151 /**
152 * Render the admin page by delegating to the wp-build dashboard.
153 *
154 * `maybe_load_wp_build()` defines the render function before this fires;
155 * render nothing on an unbuilt dev checkout rather than fatal.
156 */
157 public function render() {
158 if ( function_exists( 'jetpack_social_jetpack_social_dashboard_wp_admin_render_page' ) ) {
159 // Generated by wp-build (build/pages/jetpack-social-dashboard/page-wp-admin.php), so phan can't see it.
160 // @phan-suppress-next-line PhanUndeclaredFunction
161 jetpack_social_jetpack_social_dashboard_wp_admin_render_page();
162 }
163 }
164
165 /**
166 * Enqueue admin scripts and styles.
167 */
168 public function enqueue_admin_scripts() {
169 /*
170 * wp-build owns its own enqueue pipeline. The chassis reads connection
171 * state via `useConnection()`, which has no REST resolver, so hydrate it
172 * inline onto the prerequisites script that loads before the chassis module.
173 */
174 if ( wp_script_is( 'jetpack-social-dashboard-wp-admin-prerequisites', 'registered' ) ) {
175 Connection_Initial_State::render_script( 'jetpack-social-dashboard-wp-admin-prerequisites' );
176 }
177
178 // The i18n loader is registered on every admin page by jetpack-assets but
179 // only enqueued when depended on; the esbuild bundles don't pull it in.
180 if ( wp_script_is( 'wp-jp-i18n-loader', 'registered' ) ) {
181 wp_enqueue_script( 'wp-jp-i18n-loader' );
182 }
183 }
184
185 /**
186 * Load the wp-build entry file and register its polyfills.
187 *
188 * Only called on `?page=jetpack-social` admin requests. Keeps wp-build off
189 * every other request.
190 *
191 * @return void
192 */
193 private static function load_wp_build() {
194 $build_index = dirname( __DIR__ ) . '/build/build.php';
195
196 if ( ! file_exists( $build_index ) ) {
197 return;
198 }
199
200 require_once $build_index;
201
202 // The wp-build dashboard (unlike the Social bundles) uses the full polyfill set:
203 // the @wordpress/boot|route|a11y modules, wp-notices, wp-views, etc.
204 if ( ! class_exists( '\Automattic\Jetpack\WP_Build_Polyfills\WP_Build_Polyfills' ) ) {
205 return;
206 }
207
208 \Automattic\Jetpack\WP_Build_Polyfills\WP_Build_Polyfills::register(
209 'jetpack-social',
210 array_merge(
211 \Automattic\Jetpack\WP_Build_Polyfills\WP_Build_Polyfills::SCRIPT_HANDLES,
212 \Automattic\Jetpack\WP_Build_Polyfills\WP_Build_Polyfills::MODULE_IDS
213 )
214 );
215 }
216
217 /**
218 * Load wp-build with the screen ID aliased across its generated enqueue check.
219 *
220 * @see WP_Build_Screen_Id::load_with_alias()
221 * @return void
222 */
223 private static function load_wp_build_with_screen_alias() {
224 // Fallback: an older wp-build-polyfills under the jetpack-autoloader may predate load_with_alias().
225 if ( method_exists( \Automattic\Jetpack\WP_Build_Polyfills\WP_Build_Screen_Id::class, 'load_with_alias' ) ) {
226 \Automattic\Jetpack\WP_Build_Polyfills\WP_Build_Screen_Id::load_with_alias(
227 array( __CLASS__, 'alias_screen_id_for_wp_build' ),
228 array( __CLASS__, 'restore_screen_id_after_wp_build' ),
229 function () {
230 self::load_wp_build();
231 }
232 );
233 return;
234 }
235
236 add_action( 'admin_enqueue_scripts', array( __CLASS__, 'alias_screen_id_for_wp_build' ) );
237 self::load_wp_build();
238 add_action( 'admin_enqueue_scripts', array( __CLASS__, 'restore_screen_id_after_wp_build' ) );
239 }
240
241 /**
242 * Alias the current screen ID to satisfy wp-build's auto-generated enqueue check.
243 *
244 * The wp-build `<page>-wp-admin` enqueue callback fires only when the screen ID
245 * matches the wp-build page slug (`jetpack-social-dashboard`). Our wp-admin menu
246 * slug stays `jetpack-social`, so we mutate the screen object in place to make
247 * the check pass without changing the user-facing URL.
248 *
249 * Hooked only when we're on the Social admin page, so this never affects any
250 * other request.
251 *
252 * @since 0.87.1 Takes no argument; hooked on `admin_enqueue_scripts`.
253 *
254 * @return void
255 */
256 public static function alias_screen_id_for_wp_build() {
257 $screen = get_current_screen();
258 if ( ! $screen ) {
259 return;
260 }
261
262 self::$wp_build_original_screen_id = $screen->id;
263 $screen->id = 'jetpack-social-dashboard';
264 }
265
266 /**
267 * Undo alias_screen_id_for_wp_build(), so code after the generated check sees the real screen ID.
268 *
269 * @since 0.87.1
270 *
271 * @return void
272 */
273 public static function restore_screen_id_after_wp_build() {
274 $screen = get_current_screen();
275 if ( ! $screen || null === self::$wp_build_original_screen_id ) {
276 return;
277 }
278
279 $screen->id = self::$wp_build_original_screen_id;
280 self::$wp_build_original_screen_id = null;
281 }
282
283 /**
284 * Returns true when the current request targets the Social admin page.
285 *
286 * Used to scope wp-build loading to the one page that needs it. The
287 * `$_GET['page']` value is populated by wp-admin/admin.php before any of
288 * our hooks fire, so this check is reliable from the constructor onwards.
289 *
290 * @return bool
291 */
292 private static function is_social_admin_request() {
293 if ( ! is_admin() || ! isset( $_GET['page'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
294 return false;
295 }
296
297 return sanitize_text_field( wp_unslash( $_GET['page'] ) ) === 'jetpack-social'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
298 }
299 }
300