| 1 |
<?php |
| 2 |
/** |
| 3 |
* class-groups-comment-access.php |
| 4 |
* |
| 5 |
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com |
| 6 |
* |
| 7 |
* This code is released under the GNU General Public License. |
| 8 |
* See COPYRIGHT.txt and LICENSE.txt. |
| 9 |
* |
| 10 |
* This code is distributed in the hope that it will be useful, |
| 11 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 |
* GNU General Public License for more details. |
| 14 |
* |
| 15 |
* This header and all notices must be kept intact. |
| 16 |
* |
| 17 |
* @author Karim Rahimpur |
| 18 |
* @package groups |
| 19 |
* @since groups 2.2.0 |
| 20 |
*/ |
| 21 |
|
| 22 |
if ( !defined( 'ABSPATH' ) ) { |
| 23 |
exit; |
| 24 |
} |
| 25 |
|
| 26 |
// phpcs:disable PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching |
| 27 |
|
| 28 |
/** |
| 29 |
* Comment access restrictions. |
| 30 |
*/ |
| 31 |
class Groups_Comment_Access { |
| 32 |
|
| 33 |
/** |
| 34 |
* @var string cache group key |
| 35 |
*/ |
| 36 |
const CACHE_GROUP = 'groups'; |
| 37 |
|
| 38 |
/** |
| 39 |
* @var string comment counts key |
| 40 |
*/ |
| 41 |
const COMMENT_COUNTS = 'comment_counts'; |
| 42 |
|
| 43 |
/** |
| 44 |
* Adds filters. |
| 45 |
*/ |
| 46 |
public static function init() { |
| 47 |
add_filter( 'comments_array', array( __CLASS__, 'comments_array' ), 10, 2 ); |
| 48 |
add_filter( 'comment_feed_where', array( __CLASS__, 'comment_feed_where' ), 10, 2 ); |
| 49 |
add_filter( 'comments_clauses', array( __CLASS__, 'comments_clauses' ), 10, 2 ); |
| 50 |
// the comments_clauses filter is used in WP_Comment_Query::get_comment_ids() before the |
| 51 |
// comments are filtered with the_comments in WP_Comment_Query::get_comments() so we don't need to do this again |
| 52 |
// add_filter( 'the_comments', array( __CLASS__, 'the_comments' ), 10, 2 ); |
| 53 |
add_filter( 'wp_count_comments', array( __CLASS__, 'wp_count_comments' ), 999, 2 ); // see wp-includes/comment.php function wp_count_comments(...) |
| 54 |
add_filter( 'get_comments_number', array( __CLASS__, 'get_comments_number' ), 10, 2 ); |
| 55 |
} |
| 56 |
|
| 57 |
/** |
| 58 |
* Filter comments on the post if the user can't read the post. |
| 59 |
* |
| 60 |
* @param array $comments |
| 61 |
* @param int $post_id |
| 62 |
* |
| 63 |
* @return array |
| 64 |
*/ |
| 65 |
public static function comments_array( $comments, $post_id ) { |
| 66 |
|
| 67 |
if ( !apply_filters( 'groups_comment_access_comments_array_apply', true, $comments, $post_id ) ) { |
| 68 |
return $comments; |
| 69 |
} |
| 70 |
|
| 71 |
$result = array(); |
| 72 |
if ( Groups_Post_Access::user_can_read_post( $post_id ) ) { |
| 73 |
$result = $comments; |
| 74 |
} |
| 75 |
|
| 76 |
return $result; |
| 77 |
} |
| 78 |
|
| 79 |
/** |
| 80 |
* Remove comments on posts that the user cannot read. |
| 81 |
* |
| 82 |
* @param array $comments |
| 83 |
* @param WP_Comment_Query $comment_query |
| 84 |
* |
| 85 |
* @return array |
| 86 |
*/ |
| 87 |
public static function the_comments( $comments, $comment_query ) { |
| 88 |
|
| 89 |
if ( !apply_filters( 'groups_comment_access_the_comments_apply', true, $comments, $comment_query ) ) { |
| 90 |
return $comments; |
| 91 |
} |
| 92 |
|
| 93 |
$_comments = array(); |
| 94 |
foreach ( $comments as $comment ) { |
| 95 |
if ( isset( $comment->comment_post_ID ) ) { |
| 96 |
if ( Groups_Post_Access::user_can_read_post( $comment->comment_post_ID ) ) { |
| 97 |
$_comments[] = $comment; |
| 98 |
} |
| 99 |
} |
| 100 |
} |
| 101 |
|
| 102 |
return $_comments; |
| 103 |
} |
| 104 |
|
| 105 |
/** |
| 106 |
* Filter feed comments. |
| 107 |
* |
| 108 |
* @param string $where |
| 109 |
* @param WP_Query $query |
| 110 |
* |
| 111 |
* @return string |
| 112 |
*/ |
| 113 |
public static function comment_feed_where( $where, $query ) { |
| 114 |
|
| 115 |
if ( !apply_filters( 'groups_comment_access_comment_feed_where_apply', true, $where, $query ) ) { |
| 116 |
return $where; |
| 117 |
} |
| 118 |
|
| 119 |
if ( _groups_admin_override() ) { |
| 120 |
return $where; |
| 121 |
} |
| 122 |
|
| 123 |
if ( Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) ) { |
| 124 |
return $where; |
| 125 |
} |
| 126 |
|
| 127 |
$where = self::build_where( $where ); |
| 128 |
return $where; |
| 129 |
} |
| 130 |
|
| 131 |
/** |
| 132 |
* Filter the comments based on post read access restrictions. |
| 133 |
* |
| 134 |
* @param array $pieces |
| 135 |
* @param WP_Comment_Query $comment_query |
| 136 |
* |
| 137 |
* @return array |
| 138 |
*/ |
| 139 |
public static function comments_clauses( $pieces, $comment_query ) { |
| 140 |
|
| 141 |
if ( !apply_filters( 'groups_comment_access_comments_clauses_apply', true, $pieces, $comment_query ) ) { |
| 142 |
return $pieces; |
| 143 |
} |
| 144 |
|
| 145 |
if ( _groups_admin_override() ) { |
| 146 |
return $pieces; |
| 147 |
} |
| 148 |
|
| 149 |
if ( Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) ) { |
| 150 |
return $pieces; |
| 151 |
} |
| 152 |
|
| 153 |
$where = isset( $pieces['where'] ) ? $pieces['where'] : ''; |
| 154 |
$where = self::build_where( $where ); |
| 155 |
$pieces['where'] = $where; |
| 156 |
return $pieces; |
| 157 |
} |
| 158 |
|
| 159 |
/** |
| 160 |
* Adds conditions to $where to restrict comment access. |
| 161 |
* |
| 162 |
* @param string $where |
| 163 |
* |
| 164 |
* @return string |
| 165 |
*/ |
| 166 |
private static function build_where( $where ) { |
| 167 |
|
| 168 |
global $wpdb; |
| 169 |
|
| 170 |
$handles_post_types = Groups_Post_Access::get_handles_post_types(); |
| 171 |
$post_types = array(); |
| 172 |
foreach ( $handles_post_types as $post_type => $handles ) { |
| 173 |
if ( $handles ) { |
| 174 |
$post_types[] = $post_type; |
| 175 |
} |
| 176 |
} |
| 177 |
if ( count( $post_types ) == 0 ) { |
| 178 |
return $where; |
| 179 |
} |
| 180 |
$post_types_in = "'" . implode( "','", array_map( 'esc_sql', $post_types ) ) . "'"; |
| 181 |
|
| 182 |
// group_ids : all the groups that the user belongs to, including those that are inherited |
| 183 |
$user_id = get_current_user_id(); |
| 184 |
$group_ids = array(); |
| 185 |
if ( $user = new Groups_User( $user_id ) ) { |
| 186 |
$group_ids_deep = $user->get_group_ids_deep(); |
| 187 |
if ( is_array( $group_ids_deep ) ) { |
| 188 |
$group_ids = $group_ids_deep; |
| 189 |
} |
| 190 |
} |
| 191 |
|
| 192 |
if ( count( $group_ids ) > 0 ) { |
| 193 |
$group_ids = "'" . implode( "','", array_map( 'esc_sql', $group_ids ) ) . "'"; |
| 194 |
} else { |
| 195 |
$group_ids = '\'\''; |
| 196 |
} |
| 197 |
|
| 198 |
// only comments from posts that the user can read |
| 199 |
$where .= sprintf( |
| 200 |
" AND {$wpdb->comments}.comment_post_ID NOT IN ( " . |
| 201 |
"SELECT ID FROM $wpdb->posts WHERE " . |
| 202 |
"post_type IN (%s) AND " . |
| 203 |
"ID IN ( " . |
| 204 |
"SELECT post_id FROM $wpdb->postmeta pm WHERE " . |
| 205 |
"pm.meta_key = '%s' AND pm.meta_value NOT IN (%s) AND " . |
| 206 |
"post_id NOT IN ( SELECT post_id FROM $wpdb->postmeta pm WHERE pm.meta_key = '%s' AND pm.meta_value IN (%s) ) " . |
| 207 |
") " . |
| 208 |
") ", |
| 209 |
$post_types_in, |
| 210 |
esc_sql( Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ ), |
| 211 |
$group_ids, |
| 212 |
esc_sql( Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ ), |
| 213 |
$group_ids |
| 214 |
); |
| 215 |
|
| 216 |
return $where; |
| 217 |
} |
| 218 |
|
| 219 |
/** |
| 220 |
* Filters comment counts. |
| 221 |
* |
| 222 |
* @param array $count |
| 223 |
* @param int $post_id |
| 224 |
* |
| 225 |
* @return object comment counts as properties of the object |
| 226 |
*/ |
| 227 |
public static function wp_count_comments( $count, $post_id ) { |
| 228 |
|
| 229 |
if ( !apply_filters( 'groups_comment_access_wp_count_comments_apply', true, $count, $post_id ) ) { |
| 230 |
return $count; |
| 231 |
} |
| 232 |
|
| 233 |
if ( _groups_admin_override() ) { |
| 234 |
return $count; |
| 235 |
} |
| 236 |
|
| 237 |
if ( Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) ) { |
| 238 |
return $count; |
| 239 |
} |
| 240 |
|
| 241 |
$user_id = get_current_user_id(); |
| 242 |
$cached = Groups_Cache::get( self::COMMENT_COUNTS . '_' . $user_id . '_' . intval( $post_id ), self::CACHE_GROUP ); |
| 243 |
if ( $cached !== null ) { |
| 244 |
$count = $cached->get_value(); |
| 245 |
unset( $cached ); |
| 246 |
} else { |
| 247 |
$count = self::get_comment_count( $post_id ); |
| 248 |
Groups_Cache::set( self::COMMENT_COUNTS . '_' . $user_id . '_' . intval( $post_id ), $count, self::CACHE_GROUP ); |
| 249 |
} |
| 250 |
return $count; |
| 251 |
} |
| 252 |
|
| 253 |
/** |
| 254 |
* Filters the comments number of a post. |
| 255 |
* |
| 256 |
* @param int $count |
| 257 |
* @param int $post_id |
| 258 |
* |
| 259 |
* @return int number of comments (0 if there are none or the user can't read the post) |
| 260 |
*/ |
| 261 |
public static function get_comments_number( $count, $post_id ) { |
| 262 |
$num_comments = 0; |
| 263 |
if ( Groups_Post_Access::user_can_read_post( $post_id ) ) { |
| 264 |
$num_comments = $count; |
| 265 |
} |
| 266 |
return $num_comments; |
| 267 |
} |
| 268 |
|
| 269 |
/** |
| 270 |
* Adapated from get_comment_count() to user our filter. |
| 271 |
* |
| 272 |
* @param number $post_id |
| 273 |
* |
| 274 |
* @return object comment counts as properties of the returned object |
| 275 |
*/ |
| 276 |
private static function get_comment_count( $post_id = 0 ) { |
| 277 |
global $wpdb; |
| 278 |
|
| 279 |
$post_id = (int) $post_id; |
| 280 |
|
| 281 |
$where = ''; |
| 282 |
if ( $post_id > 0 ) { |
| 283 |
$where = $wpdb->prepare( "WHERE comment_post_ID = %d ", $post_id ); |
| 284 |
} else { |
| 285 |
$where = 'WHERE 1=1 '; |
| 286 |
} |
| 287 |
|
| 288 |
$where = self::build_where( $where ); |
| 289 |
|
| 290 |
$where = apply_filters( 'groups_comment_access_comment_count_where', $where, $post_id ); |
| 291 |
|
| 292 |
$totals = (array) $wpdb->get_results( |
| 293 |
"SELECT comment_approved, COUNT( * ) AS total " . |
| 294 |
"FROM {$wpdb->comments} " . |
| 295 |
"{$where} " . // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared |
| 296 |
"GROUP BY comment_approved ", |
| 297 |
ARRAY_A |
| 298 |
); |
| 299 |
|
| 300 |
$comment_count = array( |
| 301 |
'approved' => 0, |
| 302 |
'awaiting_moderation' => 0, |
| 303 |
'spam' => 0, |
| 304 |
'trash' => 0, |
| 305 |
'post-trashed' => 0, |
| 306 |
'total_comments' => 0, |
| 307 |
'all' => 0, |
| 308 |
); |
| 309 |
|
| 310 |
foreach ( $totals as $row ) { |
| 311 |
switch ( $row['comment_approved'] ) { |
| 312 |
case 'trash': |
| 313 |
$comment_count['trash'] = $row['total']; |
| 314 |
break; |
| 315 |
case 'post-trashed': |
| 316 |
$comment_count['post-trashed'] = $row['total']; |
| 317 |
break; |
| 318 |
case 'spam': |
| 319 |
$comment_count['spam'] = $row['total']; |
| 320 |
$comment_count['total_comments'] += $row['total']; |
| 321 |
break; |
| 322 |
case '1': |
| 323 |
$comment_count['approved'] = $row['total']; |
| 324 |
$comment_count['total_comments'] += $row['total']; |
| 325 |
$comment_count['all'] += $row['total']; |
| 326 |
break; |
| 327 |
case '0': |
| 328 |
$comment_count['awaiting_moderation'] = $row['total']; |
| 329 |
$comment_count['total_comments'] += $row['total']; |
| 330 |
$comment_count['all'] += $row['total']; |
| 331 |
break; |
| 332 |
default: |
| 333 |
break; |
| 334 |
} |
| 335 |
} |
| 336 |
$comment_count['moderated'] = $comment_count['awaiting_moderation']; |
| 337 |
// unset( $stats['awaiting_moderation'] ); |
| 338 |
return (object) $comment_count; |
| 339 |
} |
| 340 |
} |
| 341 |
Groups_Comment_Access::init(); |
| 342 |
|