wp/wp-admin/includes/class-wp-media-list-table.php
changeset 21 48c4eec2b7e6
parent 19 3d72ae0968f4
child 22 8c2e4d02f4ef
equal deleted inserted replaced
20:7b1b88e27a20 21:48c4eec2b7e6
     9 
     9 
    10 /**
    10 /**
    11  * Core class used to implement displaying media items in a list table.
    11  * Core class used to implement displaying media items in a list table.
    12  *
    12  *
    13  * @since 3.1.0
    13  * @since 3.1.0
    14  * @access private
       
    15  *
    14  *
    16  * @see WP_List_Table
    15  * @see WP_List_Table
    17  */
    16  */
    18 class WP_Media_List_Table extends WP_List_Table {
    17 class WP_Media_List_Table extends WP_List_Table {
    19 	/**
    18 	/**
   109 				'total_items' => $wp_query->found_posts,
   108 				'total_items' => $wp_query->found_posts,
   110 				'total_pages' => $wp_query->max_num_pages,
   109 				'total_pages' => $wp_query->max_num_pages,
   111 				'per_page'    => $wp_query->query_vars['posts_per_page'],
   110 				'per_page'    => $wp_query->query_vars['posts_per_page'],
   112 			)
   111 			)
   113 		);
   112 		);
       
   113 		if ( $wp_query->posts ) {
       
   114 			update_post_thumbnail_cache( $wp_query );
       
   115 			update_post_parent_caches( $wp_query->posts );
       
   116 		}
   114 	}
   117 	}
   115 
   118 
   116 	/**
   119 	/**
   117 	 * @global array $post_mime_types
   120 	 * @global array $post_mime_types
   118 	 * @global array $avail_post_mime_types
   121 	 * @global array $avail_post_mime_types
   135 			if ( ! wp_match_mime_types( $mime_type, $avail_post_mime_types ) ) {
   138 			if ( ! wp_match_mime_types( $mime_type, $avail_post_mime_types ) ) {
   136 				continue;
   139 				continue;
   137 			}
   140 			}
   138 
   141 
   139 			$selected = selected(
   142 			$selected = selected(
   140 				$filter && 0 === strpos( $filter, 'post_mime_type:' ) &&
   143 				$filter && str_starts_with( $filter, 'post_mime_type:' ) &&
   141 					wp_match_mime_types( $mime_type, str_replace( 'post_mime_type:', '', $filter ) ),
   144 					wp_match_mime_types( $mime_type, str_replace( 'post_mime_type:', '', $filter ) ),
   142 				true,
   145 				true,
   143 				false
   146 				false
   144 			);
   147 			);
   145 
   148 
   258 			_e( 'No media files found.' );
   261 			_e( 'No media files found.' );
   259 		}
   262 		}
   260 	}
   263 	}
   261 
   264 
   262 	/**
   265 	/**
   263 	 * Override parent views so we can use the filter bar display.
   266 	 * Overrides parent views to use the filter bar display.
   264 	 *
   267 	 *
   265 	 * @global string $mode List table view mode.
   268 	 * @global string $mode List table view mode.
   266 	 */
   269 	 */
   267 	public function views() {
   270 	public function views() {
   268 		global $mode;
   271 		global $mode;
   273 		?>
   276 		?>
   274 		<div class="wp-filter">
   277 		<div class="wp-filter">
   275 			<div class="filter-items">
   278 			<div class="filter-items">
   276 				<?php $this->view_switcher( $mode ); ?>
   279 				<?php $this->view_switcher( $mode ); ?>
   277 
   280 
   278 				<label for="attachment-filter" class="screen-reader-text"><?php _e( 'Filter by type' ); ?></label>
   281 				<label for="attachment-filter" class="screen-reader-text">
       
   282 					<?php
       
   283 					/* translators: Hidden accessibility text. */
       
   284 					_e( 'Filter by type' );
       
   285 					?>
       
   286 				</label>
   279 				<select class="attachment-filters" name="attachment-filter" id="attachment-filter">
   287 				<select class="attachment-filters" name="attachment-filter" id="attachment-filter">
   280 					<?php
   288 					<?php
   281 					if ( ! empty( $views ) ) {
   289 					if ( ! empty( $views ) ) {
   282 						foreach ( $views as $class => $view ) {
   290 						foreach ( $views as $class => $view ) {
   283 							echo "\t$view\n";
   291 							echo "\t$view\n";
   287 				</select>
   295 				</select>
   288 
   296 
   289 				<?php
   297 				<?php
   290 				$this->extra_tablenav( 'bar' );
   298 				$this->extra_tablenav( 'bar' );
   291 
   299 
   292 				/** This filter is documented in wp-admin/inclues/class-wp-list-table.php */
   300 				/** This filter is documented in wp-admin/includes/class-wp-list-table.php */
   293 				$views = apply_filters( "views_{$this->screen->id}", array() );
   301 				$views = apply_filters( "views_{$this->screen->id}", array() );
   294 
   302 
   295 				// Back compat for pre-4.0 view links.
   303 				// Back compat for pre-4.0 view links.
   296 				if ( ! empty( $views ) ) {
   304 				if ( ! empty( $views ) ) {
   297 					echo '<ul class="filter-links">';
   305 					echo '<ul class="filter-links">';
   302 				}
   310 				}
   303 				?>
   311 				?>
   304 			</div>
   312 			</div>
   305 
   313 
   306 			<div class="search-form">
   314 			<div class="search-form">
   307 				<label for="media-search-input" class="media-search-input-label"><?php esc_html_e( 'Search' ); ?></label>
   315 				<p class="search-box">
   308 				<input type="search" id="media-search-input" class="search" name="s" value="<?php _admin_search_query(); ?>">
   316 					<label class="screen-reader-text" for="media-search-input">
       
   317 					<?php
       
   318 					/* translators: Hidden accessibility text. */
       
   319 					esc_html_e( 'Search Media' );
       
   320 					?>
       
   321 					</label>
       
   322 					<input type="search" id="media-search-input" class="search" name="s" value="<?php _admin_search_query(); ?>">
       
   323 					<input id="search-submit" type="submit" class="button" value="<?php esc_attr_e( 'Search Media' ); ?>">
       
   324 				</p>
   309 			</div>
   325 			</div>
   310 		</div>
   326 		</div>
   311 		<?php
   327 		<?php
   312 	}
   328 	}
   313 
   329 
   314 	/**
   330 	/**
   315 	 * @return array
   331 	 * @return string[] Array of column titles keyed by their column name.
   316 	 */
   332 	 */
   317 	public function get_columns() {
   333 	public function get_columns() {
   318 		$posts_columns       = array();
   334 		$posts_columns       = array();
   319 		$posts_columns['cb'] = '<input type="checkbox" />';
   335 		$posts_columns['cb'] = '<input type="checkbox" />';
   320 		/* translators: Column name. */
   336 		/* translators: Column name. */
   348 		}
   364 		}
   349 
   365 
   350 		/* translators: Column name. */
   366 		/* translators: Column name. */
   351 		if ( ! $this->detached ) {
   367 		if ( ! $this->detached ) {
   352 			$posts_columns['parent'] = _x( 'Uploaded to', 'column name' );
   368 			$posts_columns['parent'] = _x( 'Uploaded to', 'column name' );
       
   369 
   353 			if ( post_type_supports( 'attachment', 'comments' ) ) {
   370 			if ( post_type_supports( 'attachment', 'comments' ) ) {
   354 				$posts_columns['comments'] = '<span class="vers comment-grey-bubble" title="' . esc_attr__( 'Comments' ) . '"><span class="screen-reader-text">' . __( 'Comments' ) . '</span></span>';
   371 				$posts_columns['comments'] = sprintf(
       
   372 					'<span class="vers comment-grey-bubble" title="%1$s" aria-hidden="true"></span><span class="screen-reader-text">%2$s</span>',
       
   373 					esc_attr__( 'Comments' ),
       
   374 					/* translators: Hidden accessibility text. */
       
   375 					__( 'Comments' )
       
   376 				);
   355 			}
   377 			}
   356 		}
   378 		}
   357 
   379 
   358 		/* translators: Column name. */
   380 		/* translators: Column name. */
   359 		$posts_columns['date'] = _x( 'Date', 'column name' );
   381 		$posts_columns['date'] = _x( 'Date', 'column name' );
   373 	/**
   395 	/**
   374 	 * @return array
   396 	 * @return array
   375 	 */
   397 	 */
   376 	protected function get_sortable_columns() {
   398 	protected function get_sortable_columns() {
   377 		return array(
   399 		return array(
   378 			'title'    => 'title',
   400 			'title'    => array( 'title', false, _x( 'File', 'column name' ), __( 'Table ordered by File Name.' ) ),
   379 			'author'   => 'author',
   401 			'author'   => array( 'author', false, __( 'Author' ), __( 'Table ordered by Author.' ) ),
   380 			'parent'   => 'parent',
   402 			'parent'   => array( 'parent', false, _x( 'Uploaded to', 'column name' ), __( 'Table ordered by Uploaded To.' ) ),
   381 			'comments' => 'comment_count',
   403 			'comments' => array( 'comment_count', __( 'Comments' ), false, __( 'Table ordered by Comments.' ) ),
   382 			'date'     => array( 'date', true ),
   404 			'date'     => array( 'date', true, __( 'Date' ), __( 'Table ordered by Date.' ), 'desc' ),
   383 		);
   405 		);
   384 	}
   406 	}
   385 
   407 
   386 	/**
   408 	/**
   387 	 * Handles the checkbox column output.
   409 	 * Handles the checkbox column output.
   395 		// Restores the more descriptive, specific name for use within this method.
   417 		// Restores the more descriptive, specific name for use within this method.
   396 		$post = $item;
   418 		$post = $item;
   397 
   419 
   398 		if ( current_user_can( 'edit_post', $post->ID ) ) {
   420 		if ( current_user_can( 'edit_post', $post->ID ) ) {
   399 			?>
   421 			?>
   400 			<label class="screen-reader-text" for="cb-select-<?php echo $post->ID; ?>">
   422 			<input type="checkbox" name="media[]" id="cb-select-<?php echo $post->ID; ?>" value="<?php echo $post->ID; ?>" />
       
   423 			<label for="cb-select-<?php echo $post->ID; ?>">
       
   424 				<span class="screen-reader-text">
   401 				<?php
   425 				<?php
   402 				/* translators: %s: Attachment title. */
   426 				/* translators: Hidden accessibility text. %s: Attachment title. */
   403 				printf( __( 'Select %s' ), _draft_or_post_title() );
   427 				printf( __( 'Select %s' ), _draft_or_post_title() );
   404 				?>
   428 				?>
       
   429 				</span>
   405 			</label>
   430 			</label>
   406 			<input type="checkbox" name="media[]" id="cb-select-<?php echo $post->ID; ?>" value="<?php echo $post->ID; ?>" />
       
   407 			<?php
   431 			<?php
   408 		}
   432 		}
   409 	}
   433 	}
   410 
   434 
   411 	/**
   435 	/**
   416 	 * @param WP_Post $post The current WP_Post object.
   440 	 * @param WP_Post $post The current WP_Post object.
   417 	 */
   441 	 */
   418 	public function column_title( $post ) {
   442 	public function column_title( $post ) {
   419 		list( $mime ) = explode( '/', $post->post_mime_type );
   443 		list( $mime ) = explode( '/', $post->post_mime_type );
   420 
   444 
       
   445 		$attachment_id = $post->ID;
       
   446 
       
   447 		if ( has_post_thumbnail( $post ) ) {
       
   448 			$thumbnail_id = get_post_thumbnail_id( $post );
       
   449 
       
   450 			if ( ! empty( $thumbnail_id ) ) {
       
   451 				$attachment_id = $thumbnail_id;
       
   452 			}
       
   453 		}
       
   454 
   421 		$title      = _draft_or_post_title();
   455 		$title      = _draft_or_post_title();
   422 		$thumb      = wp_get_attachment_image( $post->ID, array( 60, 60 ), true, array( 'alt' => '' ) );
   456 		$thumb      = wp_get_attachment_image( $attachment_id, array( 60, 60 ), true, array( 'alt' => '' ) );
   423 		$link_start = '';
   457 		$link_start = '';
   424 		$link_end   = '';
   458 		$link_end   = '';
   425 
   459 
   426 		if ( current_user_can( 'edit_post', $post->ID ) && ! $this->is_trash ) {
   460 		if ( current_user_can( 'edit_post', $post->ID ) && ! $this->is_trash ) {
   427 			$link_start = sprintf(
   461 			$link_start = sprintf(
   449 
   483 
   450 			_media_states( $post );
   484 			_media_states( $post );
   451 			?>
   485 			?>
   452 		</strong>
   486 		</strong>
   453 		<p class="filename">
   487 		<p class="filename">
   454 			<span class="screen-reader-text"><?php _e( 'File name:' ); ?> </span>
   488 			<span class="screen-reader-text">
       
   489 				<?php
       
   490 				/* translators: Hidden accessibility text. */
       
   491 				_e( 'File name:' );
       
   492 				?>
       
   493 			</span>
   455 			<?php
   494 			<?php
   456 			$file = get_attached_file( $post->ID );
   495 			$file = get_attached_file( $post->ID );
   457 			echo esc_html( wp_basename( $file ) );
   496 			echo esc_html( wp_basename( $file ) );
   458 			?>
   497 			?>
   459 		</p>
   498 		</p>
   477 
   516 
   478 	/**
   517 	/**
   479 	 * Handles the description column output.
   518 	 * Handles the description column output.
   480 	 *
   519 	 *
   481 	 * @since 4.3.0
   520 	 * @since 4.3.0
       
   521 	 * @deprecated 6.2.0
   482 	 *
   522 	 *
   483 	 * @param WP_Post $post The current WP_Post object.
   523 	 * @param WP_Post $post The current WP_Post object.
   484 	 */
   524 	 */
   485 	public function column_desc( $post ) {
   525 	public function column_desc( $post ) {
       
   526 		_deprecated_function( __METHOD__, '6.2.0' );
       
   527 
   486 		echo has_excerpt() ? $post->post_excerpt : '';
   528 		echo has_excerpt() ? $post->post_excerpt : '';
   487 	}
   529 	}
   488 
   530 
   489 	/**
   531 	/**
   490 	 * Handles the date column output.
   532 	 * Handles the date column output.
   618 
   660 
   619 		if ( 'categories' === $column_name ) {
   661 		if ( 'categories' === $column_name ) {
   620 			$taxonomy = 'category';
   662 			$taxonomy = 'category';
   621 		} elseif ( 'tags' === $column_name ) {
   663 		} elseif ( 'tags' === $column_name ) {
   622 			$taxonomy = 'post_tag';
   664 			$taxonomy = 'post_tag';
   623 		} elseif ( 0 === strpos( $column_name, 'taxonomy-' ) ) {
   665 		} elseif ( str_starts_with( $column_name, 'taxonomy-' ) ) {
   624 			$taxonomy = substr( $column_name, 9 );
   666 			$taxonomy = substr( $column_name, 9 );
   625 		} else {
   667 		} else {
   626 			$taxonomy = false;
   668 			$taxonomy = false;
   627 		}
   669 		}
   628 
   670 
   629 		if ( $taxonomy ) {
   671 		if ( $taxonomy ) {
   630 			$terms = get_the_terms( $post->ID, $taxonomy );
   672 			$terms = get_the_terms( $post->ID, $taxonomy );
   631 
   673 
   632 			if ( is_array( $terms ) ) {
   674 			if ( is_array( $terms ) ) {
   633 				$out = array();
   675 				$output = array();
       
   676 
   634 				foreach ( $terms as $t ) {
   677 				foreach ( $terms as $t ) {
   635 					$posts_in_term_qv             = array();
   678 					$posts_in_term_qv             = array();
   636 					$posts_in_term_qv['taxonomy'] = $taxonomy;
   679 					$posts_in_term_qv['taxonomy'] = $taxonomy;
   637 					$posts_in_term_qv['term']     = $t->slug;
   680 					$posts_in_term_qv['term']     = $t->slug;
   638 
   681 
   639 					$out[] = sprintf(
   682 					$output[] = sprintf(
   640 						'<a href="%s">%s</a>',
   683 						'<a href="%s">%s</a>',
   641 						esc_url( add_query_arg( $posts_in_term_qv, 'upload.php' ) ),
   684 						esc_url( add_query_arg( $posts_in_term_qv, 'upload.php' ) ),
   642 						esc_html( sanitize_term_field( 'name', $t->name, $t->term_id, $taxonomy, 'display' ) )
   685 						esc_html( sanitize_term_field( 'name', $t->name, $t->term_id, $taxonomy, 'display' ) )
   643 					);
   686 					);
   644 				}
   687 				}
   645 				echo implode( wp_get_list_item_separator(), $out );
   688 
       
   689 				echo implode( wp_get_list_item_separator(), $output );
   646 			} else {
   690 			} else {
   647 				echo '<span aria-hidden="true">&#8212;</span><span class="screen-reader-text">' . get_taxonomy( $taxonomy )->labels->no_terms . '</span>';
   691 				echo '<span aria-hidden="true">&#8212;</span><span class="screen-reader-text">' . get_taxonomy( $taxonomy )->labels->no_terms . '</span>';
   648 			}
   692 			}
   649 
   693 
   650 			return;
   694 			return;
   662 		 */
   706 		 */
   663 		do_action( 'manage_media_custom_column', $column_name, $post->ID );
   707 		do_action( 'manage_media_custom_column', $column_name, $post->ID );
   664 	}
   708 	}
   665 
   709 
   666 	/**
   710 	/**
   667 	 * @global WP_Post $post Global post object.
   711 	 * @global WP_Post  $post     Global post object.
       
   712 	 * @global WP_Query $wp_query WordPress Query object.
   668 	 */
   713 	 */
   669 	public function display_rows() {
   714 	public function display_rows() {
   670 		global $post, $wp_query;
   715 		global $post, $wp_query;
   671 
   716 
   672 		$post_ids = wp_list_pluck( $wp_query->posts, 'ID' );
   717 		$post_ids = wp_list_pluck( $wp_query->posts, 'ID' );
   711 	 * @return array
   756 	 * @return array
   712 	 */
   757 	 */
   713 	private function _get_row_actions( $post, $att_title ) {
   758 	private function _get_row_actions( $post, $att_title ) {
   714 		$actions = array();
   759 		$actions = array();
   715 
   760 
   716 		if ( $this->detached ) {
   761 		if ( ! $this->is_trash && current_user_can( 'edit_post', $post->ID ) ) {
   717 			if ( current_user_can( 'edit_post', $post->ID ) ) {
   762 			$actions['edit'] = sprintf(
   718 				$actions['edit'] = sprintf(
   763 				'<a href="%s" aria-label="%s">%s</a>',
   719 					'<a href="%s" aria-label="%s">%s</a>',
   764 				esc_url( get_edit_post_link( $post->ID ) ),
   720 					get_edit_post_link( $post->ID ),
   765 				/* translators: %s: Attachment title. */
       
   766 				esc_attr( sprintf( __( 'Edit &#8220;%s&#8221;' ), $att_title ) ),
       
   767 				__( 'Edit' )
       
   768 			);
       
   769 		}
       
   770 
       
   771 		if ( current_user_can( 'delete_post', $post->ID ) ) {
       
   772 			if ( $this->is_trash ) {
       
   773 				$actions['untrash'] = sprintf(
       
   774 					'<a href="%s" class="submitdelete aria-button-if-js" aria-label="%s">%s</a>',
       
   775 					esc_url( wp_nonce_url( "post.php?action=untrash&amp;post=$post->ID", 'untrash-post_' . $post->ID ) ),
   721 					/* translators: %s: Attachment title. */
   776 					/* translators: %s: Attachment title. */
   722 					esc_attr( sprintf( __( 'Edit &#8220;%s&#8221;' ), $att_title ) ),
   777 					esc_attr( sprintf( __( 'Restore &#8220;%s&#8221; from the Trash' ), $att_title ) ),
   723 					__( 'Edit' )
   778 					__( 'Restore' )
   724 				);
   779 				);
   725 			}
   780 			} elseif ( EMPTY_TRASH_DAYS && MEDIA_TRASH ) {
   726 
   781 				$actions['trash'] = sprintf(
   727 			if ( current_user_can( 'delete_post', $post->ID ) ) {
   782 					'<a href="%s" class="submitdelete aria-button-if-js" aria-label="%s">%s</a>',
   728 				if ( EMPTY_TRASH_DAYS && MEDIA_TRASH ) {
   783 					esc_url( wp_nonce_url( "post.php?action=trash&amp;post=$post->ID", 'trash-post_' . $post->ID ) ),
   729 					$actions['trash'] = sprintf(
       
   730 						'<a href="%s" class="submitdelete aria-button-if-js" aria-label="%s">%s</a>',
       
   731 						wp_nonce_url( "post.php?action=trash&amp;post=$post->ID", 'trash-post_' . $post->ID ),
       
   732 						/* translators: %s: Attachment title. */
       
   733 						esc_attr( sprintf( __( 'Move &#8220;%s&#8221; to the Trash' ), $att_title ) ),
       
   734 						_x( 'Trash', 'verb' )
       
   735 					);
       
   736 				} else {
       
   737 					$delete_ays        = ! MEDIA_TRASH ? " onclick='return showNotice.warn();'" : '';
       
   738 					$actions['delete'] = sprintf(
       
   739 						'<a href="%s" class="submitdelete aria-button-if-js"%s aria-label="%s">%s</a>',
       
   740 						wp_nonce_url( "post.php?action=delete&amp;post=$post->ID", 'delete-post_' . $post->ID ),
       
   741 						$delete_ays,
       
   742 						/* translators: %s: Attachment title. */
       
   743 						esc_attr( sprintf( __( 'Delete &#8220;%s&#8221; permanently' ), $att_title ) ),
       
   744 						__( 'Delete Permanently' )
       
   745 					);
       
   746 				}
       
   747 			}
       
   748 
       
   749 			$actions['view'] = sprintf(
       
   750 				'<a href="%s" aria-label="%s" rel="bookmark">%s</a>',
       
   751 				get_permalink( $post->ID ),
       
   752 				/* translators: %s: Attachment title. */
       
   753 				esc_attr( sprintf( __( 'View &#8220;%s&#8221;' ), $att_title ) ),
       
   754 				__( 'View' )
       
   755 			);
       
   756 
       
   757 			if ( current_user_can( 'edit_post', $post->ID ) ) {
       
   758 				$actions['attach'] = sprintf(
       
   759 					'<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>',
       
   760 					$post->ID,
       
   761 					/* translators: %s: Attachment title. */
   784 					/* translators: %s: Attachment title. */
   762 					esc_attr( sprintf( __( 'Attach &#8220;%s&#8221; to existing content' ), $att_title ) ),
   785 					esc_attr( sprintf( __( 'Move &#8220;%s&#8221; to the Trash' ), $att_title ) ),
   763 					__( 'Attach' )
   786 					_x( 'Trash', 'verb' )
   764 				);
   787 				);
   765 			}
   788 			}
   766 		} else {
   789 
   767 			if ( current_user_can( 'edit_post', $post->ID ) && ! $this->is_trash ) {
   790 			if ( $this->is_trash || ! EMPTY_TRASH_DAYS || ! MEDIA_TRASH ) {
   768 				$actions['edit'] = sprintf(
   791 				$show_confirmation = ( ! $this->is_trash && ! MEDIA_TRASH ) ? " onclick='return showNotice.warn();'" : '';
   769 					'<a href="%s" aria-label="%s">%s</a>',
   792 
   770 					get_edit_post_link( $post->ID ),
   793 				$actions['delete'] = sprintf(
       
   794 					'<a href="%s" class="submitdelete aria-button-if-js"%s aria-label="%s">%s</a>',
       
   795 					esc_url( wp_nonce_url( "post.php?action=delete&amp;post=$post->ID", 'delete-post_' . $post->ID ) ),
       
   796 					$show_confirmation,
   771 					/* translators: %s: Attachment title. */
   797 					/* translators: %s: Attachment title. */
   772 					esc_attr( sprintf( __( 'Edit &#8220;%s&#8221;' ), $att_title ) ),
   798 					esc_attr( sprintf( __( 'Delete &#8220;%s&#8221; permanently' ), $att_title ) ),
   773 					__( 'Edit' )
   799 					__( 'Delete Permanently' )
   774 				);
   800 				);
   775 			}
   801 			}
   776 
   802 		}
   777 			if ( current_user_can( 'delete_post', $post->ID ) ) {
   803 
   778 				if ( $this->is_trash ) {
   804 		$attachment_url = wp_get_attachment_url( $post->ID );
   779 					$actions['untrash'] = sprintf(
   805 
   780 						'<a href="%s" class="submitdelete aria-button-if-js" aria-label="%s">%s</a>',
   806 		if ( ! $this->is_trash ) {
   781 						wp_nonce_url( "post.php?action=untrash&amp;post=$post->ID", 'untrash-post_' . $post->ID ),
   807 			$permalink = get_permalink( $post->ID );
   782 						/* translators: %s: Attachment title. */
   808 
   783 						esc_attr( sprintf( __( 'Restore &#8220;%s&#8221; from the Trash' ), $att_title ) ),
   809 			if ( $permalink ) {
   784 						__( 'Restore' )
       
   785 					);
       
   786 				} elseif ( EMPTY_TRASH_DAYS && MEDIA_TRASH ) {
       
   787 					$actions['trash'] = sprintf(
       
   788 						'<a href="%s" class="submitdelete aria-button-if-js" aria-label="%s">%s</a>',
       
   789 						wp_nonce_url( "post.php?action=trash&amp;post=$post->ID", 'trash-post_' . $post->ID ),
       
   790 						/* translators: %s: Attachment title. */
       
   791 						esc_attr( sprintf( __( 'Move &#8220;%s&#8221; to the Trash' ), $att_title ) ),
       
   792 						_x( 'Trash', 'verb' )
       
   793 					);
       
   794 				}
       
   795 
       
   796 				if ( $this->is_trash || ! EMPTY_TRASH_DAYS || ! MEDIA_TRASH ) {
       
   797 					$delete_ays        = ( ! $this->is_trash && ! MEDIA_TRASH ) ? " onclick='return showNotice.warn();'" : '';
       
   798 					$actions['delete'] = sprintf(
       
   799 						'<a href="%s" class="submitdelete aria-button-if-js"%s aria-label="%s">%s</a>',
       
   800 						wp_nonce_url( "post.php?action=delete&amp;post=$post->ID", 'delete-post_' . $post->ID ),
       
   801 						$delete_ays,
       
   802 						/* translators: %s: Attachment title. */
       
   803 						esc_attr( sprintf( __( 'Delete &#8220;%s&#8221; permanently' ), $att_title ) ),
       
   804 						__( 'Delete Permanently' )
       
   805 					);
       
   806 				}
       
   807 			}
       
   808 
       
   809 			if ( ! $this->is_trash ) {
       
   810 				$actions['view'] = sprintf(
   810 				$actions['view'] = sprintf(
   811 					'<a href="%s" aria-label="%s" rel="bookmark">%s</a>',
   811 					'<a href="%s" aria-label="%s" rel="bookmark">%s</a>',
   812 					get_permalink( $post->ID ),
   812 					esc_url( $permalink ),
   813 					/* translators: %s: Attachment title. */
   813 					/* translators: %s: Attachment title. */
   814 					esc_attr( sprintf( __( 'View &#8220;%s&#8221;' ), $att_title ) ),
   814 					esc_attr( sprintf( __( 'View &#8220;%s&#8221;' ), $att_title ) ),
   815 					__( 'View' )
   815 					__( 'View' )
   816 				);
   816 				);
   817 
   817 			}
       
   818 
       
   819 			if ( $attachment_url ) {
   818 				$actions['copy'] = sprintf(
   820 				$actions['copy'] = sprintf(
   819 					'<span class="copy-to-clipboard-container"><button type="button" class="button-link copy-attachment-url media-library" data-clipboard-text="%s" aria-label="%s">%s</button><span class="success hidden" aria-hidden="true">%s</span></span>',
   821 					'<span class="copy-to-clipboard-container"><button type="button" class="button-link copy-attachment-url media-library" data-clipboard-text="%s" aria-label="%s">%s</button><span class="success hidden" aria-hidden="true">%s</span></span>',
   820 					esc_url( wp_get_attachment_url( $post->ID ) ),
   822 					esc_url( $attachment_url ),
   821 					/* translators: %s: Attachment title. */
   823 					/* translators: %s: Attachment title. */
   822 					esc_attr( sprintf( __( 'Copy &#8220;%s&#8221; URL to clipboard' ), $att_title ) ),
   824 					esc_attr( sprintf( __( 'Copy &#8220;%s&#8221; URL to clipboard' ), $att_title ) ),
   823 					__( 'Copy URL to clipboard' ),
   825 					__( 'Copy URL' ),
   824 					__( 'Copied!' )
   826 					__( 'Copied!' )
   825 				);
   827 				);
   826 			}
   828 			}
   827 		}
   829 		}
   828 
   830 
       
   831 		if ( $attachment_url ) {
       
   832 			$actions['download'] = sprintf(
       
   833 				'<a href="%s" aria-label="%s" download>%s</a>',
       
   834 				esc_url( $attachment_url ),
       
   835 				/* translators: %s: Attachment title. */
       
   836 				esc_attr( sprintf( __( 'Download &#8220;%s&#8221;' ), $att_title ) ),
       
   837 				__( 'Download file' )
       
   838 			);
       
   839 		}
       
   840 
       
   841 		if ( $this->detached && current_user_can( 'edit_post', $post->ID ) ) {
       
   842 			$actions['attach'] = sprintf(
       
   843 				'<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>',
       
   844 				$post->ID,
       
   845 				/* translators: %s: Attachment title. */
       
   846 				esc_attr( sprintf( __( 'Attach &#8220;%s&#8221; to existing content' ), $att_title ) ),
       
   847 				__( 'Attach' )
       
   848 			);
       
   849 		}
       
   850 
   829 		/**
   851 		/**
   830 		 * Filters the action links for each attachment in the Media list table.
   852 		 * Filters the action links for each attachment in the Media list table.
   831 		 *
   853 		 *
   832 		 * @since 2.8.0
   854 		 * @since 2.8.0
   833 		 *
   855 		 *
   834 		 * @param string[] $actions  An array of action links for each attachment.
   856 		 * @param string[] $actions  An array of action links for each attachment.
   835 		 *                           Default 'Edit', 'Delete Permanently', 'View'.
   857 		 *                           Includes 'Edit', 'Delete Permanently', 'View',
       
   858 		 *                           'Copy URL' and 'Download file'.
   836 		 * @param WP_Post  $post     WP_Post object for the current attachment.
   859 		 * @param WP_Post  $post     WP_Post object for the current attachment.
   837 		 * @param bool     $detached Whether the list table contains media not attached
   860 		 * @param bool     $detached Whether the list table contains media not attached
   838 		 *                           to any posts. Default true.
   861 		 *                           to any posts. Default true.
   839 		 */
   862 		 */
   840 		return apply_filters( 'media_row_actions', $actions, $post, $this->detached );
   863 		return apply_filters( 'media_row_actions', $actions, $post, $this->detached );
   855 	protected function handle_row_actions( $item, $column_name, $primary ) {
   878 	protected function handle_row_actions( $item, $column_name, $primary ) {
   856 		if ( $primary !== $column_name ) {
   879 		if ( $primary !== $column_name ) {
   857 			return '';
   880 			return '';
   858 		}
   881 		}
   859 
   882 
       
   883 		// Restores the more descriptive, specific name for use within this method.
       
   884 		$post = $item;
       
   885 
   860 		$att_title = _draft_or_post_title();
   886 		$att_title = _draft_or_post_title();
   861 		$actions   = $this->_get_row_actions(
   887 		$actions   = $this->_get_row_actions( $post, $att_title );
   862 			$item, // WP_Post object for an attachment.
       
   863 			$att_title
       
   864 		);
       
   865 
   888 
   866 		return $this->row_actions( $actions );
   889 		return $this->row_actions( $actions );
   867 	}
   890 	}
   868 }
   891 }