PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 13.6.2
Jetpack – WP Security, Backup, Speed, & Growth v13.6.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 14.5.1 14.6.1 14.7.1 14.8.1 14.9.2 15.0.3 15.1.2 15.2.1 15.3.2 15.4.1 15.5.1 15.6.1 15.7.2 15.8.1 15.9.2 16.0.2 16.1.3 16.2-a.5 16.2-a.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 / modules / wpcom-block-editor / class-jetpack-wpcom-block-editor.php
jetpack / modules / wpcom-block-editor Last commit date
class-jetpack-wpcom-block-editor.php 2 years ago functions.editor-type.php 2 years ago
class-jetpack-wpcom-block-editor.php
640 lines
1 <?php
2 /**
3 * WordPress.com Block Editor
4 * Allow new block editor posts to be composed on WordPress.com.
5 * This is auto-loaded as of Jetpack v7.4 for sites connected to WordPress.com only.
6 *
7 * @package automattic/jetpack
8 */
9
10 _deprecated_file( __FILE__, 'jetpack-13.6', 'Automattic\\Jetpack\\Jetpack_Mu_Wpcom\\WPCOM_Block_Editor\\Jetpack_WPCOM_Block_Editor' );
11 use Automattic\Jetpack\Connection\Manager as Connection_Manager;
12 use Automattic\Jetpack\Connection\Tokens;
13 use Automattic\Jetpack\Modules;
14 use Automattic\Jetpack\Status\Host;
15
16 /**
17 * WordPress.com Block editor for Jetpack
18 *
19 * @deprecated 13.6
20 */
21 class Jetpack_WPCOM_Block_Editor {
22 /**
23 * ID of the user who signed the nonce.
24 *
25 * @var int
26 */
27 private $nonce_user_id;
28
29 /**
30 * An array to store auth cookies until we can determine if they should be sent
31 *
32 * @var array
33 */
34 private $set_cookie_args;
35
36 /**
37 * Singleton
38 *
39 * @deprecated 13.6
40 */
41 public static function init() {
42 _deprecated_function( __METHOD__, 'jetpack-13.6', 'Automattic\\Jetpack\\Jetpack_Mu_Wpcom\\WPCOM_Block_Editor\\Jetpack_WPCOM_Block_Editor::init' );
43 static $instance = false;
44
45 if ( ! $instance ) {
46 $instance = new Jetpack_WPCOM_Block_Editor();
47 }
48
49 return $instance;
50 }
51
52 /**
53 * Jetpack_WPCOM_Block_Editor constructor.
54 *
55 * @deprecated 13.6
56 */
57 private function __construct() {
58 _deprecated_function( __METHOD__, 'jetpack-13.6', 'Automattic\\Jetpack\\Jetpack_Mu_Wpcom\\WPCOM_Block_Editor\\Jetpack_WPCOM_Block_Editor::__construct' );
59 $this->set_cookie_args = array();
60 add_action( 'init', array( $this, 'init_actions' ) );
61 }
62
63 /**
64 * Add in all hooks.
65 */
66 public function init_actions() {
67 // Bail early if Jetpack's block editor extensions are disabled on the site.
68 /* This filter is documented in class.jetpack-gutenberg.php */
69 if ( ! apply_filters( 'jetpack_gutenberg', true ) ) {
70 return;
71 }
72
73 if ( $this->is_iframed_block_editor() ) {
74 add_action( 'admin_init', array( $this, 'disable_send_frame_options_header' ), 9 );
75 add_filter( 'admin_body_class', array( $this, 'add_iframed_body_class' ) );
76 }
77
78 require_once __DIR__ . '/functions.editor-type.php';
79 add_action( 'edit_form_top', 'Jetpack\EditorType\remember_classic_editor' );
80 add_action( 'login_init', array( $this, 'allow_block_editor_login' ), 1 );
81 add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_block_editor_assets' ), 9 );
82 add_action( 'enqueue_block_assets', array( $this, 'enqueue_block_assets' ) );
83 add_filter( 'mce_external_plugins', array( $this, 'add_tinymce_plugins' ) );
84 add_filter( 'block_editor_settings_all', 'Jetpack\EditorType\remember_block_editor', 10, 2 );
85
86 $this->enable_cross_site_auth_cookies();
87 }
88
89 /**
90 * Checks if we are embedding the block editor in an iframe in WordPress.com.
91 *
92 * @return bool Whether the current request is from the iframed block editor.
93 */
94 public function is_iframed_block_editor() {
95 global $pagenow;
96
97 // phpcs:ignore WordPress.Security.NonceVerification
98 return ( 'post.php' === $pagenow || 'post-new.php' === $pagenow ) && ! empty( $_GET['frame-nonce'] );
99 }
100
101 /**
102 * Prevents frame options header from firing if this is a allowed iframe request.
103 */
104 public function disable_send_frame_options_header() {
105 // phpcs:ignore WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
106 if ( isset( $_GET['frame-nonce'] ) && $this->framing_allowed( $_GET['frame-nonce'] ) ) {
107 remove_action( 'admin_init', 'send_frame_options_header' );
108 }
109 }
110
111 /**
112 * Adds custom admin body class if this is a allowed iframe request.
113 *
114 * @param string $classes Admin body classes.
115 * @return string
116 */
117 public function add_iframed_body_class( $classes ) {
118 // phpcs:ignore WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
119 if ( isset( $_GET['frame-nonce'] ) && $this->framing_allowed( $_GET['frame-nonce'] ) ) {
120 $classes .= ' is-iframed ';
121 }
122
123 return $classes;
124 }
125
126 /**
127 * Checks to see if cookie can be set in current context. If 3rd party cookie blocking
128 * is enabled the editor can't load in iFrame, so emiting X-Frame-Options: DENY will
129 * force the editor to break out of the iFrame.
130 */
131 private function check_iframe_cookie_setting() {
132 if ( ! isset( $_SERVER['QUERY_STRING'] ) || ! strpos( filter_var( wp_unslash( $_SERVER['QUERY_STRING'] ) ), 'calypsoify%3D1%26block-editor' ) || isset( $_COOKIE['wordpress_test_cookie'] ) ) {
133 return;
134 }
135
136 if ( isset( $_SERVER['REQUEST_URI'] ) && empty( $_GET['calypsoify_cookie_check'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
137 header( 'Location: ' . esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) . '&calypsoify_cookie_check=true' ) );
138 exit;
139 }
140
141 header( 'X-Frame-Options: DENY' );
142 exit;
143 }
144
145 /**
146 * Allows to iframe the login page if a user is logged out
147 * while trying to access the block editor from wordpress.com.
148 */
149 public function allow_block_editor_login() {
150 // phpcs:ignore WordPress.Security.NonceVerification
151 if ( empty( $_REQUEST['redirect_to'] ) ) {
152 return;
153 }
154 // phpcs:ignore WordPress.Security.NonceVerification
155 $redirect_to = esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] ) );
156
157 $this->check_iframe_cookie_setting();
158
159 $query = wp_parse_url( urldecode( $redirect_to ), PHP_URL_QUERY );
160 $args = wp_parse_args( $query );
161
162 // Check nonce and make sure this is a Gutenframe request.
163 if ( ! empty( $args['frame-nonce'] ) && $this->framing_allowed( $args['frame-nonce'] ) ) {
164
165 // If SSO is active, we'll let WordPress.com handle authentication...
166 if ( ( new Modules() )->is_active( 'sso' ) ) {
167 // ...but only if it's not an Atomic site. They already do that.
168 if ( ! ( new Host() )->is_woa_site() ) {
169 add_filter( 'jetpack_sso_bypass_login_forward_wpcom', '__return_true' );
170 }
171 } else {
172 $_REQUEST['interim-login'] = true;
173 add_action( 'wp_login', array( $this, 'do_redirect' ) );
174 add_action( 'login_form', array( $this, 'add_login_html' ) );
175 add_filter( 'wp_login_errors', array( $this, 'add_login_message' ) );
176 remove_action( 'login_init', 'send_frame_options_header' );
177 wp_add_inline_style( 'login', '.interim-login #login{padding-top:8%}' );
178 }
179 }
180 }
181
182 /**
183 * Adds a login message.
184 *
185 * Intended to soften the expectation mismatch of ending up with a login screen rather than the editor.
186 *
187 * @param WP_Error $errors WP Error object.
188 * @return \WP_Error
189 */
190 public function add_login_message( $errors ) {
191 $errors->remove( 'expired' );
192 $errors->add( 'info', __( 'Before we continue, please log in to your Jetpack site.', 'jetpack' ), 'message' );
193
194 return $errors;
195 }
196
197 /**
198 * Maintains the `redirect_to` parameter in login form links.
199 * Adds visual feedback of login in progress.
200 */
201 public function add_login_html() {
202 ?>
203 <input type="hidden" name="redirect_to" value="<?php echo isset( $_REQUEST['redirect_to'] ) ? esc_url( wp_unslash( $_REQUEST['redirect_to'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized ?>" />
204 <script type="application/javascript">
205 document.getElementById( 'loginform' ).addEventListener( 'submit' , function() {
206 document.getElementById( 'wp-submit' ).setAttribute( 'disabled', 'disabled' );
207 document.getElementById( 'wp-submit' ).value = '<?php echo esc_js( __( 'Logging In...', 'jetpack' ) ); ?>';
208 } );
209 </script>
210 <?php
211 }
212
213 /**
214 * Does the redirect to the block editor.
215 *
216 * @return never
217 */
218 public function do_redirect() {
219 wp_safe_redirect( $GLOBALS['redirect_to'] );
220 exit;
221 }
222
223 /**
224 * Checks whether this is an allowed iframe request.
225 *
226 * @param string $nonce Nonce to verify.
227 * @return bool
228 */
229 public function framing_allowed( $nonce ) {
230 $blog_id = Connection_Manager::get_site_id();
231 if ( is_wp_error( $blog_id ) ) {
232 return false;
233 }
234
235 $verified = $this->verify_frame_nonce( $nonce, 'frame-' . $blog_id );
236
237 if ( is_wp_error( $verified ) ) {
238 wp_die( $verified ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
239 }
240
241 if ( $verified && ! defined( 'IFRAME_REQUEST' ) ) {
242 define( 'IFRAME_REQUEST', true );
243 }
244
245 return (bool) $verified;
246 }
247
248 /**
249 * Verify that correct nonce was used with time limit.
250 *
251 * The user is given an amount of time to use the token, so therefore, since the
252 * UID and $action remain the same, the independent variable is the time.
253 *
254 * @param string $nonce Nonce that was used in the form to verify.
255 * @param string $action Should give context to what is taking place and be the same when nonce was created.
256 * @return boolean|WP_Error Whether the nonce is valid.
257 */
258 public function verify_frame_nonce( $nonce, $action ) {
259 if ( empty( $nonce ) ) {
260 return false;
261 }
262
263 list( $expiration, $user_id, $hash ) = explode( ':', $nonce, 3 );
264
265 $this->nonce_user_id = (int) $user_id;
266 if ( ! $this->nonce_user_id ) {
267 return false;
268 }
269
270 $token = ( new Tokens() )->get_access_token( $this->nonce_user_id );
271 if ( ! $token ) {
272 return false;
273 }
274
275 /*
276 * Failures must return `false` (blocking the iframe) prior to the
277 * signature verification.
278 */
279
280 add_filter( 'salt', array( $this, 'filter_salt' ), 10, 2 );
281 $expected_hash = wp_hash( "$expiration|$action|{$this->nonce_user_id}", 'jetpack_frame_nonce' );
282 remove_filter( 'salt', array( $this, 'filter_salt' ) );
283
284 if ( ! hash_equals( $hash, $expected_hash ) ) {
285 return false;
286 }
287
288 /*
289 * Failures may return `WP_Error` (showing an error in the iframe) after the
290 * signature verification passes.
291 */
292
293 if ( time() > $expiration ) {
294 return new WP_Error( 'nonce_invalid_expired', 'Expired nonce.', array( 'status' => 401 ) );
295 }
296
297 // Check if it matches the current user, unless they're trying to log in.
298 if ( get_current_user_id() !== $this->nonce_user_id && ! doing_action( 'login_init' ) ) {
299 return new WP_Error( 'nonce_invalid_user_mismatch', 'User ID mismatch.', array( 'status' => 401 ) );
300 }
301
302 return true;
303 }
304
305 /**
306 * Filters the WordPress salt.
307 *
308 * @param string $salt Salt for the given scheme.
309 * @param string $scheme Authentication scheme.
310 * @return string
311 */
312 public function filter_salt( $salt, $scheme ) {
313 if ( 'jetpack_frame_nonce' === $scheme ) {
314 $token = ( new Tokens() )->get_access_token( $this->nonce_user_id );
315
316 if ( $token ) {
317 $salt = $token->secret;
318 }
319 }
320
321 return $salt;
322 }
323
324 /**
325 * Enqueues the WordPress.com block editor integration assets for the editor.
326 */
327 public function enqueue_block_editor_assets() {
328 global $pagenow;
329
330 // Bail if we're not in the post editor, but on the widget settings screen.
331 if ( is_customize_preview() || 'widgets.php' === $pagenow ) {
332 return;
333 }
334
335 $debug = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG;
336 $version = gmdate( 'Ymd' );
337
338 wp_enqueue_script(
339 'wpcom-block-editor-default-editor-script',
340 $debug
341 ? '//widgets.wp.com/wpcom-block-editor/default.editor.js?minify=false'
342 : '//widgets.wp.com/wpcom-block-editor/default.editor.min.js',
343 array(
344 'jquery',
345 'lodash',
346 'wp-annotations',
347 'wp-compose',
348 'wp-data',
349 'wp-editor',
350 'wp-element',
351 'wp-rich-text',
352 ),
353 $version,
354 true
355 );
356
357 wp_localize_script(
358 'wpcom-block-editor-default-editor-script',
359 'wpcomGutenberg',
360 array(
361 'richTextToolbar' => array(
362 'justify' => __( 'Justify', 'jetpack' ),
363 'underline' => __( 'Underline', 'jetpack' ),
364 ),
365 )
366 );
367
368 if ( ( new Host() )->is_woa_site() ) {
369 wp_enqueue_script(
370 'wpcom-block-editor-wpcom-editor-script',
371 $debug
372 ? '//widgets.wp.com/wpcom-block-editor/wpcom.editor.js?minify=false'
373 : '//widgets.wp.com/wpcom-block-editor/wpcom.editor.min.js',
374 array(
375 'lodash',
376 'wp-blocks',
377 'wp-data',
378 'wp-dom-ready',
379 'wp-plugins',
380 ),
381 $version,
382 true
383 );
384 wp_enqueue_style(
385 'wpcom-block-editor-wpcom-editor-styles',
386 $debug
387 ? '//widgets.wp.com/wpcom-block-editor/wpcom.editor.css?minify=false'
388 : '//widgets.wp.com/wpcom-block-editor/wpcom.editor.min.css',
389 array(),
390 $version
391 );
392 }
393
394 if ( $this->is_iframed_block_editor() ) {
395 wp_enqueue_script(
396 'wpcom-block-editor-calypso-editor-script',
397 $debug
398 ? '//widgets.wp.com/wpcom-block-editor/calypso.editor.js?minify=false'
399 : '//widgets.wp.com/wpcom-block-editor/calypso.editor.min.js',
400 array(
401 'calypsoify_wpadminmods_js',
402 'jquery',
403 'lodash',
404 'react',
405 'wp-blocks',
406 'wp-data',
407 'wp-hooks',
408 'wp-tinymce',
409 'wp-url',
410 ),
411 $version,
412 true
413 );
414
415 wp_enqueue_style(
416 'wpcom-block-editor-calypso-editor-styles',
417 $debug
418 ? '//widgets.wp.com/wpcom-block-editor/calypso.editor.css?minify=false'
419 : '//widgets.wp.com/wpcom-block-editor/calypso.editor.min.css',
420 array(),
421 $version
422 );
423 }
424 }
425
426 /**
427 * Enqueues the WordPress.com block editor integration assets for both editor and front-end.
428 */
429 public function enqueue_block_assets() {
430 // These styles are manually copied from //widgets.wp.com/wpcom-block-editor/default.view.css in order to
431 // improve the performance by avoiding an extra network request to download the CSS file on every page.
432 wp_add_inline_style( 'wp-block-library', '.has-text-align-justify{text-align:justify;}' );
433 }
434
435 /**
436 * Determines if the current $post contains a justified paragraph block.
437 *
438 * @return boolean true if justified paragraph is found, false otherwise.
439 */
440 public function has_justified_block() {
441 global $post;
442 if ( ! $post instanceof WP_Post ) {
443 return false;
444 }
445
446 if ( ! has_blocks( $post ) ) {
447 return false;
448 }
449
450 return str_contains( $post->post_content, '<!-- wp:paragraph {"align":"justify"' );
451 }
452
453 /**
454 * Register the Tiny MCE plugins for the WordPress.com block editor integration.
455 *
456 * @param array $plugin_array An array of external Tiny MCE plugins.
457 * @return array External TinyMCE plugins.
458 */
459 public function add_tinymce_plugins( $plugin_array ) {
460 if ( $this->is_iframed_block_editor() ) {
461 $debug = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG;
462
463 $plugin_array['gutenberg-wpcom-iframe-media-modal'] = add_query_arg(
464 'v',
465 gmdate( 'YW' ),
466 $debug
467 ? '//widgets.wp.com/wpcom-block-editor/calypso.tinymce.js?minify=false'
468 : '//widgets.wp.com/wpcom-block-editor/calypso.tinymce.min.js'
469 );
470 }
471
472 return $plugin_array;
473 }
474
475 /**
476 * Ensures the authentication cookies are designated for cross-site access.
477 */
478 private function enable_cross_site_auth_cookies() {
479 /**
480 * Allow plugins to disable the cross-site auth cookies.
481 *
482 * @since 8.1.1
483 *
484 * @param false bool Whether auth cookies should be disabled for cross-site access. False by default.
485 */
486 if ( apply_filters( 'jetpack_disable_cross_site_auth_cookies', false ) ) {
487 return;
488 }
489
490 add_action( 'set_auth_cookie', array( $this, 'set_samesite_auth_cookies' ), 10, 5 );
491 add_action( 'set_logged_in_cookie', array( $this, 'set_samesite_logged_in_cookies' ), 10, 4 );
492 add_filter( 'send_auth_cookies', array( $this, 'maybe_send_cookies' ), 9999 );
493 }
494
495 /**
496 * Checks if we've stored any cookies to send and then sends them
497 * if the send_auth_cookies value is true.
498 *
499 * @param bool $send_cookies The filtered value that determines whether to send auth cookies.
500 */
501 public function maybe_send_cookies( $send_cookies ) {
502
503 if ( ! empty( $this->set_cookie_args ) && $send_cookies ) {
504 array_map(
505 function ( $cookie ) {
506 call_user_func_array( 'jetpack_shim_setcookie', $cookie );
507 },
508 $this->set_cookie_args
509 );
510 $this->set_cookie_args = array();
511 return false;
512 }
513
514 return $send_cookies;
515 }
516
517 /**
518 * Gets the SameSite attribute to use in auth cookies.
519 *
520 * @param bool $secure Whether the connection is secure.
521 * @return string SameSite attribute to use on auth cookies.
522 */
523 public function get_samesite_attr_for_auth_cookies( $secure ) {
524 $samesite = $secure ? 'None' : 'Lax';
525 /**
526 * Filters the SameSite attribute to use in auth cookies.
527 *
528 * @param string $samesite SameSite attribute to use in auth cookies.
529 *
530 * @since 8.1.1
531 */
532 $samesite = apply_filters( 'jetpack_auth_cookie_samesite', $samesite );
533
534 return $samesite;
535 }
536
537 /**
538 * Generates cross-site auth cookies so they can be accessed by WordPress.com.
539 *
540 * @param string $auth_cookie Authentication cookie value.
541 * @param int $expire The time the login grace period expires as a UNIX timestamp.
542 * Default is 12 hours past the cookie's expiration time.
543 * @param int $expiration The time when the authentication cookie expires as a UNIX timestamp.
544 * Default is 14 days from now.
545 * @param int $user_id User ID.
546 * @param string $scheme Authentication scheme. Values include 'auth' or 'secure_auth'.
547 */
548 public function set_samesite_auth_cookies( $auth_cookie, $expire, $expiration, $user_id, $scheme ) {
549 if ( wp_startswith( $scheme, 'secure_' ) ) {
550 $secure = true;
551 $auth_cookie_name = SECURE_AUTH_COOKIE;
552 } else {
553 $secure = false;
554 $auth_cookie_name = AUTH_COOKIE;
555 }
556 $samesite = $this->get_samesite_attr_for_auth_cookies( $secure );
557
558 $this->set_cookie_args[] = array(
559 $auth_cookie_name,
560 $auth_cookie,
561 array(
562 'expires' => $expire,
563 'path' => PLUGINS_COOKIE_PATH,
564 'domain' => COOKIE_DOMAIN,
565 'secure' => $secure,
566 'httponly' => true,
567 'samesite' => $samesite,
568 ),
569 );
570
571 $this->set_cookie_args[] = array(
572 $auth_cookie_name,
573 $auth_cookie,
574 array(
575 'expires' => $expire,
576 'path' => ADMIN_COOKIE_PATH,
577 'domain' => COOKIE_DOMAIN,
578 'secure' => $secure,
579 'httponly' => true,
580 'samesite' => $samesite,
581 ),
582 );
583 }
584
585 /**
586 * Generates cross-site logged in cookies so they can be accessed by WordPress.com.
587 *
588 * @param string $logged_in_cookie The logged-in cookie value.
589 * @param int $expire The time the login grace period expires as a UNIX timestamp.
590 * Default is 12 hours past the cookie's expiration time.
591 * @param int $expiration The time when the logged-in cookie expires as a UNIX timestamp.
592 * Default is 14 days from now.
593 * @param int $user_id User ID.
594 */
595 public function set_samesite_logged_in_cookies( $logged_in_cookie, $expire, $expiration, $user_id ) {
596 $secure = is_ssl();
597
598 // Front-end cookie is secure when the auth cookie is secure and the site's home URL is forced HTTPS.
599 $secure_logged_in_cookie = $secure && 'https' === wp_parse_url( get_option( 'home' ), PHP_URL_SCHEME );
600
601 /** This filter is documented in core/src/wp-includes/pluggable.php */
602 $secure = apply_filters( 'secure_auth_cookie', $secure, $user_id );
603
604 /** This filter is documented in core/src/wp-includes/pluggable.php */
605 $secure_logged_in_cookie = apply_filters( 'secure_logged_in_cookie', $secure_logged_in_cookie, $user_id, $secure );
606
607 $samesite = $this->get_samesite_attr_for_auth_cookies( $secure_logged_in_cookie );
608
609 $this->set_cookie_args[] = array(
610 LOGGED_IN_COOKIE,
611 $logged_in_cookie,
612 array(
613 'expires' => $expire,
614 'path' => COOKIEPATH,
615 'domain' => COOKIE_DOMAIN,
616 'secure' => $secure_logged_in_cookie,
617 'httponly' => true,
618 'samesite' => $samesite,
619 ),
620 );
621
622 if ( COOKIEPATH !== SITECOOKIEPATH ) {
623 $this->set_cookie_args[] = array(
624 LOGGED_IN_COOKIE,
625 $logged_in_cookie,
626 array(
627 'expires' => $expire,
628 'path' => SITECOOKIEPATH,
629 'domain' => COOKIE_DOMAIN,
630 'secure' => $secure_logged_in_cookie,
631 'httponly' => true,
632 'samesite' => $samesite,
633 ),
634 );
635 }
636 }
637 }
638
639 Jetpack_WPCOM_Block_Editor::init();
640