/wp-admin/includes/class-wp-media-list-table.php

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