PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 12.8.3
Jetpack – WP Security, Backup, Speed, & Growth v12.8.3
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 / comments / comments.php
jetpack / modules / comments Last commit date
admin.php 2 years ago base.php 2 years ago comments.php 2 years ago
comments.php
718 lines
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * Module: Comments
4 *
5 * @package automattic/jetpack
6 */
7
8 require __DIR__ . '/base.php';
9 use Automattic\Jetpack\Connection\Tokens;
10
11 /**
12 * Main Comments class
13 *
14 * @package automattic/jetpack
15 * @since 1.4
16 */
17 class Jetpack_Comments extends Highlander_Comments_Base {
18
19 /** Variables *************************************************************/
20
21 /**
22 * Possible comment form sources - empty array as default
23 *
24 * @var array
25 */
26 public $id_sources = array();
27
28 /**
29 * Remote comment URL - empty string as default
30 *
31 * @var string
32 */
33 public $signed_url = '';
34
35 /**
36 * The default comment form color scheme - default is light
37 *
38 * @var string
39 * @see ::set_default_color_theme_based_on_theme_settings()
40 */
41 public $default_color_scheme = 'light';
42
43 /** Methods ***************************************************************/
44
45 /**
46 * Initialize class
47 */
48 public static function init() {
49 static $instance = false;
50
51 if ( ! $instance ) {
52 $instance = new Jetpack_Comments();
53 }
54
55 return $instance;
56 }
57
58 /**
59 * Main constructor for Comments
60 *
61 * @since 1.4
62 */
63 public function __construct() {
64 parent::__construct();
65
66 // Comments is loaded.
67
68 /**
69 * Fires after the Jetpack_Comments object has been instantiated
70 *
71 * @module comments
72 *
73 * @since 1.4.0
74 *
75 * @param array $jetpack_comments_loaded First element in array of type Jetpack_Comments
76 */
77 do_action_ref_array( 'jetpack_comments_loaded', array( $this ) );
78 add_action( 'after_setup_theme', array( $this, 'set_default_color_theme_based_on_theme_settings' ), 100 );
79 }
80
81 /**
82 * Set the default comments color theme based on theme settings
83 */
84 public function set_default_color_theme_based_on_theme_settings() {
85 if ( function_exists( 'twentyeleven_get_theme_options' ) ) {
86 $theme_options = twentyeleven_get_theme_options();
87 $theme_color_scheme = isset( $theme_options['color_scheme'] ) ? $theme_options['color_scheme'] : 'transparent';
88 } else {
89 $theme_color_scheme = get_theme_mod( 'color_scheme', 'transparent' );
90 }
91 // Default for $theme_color_scheme is 'transparent' just so it doesn't match 'light' or 'dark'.
92 // The default for Jetpack's color scheme is still defined above as 'light'.
93
94 if ( false !== stripos( $theme_color_scheme, 'light' ) ) {
95 $this->default_color_scheme = 'light';
96 } elseif ( false !== stripos( $theme_color_scheme, 'dark' ) ) {
97 $this->default_color_scheme = 'dark';
98 }
99 }
100
101 /** Private Methods *******************************************************/
102
103 /**
104 * Set any global variables or class variables
105 *
106 * This is primarily defining the comment form sources.
107 *
108 * @since 1.4
109 */
110 protected function setup_globals() {
111 parent::setup_globals();
112
113 // Sources.
114 $this->id_sources = array(
115 'guest',
116 'jetpack',
117 'wordpress',
118 'facebook',
119 );
120 }
121
122 /**
123 * Setup actions for methods in this class
124 *
125 * @since 1.4
126 */
127 protected function setup_actions() {
128 parent::setup_actions();
129
130 // Selfishly remove everything from the existing comment form.
131 remove_all_actions( 'comment_form_before' );
132
133 // Selfishly add only our actions back to the comment form.
134 add_action( 'comment_form_before', array( $this, 'comment_form_before' ) );
135 add_action( 'comment_form_after', array( $this, 'comment_form_after' ), 1 ); // Set very early since we remove everything outputed before our action.
136
137 // Before a comment is posted.
138 add_action( 'pre_comment_on_post', array( $this, 'pre_comment_on_post' ), 1 );
139
140 // After a comment is posted.
141 add_action( 'comment_post', array( $this, 'add_comment_meta' ) );
142 }
143
144 /**
145 * Setup filters for methods in this class
146 *
147 * @since 1.6.2
148 */
149 protected function setup_filters() {
150 parent::setup_filters();
151
152 add_filter( 'comment_post_redirect', array( $this, 'capture_comment_post_redirect_to_reload_parent_frame' ), 100 );
153 add_filter( 'get_avatar', array( $this, 'get_avatar' ), 10, 4 );
154 }
155
156 /**
157 * Get the comment avatar from Gravatar or Twitter/Facebook.
158 *
159 * Leaving the Twitter reference for legacy comments even though support is no longer offered.
160 *
161 * @since 1.4
162 *
163 * @param string $avatar Current avatar URL.
164 * @param string $comment Comment for the avatar.
165 * @param int $size Size of the avatar.
166 *
167 * @return string New avatar
168 */
169 public function get_avatar( $avatar, $comment, $size ) {
170 if ( ! isset( $comment->comment_post_ID ) || ! isset( $comment->comment_ID ) ) {
171 // it's not a comment - bail.
172 return $avatar;
173 }
174
175 // Detect whether it's a Facebook avatar.
176 $foreign_avatar = get_comment_meta( $comment->comment_ID, 'hc_avatar', true );
177 $foreign_avatar_hostname = wp_parse_url( $foreign_avatar, PHP_URL_HOST );
178 if ( ! $foreign_avatar_hostname ||
179 ! preg_match( '/\.?(graph\.facebook\.com|twimg\.com)$/', $foreign_avatar_hostname ) ) {
180 return $avatar;
181 }
182
183 // Return the Facebook or Twitter avatar.
184 return preg_replace( '#src=([\'"])[^\'"]+\\1#', 'src=\\1' . esc_url( set_url_scheme( $this->photon_avatar( $foreign_avatar, $size ), 'https' ) ) . '\\1', $avatar );
185 }
186
187 /**
188 * Get the site's blog token.
189 * This can be used to bypass Comments entirely if Jetpack is not properly connected.
190 *
191 * @since 11.2
192 *
193 * @return bool|object False if not properly connected. Object with the blog token if connected.
194 */
195 private function get_blog_token() {
196 $blog_token = ( new Tokens() )->get_access_token();
197 // If we have no token, bail.
198 if ( ! $blog_token || is_wp_error( $blog_token ) ) {
199 return false;
200 }
201
202 return $blog_token;
203 }
204
205 /** Output Methods ********************************************************/
206
207 /**
208 * Start capturing the core comment_form() output
209 *
210 * Comment form output will only be captured if comments are enabled - we return otherwise.
211 *
212 * @since 1.4
213 */
214 public function comment_form_before() {
215 /**
216 * Filters the setting that determines if Jetpack comments should be enabled for
217 * the current post type.
218 *
219 * @module comments
220 *
221 * @since 3.8.1
222 *
223 * @param boolean $return Should comments be enabled?
224 */
225 if ( ! apply_filters( 'jetpack_comment_form_enabled_for_' . get_post_type(), true ) ) {
226 return;
227 }
228
229 // If the Jetpack connection is not healthy, bail.
230 if ( ! $this->get_blog_token() ) {
231 return;
232 }
233
234 // Add some JS to the footer.
235 add_action( 'wp_footer', array( $this, 'watch_comment_parent' ), 100 );
236
237 ob_start();
238 }
239
240 /**
241 * Noop the default comment form output, get some options, and output our
242 * tricked out totally radical comment form.
243 *
244 * @since 1.4
245 */
246 public function comment_form_after() {
247 /** This filter is documented in modules/comments/comments.php */
248 if ( ! apply_filters( 'jetpack_comment_form_enabled_for_' . get_post_type(), true ) ) {
249 return;
250 }
251
252 $blog_token = $this->get_blog_token();
253 // If the Jetpack connection is not healthy, bail.
254 if ( ! $blog_token ) {
255 return;
256 }
257
258 // Throw it all out and drop in our replacement.
259 ob_end_clean();
260
261 if ( in_array( 'subscriptions', Jetpack::get_active_modules(), true ) ) {
262 $stb_enabled = get_option( 'stb_enabled', 1 );
263 $stb_enabled = empty( $stb_enabled ) ? 0 : 1;
264
265 $stc_enabled = get_option( 'stc_enabled', 1 );
266 $stc_enabled = empty( $stc_enabled ) ? 0 : 1;
267 } else {
268 $stb_enabled = 0;
269 $stc_enabled = 0;
270 }
271
272 $params = array(
273 'blogid' => Jetpack_Options::get_option( 'id' ),
274 'postid' => get_the_ID(),
275 'comment_registration' => ( get_option( 'comment_registration' ) ? '1' : '0' ), // Need to explicitly send a '1' or a '0' for these.
276 'require_name_email' => ( get_option( 'require_name_email' ) ? '1' : '0' ),
277 'stc_enabled' => $stc_enabled,
278 'stb_enabled' => $stb_enabled,
279 'show_avatars' => ( get_option( 'show_avatars' ) ? '1' : '0' ),
280 'avatar_default' => get_option( 'avatar_default' ),
281 'greeting' => get_option( 'highlander_comment_form_prompt', __( 'Leave a Reply', 'jetpack' ) ),
282 'jetpack_comments_nonce' => wp_create_nonce( 'jetpack_comments_nonce-' . get_the_ID() ),
283 /**
284 * Changes the comment form prompt.
285 *
286 * @module comments
287 *
288 * @since 2.3.0
289 *
290 * @param string $var Default is "Leave a Reply to %s."
291 */
292 'greeting_reply' => apply_filters(
293 'jetpack_comment_form_prompt_reply',
294 /* translators: %s is the displayed username of the post (or comment) author */
295 __( 'Leave a Reply to %s', 'jetpack' )
296 ),
297 'color_scheme' => get_option( 'jetpack_comment_form_color_scheme', $this->default_color_scheme ),
298 'lang' => get_locale(),
299 'jetpack_version' => JETPACK__VERSION,
300 );
301
302 // Extra parameters for logged in user.
303 if ( is_user_logged_in() ) {
304 $current_user = wp_get_current_user();
305 $params['hc_post_as'] = 'jetpack';
306 $params['hc_userid'] = $current_user->ID;
307 $params['hc_username'] = $current_user->display_name;
308 $params['hc_userurl'] = $current_user->user_url;
309 $params['hc_useremail'] = md5( strtolower( trim( $current_user->user_email ) ) );
310 if ( current_user_can( 'unfiltered_html' ) ) {
311 $params['_wp_unfiltered_html_comment'] = wp_create_nonce( 'unfiltered-html-comment_' . get_the_ID() );
312 }
313 } else {
314 $commenter = wp_get_current_commenter();
315 $params['show_cookie_consent'] = (int) has_action( 'set_comment_cookies', 'wp_set_comment_cookies' );
316 $params['has_cookie_consent'] = (int) ! empty( $commenter['comment_author_email'] );
317 }
318
319 list( $token_key ) = explode( '.', $blog_token->secret, 2 );
320 // Prophylactic check: anything else should never happen.
321 if ( $token_key && $token_key !== $blog_token->secret ) {
322 // Is the token a Special Token (@see class.tokens.php)?
323 if ( preg_match( '/^;.\d+;\d+;$/', $token_key, $matches ) ) {
324 // The token key for a Special Token is public.
325 $params['token_key'] = $token_key;
326 } else {
327 /*
328 * The token key for a Normal Token is public but
329 * looks like sensitive data. Since there can only be
330 * one Normal Token per site, avoid concern by
331 * sending the magic "use the Normal Token" token key.
332 */
333 $params['token_key'] = Tokens::MAGIC_NORMAL_TOKEN_KEY;
334 }
335 }
336
337 $signature = self::sign_remote_comment_parameters( $params, $blog_token->secret );
338 if ( is_wp_error( $signature ) ) {
339 $signature = 'error';
340 }
341
342 $params['sig'] = $signature;
343 $url_origin = 'https://jetpack.wordpress.com';
344 $url = "{$url_origin}/jetpack-comment/?" . http_build_query( $params );
345 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sniff misses the esc_url_raw.
346 $url = "{$url}#parent=" . rawurlencode( esc_url_raw( set_url_scheme( 'http://' . ( isset( $_SERVER['HTTP_HOST'] ) ? wp_unslash( $_SERVER['HTTP_HOST'] ) : '' ) . ( isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '' ) ) ) );
347 $this->signed_url = $url;
348 $height = $params['comment_registration'] || is_user_logged_in() ? '315' : '430'; // Iframe can be shorter if we're not allowing guest commenting.
349 $transparent = ( 'transparent' === $params['color_scheme'] ) ? 'true' : 'false';
350
351 if ( isset( $_GET['replytocom'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
352 $url .= '&replytocom=' . (int) $_GET['replytocom']; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
353 }
354
355 /**
356 * Filter whether the comment title can be displayed.
357 *
358 * @module comments
359 *
360 * @since 4.7.0
361 *
362 * @param bool $show Can the comment be displayed? Default to true.
363 */
364 $show_greeting = apply_filters( 'jetpack_comment_form_display_greeting', true );
365
366 /**
367 * Filter the comment title tag.
368 *
369 * @module comments
370 * @since 12.4
371 *
372 * @param string $comment_reply_title_tag The comment title tag. Default to h3.
373 */
374 $comment_reply_title_tag = apply_filters( 'jetpack_comment_reply_title_tag', 'h3' );
375
376 // The actual iframe (loads comment form from Jetpack server).
377
378 $is_amp = class_exists( Jetpack_AMP_Support::class ) && Jetpack_AMP_Support::is_amp_request();
379 ?>
380
381 <div id="respond" class="comment-respond">
382 <?php
383 if ( true === $show_greeting ) :
384 printf(
385 '<%1$s id="reply-title" class="comment-reply-title">',
386 esc_html( $comment_reply_title_tag )
387 );
388
389 comment_form_title(
390 esc_html( $params['greeting'] ),
391 esc_html( $params['greeting_reply'] )
392 );
393 echo '<small>';
394 cancel_comment_reply_link( esc_html__( 'Cancel reply', 'jetpack' ) );
395 echo '</small>';
396
397 printf(
398 '</%1$s>',
399 esc_html( $comment_reply_title_tag )
400 );
401 endif;
402 ?>
403 <form id="commentform" class="comment-form">
404 <iframe
405 title="<?php esc_attr_e( 'Comment Form', 'jetpack' ); ?>"
406 src="<?php echo esc_url( $url ); ?>"
407 <?php if ( $is_amp ) : ?>
408 resizable
409 layout="fixed-height"
410 height="<?php echo esc_attr( $height ); ?>"
411 <?php else : ?>
412 name="jetpack_remote_comment"
413 style="width:100%; height: <?php echo esc_attr( $height ); ?>px; border:0;"
414 <?php endif; ?>
415 class="jetpack_remote_comment"
416 id="jetpack_remote_comment"
417 sandbox="allow-same-origin allow-top-navigation allow-scripts allow-forms allow-popups"
418 >
419 <?php if ( $is_amp ) : ?>
420 <button overflow><?php esc_html_e( 'Show more', 'jetpack' ); ?></button>
421 <?php endif; ?>
422 </iframe>
423 <?php if ( ! $is_amp ) : ?>
424 <!--[if !IE]><!-->
425 <script>
426 document.addEventListener('DOMContentLoaded', function () {
427 var commentForms = document.getElementsByClassName('jetpack_remote_comment');
428 for (var i = 0; i < commentForms.length; i++) {
429 commentForms[i].allowTransparency = <?php echo esc_html( $transparent ); ?>;
430 commentForms[i].scrolling = 'no';
431 }
432 });
433 </script>
434 <!--<![endif]-->
435 <?php endif; ?>
436 </form>
437 </div>
438
439 <?php // Below is required for comment reply JS to work. ?>
440
441 <input type="hidden" name="comment_parent" id="comment_parent" value="" />
442
443 <?php
444 }
445
446 /**
447 * Add some JS to wp_footer to watch for hierarchical reply parent change
448 *
449 * If AMP is enabled, we don't make any changes.
450 *
451 * @since 1.4
452 */
453 public function watch_comment_parent() {
454 if ( class_exists( Jetpack_AMP_Support::class ) && Jetpack_AMP_Support::is_amp_request() ) {
455 // @todo Implement AMP support.
456 return;
457 }
458 ?>
459 <script type="text/javascript">
460 const iframe = document.getElementById( 'jetpack_remote_comment' );
461 <?php if ( get_option( 'thread_comments' ) && get_option( 'thread_comments_depth' ) ) : ?>
462 const watchReply = function() {
463 // Check addComment._Jetpack_moveForm to make sure we don't monkey-patch twice.
464 if ( 'undefined' !== typeof addComment && ! addComment._Jetpack_moveForm ) {
465 // Cache the Core function.
466 addComment._Jetpack_moveForm = addComment.moveForm;
467 const commentParent = document.getElementById( 'comment_parent' );
468 const cancel = document.getElementById( 'cancel-comment-reply-link' );
469
470 function tellFrameNewParent ( commentParentValue ) {
471 const url = new URL( iframe.src );
472 if ( commentParentValue ) {
473 url.searchParams.set( 'replytocom', commentParentValue )
474 } else {
475 url.searchParams.delete( 'replytocom' );
476 }
477 if( iframe.src !== url.href ) {
478 iframe.src = url.href;
479 }
480 };
481
482 cancel.addEventListener( 'click', function () {
483 tellFrameNewParent( false );
484 } );
485
486 addComment.moveForm = function ( _, parentId ) {
487 tellFrameNewParent( parentId );
488 return addComment._Jetpack_moveForm.apply( null, arguments );
489 };
490 }
491 }
492 document.addEventListener( 'DOMContentLoaded', watchReply );
493 // In WP 6.4+, the script is loaded asynchronously, so we need to wait for it to load before we monkey-patch the functions it introduces.
494 document.querySelector('#comment-reply-js')?.addEventListener( 'load', watchReply );
495
496 <?php endif; ?>
497
498 window.addEventListener( 'message', function ( event ) {
499 if ( event.origin !== 'https://jetpack.wordpress.com' ) {
500 return;
501 }
502 iframe.style.height = event.data + 'px';
503 });
504 </script>
505 <?php
506 }
507
508 /**
509 * Verify the hash included in remote comments.
510 *
511 * If the Jetpack token is missing we return nothing,
512 * and if the token is unknown or invalid, or comments not allowed, an error is returned.
513 *
514 * @since 1.4
515 */
516 public function pre_comment_on_post() {
517 $post_array = stripslashes_deep( $_POST );
518
519 // Bail if missing the Jetpack token.
520 if ( ! isset( $post_array['sig'] ) || ! isset( $post_array['token_key'] ) ) {
521 unset( $_POST['hc_post_as'] );
522
523 return;
524 }
525
526 if ( empty( $post_array['jetpack_comments_nonce'] ) || ! wp_verify_nonce( $post_array['jetpack_comments_nonce'], "jetpack_comments_nonce-{$post_array['comment_post_ID']}" ) ) {
527 wp_die( esc_html__( 'Nonce verification failed.', 'jetpack' ), 400 );
528 }
529
530 if ( false !== strpos( $post_array['hc_avatar'], '.gravatar.com' ) ) {
531 $post_array['hc_avatar'] = htmlentities( $post_array['hc_avatar'], ENT_COMPAT );
532 }
533
534 $blog_token = ( new Tokens() )->get_access_token( false, $post_array['token_key'] );
535 if ( ! $blog_token || is_wp_error( $blog_token ) ) {
536 wp_die( esc_html__( 'Unknown security token.', 'jetpack' ), 400 );
537 }
538 $check = self::sign_remote_comment_parameters( $post_array, $blog_token->secret );
539 if ( is_wp_error( $check ) ) {
540 wp_die( esc_html( $check ) );
541 }
542
543 // Bail if token is expired or not valid.
544 if ( ! hash_equals( $check, $post_array['sig'] ) ) {
545 wp_die( esc_html__( 'Invalid security token.', 'jetpack' ), 400 );
546 }
547
548 /** This filter is documented in modules/comments/comments.php */
549 if ( ! apply_filters( 'jetpack_comment_form_enabled_for_' . get_post_type( $post_array['comment_post_ID'] ), true ) ) {
550 // In case the comment POST is legit, but the comments are
551 // now disabled, we don't allow the comment.
552
553 wp_die( esc_html__( 'Comments are not allowed.', 'jetpack' ), 403 );
554 }
555 }
556
557 /** Capabilities **********************************************************/
558
559 /**
560 * Add some additional comment meta after comment is saved about what
561 * service the comment is from, the avatar, user_id, etc...
562 *
563 * @since 1.4
564 *
565 * @param int $comment_id The comment ID.
566 */
567 public function add_comment_meta( $comment_id ) {
568 $comment_meta = array();
569
570 // phpcs:disable WordPress.Security.NonceVerification.Missing
571 switch ( $this->is_highlander_comment_post() ) {
572 case 'facebook':
573 $comment_meta['hc_post_as'] = 'facebook';
574 $comment_meta['hc_avatar'] = isset( $_POST['hc_avatar'] ) ? filter_var( wp_unslash( $_POST['hc_avatar'] ) ) : null;
575 $comment_meta['hc_foreign_user_id'] = isset( $_POST['hc_userid'] ) ? filter_var( wp_unslash( $_POST['hc_userid'] ) ) : null;
576 break;
577
578 // phpcs:ignore WordPress.WP.CapitalPDangit
579 case 'wordpress':
580 // phpcs:ignore WordPress.WP.CapitalPDangit
581 $comment_meta['hc_post_as'] = 'wordpress';
582 $comment_meta['hc_avatar'] = isset( $_POST['hc_avatar'] ) ? filter_var( wp_unslash( $_POST['hc_avatar'] ) ) : null;
583 $comment_meta['hc_foreign_user_id'] = isset( $_POST['hc_userid'] ) ? filter_var( wp_unslash( $_POST['hc_userid'] ) ) : null;
584 $comment_meta['hc_wpcom_id_sig'] = isset( $_POST['hc_wpcom_id_sig'] ) ? filter_var( wp_unslash( $_POST['hc_wpcom_id_sig'] ) ) : null; // since 1.9.
585 break;
586
587 case 'jetpack':
588 $comment_meta['hc_post_as'] = 'jetpack';
589 $comment_meta['hc_avatar'] = isset( $_POST['hc_avatar'] ) ? filter_var( wp_unslash( $_POST['hc_avatar'] ) ) : null;
590 $comment_meta['hc_foreign_user_id'] = isset( $_POST['hc_userid'] ) ? filter_var( wp_unslash( $_POST['hc_userid'] ) ) : null;
591 break;
592
593 }
594 // phpcs:enable WordPress.Security.NonceVerification.Missing
595
596 // Bail if no extra comment meta.
597 if ( empty( $comment_meta ) ) {
598 return;
599 }
600
601 // Loop through extra meta and add values.
602 foreach ( $comment_meta as $key => $value ) {
603 add_comment_meta( $comment_id, $key, $value, true );
604 }
605 }
606
607 /**
608 * POST the submitted comment to the iframe
609 *
610 * @param string $url The comment URL origin.
611 */
612 public function capture_comment_post_redirect_to_reload_parent_frame( $url ) {
613 if ( ! isset( $_GET['for'] ) || 'jetpack' !== $_GET['for'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
614 return $url;
615 }
616 ?>
617 <!DOCTYPE html>
618 <html <?php language_attributes(); ?>>
619 <!--<![endif]-->
620 <head>
621 <meta charset="<?php bloginfo( 'charset' ); ?>" />
622 <title>
623 <?php
624 wp_kses_post(
625 printf(
626 /* translators: %s is replaced by an ellipsis */
627 __( 'Submitting Comment%s', 'jetpack' ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
628 '&hellip;'
629 )
630 );
631 ?>
632 </title>
633 <style type="text/css">
634 body {
635 display: table;
636 width: 100%;
637 height: 60%;
638 position: absolute;
639 top: 0;
640 left: 0;
641 overflow: hidden;
642 color: #333;
643 }
644
645 h1 {
646 text-align: center;
647 margin: 0;
648 padding: 0;
649 display: table-cell;
650 vertical-align: middle;
651 font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", sans-serif;
652 font-weight: normal;
653 }
654
655 .hidden {
656 opacity: 0;
657 }
658
659 h1 span {
660 -moz-transition-property: opacity;
661 -moz-transition-duration: 1s;
662 -moz-transition-timing-function: ease-in-out;
663
664 -webkit-transition-property: opacity;
665 -webkit-transition-duration: 1s;
666 -webbit-transition-timing-function: ease-in-out;
667
668 -o-transition-property: opacity;
669 -o-transition-duration: 1s;
670 -o-transition-timing-function: ease-in-out;
671
672 -ms-transition-property: opacity;
673 -ms-transition-duration: 1s;
674 -ms-transition-timing-function: ease-in-out;
675
676 transition-property: opacity;
677 transition-duration: 1s;
678 transition-timing-function: ease-in-out;
679 }
680 </style>
681 </head>
682 <body>
683 <h1>
684 <?php
685 wp_kses_post(
686 printf(
687 /* translators: %s is replaced by HTML markup to include an ellipsis */
688 __( 'Submitting Comment%s', 'jetpack' ), // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
689 '<span id="ellipsis" class="hidden">&hellip;</span>'
690 )
691 );
692 ?>
693 </h1>
694 <script type="text/javascript">
695 try {
696 window.parent.location = <?php echo wp_json_encode( $url ); ?>;
697 window.parent.location.reload(true);
698 } catch (e) {
699 window.location = <?php echo wp_json_encode( $url ); ?>;
700 window.location.reload(true);
701 }
702 ellipsis = document.getElementById('ellipsis');
703
704 function toggleEllipsis() {
705 ellipsis.className = ellipsis.className ? '' : 'hidden';
706 }
707
708 setInterval(toggleEllipsis, 1200);
709 </script>
710 </body>
711 </html>
712 <?php
713 exit;
714 }
715 }
716
717 Jetpack_Comments::init();
718