angelovcom.net

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

class-wp-media-list-table.php (23803B)


      1 <?php
      2 /**
      3  * List Table API: WP_Media_List_Table class
      4  *
      5  * @package WordPress
      6  * @subpackage Administration
      7  * @since 3.1.0
      8  */
      9 
     10 /**
     11  * Core class used to implement displaying media items in a list table.
     12  *
     13  * @since 3.1.0
     14  * @access private
     15  *
     16  * @see WP_List_Table
     17  */
     18 class WP_Media_List_Table extends WP_List_Table {
     19 	/**
     20 	 * Holds the number of pending comments for each post.
     21 	 *
     22 	 * @since 4.4.0
     23 	 * @var array
     24 	 */
     25 	protected $comment_pending_count = array();
     26 
     27 	private $detached;
     28 
     29 	private $is_trash;
     30 
     31 	/**
     32 	 * Constructor.
     33 	 *
     34 	 * @since 3.1.0
     35 	 *
     36 	 * @see WP_List_Table::__construct() for more information on default arguments.
     37 	 *
     38 	 * @param array $args An associative array of arguments.
     39 	 */
     40 	public function __construct( $args = array() ) {
     41 		$this->detached = ( isset( $_REQUEST['attachment-filter'] ) && 'detached' === $_REQUEST['attachment-filter'] );
     42 
     43 		$this->modes = array(
     44 			'list' => __( 'List view' ),
     45 			'grid' => __( 'Grid view' ),
     46 		);
     47 
     48 		parent::__construct(
     49 			array(
     50 				'plural' => 'media',
     51 				'screen' => isset( $args['screen'] ) ? $args['screen'] : null,
     52 			)
     53 		);
     54 	}
     55 
     56 	/**
     57 	 * @return bool
     58 	 */
     59 	public function ajax_user_can() {
     60 		return current_user_can( 'upload_files' );
     61 	}
     62 
     63 	/**
     64 	 * @global string   $mode                  List table view mode.
     65 	 * @global WP_Query $wp_query              WordPress Query object.
     66 	 * @global array    $post_mime_types
     67 	 * @global array    $avail_post_mime_types
     68 	 */
     69 	public function prepare_items() {
     70 		global $mode, $wp_query, $post_mime_types, $avail_post_mime_types;
     71 
     72 		$mode = empty( $_REQUEST['mode'] ) ? 'list' : $_REQUEST['mode'];
     73 
     74 		/*
     75 		 * Exclude attachments scheduled for deletion in the next two hours
     76 		 * if they are for zip packages for interrupted or failed updates.
     77 		 * See File_Upload_Upgrader class.
     78 		 */
     79 		$not_in = array();
     80 
     81 		foreach ( _get_cron_array() as $cron ) {
     82 			if ( isset( $cron['upgrader_scheduled_cleanup'] ) ) {
     83 				$details = reset( $cron['upgrader_scheduled_cleanup'] );
     84 
     85 				if ( ! empty( $details['args'][0] ) ) {
     86 					$not_in[] = (int) $details['args'][0];
     87 				}
     88 			}
     89 		}
     90 
     91 		if ( ! empty( $_REQUEST['post__not_in'] ) && is_array( $_REQUEST['post__not_in'] ) ) {
     92 			$not_in = array_merge( array_values( $_REQUEST['post__not_in'] ), $not_in );
     93 		}
     94 
     95 		if ( ! empty( $not_in ) ) {
     96 			$_REQUEST['post__not_in'] = $not_in;
     97 		}
     98 
     99 		list( $post_mime_types, $avail_post_mime_types ) = wp_edit_attachments_query( $_REQUEST );
    100 
    101 		$this->is_trash = isset( $_REQUEST['attachment-filter'] ) && 'trash' === $_REQUEST['attachment-filter'];
    102 
    103 		$this->set_pagination_args(
    104 			array(
    105 				'total_items' => $wp_query->found_posts,
    106 				'total_pages' => $wp_query->max_num_pages,
    107 				'per_page'    => $wp_query->query_vars['posts_per_page'],
    108 			)
    109 		);
    110 	}
    111 
    112 	/**
    113 	 * @global array $post_mime_types
    114 	 * @global array $avail_post_mime_types
    115 	 * @return array
    116 	 */
    117 	protected function get_views() {
    118 		global $post_mime_types, $avail_post_mime_types;
    119 
    120 		$type_links = array();
    121 
    122 		$filter = empty( $_GET['attachment-filter'] ) ? '' : $_GET['attachment-filter'];
    123 
    124 		$type_links['all'] = sprintf(
    125 			'<option value=""%s>%s</option>',
    126 			selected( $filter, true, false ),
    127 			__( 'All media items' )
    128 		);
    129 
    130 		foreach ( $post_mime_types as $mime_type => $label ) {
    131 			if ( ! wp_match_mime_types( $mime_type, $avail_post_mime_types ) ) {
    132 				continue;
    133 			}
    134 
    135 			$selected = selected(
    136 				$filter && 0 === strpos( $filter, 'post_mime_type:' ) &&
    137 					wp_match_mime_types( $mime_type, str_replace( 'post_mime_type:', '', $filter ) ),
    138 				true,
    139 				false
    140 			);
    141 
    142 			$type_links[ $mime_type ] = sprintf(
    143 				'<option value="post_mime_type:%s"%s>%s</option>',
    144 				esc_attr( $mime_type ),
    145 				$selected,
    146 				$label[0]
    147 			);
    148 		}
    149 
    150 		$type_links['detached'] = '<option value="detached"' . ( $this->detached ? ' selected="selected"' : '' ) . '>' . __( 'Unattached' ) . '</option>';
    151 
    152 		$type_links['mine'] = sprintf(
    153 			'<option value="mine"%s>%s</option>',
    154 			selected( 'mine' === $filter, true, false ),
    155 			_x( 'Mine', 'media items' )
    156 		);
    157 
    158 		if ( $this->is_trash || ( defined( 'MEDIA_TRASH' ) && MEDIA_TRASH ) ) {
    159 			$type_links['trash'] = sprintf(
    160 				'<option value="trash"%s>%s</option>',
    161 				selected( 'trash' === $filter, true, false ),
    162 				_x( 'Trash', 'attachment filter' )
    163 			);
    164 		}
    165 
    166 		return $type_links;
    167 	}
    168 
    169 	/**
    170 	 * @return array
    171 	 */
    172 	protected function get_bulk_actions() {
    173 		$actions = array();
    174 
    175 		if ( MEDIA_TRASH ) {
    176 			if ( $this->is_trash ) {
    177 				$actions['untrash'] = __( 'Restore' );
    178 				$actions['delete']  = __( 'Delete permanently' );
    179 			} else {
    180 				$actions['trash'] = __( 'Move to Trash' );
    181 			}
    182 		} else {
    183 			$actions['delete'] = __( 'Delete permanently' );
    184 		}
    185 
    186 		if ( $this->detached ) {
    187 			$actions['attach'] = __( 'Attach' );
    188 		}
    189 
    190 		return $actions;
    191 	}
    192 
    193 	/**
    194 	 * @param string $which
    195 	 */
    196 	protected function extra_tablenav( $which ) {
    197 		if ( 'bar' !== $which ) {
    198 			return;
    199 		}
    200 		?>
    201 		<div class="actions">
    202 			<?php
    203 			if ( ! $this->is_trash ) {
    204 				$this->months_dropdown( 'attachment' );
    205 			}
    206 
    207 			/** This action is documented in wp-admin/includes/class-wp-posts-list-table.php */
    208 			do_action( 'restrict_manage_posts', $this->screen->post_type, $which );
    209 
    210 			submit_button( __( 'Filter' ), '', 'filter_action', false, array( 'id' => 'post-query-submit' ) );
    211 
    212 			if ( $this->is_trash && $this->has_items()
    213 				&& current_user_can( 'edit_others_posts' )
    214 			) {
    215 				submit_button( __( 'Empty Trash' ), 'apply', 'delete_all', false );
    216 			}
    217 			?>
    218 		</div>
    219 		<?php
    220 	}
    221 
    222 	/**
    223 	 * @return string
    224 	 */
    225 	public function current_action() {
    226 		if ( isset( $_REQUEST['found_post_id'] ) && isset( $_REQUEST['media'] ) ) {
    227 			return 'attach';
    228 		}
    229 
    230 		if ( isset( $_REQUEST['parent_post_id'] ) && isset( $_REQUEST['media'] ) ) {
    231 			return 'detach';
    232 		}
    233 
    234 		if ( isset( $_REQUEST['delete_all'] ) || isset( $_REQUEST['delete_all2'] ) ) {
    235 			return 'delete_all';
    236 		}
    237 
    238 		return parent::current_action();
    239 	}
    240 
    241 	/**
    242 	 * @return bool
    243 	 */
    244 	public function has_items() {
    245 		return have_posts();
    246 	}
    247 
    248 	/**
    249 	 */
    250 	public function no_items() {
    251 		if ( $this->is_trash ) {
    252 			_e( 'No media files found in Trash.' );
    253 		} else {
    254 			_e( 'No media files found.' );
    255 		}
    256 	}
    257 
    258 	/**
    259 	 * Override parent views so we can use the filter bar display.
    260 	 *
    261 	 * @global string $mode List table view mode.
    262 	 */
    263 	public function views() {
    264 		global $mode;
    265 
    266 		$views = $this->get_views();
    267 
    268 		$this->screen->render_screen_reader_content( 'heading_views' );
    269 		?>
    270 		<div class="wp-filter">
    271 			<div class="filter-items">
    272 				<?php $this->view_switcher( $mode ); ?>
    273 
    274 				<label for="attachment-filter" class="screen-reader-text"><?php _e( 'Filter by type' ); ?></label>
    275 				<select class="attachment-filters" name="attachment-filter" id="attachment-filter">
    276 					<?php
    277 					if ( ! empty( $views ) ) {
    278 						foreach ( $views as $class => $view ) {
    279 							echo "\t$view\n";
    280 						}
    281 					}
    282 					?>
    283 				</select>
    284 
    285 				<?php
    286 				$this->extra_tablenav( 'bar' );
    287 
    288 				/** This filter is documented in wp-admin/inclues/class-wp-list-table.php */
    289 				$views = apply_filters( "views_{$this->screen->id}", array() );
    290 
    291 				// Back compat for pre-4.0 view links.
    292 				if ( ! empty( $views ) ) {
    293 					echo '<ul class="filter-links">';
    294 					foreach ( $views as $class => $view ) {
    295 						echo "<li class='$class'>$view</li>";
    296 					}
    297 					echo '</ul>';
    298 				}
    299 				?>
    300 			</div>
    301 
    302 			<div class="search-form">
    303 				<label for="media-search-input" class="media-search-input-label"><?php esc_html_e( 'Search' ); ?></label>
    304 				<input type="search" id="media-search-input" class="search" name="s" value="<?php _admin_search_query(); ?>">
    305 			</div>
    306 		</div>
    307 		<?php
    308 	}
    309 
    310 	/**
    311 	 * @return array
    312 	 */
    313 	public function get_columns() {
    314 		$posts_columns       = array();
    315 		$posts_columns['cb'] = '<input type="checkbox" />';
    316 		/* translators: Column name. */
    317 		$posts_columns['title']  = _x( 'File', 'column name' );
    318 		$posts_columns['author'] = __( 'Author' );
    319 
    320 		$taxonomies = get_taxonomies_for_attachments( 'objects' );
    321 		$taxonomies = wp_filter_object_list( $taxonomies, array( 'show_admin_column' => true ), 'and', 'name' );
    322 
    323 		/**
    324 		 * Filters the taxonomy columns for attachments in the Media list table.
    325 		 *
    326 		 * @since 3.5.0
    327 		 *
    328 		 * @param string[] $taxonomies An array of registered taxonomy names to show for attachments.
    329 		 * @param string   $post_type  The post type. Default 'attachment'.
    330 		 */
    331 		$taxonomies = apply_filters( 'manage_taxonomies_for_attachment_columns', $taxonomies, 'attachment' );
    332 		$taxonomies = array_filter( $taxonomies, 'taxonomy_exists' );
    333 
    334 		foreach ( $taxonomies as $taxonomy ) {
    335 			if ( 'category' === $taxonomy ) {
    336 				$column_key = 'categories';
    337 			} elseif ( 'post_tag' === $taxonomy ) {
    338 				$column_key = 'tags';
    339 			} else {
    340 				$column_key = 'taxonomy-' . $taxonomy;
    341 			}
    342 
    343 			$posts_columns[ $column_key ] = get_taxonomy( $taxonomy )->labels->name;
    344 		}
    345 
    346 		/* translators: Column name. */
    347 		if ( ! $this->detached ) {
    348 			$posts_columns['parent'] = _x( 'Uploaded to', 'column name' );
    349 			if ( post_type_supports( 'attachment', 'comments' ) ) {
    350 				$posts_columns['comments'] = '<span class="vers comment-grey-bubble" title="' . esc_attr__( 'Comments' ) . '"><span class="screen-reader-text">' . __( 'Comments' ) . '</span></span>';
    351 			}
    352 		}
    353 
    354 		/* translators: Column name. */
    355 		$posts_columns['date'] = _x( 'Date', 'column name' );
    356 
    357 		/**
    358 		 * Filters the Media list table columns.
    359 		 *
    360 		 * @since 2.5.0
    361 		 *
    362 		 * @param string[] $posts_columns An array of columns displayed in the Media list table.
    363 		 * @param bool     $detached      Whether the list table contains media not attached
    364 		 *                                to any posts. Default true.
    365 		 */
    366 		return apply_filters( 'manage_media_columns', $posts_columns, $this->detached );
    367 	}
    368 
    369 	/**
    370 	 * @return array
    371 	 */
    372 	protected function get_sortable_columns() {
    373 		return array(
    374 			'title'    => 'title',
    375 			'author'   => 'author',
    376 			'parent'   => 'parent',
    377 			'comments' => 'comment_count',
    378 			'date'     => array( 'date', true ),
    379 		);
    380 	}
    381 
    382 	/**
    383 	 * Handles the checkbox column output.
    384 	 *
    385 	 * @since 4.3.0
    386 	 *
    387 	 * @param WP_Post $post The current WP_Post object.
    388 	 */
    389 	public function column_cb( $post ) {
    390 		if ( current_user_can( 'edit_post', $post->ID ) ) {
    391 			?>
    392 			<label class="screen-reader-text" for="cb-select-<?php echo $post->ID; ?>">
    393 				<?php
    394 				/* translators: %s: Attachment title. */
    395 				printf( __( 'Select %s' ), _draft_or_post_title() );
    396 				?>
    397 			</label>
    398 			<input type="checkbox" name="media[]" id="cb-select-<?php echo $post->ID; ?>" value="<?php echo $post->ID; ?>" />
    399 			<?php
    400 		}
    401 	}
    402 
    403 	/**
    404 	 * Handles the title column output.
    405 	 *
    406 	 * @since 4.3.0
    407 	 *
    408 	 * @param WP_Post $post The current WP_Post object.
    409 	 */
    410 	public function column_title( $post ) {
    411 		list( $mime ) = explode( '/', $post->post_mime_type );
    412 
    413 		$title      = _draft_or_post_title();
    414 		$thumb      = wp_get_attachment_image( $post->ID, array( 60, 60 ), true, array( 'alt' => '' ) );
    415 		$link_start = '';
    416 		$link_end   = '';
    417 
    418 		if ( current_user_can( 'edit_post', $post->ID ) && ! $this->is_trash ) {
    419 			$link_start = sprintf(
    420 				'<a href="%s" aria-label="%s">',
    421 				get_edit_post_link( $post->ID ),
    422 				/* translators: %s: Attachment title. */
    423 				esc_attr( sprintf( __( '&#8220;%s&#8221; (Edit)' ), $title ) )
    424 			);
    425 			$link_end = '</a>';
    426 		}
    427 
    428 		$class = $thumb ? ' class="has-media-icon"' : '';
    429 		?>
    430 		<strong<?php echo $class; ?>>
    431 			<?php
    432 			echo $link_start;
    433 
    434 			if ( $thumb ) :
    435 				?>
    436 				<span class="media-icon <?php echo sanitize_html_class( $mime . '-icon' ); ?>"><?php echo $thumb; ?></span>
    437 				<?php
    438 			endif;
    439 
    440 			echo $title . $link_end;
    441 
    442 			_media_states( $post );
    443 			?>
    444 		</strong>
    445 		<p class="filename">
    446 			<span class="screen-reader-text"><?php _e( 'File name:' ); ?> </span>
    447 			<?php
    448 			$file = get_attached_file( $post->ID );
    449 			echo esc_html( wp_basename( $file ) );
    450 			?>
    451 		</p>
    452 		<?php
    453 	}
    454 
    455 	/**
    456 	 * Handles the author column output.
    457 	 *
    458 	 * @since 4.3.0
    459 	 *
    460 	 * @param WP_Post $post The current WP_Post object.
    461 	 */
    462 	public function column_author( $post ) {
    463 		printf(
    464 			'<a href="%s">%s</a>',
    465 			esc_url( add_query_arg( array( 'author' => get_the_author_meta( 'ID' ) ), 'upload.php' ) ),
    466 			get_the_author()
    467 		);
    468 	}
    469 
    470 	/**
    471 	 * Handles the description column output.
    472 	 *
    473 	 * @since 4.3.0
    474 	 *
    475 	 * @param WP_Post $post The current WP_Post object.
    476 	 */
    477 	public function column_desc( $post ) {
    478 		echo has_excerpt() ? $post->post_excerpt : '';
    479 	}
    480 
    481 	/**
    482 	 * Handles the date column output.
    483 	 *
    484 	 * @since 4.3.0
    485 	 *
    486 	 * @param WP_Post $post The current WP_Post object.
    487 	 */
    488 	public function column_date( $post ) {
    489 		if ( '0000-00-00 00:00:00' === $post->post_date ) {
    490 			$h_time = __( 'Unpublished' );
    491 		} else {
    492 			$time      = get_post_timestamp( $post );
    493 			$time_diff = time() - $time;
    494 
    495 			if ( $time && $time_diff > 0 && $time_diff < DAY_IN_SECONDS ) {
    496 				/* translators: %s: Human-readable time difference. */
    497 				$h_time = sprintf( __( '%s ago' ), human_time_diff( $time ) );
    498 			} else {
    499 				$h_time = get_the_time( __( 'Y/m/d' ), $post );
    500 			}
    501 		}
    502 
    503 		echo $h_time;
    504 	}
    505 
    506 	/**
    507 	 * Handles the parent column output.
    508 	 *
    509 	 * @since 4.3.0
    510 	 *
    511 	 * @param WP_Post $post The current WP_Post object.
    512 	 */
    513 	public function column_parent( $post ) {
    514 		$user_can_edit = current_user_can( 'edit_post', $post->ID );
    515 
    516 		if ( $post->post_parent > 0 ) {
    517 			$parent = get_post( $post->post_parent );
    518 		} else {
    519 			$parent = false;
    520 		}
    521 
    522 		if ( $parent ) {
    523 			$title       = _draft_or_post_title( $post->post_parent );
    524 			$parent_type = get_post_type_object( $parent->post_type );
    525 
    526 			if ( $parent_type && $parent_type->show_ui && current_user_can( 'edit_post', $post->post_parent ) ) {
    527 				printf( '<strong><a href="%s">%s</a></strong>', get_edit_post_link( $post->post_parent ), $title );
    528 			} elseif ( $parent_type && current_user_can( 'read_post', $post->post_parent ) ) {
    529 				printf( '<strong>%s</strong>', $title );
    530 			} else {
    531 				_e( '(Private post)' );
    532 			}
    533 
    534 			if ( $user_can_edit ) :
    535 				$detach_url = add_query_arg(
    536 					array(
    537 						'parent_post_id' => $post->post_parent,
    538 						'media[]'        => $post->ID,
    539 						'_wpnonce'       => wp_create_nonce( 'bulk-' . $this->_args['plural'] ),
    540 					),
    541 					'upload.php'
    542 				);
    543 				printf(
    544 					'<br /><a href="%s" class="hide-if-no-js detach-from-parent" aria-label="%s">%s</a>',
    545 					$detach_url,
    546 					/* translators: %s: Title of the post the attachment is attached to. */
    547 					esc_attr( sprintf( __( 'Detach from &#8220;%s&#8221;' ), $title ) ),
    548 					__( 'Detach' )
    549 				);
    550 			endif;
    551 		} else {
    552 			_e( '(Unattached)' );
    553 			?>
    554 			<?php
    555 			if ( $user_can_edit ) {
    556 				$title = _draft_or_post_title( $post->post_parent );
    557 				printf(
    558 					'<br /><a href="#the-list" onclick="findPosts.open( \'media[]\', \'%s\' ); return false;" class="hide-if-no-js aria-button-if-js" aria-label="%s">%s</a>',
    559 					$post->ID,
    560 					/* translators: %s: Attachment title. */
    561 					esc_attr( sprintf( __( 'Attach &#8220;%s&#8221; to existing content' ), $title ) ),
    562 					__( 'Attach' )
    563 				);
    564 			}
    565 		}
    566 	}
    567 
    568 	/**
    569 	 * Handles the comments column output.
    570 	 *
    571 	 * @since 4.3.0
    572 	 *
    573 	 * @param WP_Post $post The current WP_Post object.
    574 	 */
    575 	public function column_comments( $post ) {
    576 		echo '<div class="post-com-count-wrapper">';
    577 
    578 		if ( isset( $this->comment_pending_count[ $post->ID ] ) ) {
    579 			$pending_comments = $this->comment_pending_count[ $post->ID ];
    580 		} else {
    581 			$pending_comments = get_pending_comments_num( $post->ID );
    582 		}
    583 
    584 		$this->comments_bubble( $post->ID, $pending_comments );
    585 
    586 		echo '</div>';
    587 	}
    588 
    589 	/**
    590 	 * Handles output for the default column.
    591 	 *
    592 	 * @since 4.3.0
    593 	 *
    594 	 * @param WP_Post $post        The current WP_Post object.
    595 	 * @param string  $column_name Current column name.
    596 	 */
    597 	public function column_default( $post, $column_name ) {
    598 		if ( 'categories' === $column_name ) {
    599 			$taxonomy = 'category';
    600 		} elseif ( 'tags' === $column_name ) {
    601 			$taxonomy = 'post_tag';
    602 		} elseif ( 0 === strpos( $column_name, 'taxonomy-' ) ) {
    603 			$taxonomy = substr( $column_name, 9 );
    604 		} else {
    605 			$taxonomy = false;
    606 		}
    607 
    608 		if ( $taxonomy ) {
    609 			$terms = get_the_terms( $post->ID, $taxonomy );
    610 
    611 			if ( is_array( $terms ) ) {
    612 				$out = array();
    613 				foreach ( $terms as $t ) {
    614 					$posts_in_term_qv             = array();
    615 					$posts_in_term_qv['taxonomy'] = $taxonomy;
    616 					$posts_in_term_qv['term']     = $t->slug;
    617 
    618 					$out[] = sprintf(
    619 						'<a href="%s">%s</a>',
    620 						esc_url( add_query_arg( $posts_in_term_qv, 'upload.php' ) ),
    621 						esc_html( sanitize_term_field( 'name', $t->name, $t->term_id, $taxonomy, 'display' ) )
    622 					);
    623 				}
    624 				/* translators: Used between list items, there is a space after the comma. */
    625 				echo implode( __( ', ' ), $out );
    626 			} else {
    627 				echo '<span aria-hidden="true">&#8212;</span><span class="screen-reader-text">' . get_taxonomy( $taxonomy )->labels->no_terms . '</span>';
    628 			}
    629 
    630 			return;
    631 		}
    632 
    633 		/**
    634 		 * Fires for each custom column in the Media list table.
    635 		 *
    636 		 * Custom columns are registered using the {@see 'manage_media_columns'} filter.
    637 		 *
    638 		 * @since 2.5.0
    639 		 *
    640 		 * @param string $column_name Name of the custom column.
    641 		 * @param int    $post_id     Attachment ID.
    642 		 */
    643 		do_action( 'manage_media_custom_column', $column_name, $post->ID );
    644 	}
    645 
    646 	/**
    647 	 * @global WP_Post $post Global post object.
    648 	 */
    649 	public function display_rows() {
    650 		global $post, $wp_query;
    651 
    652 		$post_ids = wp_list_pluck( $wp_query->posts, 'ID' );
    653 		reset( $wp_query->posts );
    654 
    655 		$this->comment_pending_count = get_pending_comments_num( $post_ids );
    656 
    657 		add_filter( 'the_title', 'esc_html' );
    658 
    659 		while ( have_posts() ) :
    660 			the_post();
    661 
    662 			if ( $this->is_trash && 'trash' !== $post->post_status
    663 				|| ! $this->is_trash && 'trash' === $post->post_status
    664 			) {
    665 				continue;
    666 			}
    667 
    668 			$post_owner = ( get_current_user_id() === (int) $post->post_author ) ? 'self' : 'other';
    669 			?>
    670 			<tr id="post-<?php echo $post->ID; ?>" class="<?php echo trim( ' author-' . $post_owner . ' status-' . $post->post_status ); ?>">
    671 				<?php $this->single_row_columns( $post ); ?>
    672 			</tr>
    673 			<?php
    674 		endwhile;
    675 	}
    676 
    677 	/**
    678 	 * Gets the name of the default primary column.
    679 	 *
    680 	 * @since 4.3.0
    681 	 *
    682 	 * @return string Name of the default primary column, in this case, 'title'.
    683 	 */
    684 	protected function get_default_primary_column_name() {
    685 		return 'title';
    686 	}
    687 
    688 	/**
    689 	 * @param WP_Post $post
    690 	 * @param string  $att_title
    691 	 * @return array
    692 	 */
    693 	private function _get_row_actions( $post, $att_title ) {
    694 		$actions = array();
    695 
    696 		if ( $this->detached ) {
    697 			if ( current_user_can( 'edit_post', $post->ID ) ) {
    698 				$actions['edit'] = sprintf(
    699 					'<a href="%s" aria-label="%s">%s</a>',
    700 					get_edit_post_link( $post->ID ),
    701 					/* translators: %s: Attachment title. */
    702 					esc_attr( sprintf( __( 'Edit &#8220;%s&#8221;' ), $att_title ) ),
    703 					__( 'Edit' )
    704 				);
    705 			}
    706 
    707 			if ( current_user_can( 'delete_post', $post->ID ) ) {
    708 				if ( EMPTY_TRASH_DAYS && MEDIA_TRASH ) {
    709 					$actions['trash'] = sprintf(
    710 						'<a href="%s" class="submitdelete aria-button-if-js" aria-label="%s">%s</a>',
    711 						wp_nonce_url( "post.php?action=trash&amp;post=$post->ID", 'trash-post_' . $post->ID ),
    712 						/* translators: %s: Attachment title. */
    713 						esc_attr( sprintf( __( 'Move &#8220;%s&#8221; to the Trash' ), $att_title ) ),
    714 						_x( 'Trash', 'verb' )
    715 					);
    716 				} else {
    717 					$delete_ays        = ! MEDIA_TRASH ? " onclick='return showNotice.warn();'" : '';
    718 					$actions['delete'] = sprintf(
    719 						'<a href="%s" class="submitdelete aria-button-if-js"%s aria-label="%s">%s</a>',
    720 						wp_nonce_url( "post.php?action=delete&amp;post=$post->ID", 'delete-post_' . $post->ID ),
    721 						$delete_ays,
    722 						/* translators: %s: Attachment title. */
    723 						esc_attr( sprintf( __( 'Delete &#8220;%s&#8221; permanently' ), $att_title ) ),
    724 						__( 'Delete Permanently' )
    725 					);
    726 				}
    727 			}
    728 
    729 			$actions['view'] = sprintf(
    730 				'<a href="%s" aria-label="%s" rel="bookmark">%s</a>',
    731 				get_permalink( $post->ID ),
    732 				/* translators: %s: Attachment title. */
    733 				esc_attr( sprintf( __( 'View &#8220;%s&#8221;' ), $att_title ) ),
    734 				__( 'View' )
    735 			);
    736 
    737 			if ( current_user_can( 'edit_post', $post->ID ) ) {
    738 				$actions['attach'] = sprintf(
    739 					'<a href="#the-list" onclick="findPosts.open( \'media[]\', \'%s\' ); return false;" class="hide-if-no-js aria-button-if-js" aria-label="%s">%s</a>',
    740 					$post->ID,
    741 					/* translators: %s: Attachment title. */
    742 					esc_attr( sprintf( __( 'Attach &#8220;%s&#8221; to existing content' ), $att_title ) ),
    743 					__( 'Attach' )
    744 				);
    745 			}
    746 		} else {
    747 			if ( current_user_can( 'edit_post', $post->ID ) && ! $this->is_trash ) {
    748 				$actions['edit'] = sprintf(
    749 					'<a href="%s" aria-label="%s">%s</a>',
    750 					get_edit_post_link( $post->ID ),
    751 					/* translators: %s: Attachment title. */
    752 					esc_attr( sprintf( __( 'Edit &#8220;%s&#8221;' ), $att_title ) ),
    753 					__( 'Edit' )
    754 				);
    755 			}
    756 
    757 			if ( current_user_can( 'delete_post', $post->ID ) ) {
    758 				if ( $this->is_trash ) {
    759 					$actions['untrash'] = sprintf(
    760 						'<a href="%s" class="submitdelete aria-button-if-js" aria-label="%s">%s</a>',
    761 						wp_nonce_url( "post.php?action=untrash&amp;post=$post->ID", 'untrash-post_' . $post->ID ),
    762 						/* translators: %s: Attachment title. */
    763 						esc_attr( sprintf( __( 'Restore &#8220;%s&#8221; from the Trash' ), $att_title ) ),
    764 						__( 'Restore' )
    765 					);
    766 				} elseif ( EMPTY_TRASH_DAYS && MEDIA_TRASH ) {
    767 					$actions['trash'] = sprintf(
    768 						'<a href="%s" class="submitdelete aria-button-if-js" aria-label="%s">%s</a>',
    769 						wp_nonce_url( "post.php?action=trash&amp;post=$post->ID", 'trash-post_' . $post->ID ),
    770 						/* translators: %s: Attachment title. */
    771 						esc_attr( sprintf( __( 'Move &#8220;%s&#8221; to the Trash' ), $att_title ) ),
    772 						_x( 'Trash', 'verb' )
    773 					);
    774 				}
    775 
    776 				if ( $this->is_trash || ! EMPTY_TRASH_DAYS || ! MEDIA_TRASH ) {
    777 					$delete_ays        = ( ! $this->is_trash && ! MEDIA_TRASH ) ? " onclick='return showNotice.warn();'" : '';
    778 					$actions['delete'] = sprintf(
    779 						'<a href="%s" class="submitdelete aria-button-if-js"%s aria-label="%s">%s</a>',
    780 						wp_nonce_url( "post.php?action=delete&amp;post=$post->ID", 'delete-post_' . $post->ID ),
    781 						$delete_ays,
    782 						/* translators: %s: Attachment title. */
    783 						esc_attr( sprintf( __( 'Delete &#8220;%s&#8221; permanently' ), $att_title ) ),
    784 						__( 'Delete Permanently' )
    785 					);
    786 				}
    787 			}
    788 
    789 			if ( ! $this->is_trash ) {
    790 				$actions['view'] = sprintf(
    791 					'<a href="%s" aria-label="%s" rel="bookmark">%s</a>',
    792 					get_permalink( $post->ID ),
    793 					/* translators: %s: Attachment title. */
    794 					esc_attr( sprintf( __( 'View &#8220;%s&#8221;' ), $att_title ) ),
    795 					__( 'View' )
    796 				);
    797 			}
    798 		}
    799 
    800 		/**
    801 		 * Filters the action links for each attachment in the Media list table.
    802 		 *
    803 		 * @since 2.8.0
    804 		 *
    805 		 * @param string[] $actions  An array of action links for each attachment.
    806 		 *                           Default 'Edit', 'Delete Permanently', 'View'.
    807 		 * @param WP_Post  $post     WP_Post object for the current attachment.
    808 		 * @param bool     $detached Whether the list table contains media not attached
    809 		 *                           to any posts. Default true.
    810 		 */
    811 		return apply_filters( 'media_row_actions', $actions, $post, $this->detached );
    812 	}
    813 
    814 	/**
    815 	 * Generates and displays row action links.
    816 	 *
    817 	 * @since 4.3.0
    818 	 *
    819 	 * @param WP_Post $post        Attachment being acted upon.
    820 	 * @param string  $column_name Current column name.
    821 	 * @param string  $primary     Primary column name.
    822 	 * @return string Row actions output for media attachments, or an empty string
    823 	 *                if the current column is not the primary column.
    824 	 */
    825 	protected function handle_row_actions( $post, $column_name, $primary ) {
    826 		if ( $primary !== $column_name ) {
    827 			return '';
    828 		}
    829 
    830 		$att_title = _draft_or_post_title();
    831 
    832 		return $this->row_actions( $this->_get_row_actions( $post, $att_title ) );
    833 	}
    834 }