2022-06-16 14:01:47 +02:00
< ? php //phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
/**
* Jetpack comments base file - where the code shared between WP . com Highlander and Jetpack Highlander is defined
*
* @ package automattic / jetpack
*/
2023-09-26 10:24:36 +02:00
use Automattic\Jetpack\Image_CDN\Image_CDN_Core ;
2019-11-15 23:26:29 +01:00
/**
* All the code shared between WP . com Highlander and Jetpack Highlander
*/
class Highlander_Comments_Base {
2022-06-16 14:01:47 +02:00
/**
* Constructor
*/
public function __construct () {
2019-11-15 23:26:29 +01:00
$this -> setup_globals ();
$this -> setup_actions ();
$this -> setup_filters ();
}
/**
* Set any global variables or class variables
2022-06-16 14:01:47 +02:00
*
2023-09-26 10:24:36 +02:00
* @ since 1.4
2019-11-15 23:26:29 +01:00
*/
protected function setup_globals () {}
/**
* Setup actions for methods in this class
2022-06-16 14:01:47 +02:00
*
2023-09-26 10:24:36 +02:00
* @ since 1.4
2019-11-15 23:26:29 +01:00
*/
protected function setup_actions () {
2022-06-16 14:01:47 +02:00
// Before a comment is posted.
2019-11-15 23:26:29 +01:00
add_action ( 'pre_comment_on_post' , array ( $this , 'allow_logged_out_user_to_comment_as_external' ) );
2022-06-16 14:01:47 +02:00
// After a comment is posted.
2019-11-15 23:26:29 +01:00
add_action ( 'comment_post' , array ( $this , 'set_comment_cookies' ) );
}
/**
* Setup filters for methods in this class
2022-06-16 14:01:47 +02:00
*
2023-09-26 10:24:36 +02:00
* @ since 1.4
2019-11-15 23:26:29 +01:00
*/
protected function setup_filters () {
add_filter ( 'comments_array' , array ( $this , 'comments_array' ) );
add_filter ( 'preprocess_comment' , array ( $this , 'allow_logged_in_user_to_comment_as_guest' ), 0 );
}
/**
* Is this a Highlander POST request ?
2023-09-26 10:24:36 +02:00
* Optionally restrict to one or more credentials slug ( facebook , ... )
2019-11-15 23:26:29 +01:00
*
2022-06-16 14:01:47 +02:00
* @ param mixed ... $args Comments credentials slugs .
2019-11-15 23:26:29 +01:00
* @ return false | string false if it ' s not a Highlander POST request . The matching credentials slug if it is .
*/
2022-06-16 14:01:47 +02:00
public function is_highlander_comment_post ( ... $args ) {
// phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce verification should happen in Jetpack_Comments::pre_comment_on_post(). Internal ref for details: p1645643468937519/1645189749.180299-slack-C02HQGKMFJ8
2019-11-15 23:26:29 +01:00
if ( empty ( $_POST [ 'hc_post_as' ] ) ) {
return false ;
}
2022-06-16 14:01:47 +02:00
$hc_post_as = wp_unslash ( $_POST [ 'hc_post_as' ] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized here by comparing against known values.
// phpcs:enable WordPress.Security.NonceVerification.Missing
2019-11-15 23:26:29 +01:00
if ( $args ) {
foreach ( $args as $id_source ) {
2022-06-16 14:01:47 +02:00
if ( $id_source === $hc_post_as ) {
2019-11-15 23:26:29 +01:00
return $id_source ;
}
}
return false ;
}
2022-06-16 14:01:47 +02:00
return is_string ( $hc_post_as ) && in_array ( $hc_post_as , $this -> id_sources , true ) ? $hc_post_as : false ;
2019-11-15 23:26:29 +01:00
}
/**
* Signs an array of scalars with the self - hosted blog ' s Jetpack Token
*
2022-06-16 14:01:47 +02:00
* If parameter values are not scalars a WP_Error is returned , otherwise a keyed hash value is returned using the HMAC method .
*
* @ param array $parameters Comment parameters .
* @ param string $key Key used for generating the HMAC variant of the message digest .
2019-11-15 23:26:29 +01:00
* @ return string HMAC
*/
2022-06-16 14:01:47 +02:00
public static function sign_remote_comment_parameters ( $parameters , $key ) {
2019-11-15 23:26:29 +01:00
unset (
2022-06-16 14:01:47 +02:00
$parameters [ 'sig' ], // Don't sign the signature.
$parameters [ 'replytocom' ] // This parameter is unsigned - it changes dynamically as the comment form moves from parent comment to parent comment.
2019-11-15 23:26:29 +01:00
);
ksort ( $parameters );
$signing = array ();
foreach ( $parameters as $k => $v ) {
if ( ! is_scalar ( $v ) ) {
return new WP_Error ( 'invalid_input' , __ ( 'Invalid request' , 'jetpack' ), array ( 'status' => 400 ) );
}
$signing [] = " { $k } = { $v } " ;
}
return hash_hmac ( 'sha1' , implode ( ':' , $signing ), $key );
}
2022-06-16 14:01:47 +02:00
/**
* Adds comment author email and whether the comment is approved to the comments array
2019-11-15 23:26:29 +01:00
*
2022-06-16 14:01:47 +02:00
* After commenting as a guest while logged in , the user needs to see both :
2019-11-15 23:26:29 +01:00
* ( user_id = blah AND comment_approved = 0 )
2022-06-16 14:01:47 +02:00
* and ( comment_author_email = blah AND comment_approved = 0 )
* Core only does the first since the user is logged in , so this adds the second to the comments array .
2019-11-15 23:26:29 +01:00
*
2022-06-16 14:01:47 +02:00
* @ param array $comments All comment data .
* @ return array A modified array of comment data .
*/
public function comments_array ( $comments ) {
2019-11-15 23:26:29 +01:00
global $wpdb , $post ;
$commenter = $this -> get_current_commenter ();
if ( ! $commenter [ 'user_id' ] ) {
return $comments ;
}
if ( ! $commenter [ 'comment_author' ] ) {
return $comments ;
}
$in_moderation_comments = $wpdb -> get_results (
$wpdb -> prepare (
" SELECT * FROM ` $wpdb->comments ` WHERE `comment_post_ID` = %d AND `user_id` = 0 AND `comment_author` = %s AND `comment_author_email` = %s AND `comment_approved` = '0' ORDER BY `comment_date_gmt` /* Highlander_Comments_Base::comments_array() */ " ,
$post -> ID ,
wp_specialchars_decode ( $commenter [ 'comment_author' ], ENT_QUOTES ),
$commenter [ 'comment_author_email' ]
)
);
if ( ! $in_moderation_comments ) {
return $comments ;
}
// @todo ZOMG this is a bad idea
$comments = array_merge ( $comments , $in_moderation_comments );
usort ( $comments , array ( $this , 'sort_comments_by_comment_date_gmt' ) );
return $comments ;
}
/**
* Comment sort comparator : comment_date_gmt
*
2023-09-26 10:24:36 +02:00
* @ since 1.4
2022-06-16 14:01:47 +02:00
* @ param object $a The first comment to compare dates with .
* @ param object $b The second comment to compare dates with .
2019-11-15 23:26:29 +01:00
* @ return int
*/
public function sort_comments_by_comment_date_gmt ( $a , $b ) {
2023-12-07 09:44:11 +01:00
return $a -> comment_date_gmt <=> $b -> comment_date_gmt ;
2019-11-15 23:26:29 +01:00
}
/**
* Get the current commenter ' s information from their cookie
*
2023-09-26 10:24:36 +02:00
* @ since 1.4
2019-11-15 23:26:29 +01:00
* @ return array Commenters information from cookie
*/
protected function get_current_commenter () {
2022-06-16 14:01:47 +02:00
// Defaults.
2019-11-15 23:26:29 +01:00
$user_id = 0 ;
$comment_author = '' ;
$comment_author_email = '' ;
$comment_author_url = '' ;
if ( isset ( $_COOKIE [ 'comment_author_' . COOKIEHASH ] ) ) {
2022-06-16 14:01:47 +02:00
$comment_author = sanitize_text_field ( wp_unslash ( $_COOKIE [ 'comment_author_' . COOKIEHASH ] ) );
2019-11-15 23:26:29 +01:00
}
if ( isset ( $_COOKIE [ 'comment_author_email_' . COOKIEHASH ] ) ) {
2022-06-16 14:01:47 +02:00
$comment_author_email = sanitize_email ( wp_unslash ( $_COOKIE [ 'comment_author_email_' . COOKIEHASH ] ) );
2019-11-15 23:26:29 +01:00
}
if ( isset ( $_COOKIE [ 'comment_author_url_' . COOKIEHASH ] ) ) {
2022-06-16 14:01:47 +02:00
$comment_author_url = esc_url_raw ( wp_unslash ( $_COOKIE [ 'comment_author_url_' . COOKIEHASH ] ) );
2019-11-15 23:26:29 +01:00
}
if ( is_user_logged_in () ) {
$user = wp_get_current_user ();
$user_id = $user -> ID ;
}
return compact ( 'comment_author' , 'comment_author_email' , 'comment_author_url' , 'user_id' );
}
/**
2023-12-07 09:44:11 +01:00
* Allows a logged out user to leave a comment as a facebook / wp . com credentialed user .
2019-11-15 23:26:29 +01:00
* Overrides WordPress ' core comment_registration option to treat these commenters as " registered " ( verified ) users .
*
2023-09-26 10:24:36 +02:00
* @ since 1.4
2019-11-15 23:26:29 +01:00
*/
2022-06-16 14:01:47 +02:00
public function allow_logged_out_user_to_comment_as_external () {
2023-12-07 09:44:11 +01:00
// phpcs:ignore WordPress.WP.CapitalPDangit.MisspelledInText
if ( ! $this -> is_highlander_comment_post ( 'facebook' , 'wordpress' ) ) {
2019-11-15 23:26:29 +01:00
return ;
}
add_filter ( 'pre_option_comment_registration' , '__return_zero' );
2021-08-17 08:33:07 +02:00
add_filter ( 'pre_option_require_name_email' , '__return_zero' );
2019-11-15 23:26:29 +01:00
}
/**
2023-09-26 10:24:36 +02:00
* Allow a logged in user to post as a guest , or FB credentialed request .
2019-11-15 23:26:29 +01:00
* Bypasses WordPress ' core overrides that force a logged in user to comment as that user .
* Respects comment_registration option .
*
2023-09-26 10:24:36 +02:00
* @ since 1.4
2022-06-16 14:01:47 +02:00
* @ param array $comment_data All data for a specific comment .
* @ return array Modified comment data , or an error if the required fields or a valid email address are not entered .
2019-11-15 23:26:29 +01:00
*/
2022-06-16 14:01:47 +02:00
public function allow_logged_in_user_to_comment_as_guest ( $comment_data ) {
// Bail if user registration is allowed.
2019-11-15 23:26:29 +01:00
if ( get_option ( 'comment_registration' ) ) {
return $comment_data ;
}
2022-06-16 14:01:47 +02:00
// Bail if user is not logged in or not a post request.
if ( ! isset ( $_SERVER [ 'REQUEST_METHOD' ] ) || 'POST' !== strtoupper ( $_SERVER [ 'REQUEST_METHOD' ] ) || ! is_user_logged_in () ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- simple comparison
2019-11-15 23:26:29 +01:00
return $comment_data ;
}
2022-06-16 14:01:47 +02:00
// Bail if this is not a guest or external service credentialed request.
2023-09-26 10:24:36 +02:00
if ( ! $this -> is_highlander_comment_post ( 'guest' , 'facebook' ) ) {
2019-11-15 23:26:29 +01:00
return $comment_data ;
}
$user = wp_get_current_user ();
foreach ( array (
'comment_author' => 'display_name' ,
'comment_author_email' => 'user_email' ,
'comment_author_url' => 'user_url' ,
) as $comment_field => $user_field ) {
2022-06-16 14:01:47 +02:00
if ( addslashes ( $user -> $user_field ) !== $comment_data [ $comment_field ] ) {
return $comment_data ; // some other plugin already did something funky.
2019-11-15 23:26:29 +01:00
}
}
2022-06-16 14:01:47 +02:00
// phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce verification should happen in Jetpack_Comments::pre_comment_on_post()
// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitization too
2019-11-15 23:26:29 +01:00
if ( get_option ( 'require_name_email' ) ) {
2022-06-16 14:01:47 +02:00
if ( isset ( $_POST [ 'email' ] ) && 6 > strlen ( wp_unslash ( $_POST [ 'email' ] ) ) || empty ( $_POST [ 'author' ] ) ) {
wp_die ( esc_html__ ( 'Error: please fill the required fields (name, email).' , 'jetpack' ), 400 );
} elseif ( ! isset ( $_POST [ 'email' ] ) || ! is_email ( wp_unslash ( $_POST [ 'email' ] ) ) ) {
wp_die ( esc_html__ ( 'Error: please enter a valid email address.' , 'jetpack' ), 400 );
2019-11-15 23:26:29 +01:00
}
}
$author_change = false ;
foreach ( array (
'comment_author' => 'author' ,
'comment_author_email' => 'email' ,
'comment_author_url' => 'url' ,
) as $comment_field => $post_field ) {
2022-06-16 14:01:47 +02:00
if ( ( ! isset ( $_POST [ $post_field ] ) || $comment_data [ $comment_field ] !== $_POST [ $post_field ] ) && 'url' !== $post_field ) {
2019-11-15 23:26:29 +01:00
$author_change = true ;
}
2022-06-16 14:01:47 +02:00
$comment_data [ $comment_field ] = isset ( $_POST [ $post_field ] ) ? wp_unslash ( $_POST [ $post_field ] ) : null ;
2019-11-15 23:26:29 +01:00
}
2022-06-16 14:01:47 +02:00
// phpcs:enable WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
2019-11-15 23:26:29 +01:00
2022-06-16 14:01:47 +02:00
// Mark as guest comment if name or email were changed.
2019-11-15 23:26:29 +01:00
if ( $author_change ) {
2022-06-16 14:01:47 +02:00
$comment_data [ 'user_ID' ] = 0 ;
$comment_data [ 'user_id' ] = $comment_data [ 'user_ID' ];
2019-11-15 23:26:29 +01:00
}
return $comment_data ;
}
/**
* Set the comment cookies or bail if comment is invalid
*
2023-09-26 10:24:36 +02:00
* @ since 1.4
2022-06-16 14:01:47 +02:00
* @ param int $comment_id The comment ID .
2019-11-15 23:26:29 +01:00
*/
public function set_comment_cookies ( $comment_id ) {
2022-06-16 14:01:47 +02:00
// Get comment and bail if it's invalid somehow.
2019-11-15 23:26:29 +01:00
$comment = get_comment ( $comment_id );
if ( empty ( $comment ) || is_wp_error ( $comment ) ) {
return ;
}
$id_source = $this -> is_highlander_comment_post ();
if ( empty ( $id_source ) ) {
return ;
}
2022-06-16 14:01:47 +02:00
// Set comment author cookies.
2025-02-28 08:42:11 +01:00
// We don't set the cookies if they are logged in with WordPress.com because they already have a cookie set.
2019-11-15 23:26:29 +01:00
// phpcs:ignore WordPress.WP.CapitalPDangit
2025-02-28 08:42:11 +01:00
if ( 'wordpress' !== $id_source ) {
// phpcs:disable WordPress.Security.NonceVerification -- Nonce verification should happen in Jetpack_Comments::pre_comment_on_post().
$is_consenting_to_cookies = ( isset ( $_POST [ 'wp-comment-cookies-consent' ] ) );
$cookie_options = array (
'expires' => time () + apply_filters ( 'comment_cookie_lifetime' , YEAR_IN_SECONDS ),
'path' => COOKIEPATH ,
'domain' => COOKIE_DOMAIN ,
'secure' => is_ssl (),
'httponly' => true ,
);
// If there is no consent, remove any cookies that may have been set.
if ( ( 'guest' === $id_source ) && ! $is_consenting_to_cookies ) {
$cookie_options [ 'expires' ] = time () - YEAR_IN_SECONDS ;
}
// Set samesite to None if the request is from Jetpack iframe.
// This is needed because it is considered third party.
if ( isset ( $_REQUEST [ 'for' ] ) && 'jetpack' === $_REQUEST [ 'for' ] ) {
$cookie_options [ 'samesite' ] = 'None' ;
}
// phpcs:enable WordPress.Security.NonceVerification
// phpcs:disable Jetpack.Functions.SetCookie.MissingTrueHTTPOnly
isset ( $comment -> comment_author ) ? setcookie ( 'comment_author_' . COOKIEHASH , $comment -> comment_author , $cookie_options ) : null ;
isset ( $comment -> comment_author_email ) ? setcookie ( 'comment_author_email_' . COOKIEHASH , $comment -> comment_author_email , $cookie_options ) : null ;
isset ( $comment -> comment_author_url ) ? setcookie ( 'comment_author_url_' . COOKIEHASH , esc_url ( $comment -> comment_author_url ), $cookie_options ) : null ;
// phpcs:enable Jetpack.Functions.SetCookie.MissingTrueHTTPOnly
2019-11-15 23:26:29 +01:00
}
}
/**
* Get an avatar from Photon
*
2023-09-26 10:24:36 +02:00
* @ since 1.4
2022-06-16 14:01:47 +02:00
* @ param string $url The avatar URL .
* @ param int $size The avatar size .
2019-11-15 23:26:29 +01:00
* @ return string
*/
protected function photon_avatar ( $url , $size ) {
$size = ( int ) $size ;
2023-09-26 10:24:36 +02:00
return Image_CDN_Core :: cdn_url ( $url , array ( 'resize' => " $size , $size " ) );
2019-11-15 23:26:29 +01:00
}
}