1<?php 2/** 3 * Comment API: Walker_Comment class 4 * 5 * @package WordPress 6 * @subpackage Comments 7 * @since 4.4.0 8 */ 9 10/** 11 * Core walker class used to create an HTML list of comments. 12 * 13 * @since 2.7.0 14 * 15 * @see Walker 16 */ 17class Walker_Comment extends Walker { 18 19 /** 20 * What the class handles. 21 * 22 * @since 2.7.0 23 * @var string 24 * 25 * @see Walker::$tree_type 26 */ 27 public $tree_type = 'comment'; 28 29 /** 30 * Database fields to use. 31 * 32 * @since 2.7.0 33 * @var array 34 * 35 * @see Walker::$db_fields 36 * @todo Decouple this 37 */ 38 public $db_fields = array( 39 'parent' => 'comment_parent', 40 'id' => 'comment_ID', 41 ); 42 43 /** 44 * Starts the list before the elements are added. 45 * 46 * @since 2.7.0 47 * 48 * @see Walker::start_lvl() 49 * @global int $comment_depth 50 * 51 * @param string $output Used to append additional content (passed by reference). 52 * @param int $depth Optional. Depth of the current comment. Default 0. 53 * @param array $args Optional. Uses 'style' argument for type of HTML list. Default empty array. 54 */ 55 public function start_lvl( &$output, $depth = 0, $args = array() ) { 56 $GLOBALS['comment_depth'] = $depth + 1; 57 58 switch ( $args['style'] ) { 59 case 'div': 60 break; 61 case 'ol': 62 $output .= '<ol class="children">' . "\n"; 63 break; 64 case 'ul': 65 default: 66 $output .= '<ul class="children">' . "\n"; 67 break; 68 } 69 } 70 71 /** 72 * Ends the list of items after the elements are added. 73 * 74 * @since 2.7.0 75 * 76 * @see Walker::end_lvl() 77 * @global int $comment_depth 78 * 79 * @param string $output Used to append additional content (passed by reference). 80 * @param int $depth Optional. Depth of the current comment. Default 0. 81 * @param array $args Optional. Will only append content if style argument value is 'ol' or 'ul'. 82 * Default empty array. 83 */ 84 public function end_lvl( &$output, $depth = 0, $args = array() ) { 85 $GLOBALS['comment_depth'] = $depth + 1; 86 87 switch ( $args['style'] ) { 88 case 'div': 89 break; 90 case 'ol': 91 $output .= "</ol><!-- .children -->\n"; 92 break; 93 case 'ul': 94 default: 95 $output .= "</ul><!-- .children -->\n"; 96 break; 97 } 98 } 99 100 /** 101 * Traverses elements to create list from elements. 102 * 103 * This function is designed to enhance Walker::display_element() to 104 * display children of higher nesting levels than selected inline on 105 * the highest depth level displayed. This prevents them being orphaned 106 * at the end of the comment list. 107 * 108 * Example: max_depth = 2, with 5 levels of nested content. 109 * 1 110 * 1.1 111 * 1.1.1 112 * 1.1.1.1 113 * 1.1.1.1.1 114 * 1.1.2 115 * 1.1.2.1 116 * 2 117 * 2.2 118 * 119 * @since 2.7.0 120 * 121 * @see Walker::display_element() 122 * @see wp_list_comments() 123 * 124 * @param WP_Comment $element Comment data object. 125 * @param array $children_elements List of elements to continue traversing. Passed by reference. 126 * @param int $max_depth Max depth to traverse. 127 * @param int $depth Depth of the current element. 128 * @param array $args An array of arguments. 129 * @param string $output Used to append additional content. Passed by reference. 130 */ 131 public function display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output ) { 132 if ( ! $element ) { 133 return; 134 } 135 136 $id_field = $this->db_fields['id']; 137 $id = $element->$id_field; 138 139 parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output ); 140 141 /* 142 * If at the max depth, and the current element still has children, loop over those 143 * and display them at this level. This is to prevent them being orphaned to the end 144 * of the list. 145 */ 146 if ( $max_depth <= $depth + 1 && isset( $children_elements[ $id ] ) ) { 147 foreach ( $children_elements[ $id ] as $child ) { 148 $this->display_element( $child, $children_elements, $max_depth, $depth, $args, $output ); 149 } 150 151 unset( $children_elements[ $id ] ); 152 } 153 154 } 155 156 /** 157 * Starts the element output. 158 * 159 * @since 2.7.0 160 * 161 * @see Walker::start_el() 162 * @see wp_list_comments() 163 * @global int $comment_depth 164 * @global WP_Comment $comment Global comment object. 165 * 166 * @param string $output Used to append additional content. Passed by reference. 167 * @param WP_Comment $comment Comment data object. 168 * @param int $depth Optional. Depth of the current comment in reference to parents. Default 0. 169 * @param array $args Optional. An array of arguments. Default empty array. 170 * @param int $id Optional. ID of the current comment. Default 0 (unused). 171 */ 172 public function start_el( &$output, $comment, $depth = 0, $args = array(), $id = 0 ) { 173 $depth++; 174 $GLOBALS['comment_depth'] = $depth; 175 $GLOBALS['comment'] = $comment; 176 177 if ( ! empty( $args['callback'] ) ) { 178 ob_start(); 179 call_user_func( $args['callback'], $comment, $args, $depth ); 180 $output .= ob_get_clean(); 181 return; 182 } 183 184 if ( 'comment' === $comment->comment_type ) { 185 add_filter( 'comment_text', array( $this, 'filter_comment_text' ), 40, 2 ); 186 } 187 188 if ( ( 'pingback' === $comment->comment_type || 'trackback' === $comment->comment_type ) && $args['short_ping'] ) { 189 ob_start(); 190 $this->ping( $comment, $depth, $args ); 191 $output .= ob_get_clean(); 192 } elseif ( 'html5' === $args['format'] ) { 193 ob_start(); 194 $this->html5_comment( $comment, $depth, $args ); 195 $output .= ob_get_clean(); 196 } else { 197 ob_start(); 198 $this->comment( $comment, $depth, $args ); 199 $output .= ob_get_clean(); 200 } 201 202 if ( 'comment' === $comment->comment_type ) { 203 remove_filter( 'comment_text', array( $this, 'filter_comment_text' ), 40 ); 204 } 205 } 206 207 /** 208 * Ends the element output, if needed. 209 * 210 * @since 2.7.0 211 * 212 * @see Walker::end_el() 213 * @see wp_list_comments() 214 * 215 * @param string $output Used to append additional content. Passed by reference. 216 * @param WP_Comment $comment The current comment object. Default current comment. 217 * @param int $depth Optional. Depth of the current comment. Default 0. 218 * @param array $args Optional. An array of arguments. Default empty array. 219 */ 220 public function end_el( &$output, $comment, $depth = 0, $args = array() ) { 221 if ( ! empty( $args['end-callback'] ) ) { 222 ob_start(); 223 call_user_func( $args['end-callback'], $comment, $args, $depth ); 224 $output .= ob_get_clean(); 225 return; 226 } 227 if ( 'div' === $args['style'] ) { 228 $output .= "</div><!-- #comment-## -->\n"; 229 } else { 230 $output .= "</li><!-- #comment-## -->\n"; 231 } 232 } 233 234 /** 235 * Outputs a pingback comment. 236 * 237 * @since 3.6.0 238 * 239 * @see wp_list_comments() 240 * 241 * @param WP_Comment $comment The comment object. 242 * @param int $depth Depth of the current comment. 243 * @param array $args An array of arguments. 244 */ 245 protected function ping( $comment, $depth, $args ) { 246 $tag = ( 'div' === $args['style'] ) ? 'div' : 'li'; 247 ?> 248 <<?php echo $tag; ?> id="comment-<?php comment_ID(); ?>" <?php comment_class( '', $comment ); ?>> 249 <div class="comment-body"> 250 <?php _e( 'Pingback:' ); ?> <?php comment_author_link( $comment ); ?> <?php edit_comment_link( __( 'Edit' ), '<span class="edit-link">', '</span>' ); ?> 251 </div> 252 <?php 253 } 254 255 /** 256 * Filters the comment text. 257 * 258 * Removes links from the pending comment's text if the commenter did not consent 259 * to the comment cookies. 260 * 261 * @since 5.4.2 262 * 263 * @param string $comment_text Text of the current comment. 264 * @param WP_Comment|null $comment The comment object. Null if not found. 265 * @return string Filtered text of the current comment. 266 */ 267 public function filter_comment_text( $comment_text, $comment ) { 268 $commenter = wp_get_current_commenter(); 269 $show_pending_links = ! empty( $commenter['comment_author'] ); 270 271 if ( $comment && '0' == $comment->comment_approved && ! $show_pending_links ) { 272 $comment_text = wp_kses( $comment_text, array() ); 273 } 274 275 return $comment_text; 276 } 277 278 /** 279 * Outputs a single comment. 280 * 281 * @since 3.6.0 282 * 283 * @see wp_list_comments() 284 * 285 * @param WP_Comment $comment Comment to display. 286 * @param int $depth Depth of the current comment. 287 * @param array $args An array of arguments. 288 */ 289 protected function comment( $comment, $depth, $args ) { 290 if ( 'div' === $args['style'] ) { 291 $tag = 'div'; 292 $add_below = 'comment'; 293 } else { 294 $tag = 'li'; 295 $add_below = 'div-comment'; 296 } 297 298 $commenter = wp_get_current_commenter(); 299 $show_pending_links = isset( $commenter['comment_author'] ) && $commenter['comment_author']; 300 301 if ( $commenter['comment_author_email'] ) { 302 $moderation_note = __( 'Your comment is awaiting moderation.' ); 303 } else { 304 $moderation_note = __( 'Your comment is awaiting moderation. This is a preview; your comment will be visible after it has been approved.' ); 305 } 306 ?> 307 <<?php echo $tag; ?> <?php comment_class( $this->has_children ? 'parent' : '', $comment ); ?> id="comment-<?php comment_ID(); ?>"> 308 <?php if ( 'div' !== $args['style'] ) : ?> 309 <div id="div-comment-<?php comment_ID(); ?>" class="comment-body"> 310 <?php endif; ?> 311 <div class="comment-author vcard"> 312 <?php 313 if ( 0 != $args['avatar_size'] ) { 314 echo get_avatar( $comment, $args['avatar_size'] ); 315 } 316 ?> 317 <?php 318 $comment_author = get_comment_author_link( $comment ); 319 320 if ( '0' == $comment->comment_approved && ! $show_pending_links ) { 321 $comment_author = get_comment_author( $comment ); 322 } 323 324 printf( 325 /* translators: %s: Comment author link. */ 326 __( '%s <span class="says">says:</span>' ), 327 sprintf( '<cite class="fn">%s</cite>', $comment_author ) 328 ); 329 ?> 330 </div> 331 <?php if ( '0' == $comment->comment_approved ) : ?> 332 <em class="comment-awaiting-moderation"><?php echo $moderation_note; ?></em> 333 <br /> 334 <?php endif; ?> 335 336 <div class="comment-meta commentmetadata"> 337 <?php 338 printf( 339 '<a href="%s">%s</a>', 340 esc_url( get_comment_link( $comment, $args ) ), 341 sprintf( 342 /* translators: 1: Comment date, 2: Comment time. */ 343 __( '%1$s at %2$s' ), 344 get_comment_date( '', $comment ), 345 get_comment_time() 346 ) 347 ); 348 349 edit_comment_link( __( '(Edit)' ), ' ', '' ); 350 ?> 351 </div> 352 353 <?php 354 comment_text( 355 $comment, 356 array_merge( 357 $args, 358 array( 359 'add_below' => $add_below, 360 'depth' => $depth, 361 'max_depth' => $args['max_depth'], 362 ) 363 ) 364 ); 365 ?> 366 367 <?php 368 comment_reply_link( 369 array_merge( 370 $args, 371 array( 372 'add_below' => $add_below, 373 'depth' => $depth, 374 'max_depth' => $args['max_depth'], 375 'before' => '<div class="reply">', 376 'after' => '</div>', 377 ) 378 ) 379 ); 380 ?> 381 382 <?php if ( 'div' !== $args['style'] ) : ?> 383 </div> 384 <?php endif; ?> 385 <?php 386 } 387 388 /** 389 * Outputs a comment in the HTML5 format. 390 * 391 * @since 3.6.0 392 * 393 * @see wp_list_comments() 394 * 395 * @param WP_Comment $comment Comment to display. 396 * @param int $depth Depth of the current comment. 397 * @param array $args An array of arguments. 398 */ 399 protected function html5_comment( $comment, $depth, $args ) { 400 $tag = ( 'div' === $args['style'] ) ? 'div' : 'li'; 401 402 $commenter = wp_get_current_commenter(); 403 $show_pending_links = ! empty( $commenter['comment_author'] ); 404 405 if ( $commenter['comment_author_email'] ) { 406 $moderation_note = __( 'Your comment is awaiting moderation.' ); 407 } else { 408 $moderation_note = __( 'Your comment is awaiting moderation. This is a preview; your comment will be visible after it has been approved.' ); 409 } 410 ?> 411 <<?php echo $tag; ?> id="comment-<?php comment_ID(); ?>" <?php comment_class( $this->has_children ? 'parent' : '', $comment ); ?>> 412 <article id="div-comment-<?php comment_ID(); ?>" class="comment-body"> 413 <footer class="comment-meta"> 414 <div class="comment-author vcard"> 415 <?php 416 if ( 0 != $args['avatar_size'] ) { 417 echo get_avatar( $comment, $args['avatar_size'] ); 418 } 419 ?> 420 <?php 421 $comment_author = get_comment_author_link( $comment ); 422 423 if ( '0' == $comment->comment_approved && ! $show_pending_links ) { 424 $comment_author = get_comment_author( $comment ); 425 } 426 427 printf( 428 /* translators: %s: Comment author link. */ 429 __( '%s <span class="says">says:</span>' ), 430 sprintf( '<b class="fn">%s</b>', $comment_author ) 431 ); 432 ?> 433 </div><!-- .comment-author --> 434 435 <div class="comment-metadata"> 436 <?php 437 printf( 438 '<a href="%s"><time datetime="%s">%s</time></a>', 439 esc_url( get_comment_link( $comment, $args ) ), 440 get_comment_time( 'c' ), 441 sprintf( 442 /* translators: 1: Comment date, 2: Comment time. */ 443 __( '%1$s at %2$s' ), 444 get_comment_date( '', $comment ), 445 get_comment_time() 446 ) 447 ); 448 449 edit_comment_link( __( 'Edit' ), ' <span class="edit-link">', '</span>' ); 450 ?> 451 </div><!-- .comment-metadata --> 452 453 <?php if ( '0' == $comment->comment_approved ) : ?> 454 <em class="comment-awaiting-moderation"><?php echo $moderation_note; ?></em> 455 <?php endif; ?> 456 </footer><!-- .comment-meta --> 457 458 <div class="comment-content"> 459 <?php comment_text(); ?> 460 </div><!-- .comment-content --> 461 462 <?php 463 if ( '1' == $comment->comment_approved || $show_pending_links ) { 464 comment_reply_link( 465 array_merge( 466 $args, 467 array( 468 'add_below' => 'div-comment', 469 'depth' => $depth, 470 'max_depth' => $args['max_depth'], 471 'before' => '<div class="reply">', 472 'after' => '</div>', 473 ) 474 ) 475 ); 476 } 477 ?> 478 </article><!-- .comment-body --> 479 <?php 480 } 481} 482