Ei kuvausta

class-wp-posts-list-table.php 60KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053
  1. <?php
  2. /**
  3. * List Table API: WP_Posts_List_Table class
  4. *
  5. * @package WordPress
  6. * @subpackage Administration
  7. * @since 3.1.0
  8. */
  9. /**
  10. * Core class used to implement displaying posts in a list table.
  11. *
  12. * @since 3.1.0
  13. * @access private
  14. *
  15. * @see WP_List_Table
  16. */
  17. class WP_Posts_List_Table extends WP_List_Table {
  18. /**
  19. * Whether the items should be displayed hierarchically or linearly.
  20. *
  21. * @since 3.1.0
  22. * @var bool
  23. */
  24. protected $hierarchical_display;
  25. /**
  26. * Holds the number of pending comments for each post.
  27. *
  28. * @since 3.1.0
  29. * @var array
  30. */
  31. protected $comment_pending_count;
  32. /**
  33. * Holds the number of posts for this user.
  34. *
  35. * @since 3.1.0
  36. * @var int
  37. */
  38. private $user_posts_count;
  39. /**
  40. * Holds the number of posts which are sticky.
  41. *
  42. * @since 3.1.0
  43. * @var int
  44. */
  45. private $sticky_posts_count = 0;
  46. private $is_trash;
  47. /**
  48. * Current level for output.
  49. *
  50. * @since 4.3.0
  51. * @var int
  52. */
  53. protected $current_level = 0;
  54. /**
  55. * Constructor.
  56. *
  57. * @since 3.1.0
  58. *
  59. * @see WP_List_Table::__construct() for more information on default arguments.
  60. *
  61. * @global WP_Post_Type $post_type_object
  62. * @global wpdb $wpdb WordPress database abstraction object.
  63. *
  64. * @param array $args An associative array of arguments.
  65. */
  66. public function __construct( $args = array() ) {
  67. global $post_type_object, $wpdb;
  68. parent::__construct(
  69. array(
  70. 'plural' => 'posts',
  71. 'screen' => isset( $args['screen'] ) ? $args['screen'] : null,
  72. )
  73. );
  74. $post_type = $this->screen->post_type;
  75. $post_type_object = get_post_type_object( $post_type );
  76. $exclude_states = get_post_stati(
  77. array(
  78. 'show_in_admin_all_list' => false,
  79. )
  80. );
  81. $this->user_posts_count = (int) $wpdb->get_var(
  82. $wpdb->prepare(
  83. "SELECT COUNT( 1 )
  84. FROM $wpdb->posts
  85. WHERE post_type = %s
  86. AND post_status NOT IN ( '" . implode( "','", $exclude_states ) . "' )
  87. AND post_author = %d",
  88. $post_type,
  89. get_current_user_id()
  90. )
  91. );
  92. if ( $this->user_posts_count
  93. && ! current_user_can( $post_type_object->cap->edit_others_posts )
  94. && empty( $_REQUEST['post_status'] ) && empty( $_REQUEST['all_posts'] )
  95. && empty( $_REQUEST['author'] ) && empty( $_REQUEST['show_sticky'] )
  96. ) {
  97. $_GET['author'] = get_current_user_id();
  98. }
  99. $sticky_posts = get_option( 'sticky_posts' );
  100. if ( 'post' === $post_type && $sticky_posts ) {
  101. $sticky_posts = implode( ', ', array_map( 'absint', (array) $sticky_posts ) );
  102. $this->sticky_posts_count = (int) $wpdb->get_var(
  103. $wpdb->prepare(
  104. "SELECT COUNT( 1 )
  105. FROM $wpdb->posts
  106. WHERE post_type = %s
  107. AND post_status NOT IN ('trash', 'auto-draft')
  108. AND ID IN ($sticky_posts)",
  109. $post_type
  110. )
  111. );
  112. }
  113. }
  114. /**
  115. * Sets whether the table layout should be hierarchical or not.
  116. *
  117. * @since 4.2.0
  118. *
  119. * @param bool $display Whether the table layout should be hierarchical.
  120. */
  121. public function set_hierarchical_display( $display ) {
  122. $this->hierarchical_display = $display;
  123. }
  124. /**
  125. * @return bool
  126. */
  127. public function ajax_user_can() {
  128. return current_user_can( get_post_type_object( $this->screen->post_type )->cap->edit_posts );
  129. }
  130. /**
  131. * @global string $mode List table view mode.
  132. * @global array $avail_post_stati
  133. * @global WP_Query $wp_query WordPress Query object.
  134. * @global int $per_page
  135. */
  136. public function prepare_items() {
  137. global $mode, $avail_post_stati, $wp_query, $per_page;
  138. if ( ! empty( $_REQUEST['mode'] ) ) {
  139. $mode = 'excerpt' === $_REQUEST['mode'] ? 'excerpt' : 'list';
  140. set_user_setting( 'posts_list_mode', $mode );
  141. } else {
  142. $mode = get_user_setting( 'posts_list_mode', 'list' );
  143. }
  144. // Is going to call wp().
  145. $avail_post_stati = wp_edit_posts_query();
  146. $this->set_hierarchical_display(
  147. is_post_type_hierarchical( $this->screen->post_type )
  148. && 'menu_order title' === $wp_query->query['orderby']
  149. );
  150. $post_type = $this->screen->post_type;
  151. $per_page = $this->get_items_per_page( 'edit_' . $post_type . '_per_page' );
  152. /** This filter is documented in wp-admin/includes/post.php */
  153. $per_page = apply_filters( 'edit_posts_per_page', $per_page, $post_type );
  154. if ( $this->hierarchical_display ) {
  155. $total_items = $wp_query->post_count;
  156. } elseif ( $wp_query->found_posts || $this->get_pagenum() === 1 ) {
  157. $total_items = $wp_query->found_posts;
  158. } else {
  159. $post_counts = (array) wp_count_posts( $post_type, 'readable' );
  160. if ( isset( $_REQUEST['post_status'] ) && in_array( $_REQUEST['post_status'], $avail_post_stati, true ) ) {
  161. $total_items = $post_counts[ $_REQUEST['post_status'] ];
  162. } elseif ( isset( $_REQUEST['show_sticky'] ) && $_REQUEST['show_sticky'] ) {
  163. $total_items = $this->sticky_posts_count;
  164. } elseif ( isset( $_GET['author'] ) && get_current_user_id() === (int) $_GET['author'] ) {
  165. $total_items = $this->user_posts_count;
  166. } else {
  167. $total_items = array_sum( $post_counts );
  168. // Subtract post types that are not included in the admin all list.
  169. foreach ( get_post_stati( array( 'show_in_admin_all_list' => false ) ) as $state ) {
  170. $total_items -= $post_counts[ $state ];
  171. }
  172. }
  173. }
  174. $this->is_trash = isset( $_REQUEST['post_status'] ) && 'trash' === $_REQUEST['post_status'];
  175. $this->set_pagination_args(
  176. array(
  177. 'total_items' => $total_items,
  178. 'per_page' => $per_page,
  179. )
  180. );
  181. }
  182. /**
  183. * @return bool
  184. */
  185. public function has_items() {
  186. return have_posts();
  187. }
  188. /**
  189. */
  190. public function no_items() {
  191. if ( isset( $_REQUEST['post_status'] ) && 'trash' === $_REQUEST['post_status'] ) {
  192. echo get_post_type_object( $this->screen->post_type )->labels->not_found_in_trash;
  193. } else {
  194. echo get_post_type_object( $this->screen->post_type )->labels->not_found;
  195. }
  196. }
  197. /**
  198. * Determine if the current view is the "All" view.
  199. *
  200. * @since 4.2.0
  201. *
  202. * @return bool Whether the current view is the "All" view.
  203. */
  204. protected function is_base_request() {
  205. $vars = $_GET;
  206. unset( $vars['paged'] );
  207. if ( empty( $vars ) ) {
  208. return true;
  209. } elseif ( 1 === count( $vars ) && ! empty( $vars['post_type'] ) ) {
  210. return $this->screen->post_type === $vars['post_type'];
  211. }
  212. return 1 === count( $vars ) && ! empty( $vars['mode'] );
  213. }
  214. /**
  215. * Helper to create links to edit.php with params.
  216. *
  217. * @since 4.4.0
  218. *
  219. * @param string[] $args Associative array of URL parameters for the link.
  220. * @param string $link_text Link text.
  221. * @param string $css_class Optional. Class attribute. Default empty string.
  222. * @return string The formatted link string.
  223. */
  224. protected function get_edit_link( $args, $link_text, $css_class = '' ) {
  225. $url = add_query_arg( $args, 'edit.php' );
  226. $class_html = '';
  227. $aria_current = '';
  228. if ( ! empty( $css_class ) ) {
  229. $class_html = sprintf(
  230. ' class="%s"',
  231. esc_attr( $css_class )
  232. );
  233. if ( 'current' === $css_class ) {
  234. $aria_current = ' aria-current="page"';
  235. }
  236. }
  237. return sprintf(
  238. '<a href="%s"%s%s>%s</a>',
  239. esc_url( $url ),
  240. $class_html,
  241. $aria_current,
  242. $link_text
  243. );
  244. }
  245. /**
  246. * @global array $locked_post_status This seems to be deprecated.
  247. * @global array $avail_post_stati
  248. * @return array
  249. */
  250. protected function get_views() {
  251. global $locked_post_status, $avail_post_stati;
  252. $post_type = $this->screen->post_type;
  253. if ( ! empty( $locked_post_status ) ) {
  254. return array();
  255. }
  256. $status_links = array();
  257. $num_posts = wp_count_posts( $post_type, 'readable' );
  258. $total_posts = array_sum( (array) $num_posts );
  259. $class = '';
  260. $current_user_id = get_current_user_id();
  261. $all_args = array( 'post_type' => $post_type );
  262. $mine = '';
  263. // Subtract post types that are not included in the admin all list.
  264. foreach ( get_post_stati( array( 'show_in_admin_all_list' => false ) ) as $state ) {
  265. $total_posts -= $num_posts->$state;
  266. }
  267. if ( $this->user_posts_count && $this->user_posts_count !== $total_posts ) {
  268. if ( isset( $_GET['author'] ) && ( $current_user_id === (int) $_GET['author'] ) ) {
  269. $class = 'current';
  270. }
  271. $mine_args = array(
  272. 'post_type' => $post_type,
  273. 'author' => $current_user_id,
  274. );
  275. $mine_inner_html = sprintf(
  276. /* translators: %s: Number of posts. */
  277. _nx(
  278. 'Mine <span class="count">(%s)</span>',
  279. 'Mine <span class="count">(%s)</span>',
  280. $this->user_posts_count,
  281. 'posts'
  282. ),
  283. number_format_i18n( $this->user_posts_count )
  284. );
  285. $mine = $this->get_edit_link( $mine_args, $mine_inner_html, $class );
  286. $all_args['all_posts'] = 1;
  287. $class = '';
  288. }
  289. if ( empty( $class ) && ( $this->is_base_request() || isset( $_REQUEST['all_posts'] ) ) ) {
  290. $class = 'current';
  291. }
  292. $all_inner_html = sprintf(
  293. /* translators: %s: Number of posts. */
  294. _nx(
  295. 'All <span class="count">(%s)</span>',
  296. 'All <span class="count">(%s)</span>',
  297. $total_posts,
  298. 'posts'
  299. ),
  300. number_format_i18n( $total_posts )
  301. );
  302. $status_links['all'] = $this->get_edit_link( $all_args, $all_inner_html, $class );
  303. if ( $mine ) {
  304. $status_links['mine'] = $mine;
  305. }
  306. foreach ( get_post_stati( array( 'show_in_admin_status_list' => true ), 'objects' ) as $status ) {
  307. $class = '';
  308. $status_name = $status->name;
  309. if ( ! in_array( $status_name, $avail_post_stati, true ) || empty( $num_posts->$status_name ) ) {
  310. continue;
  311. }
  312. if ( isset( $_REQUEST['post_status'] ) && $status_name === $_REQUEST['post_status'] ) {
  313. $class = 'current';
  314. }
  315. $status_args = array(
  316. 'post_status' => $status_name,
  317. 'post_type' => $post_type,
  318. );
  319. $status_label = sprintf(
  320. translate_nooped_plural( $status->label_count, $num_posts->$status_name ),
  321. number_format_i18n( $num_posts->$status_name )
  322. );
  323. $status_links[ $status_name ] = $this->get_edit_link( $status_args, $status_label, $class );
  324. }
  325. if ( ! empty( $this->sticky_posts_count ) ) {
  326. $class = ! empty( $_REQUEST['show_sticky'] ) ? 'current' : '';
  327. $sticky_args = array(
  328. 'post_type' => $post_type,
  329. 'show_sticky' => 1,
  330. );
  331. $sticky_inner_html = sprintf(
  332. /* translators: %s: Number of posts. */
  333. _nx(
  334. 'Sticky <span class="count">(%s)</span>',
  335. 'Sticky <span class="count">(%s)</span>',
  336. $this->sticky_posts_count,
  337. 'posts'
  338. ),
  339. number_format_i18n( $this->sticky_posts_count )
  340. );
  341. $sticky_link = array(
  342. 'sticky' => $this->get_edit_link( $sticky_args, $sticky_inner_html, $class ),
  343. );
  344. // Sticky comes after Publish, or if not listed, after All.
  345. $split = 1 + array_search( ( isset( $status_links['publish'] ) ? 'publish' : 'all' ), array_keys( $status_links ), true );
  346. $status_links = array_merge( array_slice( $status_links, 0, $split ), $sticky_link, array_slice( $status_links, $split ) );
  347. }
  348. return $status_links;
  349. }
  350. /**
  351. * @return array
  352. */
  353. protected function get_bulk_actions() {
  354. $actions = array();
  355. $post_type_obj = get_post_type_object( $this->screen->post_type );
  356. if ( current_user_can( $post_type_obj->cap->edit_posts ) ) {
  357. if ( $this->is_trash ) {
  358. $actions['untrash'] = __( 'Restore' );
  359. } else {
  360. $actions['edit'] = __( 'Edit' );
  361. }
  362. }
  363. if ( current_user_can( $post_type_obj->cap->delete_posts ) ) {
  364. if ( $this->is_trash || ! EMPTY_TRASH_DAYS ) {
  365. $actions['delete'] = __( 'Delete permanently' );
  366. } else {
  367. $actions['trash'] = __( 'Move to Trash' );
  368. }
  369. }
  370. return $actions;
  371. }
  372. /**
  373. * Displays a categories drop-down for filtering on the Posts list table.
  374. *
  375. * @since 4.6.0
  376. *
  377. * @global int $cat Currently selected category.
  378. *
  379. * @param string $post_type Post type slug.
  380. */
  381. protected function categories_dropdown( $post_type ) {
  382. global $cat;
  383. /**
  384. * Filters whether to remove the 'Categories' drop-down from the post list table.
  385. *
  386. * @since 4.6.0
  387. *
  388. * @param bool $disable Whether to disable the categories drop-down. Default false.
  389. * @param string $post_type Post type slug.
  390. */
  391. if ( false !== apply_filters( 'disable_categories_dropdown', false, $post_type ) ) {
  392. return;
  393. }
  394. if ( is_object_in_taxonomy( $post_type, 'category' ) ) {
  395. $dropdown_options = array(
  396. 'show_option_all' => get_taxonomy( 'category' )->labels->all_items,
  397. 'hide_empty' => 0,
  398. 'hierarchical' => 1,
  399. 'show_count' => 0,
  400. 'orderby' => 'name',
  401. 'selected' => $cat,
  402. );
  403. echo '<label class="screen-reader-text" for="cat">' . get_taxonomy( 'category' )->labels->filter_by_item . '</label>';
  404. wp_dropdown_categories( $dropdown_options );
  405. }
  406. }
  407. /**
  408. * Displays a formats drop-down for filtering items.
  409. *
  410. * @since 5.2.0
  411. * @access protected
  412. *
  413. * @param string $post_type Post type slug.
  414. */
  415. protected function formats_dropdown( $post_type ) {
  416. /**
  417. * Filters whether to remove the 'Formats' drop-down from the post list table.
  418. *
  419. * @since 5.2.0
  420. * @since 5.5.0 The `$post_type` parameter was added.
  421. *
  422. * @param bool $disable Whether to disable the drop-down. Default false.
  423. * @param string $post_type Post type slug.
  424. */
  425. if ( apply_filters( 'disable_formats_dropdown', false, $post_type ) ) {
  426. return;
  427. }
  428. // Return if the post type doesn't have post formats or if we're in the Trash.
  429. if ( ! is_object_in_taxonomy( $post_type, 'post_format' ) || $this->is_trash ) {
  430. return;
  431. }
  432. // Make sure the dropdown shows only formats with a post count greater than 0.
  433. $used_post_formats = get_terms(
  434. array(
  435. 'taxonomy' => 'post_format',
  436. 'hide_empty' => true,
  437. )
  438. );
  439. // Return if there are no posts using formats.
  440. if ( ! $used_post_formats ) {
  441. return;
  442. }
  443. $displayed_post_format = isset( $_GET['post_format'] ) ? $_GET['post_format'] : '';
  444. ?>
  445. <label for="filter-by-format" class="screen-reader-text"><?php _e( 'Filter by post format' ); ?></label>
  446. <select name="post_format" id="filter-by-format">
  447. <option<?php selected( $displayed_post_format, '' ); ?> value=""><?php _e( 'All formats' ); ?></option>
  448. <?php
  449. foreach ( $used_post_formats as $used_post_format ) {
  450. // Post format slug.
  451. $slug = str_replace( 'post-format-', '', $used_post_format->slug );
  452. // Pretty, translated version of the post format slug.
  453. $pretty_name = get_post_format_string( $slug );
  454. // Skip the standard post format.
  455. if ( 'standard' === $slug ) {
  456. continue;
  457. }
  458. ?>
  459. <option<?php selected( $displayed_post_format, $slug ); ?> value="<?php echo esc_attr( $slug ); ?>"><?php echo esc_html( $pretty_name ); ?></option>
  460. <?php
  461. }
  462. ?>
  463. </select>
  464. <?php
  465. }
  466. /**
  467. * @param string $which
  468. */
  469. protected function extra_tablenav( $which ) {
  470. ?>
  471. <div class="alignleft actions">
  472. <?php
  473. if ( 'top' === $which ) {
  474. ob_start();
  475. $this->months_dropdown( $this->screen->post_type );
  476. $this->categories_dropdown( $this->screen->post_type );
  477. $this->formats_dropdown( $this->screen->post_type );
  478. /**
  479. * Fires before the Filter button on the Posts and Pages list tables.
  480. *
  481. * The Filter button allows sorting by date and/or category on the
  482. * Posts list table, and sorting by date on the Pages list table.
  483. *
  484. * @since 2.1.0
  485. * @since 4.4.0 The `$post_type` parameter was added.
  486. * @since 4.6.0 The `$which` parameter was added.
  487. *
  488. * @param string $post_type The post type slug.
  489. * @param string $which The location of the extra table nav markup:
  490. * 'top' or 'bottom' for WP_Posts_List_Table,
  491. * 'bar' for WP_Media_List_Table.
  492. */
  493. do_action( 'restrict_manage_posts', $this->screen->post_type, $which );
  494. $output = ob_get_clean();
  495. if ( ! empty( $output ) ) {
  496. echo $output;
  497. submit_button( __( 'Filter' ), '', 'filter_action', false, array( 'id' => 'post-query-submit' ) );
  498. }
  499. }
  500. if ( $this->is_trash && $this->has_items()
  501. && current_user_can( get_post_type_object( $this->screen->post_type )->cap->edit_others_posts )
  502. ) {
  503. submit_button( __( 'Empty Trash' ), 'apply', 'delete_all', false );
  504. }
  505. ?>
  506. </div>
  507. <?php
  508. /**
  509. * Fires immediately following the closing "actions" div in the tablenav for the posts
  510. * list table.
  511. *
  512. * @since 4.4.0
  513. *
  514. * @param string $which The location of the extra table nav markup: 'top' or 'bottom'.
  515. */
  516. do_action( 'manage_posts_extra_tablenav', $which );
  517. }
  518. /**
  519. * @return string
  520. */
  521. public function current_action() {
  522. if ( isset( $_REQUEST['delete_all'] ) || isset( $_REQUEST['delete_all2'] ) ) {
  523. return 'delete_all';
  524. }
  525. return parent::current_action();
  526. }
  527. /**
  528. * @global string $mode List table view mode.
  529. *
  530. * @return array
  531. */
  532. protected function get_table_classes() {
  533. global $mode;
  534. $mode_class = esc_attr( 'table-view-' . $mode );
  535. return array(
  536. 'widefat',
  537. 'fixed',
  538. 'striped',
  539. $mode_class,
  540. is_post_type_hierarchical( $this->screen->post_type ) ? 'pages' : 'posts',
  541. );
  542. }
  543. /**
  544. * @return array
  545. */
  546. public function get_columns() {
  547. $post_type = $this->screen->post_type;
  548. $posts_columns = array();
  549. $posts_columns['cb'] = '<input type="checkbox" />';
  550. /* translators: Posts screen column name. */
  551. $posts_columns['title'] = _x( 'Title', 'column name' );
  552. if ( post_type_supports( $post_type, 'author' ) ) {
  553. $posts_columns['author'] = __( 'Author' );
  554. }
  555. $taxonomies = get_object_taxonomies( $post_type, 'objects' );
  556. $taxonomies = wp_filter_object_list( $taxonomies, array( 'show_admin_column' => true ), 'and', 'name' );
  557. /**
  558. * Filters the taxonomy columns in the Posts list table.
  559. *
  560. * The dynamic portion of the hook name, `$post_type`, refers to the post
  561. * type slug.
  562. *
  563. * Possible hook names include:
  564. *
  565. * - `manage_taxonomies_for_post_columns`
  566. * - `manage_taxonomies_for_page_columns`
  567. *
  568. * @since 3.5.0
  569. *
  570. * @param string[] $taxonomies Array of taxonomy names to show columns for.
  571. * @param string $post_type The post type.
  572. */
  573. $taxonomies = apply_filters( "manage_taxonomies_for_{$post_type}_columns", $taxonomies, $post_type );
  574. $taxonomies = array_filter( $taxonomies, 'taxonomy_exists' );
  575. foreach ( $taxonomies as $taxonomy ) {
  576. if ( 'category' === $taxonomy ) {
  577. $column_key = 'categories';
  578. } elseif ( 'post_tag' === $taxonomy ) {
  579. $column_key = 'tags';
  580. } else {
  581. $column_key = 'taxonomy-' . $taxonomy;
  582. }
  583. $posts_columns[ $column_key ] = get_taxonomy( $taxonomy )->labels->name;
  584. }
  585. $post_status = ! empty( $_REQUEST['post_status'] ) ? $_REQUEST['post_status'] : 'all';
  586. if ( post_type_supports( $post_type, 'comments' )
  587. && ! in_array( $post_status, array( 'pending', 'draft', 'future' ), true )
  588. ) {
  589. $posts_columns['comments'] = sprintf(
  590. '<span class="vers comment-grey-bubble" title="%1$s"><span class="screen-reader-text">%2$s</span></span>',
  591. esc_attr__( 'Comments' ),
  592. __( 'Comments' )
  593. );
  594. }
  595. $posts_columns['date'] = __( 'Date' );
  596. if ( 'page' === $post_type ) {
  597. /**
  598. * Filters the columns displayed in the Pages list table.
  599. *
  600. * @since 2.5.0
  601. *
  602. * @param string[] $post_columns An associative array of column headings.
  603. */
  604. $posts_columns = apply_filters( 'manage_pages_columns', $posts_columns );
  605. } else {
  606. /**
  607. * Filters the columns displayed in the Posts list table.
  608. *
  609. * @since 1.5.0
  610. *
  611. * @param string[] $post_columns An associative array of column headings.
  612. * @param string $post_type The post type slug.
  613. */
  614. $posts_columns = apply_filters( 'manage_posts_columns', $posts_columns, $post_type );
  615. }
  616. /**
  617. * Filters the columns displayed in the Posts list table for a specific post type.
  618. *
  619. * The dynamic portion of the hook name, `$post_type`, refers to the post type slug.
  620. *
  621. * Possible hook names include:
  622. *
  623. * - `manage_post_posts_columns`
  624. * - `manage_page_posts_columns`
  625. *
  626. * @since 3.0.0
  627. *
  628. * @param string[] $post_columns An associative array of column headings.
  629. */
  630. return apply_filters( "manage_{$post_type}_posts_columns", $posts_columns );
  631. }
  632. /**
  633. * @return array
  634. */
  635. protected function get_sortable_columns() {
  636. return array(
  637. 'title' => 'title',
  638. 'parent' => 'parent',
  639. 'comments' => 'comment_count',
  640. 'date' => array( 'date', true ),
  641. );
  642. }
  643. /**
  644. * @global WP_Query $wp_query WordPress Query object.
  645. * @global int $per_page
  646. * @param array $posts
  647. * @param int $level
  648. */
  649. public function display_rows( $posts = array(), $level = 0 ) {
  650. global $wp_query, $per_page;
  651. if ( empty( $posts ) ) {
  652. $posts = $wp_query->posts;
  653. }
  654. add_filter( 'the_title', 'esc_html' );
  655. if ( $this->hierarchical_display ) {
  656. $this->_display_rows_hierarchical( $posts, $this->get_pagenum(), $per_page );
  657. } else {
  658. $this->_display_rows( $posts, $level );
  659. }
  660. }
  661. /**
  662. * @param array $posts
  663. * @param int $level
  664. */
  665. private function _display_rows( $posts, $level = 0 ) {
  666. $post_type = $this->screen->post_type;
  667. // Create array of post IDs.
  668. $post_ids = array();
  669. foreach ( $posts as $a_post ) {
  670. $post_ids[] = $a_post->ID;
  671. }
  672. if ( post_type_supports( $post_type, 'comments' ) ) {
  673. $this->comment_pending_count = get_pending_comments_num( $post_ids );
  674. }
  675. foreach ( $posts as $post ) {
  676. $this->single_row( $post, $level );
  677. }
  678. }
  679. /**
  680. * @global wpdb $wpdb WordPress database abstraction object.
  681. * @global WP_Post $post Global post object.
  682. * @param array $pages
  683. * @param int $pagenum
  684. * @param int $per_page
  685. */
  686. private function _display_rows_hierarchical( $pages, $pagenum = 1, $per_page = 20 ) {
  687. global $wpdb;
  688. $level = 0;
  689. if ( ! $pages ) {
  690. $pages = get_pages( array( 'sort_column' => 'menu_order' ) );
  691. if ( ! $pages ) {
  692. return;
  693. }
  694. }
  695. /*
  696. * Arrange pages into two parts: top level pages and children_pages.
  697. * children_pages is two dimensional array. Example:
  698. * children_pages[10][] contains all sub-pages whose parent is 10.
  699. * It only takes O( N ) to arrange this and it takes O( 1 ) for subsequent lookup operations
  700. * If searching, ignore hierarchy and treat everything as top level
  701. */
  702. if ( empty( $_REQUEST['s'] ) ) {
  703. $top_level_pages = array();
  704. $children_pages = array();
  705. foreach ( $pages as $page ) {
  706. // Catch and repair bad pages.
  707. if ( $page->post_parent === $page->ID ) {
  708. $page->post_parent = 0;
  709. $wpdb->update( $wpdb->posts, array( 'post_parent' => 0 ), array( 'ID' => $page->ID ) );
  710. clean_post_cache( $page );
  711. }
  712. if ( $page->post_parent > 0 ) {
  713. $children_pages[ $page->post_parent ][] = $page;
  714. } else {
  715. $top_level_pages[] = $page;
  716. }
  717. }
  718. $pages = &$top_level_pages;
  719. }
  720. $count = 0;
  721. $start = ( $pagenum - 1 ) * $per_page;
  722. $end = $start + $per_page;
  723. $to_display = array();
  724. foreach ( $pages as $page ) {
  725. if ( $count >= $end ) {
  726. break;
  727. }
  728. if ( $count >= $start ) {
  729. $to_display[ $page->ID ] = $level;
  730. }
  731. $count++;
  732. if ( isset( $children_pages ) ) {
  733. $this->_page_rows( $children_pages, $count, $page->ID, $level + 1, $pagenum, $per_page, $to_display );
  734. }
  735. }
  736. // If it is the last pagenum and there are orphaned pages, display them with paging as well.
  737. if ( isset( $children_pages ) && $count < $end ) {
  738. foreach ( $children_pages as $orphans ) {
  739. foreach ( $orphans as $op ) {
  740. if ( $count >= $end ) {
  741. break;
  742. }
  743. if ( $count >= $start ) {
  744. $to_display[ $op->ID ] = 0;
  745. }
  746. $count++;
  747. }
  748. }
  749. }
  750. $ids = array_keys( $to_display );
  751. _prime_post_caches( $ids );
  752. if ( ! isset( $GLOBALS['post'] ) ) {
  753. $GLOBALS['post'] = reset( $ids );
  754. }
  755. foreach ( $to_display as $page_id => $level ) {
  756. echo "\t";
  757. $this->single_row( $page_id, $level );
  758. }
  759. }
  760. /**
  761. * Given a top level page ID, display the nested hierarchy of sub-pages
  762. * together with paging support
  763. *
  764. * @since 3.1.0 (Standalone function exists since 2.6.0)
  765. * @since 4.2.0 Added the `$to_display` parameter.
  766. *
  767. * @param array $children_pages
  768. * @param int $count
  769. * @param int $parent_page
  770. * @param int $level
  771. * @param int $pagenum
  772. * @param int $per_page
  773. * @param array $to_display List of pages to be displayed. Passed by reference.
  774. */
  775. private function _page_rows( &$children_pages, &$count, $parent_page, $level, $pagenum, $per_page, &$to_display ) {
  776. if ( ! isset( $children_pages[ $parent_page ] ) ) {
  777. return;
  778. }
  779. $start = ( $pagenum - 1 ) * $per_page;
  780. $end = $start + $per_page;
  781. foreach ( $children_pages[ $parent_page ] as $page ) {
  782. if ( $count >= $end ) {
  783. break;
  784. }
  785. // If the page starts in a subtree, print the parents.
  786. if ( $count === $start && $page->post_parent > 0 ) {
  787. $my_parents = array();
  788. $my_parent = $page->post_parent;
  789. while ( $my_parent ) {
  790. // Get the ID from the list or the attribute if my_parent is an object.
  791. $parent_id = $my_parent;
  792. if ( is_object( $my_parent ) ) {
  793. $parent_id = $my_parent->ID;
  794. }
  795. $my_parent = get_post( $parent_id );
  796. $my_parents[] = $my_parent;
  797. if ( ! $my_parent->post_parent ) {
  798. break;
  799. }
  800. $my_parent = $my_parent->post_parent;
  801. }
  802. $num_parents = count( $my_parents );
  803. while ( $my_parent = array_pop( $my_parents ) ) {
  804. $to_display[ $my_parent->ID ] = $level - $num_parents;
  805. $num_parents--;
  806. }
  807. }
  808. if ( $count >= $start ) {
  809. $to_display[ $page->ID ] = $level;
  810. }
  811. $count++;
  812. $this->_page_rows( $children_pages, $count, $page->ID, $level + 1, $pagenum, $per_page, $to_display );
  813. }
  814. unset( $children_pages[ $parent_page ] ); // Required in order to keep track of orphans.
  815. }
  816. /**
  817. * Handles the checkbox column output.
  818. *
  819. * @since 4.3.0
  820. * @since 5.9.0 Renamed `$post` to `$item` to match parent class for PHP 8 named parameter support.
  821. *
  822. * @param WP_Post $item The current WP_Post object.
  823. */
  824. public function column_cb( $item ) {
  825. // Restores the more descriptive, specific name for use within this method.
  826. $post = $item;
  827. $show = current_user_can( 'edit_post', $post->ID );
  828. /**
  829. * Filters whether to show the bulk edit checkbox for a post in its list table.
  830. *
  831. * By default the checkbox is only shown if the current user can edit the post.
  832. *
  833. * @since 5.7.0
  834. *
  835. * @param bool $show Whether to show the checkbox.
  836. * @param WP_Post $post The current WP_Post object.
  837. */
  838. if ( apply_filters( 'wp_list_table_show_post_checkbox', $show, $post ) ) :
  839. ?>
  840. <label class="screen-reader-text" for="cb-select-<?php the_ID(); ?>">
  841. <?php
  842. /* translators: %s: Post title. */
  843. printf( __( 'Select %s' ), _draft_or_post_title() );
  844. ?>
  845. </label>
  846. <input id="cb-select-<?php the_ID(); ?>" type="checkbox" name="post[]" value="<?php the_ID(); ?>" />
  847. <div class="locked-indicator">
  848. <span class="locked-indicator-icon" aria-hidden="true"></span>
  849. <span class="screen-reader-text">
  850. <?php
  851. printf(
  852. /* translators: %s: Post title. */
  853. __( '&#8220;%s&#8221; is locked' ),
  854. _draft_or_post_title()
  855. );
  856. ?>
  857. </span>
  858. </div>
  859. <?php
  860. endif;
  861. }
  862. /**
  863. * @since 4.3.0
  864. *
  865. * @param WP_Post $post
  866. * @param string $classes
  867. * @param string $data
  868. * @param string $primary
  869. */
  870. protected function _column_title( $post, $classes, $data, $primary ) {
  871. echo '<td class="' . $classes . ' page-title" ', $data, '>';
  872. echo $this->column_title( $post );
  873. echo $this->handle_row_actions( $post, 'title', $primary );
  874. echo '</td>';
  875. }
  876. /**
  877. * Handles the title column output.
  878. *
  879. * @since 4.3.0
  880. *
  881. * @global string $mode List table view mode.
  882. *
  883. * @param WP_Post $post The current WP_Post object.
  884. */
  885. public function column_title( $post ) {
  886. global $mode;
  887. if ( $this->hierarchical_display ) {
  888. if ( 0 === $this->current_level && (int) $post->post_parent > 0 ) {
  889. // Sent level 0 by accident, by default, or because we don't know the actual level.
  890. $find_main_page = (int) $post->post_parent;
  891. while ( $find_main_page > 0 ) {
  892. $parent = get_post( $find_main_page );
  893. if ( is_null( $parent ) ) {
  894. break;
  895. }
  896. $this->current_level++;
  897. $find_main_page = (int) $parent->post_parent;
  898. if ( ! isset( $parent_name ) ) {
  899. /** This filter is documented in wp-includes/post-template.php */
  900. $parent_name = apply_filters( 'the_title', $parent->post_title, $parent->ID );
  901. }
  902. }
  903. }
  904. }
  905. $can_edit_post = current_user_can( 'edit_post', $post->ID );
  906. if ( $can_edit_post && 'trash' !== $post->post_status ) {
  907. $lock_holder = wp_check_post_lock( $post->ID );
  908. if ( $lock_holder ) {
  909. $lock_holder = get_userdata( $lock_holder );
  910. $locked_avatar = get_avatar( $lock_holder->ID, 18 );
  911. /* translators: %s: User's display name. */
  912. $locked_text = esc_html( sprintf( __( '%s is currently editing' ), $lock_holder->display_name ) );
  913. } else {
  914. $locked_avatar = '';
  915. $locked_text = '';
  916. }
  917. echo '<div class="locked-info"><span class="locked-avatar">' . $locked_avatar . '</span> <span class="locked-text">' . $locked_text . "</span></div>\n";
  918. }
  919. $pad = str_repeat( '&#8212; ', $this->current_level );
  920. echo '<strong>';
  921. $title = _draft_or_post_title();
  922. if ( $can_edit_post && 'trash' !== $post->post_status ) {
  923. printf(
  924. '<a class="row-title" href="%s" aria-label="%s">%s%s</a>',
  925. get_edit_post_link( $post->ID ),
  926. /* translators: %s: Post title. */
  927. esc_attr( sprintf( __( '&#8220;%s&#8221; (Edit)' ), $title ) ),
  928. $pad,
  929. $title
  930. );
  931. } else {
  932. printf(
  933. '<span>%s%s</span>',
  934. $pad,
  935. $title
  936. );
  937. }
  938. _post_states( $post );
  939. if ( isset( $parent_name ) ) {
  940. $post_type_object = get_post_type_object( $post->post_type );
  941. echo ' | ' . $post_type_object->labels->parent_item_colon . ' ' . esc_html( $parent_name );
  942. }
  943. echo "</strong>\n";
  944. if ( 'excerpt' === $mode
  945. && ! is_post_type_hierarchical( $this->screen->post_type )
  946. && current_user_can( 'read_post', $post->ID )
  947. ) {
  948. if ( post_password_required( $post ) ) {
  949. echo '<span class="protected-post-excerpt">' . esc_html( get_the_excerpt() ) . '</span>';
  950. } else {
  951. echo esc_html( get_the_excerpt() );
  952. }
  953. }
  954. get_inline_data( $post );
  955. }
  956. /**
  957. * Handles the post date column output.
  958. *
  959. * @since 4.3.0
  960. *
  961. * @global string $mode List table view mode.
  962. *
  963. * @param WP_Post $post The current WP_Post object.
  964. */
  965. public function column_date( $post ) {
  966. global $mode;
  967. if ( '0000-00-00 00:00:00' === $post->post_date ) {
  968. $t_time = __( 'Unpublished' );
  969. $time_diff = 0;
  970. } else {
  971. $t_time = sprintf(
  972. /* translators: 1: Post date, 2: Post time. */
  973. __( '%1$s at %2$s' ),
  974. /* translators: Post date format. See https://www.php.net/manual/datetime.format.php */
  975. get_the_time( __( 'Y/m/d' ), $post ),
  976. /* translators: Post time format. See https://www.php.net/manual/datetime.format.php */
  977. get_the_time( __( 'g:i a' ), $post )
  978. );
  979. $time = get_post_timestamp( $post );
  980. $time_diff = time() - $time;
  981. }
  982. if ( 'publish' === $post->post_status ) {
  983. $status = __( 'Published' );
  984. } elseif ( 'future' === $post->post_status ) {
  985. if ( $time_diff > 0 ) {
  986. $status = '<strong class="error-message">' . __( 'Missed schedule' ) . '</strong>';
  987. } else {
  988. $status = __( 'Scheduled' );
  989. }
  990. } else {
  991. $status = __( 'Last Modified' );
  992. }
  993. /**
  994. * Filters the status text of the post.
  995. *
  996. * @since 4.8.0
  997. *
  998. * @param string $status The status text.
  999. * @param WP_Post $post Post object.
  1000. * @param string $column_name The column name.
  1001. * @param string $mode The list display mode ('excerpt' or 'list').
  1002. */
  1003. $status = apply_filters( 'post_date_column_status', $status, $post, 'date', $mode );
  1004. if ( $status ) {
  1005. echo $status . '<br />';
  1006. }
  1007. /**
  1008. * Filters the published time of the post.
  1009. *
  1010. * @since 2.5.1
  1011. * @since 5.5.0 Removed the difference between 'excerpt' and 'list' modes.
  1012. * The published time and date are both displayed now,
  1013. * which is equivalent to the previous 'excerpt' mode.
  1014. *
  1015. * @param string $t_time The published time.
  1016. * @param WP_Post $post Post object.
  1017. * @param string $column_name The column name.
  1018. * @param string $mode The list display mode ('excerpt' or 'list').
  1019. */
  1020. echo apply_filters( 'post_date_column_time', $t_time, $post, 'date', $mode );
  1021. }
  1022. /**
  1023. * Handles the comments column output.
  1024. *
  1025. * @since 4.3.0
  1026. *
  1027. * @param WP_Post $post The current WP_Post object.
  1028. */
  1029. public function column_comments( $post ) {
  1030. ?>
  1031. <div class="post-com-count-wrapper">
  1032. <?php
  1033. $pending_comments = isset( $this->comment_pending_count[ $post->ID ] ) ? $this->comment_pending_count[ $post->ID ] : 0;
  1034. $this->comments_bubble( $post->ID, $pending_comments );
  1035. ?>
  1036. </div>
  1037. <?php
  1038. }
  1039. /**
  1040. * Handles the post author column output.
  1041. *
  1042. * @since 4.3.0
  1043. *
  1044. * @param WP_Post $post The current WP_Post object.
  1045. */
  1046. public function column_author( $post ) {
  1047. $args = array(
  1048. 'post_type' => $post->post_type,
  1049. 'author' => get_the_author_meta( 'ID' ),
  1050. );
  1051. echo $this->get_edit_link( $args, get_the_author() );
  1052. }
  1053. /**
  1054. * Handles the default column output.
  1055. *
  1056. * @since 4.3.0
  1057. * @since 5.9.0 Renamed `$post` to `$item` to match parent class for PHP 8 named parameter support.
  1058. *
  1059. * @param WP_Post $item The current WP_Post object.
  1060. * @param string $column_name The current column name.
  1061. */
  1062. public function column_default( $item, $column_name ) {
  1063. // Restores the more descriptive, specific name for use within this method.
  1064. $post = $item;
  1065. if ( 'categories' === $column_name ) {
  1066. $taxonomy = 'category';
  1067. } elseif ( 'tags' === $column_name ) {
  1068. $taxonomy = 'post_tag';
  1069. } elseif ( 0 === strpos( $column_name, 'taxonomy-' ) ) {
  1070. $taxonomy = substr( $column_name, 9 );
  1071. } else {
  1072. $taxonomy = false;
  1073. }
  1074. if ( $taxonomy ) {
  1075. $taxonomy_object = get_taxonomy( $taxonomy );
  1076. $terms = get_the_terms( $post->ID, $taxonomy );
  1077. if ( is_array( $terms ) ) {
  1078. $term_links = array();
  1079. foreach ( $terms as $t ) {
  1080. $posts_in_term_qv = array();
  1081. if ( 'post' !== $post->post_type ) {
  1082. $posts_in_term_qv['post_type'] = $post->post_type;
  1083. }
  1084. if ( $taxonomy_object->query_var ) {
  1085. $posts_in_term_qv[ $taxonomy_object->query_var ] = $t->slug;
  1086. } else {
  1087. $posts_in_term_qv['taxonomy'] = $taxonomy;
  1088. $posts_in_term_qv['term'] = $t->slug;
  1089. }
  1090. $label = esc_html( sanitize_term_field( 'name', $t->name, $t->term_id, $taxonomy, 'display' ) );
  1091. $term_links[] = $this->get_edit_link( $posts_in_term_qv, $label );
  1092. }
  1093. /**
  1094. * Filters the links in `$taxonomy` column of edit.php.
  1095. *
  1096. * @since 5.2.0
  1097. *
  1098. * @param string[] $term_links Array of term editing links.
  1099. * @param string $taxonomy Taxonomy name.
  1100. * @param WP_Term[] $terms Array of term objects appearing in the post row.
  1101. */
  1102. $term_links = apply_filters( 'post_column_taxonomy_links', $term_links, $taxonomy, $terms );
  1103. echo implode( wp_get_list_item_separator(), $term_links );
  1104. } else {
  1105. echo '<span aria-hidden="true">&#8212;</span><span class="screen-reader-text">' . $taxonomy_object->labels->no_terms . '</span>';
  1106. }
  1107. return;
  1108. }
  1109. if ( is_post_type_hierarchical( $post->post_type ) ) {
  1110. /**
  1111. * Fires in each custom column on the Posts list table.
  1112. *
  1113. * This hook only fires if the current post type is hierarchical,
  1114. * such as pages.
  1115. *
  1116. * @since 2.5.0
  1117. *
  1118. * @param string $column_name The name of the column to display.
  1119. * @param int $post_id The current post ID.
  1120. */
  1121. do_action( 'manage_pages_custom_column', $column_name, $post->ID );
  1122. } else {
  1123. /**
  1124. * Fires in each custom column in the Posts list table.
  1125. *
  1126. * This hook only fires if the current post type is non-hierarchical,
  1127. * such as posts.
  1128. *
  1129. * @since 1.5.0
  1130. *
  1131. * @param string $column_name The name of the column to display.
  1132. * @param int $post_id The current post ID.
  1133. */
  1134. do_action( 'manage_posts_custom_column', $column_name, $post->ID );
  1135. }
  1136. /**
  1137. * Fires for each custom column of a specific post type in the Posts list table.
  1138. *
  1139. * The dynamic portion of the hook name, `$post->post_type`, refers to the post type.
  1140. *
  1141. * Possible hook names include:
  1142. *
  1143. * - `manage_post_posts_custom_column`
  1144. * - `manage_page_posts_custom_column`
  1145. *
  1146. * @since 3.1.0
  1147. *
  1148. * @param string $column_name The name of the column to display.
  1149. * @param int $post_id The current post ID.
  1150. */
  1151. do_action( "manage_{$post->post_type}_posts_custom_column", $column_name, $post->ID );
  1152. }
  1153. /**
  1154. * @global WP_Post $post Global post object.
  1155. *
  1156. * @param int|WP_Post $post
  1157. * @param int $level
  1158. */
  1159. public function single_row( $post, $level = 0 ) {
  1160. $global_post = get_post();
  1161. $post = get_post( $post );
  1162. $this->current_level = $level;
  1163. $GLOBALS['post'] = $post;
  1164. setup_postdata( $post );
  1165. $classes = 'iedit author-' . ( get_current_user_id() === (int) $post->post_author ? 'self' : 'other' );
  1166. $lock_holder = wp_check_post_lock( $post->ID );
  1167. if ( $lock_holder ) {
  1168. $classes .= ' wp-locked';
  1169. }
  1170. if ( $post->post_parent ) {
  1171. $count = count( get_post_ancestors( $post->ID ) );
  1172. $classes .= ' level-' . $count;
  1173. } else {
  1174. $classes .= ' level-0';
  1175. }
  1176. ?>
  1177. <tr id="post-<?php echo $post->ID; ?>" class="<?php echo implode( ' ', get_post_class( $classes, $post->ID ) ); ?>">
  1178. <?php $this->single_row_columns( $post ); ?>
  1179. </tr>
  1180. <?php
  1181. $GLOBALS['post'] = $global_post;
  1182. }
  1183. /**
  1184. * Gets the name of the default primary column.
  1185. *
  1186. * @since 4.3.0
  1187. *
  1188. * @return string Name of the default primary column, in this case, 'title'.
  1189. */
  1190. protected function get_default_primary_column_name() {
  1191. return 'title';
  1192. }
  1193. /**
  1194. * Generates and displays row action links.
  1195. *
  1196. * @since 4.3.0
  1197. * @since 5.9.0 Renamed `$post` to `$item` to match parent class for PHP 8 named parameter support.
  1198. *
  1199. * @param WP_Post $item Post being acted upon.
  1200. * @param string $column_name Current column name.
  1201. * @param string $primary Primary column name.
  1202. * @return string Row actions output for posts, or an empty string
  1203. * if the current column is not the primary column.
  1204. */
  1205. protected function handle_row_actions( $item, $column_name, $primary ) {
  1206. if ( $primary !== $column_name ) {
  1207. return '';
  1208. }
  1209. // Restores the more descriptive, specific name for use within this method.
  1210. $post = $item;
  1211. $post_type_object = get_post_type_object( $post->post_type );
  1212. $can_edit_post = current_user_can( 'edit_post', $post->ID );
  1213. $actions = array();
  1214. $title = _draft_or_post_title();
  1215. if ( $can_edit_post && 'trash' !== $post->post_status ) {
  1216. $actions['edit'] = sprintf(
  1217. '<a href="%s" aria-label="%s">%s</a>',
  1218. get_edit_post_link( $post->ID ),
  1219. /* translators: %s: Post title. */
  1220. esc_attr( sprintf( __( 'Edit &#8220;%s&#8221;' ), $title ) ),
  1221. __( 'Edit' )
  1222. );
  1223. if ( 'wp_block' !== $post->post_type ) {
  1224. $actions['inline hide-if-no-js'] = sprintf(
  1225. '<button type="button" class="button-link editinline" aria-label="%s" aria-expanded="false">%s</button>',
  1226. /* translators: %s: Post title. */
  1227. esc_attr( sprintf( __( 'Quick edit &#8220;%s&#8221; inline' ), $title ) ),
  1228. __( 'Quick&nbsp;Edit' )
  1229. );
  1230. }
  1231. }
  1232. if ( current_user_can( 'delete_post', $post->ID ) ) {
  1233. if ( 'trash' === $post->post_status ) {
  1234. $actions['untrash'] = sprintf(
  1235. '<a href="%s" aria-label="%s">%s</a>',
  1236. wp_nonce_url( admin_url( sprintf( $post_type_object->_edit_link . '&amp;action=untrash', $post->ID ) ), 'untrash-post_' . $post->ID ),
  1237. /* translators: %s: Post title. */
  1238. esc_attr( sprintf( __( 'Restore &#8220;%s&#8221; from the Trash' ), $title ) ),
  1239. __( 'Restore' )
  1240. );
  1241. } elseif ( EMPTY_TRASH_DAYS ) {
  1242. $actions['trash'] = sprintf(
  1243. '<a href="%s" class="submitdelete" aria-label="%s">%s</a>',
  1244. get_delete_post_link( $post->ID ),
  1245. /* translators: %s: Post title. */
  1246. esc_attr( sprintf( __( 'Move &#8220;%s&#8221; to the Trash' ), $title ) ),
  1247. _x( 'Trash', 'verb' )
  1248. );
  1249. }
  1250. if ( 'trash' === $post->post_status || ! EMPTY_TRASH_DAYS ) {
  1251. $actions['delete'] = sprintf(
  1252. '<a href="%s" class="submitdelete" aria-label="%s">%s</a>',
  1253. get_delete_post_link( $post->ID, '', true ),
  1254. /* translators: %s: Post title. */
  1255. esc_attr( sprintf( __( 'Delete &#8220;%s&#8221; permanently' ), $title ) ),
  1256. __( 'Delete Permanently' )
  1257. );
  1258. }
  1259. }
  1260. if ( is_post_type_viewable( $post_type_object ) ) {
  1261. if ( in_array( $post->post_status, array( 'pending', 'draft', 'future' ), true ) ) {
  1262. if ( $can_edit_post ) {
  1263. $preview_link = get_preview_post_link( $post );
  1264. $actions['view'] = sprintf(
  1265. '<a href="%s" rel="bookmark" aria-label="%s">%s</a>',
  1266. esc_url( $preview_link ),
  1267. /* translators: %s: Post title. */
  1268. esc_attr( sprintf( __( 'Preview &#8220;%s&#8221;' ), $title ) ),
  1269. __( 'Preview' )
  1270. );
  1271. }
  1272. } elseif ( 'trash' !== $post->post_status ) {
  1273. $actions['view'] = sprintf(
  1274. '<a href="%s" rel="bookmark" aria-label="%s">%s</a>',
  1275. get_permalink( $post->ID ),
  1276. /* translators: %s: Post title. */
  1277. esc_attr( sprintf( __( 'View &#8220;%s&#8221;' ), $title ) ),
  1278. __( 'View' )
  1279. );
  1280. }
  1281. }
  1282. if ( 'wp_block' === $post->post_type ) {
  1283. $actions['export'] = sprintf(
  1284. '<button type="button" class="wp-list-reusable-blocks__export button-link" data-id="%s" aria-label="%s">%s</button>',
  1285. $post->ID,
  1286. /* translators: %s: Post title. */
  1287. esc_attr( sprintf( __( 'Export &#8220;%s&#8221; as JSON' ), $title ) ),
  1288. __( 'Export as JSON' )
  1289. );
  1290. }
  1291. if ( is_post_type_hierarchical( $post->post_type ) ) {
  1292. /**
  1293. * Filters the array of row action links on the Pages list table.
  1294. *
  1295. * The filter is evaluated only for hierarchical post types.
  1296. *
  1297. * @since 2.8.0
  1298. *
  1299. * @param string[] $actions An array of row action links. Defaults are
  1300. * 'Edit', 'Quick Edit', 'Restore', 'Trash',
  1301. * 'Delete Permanently', 'Preview', and 'View'.
  1302. * @param WP_Post $post The post object.
  1303. */
  1304. $actions = apply_filters( 'page_row_actions', $actions, $post );
  1305. } else {
  1306. /**
  1307. * Filters the array of row action links on the Posts list table.
  1308. *
  1309. * The filter is evaluated only for non-hierarchical post types.
  1310. *
  1311. * @since 2.8.0
  1312. *
  1313. * @param string[] $actions An array of row action links. Defaults are
  1314. * 'Edit', 'Quick Edit', 'Restore', 'Trash',
  1315. * 'Delete Permanently', 'Preview', and 'View'.
  1316. * @param WP_Post $post The post object.
  1317. */
  1318. $actions = apply_filters( 'post_row_actions', $actions, $post );
  1319. }
  1320. return $this->row_actions( $actions );
  1321. }
  1322. /**
  1323. * Outputs the hidden row displayed when inline editing
  1324. *
  1325. * @since 3.1.0
  1326. *
  1327. * @global string $mode List table view mode.
  1328. */
  1329. public function inline_edit() {
  1330. global $mode;
  1331. $screen = $this->screen;
  1332. $post = get_default_post_to_edit( $screen->post_type );
  1333. $post_type_object = get_post_type_object( $screen->post_type );
  1334. $taxonomy_names = get_object_taxonomies( $screen->post_type );
  1335. $hierarchical_taxonomies = array();
  1336. $flat_taxonomies = array();
  1337. foreach ( $taxonomy_names as $taxonomy_name ) {
  1338. $taxonomy = get_taxonomy( $taxonomy_name );
  1339. $show_in_quick_edit = $taxonomy->show_in_quick_edit;
  1340. /**
  1341. * Filters whether the current taxonomy should be shown in the Quick Edit panel.
  1342. *
  1343. * @since 4.2.0
  1344. *
  1345. * @param bool $show_in_quick_edit Whether to show the current taxonomy in Quick Edit.
  1346. * @param string $taxonomy_name Taxonomy name.
  1347. * @param string $post_type Post type of current Quick Edit post.
  1348. */
  1349. if ( ! apply_filters( 'quick_edit_show_taxonomy', $show_in_quick_edit, $taxonomy_name, $screen->post_type ) ) {
  1350. continue;
  1351. }
  1352. if ( $taxonomy->hierarchical ) {
  1353. $hierarchical_taxonomies[] = $taxonomy;
  1354. } else {
  1355. $flat_taxonomies[] = $taxonomy;
  1356. }
  1357. }
  1358. $m = ( isset( $mode ) && 'excerpt' === $mode ) ? 'excerpt' : 'list';
  1359. $can_publish = current_user_can( $post_type_object->cap->publish_posts );
  1360. $core_columns = array(
  1361. 'cb' => true,
  1362. 'date' => true,
  1363. 'title' => true,
  1364. 'categories' => true,
  1365. 'tags' => true,
  1366. 'comments' => true,
  1367. 'author' => true,
  1368. );
  1369. ?>
  1370. <form method="get">
  1371. <table style="display: none"><tbody id="inlineedit">
  1372. <?php
  1373. $hclass = count( $hierarchical_taxonomies ) ? 'post' : 'page';
  1374. $inline_edit_classes = "inline-edit-row inline-edit-row-$hclass";
  1375. $bulk_edit_classes = "bulk-edit-row bulk-edit-row-$hclass bulk-edit-{$screen->post_type}";
  1376. $quick_edit_classes = "quick-edit-row quick-edit-row-$hclass inline-edit-{$screen->post_type}";
  1377. $bulk = 0;
  1378. while ( $bulk < 2 ) :
  1379. $classes = $inline_edit_classes . ' ';
  1380. $classes .= $bulk ? $bulk_edit_classes : $quick_edit_classes;
  1381. ?>
  1382. <tr id="<?php echo $bulk ? 'bulk-edit' : 'inline-edit'; ?>" class="<?php echo $classes; ?>" style="display: none">
  1383. <td colspan="<?php echo $this->get_column_count(); ?>" class="colspanchange">
  1384. <div class="inline-edit-wrapper" role="region" aria-labelledby="<?php echo $bulk ? 'bulk' : 'quick'; ?>-edit-legend">
  1385. <fieldset class="inline-edit-col-left">
  1386. <legend class="inline-edit-legend" id="<?php echo $bulk ? 'bulk' : 'quick'; ?>-edit-legend"><?php echo $bulk ? __( 'Bulk Edit' ) : __( 'Quick Edit' ); ?></legend>
  1387. <div class="inline-edit-col">
  1388. <?php if ( post_type_supports( $screen->post_type, 'title' ) ) : ?>
  1389. <?php if ( $bulk ) : ?>
  1390. <div id="bulk-title-div">
  1391. <div id="bulk-titles"></div>
  1392. </div>
  1393. <?php else : // $bulk ?>
  1394. <label>
  1395. <span class="title"><?php _e( 'Title' ); ?></span>
  1396. <span class="input-text-wrap"><input type="text" name="post_title" class="ptitle" value="" /></span>
  1397. </label>
  1398. <?php if ( is_post_type_viewable( $screen->post_type ) ) : ?>
  1399. <label>
  1400. <span class="title"><?php _e( 'Slug' ); ?></span>
  1401. <span class="input-text-wrap"><input type="text" name="post_name" value="" autocomplete="off" spellcheck="false" /></span>
  1402. </label>
  1403. <?php endif; // is_post_type_viewable() ?>
  1404. <?php endif; // $bulk ?>
  1405. <?php endif; // post_type_supports( ... 'title' ) ?>
  1406. <?php if ( ! $bulk ) : ?>
  1407. <fieldset class="inline-edit-date">
  1408. <legend><span class="title"><?php _e( 'Date' ); ?></span></legend>
  1409. <?php touch_time( 1, 1, 0, 1 ); ?>
  1410. </fieldset>
  1411. <br class="clear" />
  1412. <?php endif; // $bulk ?>
  1413. <?php
  1414. if ( post_type_supports( $screen->post_type, 'author' ) ) {
  1415. $authors_dropdown = '';
  1416. if ( current_user_can( $post_type_object->cap->edit_others_posts ) ) {
  1417. $dropdown_name = 'post_author';
  1418. $dropdown_class = 'authors';
  1419. if ( wp_is_large_user_count() ) {
  1420. $authors_dropdown = sprintf( '<select name="%s" class="%s hidden"></select>', esc_attr( $dropdown_name ), esc_attr( $dropdown_class ) );
  1421. } else {
  1422. $users_opt = array(
  1423. 'hide_if_only_one_author' => false,
  1424. 'capability' => array( $post_type_object->cap->edit_posts ),
  1425. 'name' => $dropdown_name,
  1426. 'class' => $dropdown_class,
  1427. 'multi' => 1,
  1428. 'echo' => 0,
  1429. 'show' => 'display_name_with_login',
  1430. );
  1431. if ( $bulk ) {
  1432. $users_opt['show_option_none'] = __( '&mdash; No Change &mdash;' );
  1433. }
  1434. /**
  1435. * Filters the arguments used to generate the Quick Edit authors drop-down.
  1436. *
  1437. * @since 5.6.0
  1438. *
  1439. * @see wp_dropdown_users()
  1440. *
  1441. * @param array $users_opt An array of arguments passed to wp_dropdown_users().
  1442. * @param bool $bulk A flag to denote if it's a bulk action.
  1443. */
  1444. $users_opt = apply_filters( 'quick_edit_dropdown_authors_args', $users_opt, $bulk );
  1445. $authors = wp_dropdown_users( $users_opt );
  1446. if ( $authors ) {
  1447. $authors_dropdown = '<label class="inline-edit-author">';
  1448. $authors_dropdown .= '<span class="title">' . __( 'Author' ) . '</span>';
  1449. $authors_dropdown .= $authors;
  1450. $authors_dropdown .= '</label>';
  1451. }
  1452. }
  1453. } // current_user_can( 'edit_others_posts' )
  1454. if ( ! $bulk ) {
  1455. echo $authors_dropdown;
  1456. }
  1457. } // post_type_supports( ... 'author' )
  1458. ?>
  1459. <?php if ( ! $bulk && $can_publish ) : ?>
  1460. <div class="inline-edit-group wp-clearfix">
  1461. <label class="alignleft">
  1462. <span class="title"><?php _e( 'Password' ); ?></span>
  1463. <span class="input-text-wrap"><input type="text" name="post_password" class="inline-edit-password-input" value="" /></span>
  1464. </label>
  1465. <span class="alignleft inline-edit-or">
  1466. <?php
  1467. /* translators: Between password field and private checkbox on post quick edit interface. */
  1468. _e( '&ndash;OR&ndash;' );
  1469. ?>
  1470. </span>
  1471. <label class="alignleft inline-edit-private">
  1472. <input type="checkbox" name="keep_private" value="private" />
  1473. <span class="checkbox-title"><?php _e( 'Private' ); ?></span>
  1474. </label>
  1475. </div>
  1476. <?php endif; ?>
  1477. </div>
  1478. </fieldset>
  1479. <?php if ( count( $hierarchical_taxonomies ) && ! $bulk ) : ?>
  1480. <fieldset class="inline-edit-col-center inline-edit-categories">
  1481. <div class="inline-edit-col">
  1482. <?php foreach ( $hierarchical_taxonomies as $taxonomy ) : ?>
  1483. <span class="title inline-edit-categories-label"><?php echo esc_html( $taxonomy->labels->name ); ?></span>
  1484. <input type="hidden" name="<?php echo ( 'category' === $taxonomy->name ) ? 'post_category[]' : 'tax_input[' . esc_attr( $taxonomy->name ) . '][]'; ?>" value="0" />
  1485. <ul class="cat-checklist <?php echo esc_attr( $taxonomy->name ); ?>-checklist">
  1486. <?php wp_terms_checklist( 0, array( 'taxonomy' => $taxonomy->name ) ); ?>
  1487. </ul>
  1488. <?php endforeach; // $hierarchical_taxonomies as $taxonomy ?>
  1489. </div>
  1490. </fieldset>
  1491. <?php endif; // count( $hierarchical_taxonomies ) && ! $bulk ?>
  1492. <fieldset class="inline-edit-col-right">
  1493. <div class="inline-edit-col">
  1494. <?php
  1495. if ( post_type_supports( $screen->post_type, 'author' ) && $bulk ) {
  1496. echo $authors_dropdown;
  1497. }
  1498. ?>
  1499. <?php if ( post_type_supports( $screen->post_type, 'page-attributes' ) ) : ?>
  1500. <?php if ( $post_type_object->hierarchical ) : ?>
  1501. <label>
  1502. <span class="title"><?php _e( 'Parent' ); ?></span>
  1503. <?php
  1504. $dropdown_args = array(
  1505. 'post_type' => $post_type_object->name,
  1506. 'selected' => $post->post_parent,
  1507. 'name' => 'post_parent',
  1508. 'show_option_none' => __( 'Main Page (no parent)' ),
  1509. 'option_none_value' => 0,
  1510. 'sort_column' => 'menu_order, post_title',
  1511. );
  1512. if ( $bulk ) {
  1513. $dropdown_args['show_option_no_change'] = __( '&mdash; No Change &mdash;' );
  1514. }
  1515. /**
  1516. * Filters the arguments used to generate the Quick Edit page-parent drop-down.
  1517. *
  1518. * @since 2.7.0
  1519. * @since 5.6.0 The `$bulk` parameter was added.
  1520. *
  1521. * @see wp_dropdown_pages()
  1522. *
  1523. * @param array $dropdown_args An array of arguments passed to wp_dropdown_pages().
  1524. * @param bool $bulk A flag to denote if it's a bulk action.
  1525. */
  1526. $dropdown_args = apply_filters( 'quick_edit_dropdown_pages_args', $dropdown_args, $bulk );
  1527. wp_dropdown_pages( $dropdown_args );
  1528. ?>
  1529. </label>
  1530. <?php endif; // hierarchical ?>
  1531. <?php if ( ! $bulk ) : ?>
  1532. <label>
  1533. <span class="title"><?php _e( 'Order' ); ?></span>
  1534. <span class="input-text-wrap"><input type="text" name="menu_order" class="inline-edit-menu-order-input" value="<?php echo $post->menu_order; ?>" /></span>
  1535. </label>
  1536. <?php endif; // ! $bulk ?>
  1537. <?php endif; // post_type_supports( ... 'page-attributes' ) ?>
  1538. <?php if ( 0 < count( get_page_templates( null, $screen->post_type ) ) ) : ?>
  1539. <label>
  1540. <span class="title"><?php _e( 'Template' ); ?></span>
  1541. <select name="page_template">
  1542. <?php if ( $bulk ) : ?>
  1543. <option value="-1"><?php _e( '&mdash; No Change &mdash;' ); ?></option>
  1544. <?php endif; // $bulk ?>
  1545. <?php
  1546. /** This filter is documented in wp-admin/includes/meta-boxes.php */
  1547. $default_title = apply_filters( 'default_page_template_title', __( 'Default template' ), 'quick-edit' );
  1548. ?>
  1549. <option value="default"><?php echo esc_html( $default_title ); ?></option>
  1550. <?php page_template_dropdown( '', $screen->post_type ); ?>
  1551. </select>
  1552. </label>
  1553. <?php endif; ?>
  1554. <?php if ( count( $flat_taxonomies ) && ! $bulk ) : ?>
  1555. <?php foreach ( $flat_taxonomies as $taxonomy ) : ?>
  1556. <?php if ( current_user_can( $taxonomy->cap->assign_terms ) ) : ?>
  1557. <?php $taxonomy_name = esc_attr( $taxonomy->name ); ?>
  1558. <div class="inline-edit-tags-wrap">
  1559. <label class="inline-edit-tags">
  1560. <span class="title"><?php echo esc_html( $taxonomy->labels->name ); ?></span>
  1561. <textarea data-wp-taxonomy="<?php echo $taxonomy_name; ?>" cols="22" rows="1" name="tax_input[<?php echo esc_attr( $taxonomy->name ); ?>]" class="tax_input_<?php echo esc_attr( $taxonomy->name ); ?>" aria-describedby="inline-edit-<?php echo esc_attr( $taxonomy->name ); ?>-desc"></textarea>
  1562. </label>
  1563. <p class="howto" id="inline-edit-<?php echo esc_attr( $taxonomy->name ); ?>-desc"><?php echo esc_html( $taxonomy->labels->separate_items_with_commas ); ?></p>
  1564. </div>
  1565. <?php endif; // current_user_can( 'assign_terms' ) ?>
  1566. <?php endforeach; // $flat_taxonomies as $taxonomy ?>
  1567. <?php endif; // count( $flat_taxonomies ) && ! $bulk ?>
  1568. <?php if ( post_type_supports( $screen->post_type, 'comments' ) || post_type_supports( $screen->post_type, 'trackbacks' ) ) : ?>
  1569. <?php if ( $bulk ) : ?>
  1570. <div class="inline-edit-group wp-clearfix">
  1571. <?php if ( post_type_supports( $screen->post_type, 'comments' ) ) : ?>
  1572. <label class="alignleft">
  1573. <span class="title"><?php _e( 'Comments' ); ?></span>
  1574. <select name="comment_status">
  1575. <option value=""><?php _e( '&mdash; No Change &mdash;' ); ?></option>
  1576. <option value="open"><?php _e( 'Allow' ); ?></option>
  1577. <option value="closed"><?php _e( 'Do not allow' ); ?></option>
  1578. </select>
  1579. </label>
  1580. <?php endif; ?>
  1581. <?php if ( post_type_supports( $screen->post_type, 'trackbacks' ) ) : ?>
  1582. <label class="alignright">
  1583. <span class="title"><?php _e( 'Pings' ); ?></span>
  1584. <select name="ping_status">
  1585. <option value=""><?php _e( '&mdash; No Change &mdash;' ); ?></option>
  1586. <option value="open"><?php _e( 'Allow' ); ?></option>
  1587. <option value="closed"><?php _e( 'Do not allow' ); ?></option>
  1588. </select>
  1589. </label>
  1590. <?php endif; ?>
  1591. </div>
  1592. <?php else : // $bulk ?>
  1593. <div class="inline-edit-group wp-clearfix">
  1594. <?php if ( post_type_supports( $screen->post_type, 'comments' ) ) : ?>
  1595. <label class="alignleft">
  1596. <input type="checkbox" name="comment_status" value="open" />
  1597. <span class="checkbox-title"><?php _e( 'Allow Comments' ); ?></span>
  1598. </label>
  1599. <?php endif; ?>
  1600. <?php if ( post_type_supports( $screen->post_type, 'trackbacks' ) ) : ?>
  1601. <label class="alignleft">
  1602. <input type="checkbox" name="ping_status" value="open" />
  1603. <span class="checkbox-title"><?php _e( 'Allow Pings' ); ?></span>
  1604. </label>
  1605. <?php endif; ?>
  1606. </div>
  1607. <?php endif; // $bulk ?>
  1608. <?php endif; // post_type_supports( ... comments or pings ) ?>
  1609. <div class="inline-edit-group wp-clearfix">
  1610. <label class="inline-edit-status alignleft">
  1611. <span class="title"><?php _e( 'Status' ); ?></span>
  1612. <select name="_status">
  1613. <?php if ( $bulk ) : ?>
  1614. <option value="-1"><?php _e( '&mdash; No Change &mdash;' ); ?></option>
  1615. <?php endif; // $bulk ?>
  1616. <?php if ( $can_publish ) : // Contributors only get "Unpublished" and "Pending Review". ?>
  1617. <option value="publish"><?php _e( 'Published' ); ?></option>
  1618. <option value="future"><?php _e( 'Scheduled' ); ?></option>
  1619. <?php if ( $bulk ) : ?>
  1620. <option value="private"><?php _e( 'Private' ); ?></option>
  1621. <?php endif; // $bulk ?>
  1622. <?php endif; ?>
  1623. <option value="pending"><?php _e( 'Pending Review' ); ?></option>
  1624. <option value="draft"><?php _e( 'Draft' ); ?></option>
  1625. </select>
  1626. </label>
  1627. <?php if ( 'post' === $screen->post_type && $can_publish && current_user_can( $post_type_object->cap->edit_others_posts ) ) : ?>
  1628. <?php if ( $bulk ) : ?>
  1629. <label class="alignright">
  1630. <span class="title"><?php _e( 'Sticky' ); ?></span>
  1631. <select name="sticky">
  1632. <option value="-1"><?php _e( '&mdash; No Change &mdash;' ); ?></option>
  1633. <option value="sticky"><?php _e( 'Sticky' ); ?></option>
  1634. <option value="unsticky"><?php _e( 'Not Sticky' ); ?></option>
  1635. </select>
  1636. </label>
  1637. <?php else : // $bulk ?>
  1638. <label class="alignleft">
  1639. <input type="checkbox" name="sticky" value="sticky" />
  1640. <span class="checkbox-title"><?php _e( 'Make this post sticky' ); ?></span>
  1641. </label>
  1642. <?php endif; // $bulk ?>
  1643. <?php endif; // 'post' && $can_publish && current_user_can( 'edit_others_posts' ) ?>
  1644. </div>
  1645. <?php if ( $bulk && current_theme_supports( 'post-formats' ) && post_type_supports( $screen->post_type, 'post-formats' ) ) : ?>
  1646. <?php $post_formats = get_theme_support( 'post-formats' ); ?>
  1647. <label class="alignleft">
  1648. <span class="title"><?php _ex( 'Format', 'post format' ); ?></span>
  1649. <select name="post_format">
  1650. <option value="-1"><?php _e( '&mdash; No Change &mdash;' ); ?></option>
  1651. <option value="0"><?php echo get_post_format_string( 'standard' ); ?></option>
  1652. <?php if ( is_array( $post_formats[0] ) ) : ?>
  1653. <?php foreach ( $post_formats[0] as $format ) : ?>
  1654. <option value="<?php echo esc_attr( $format ); ?>"><?php echo esc_html( get_post_format_string( $format ) ); ?></option>
  1655. <?php endforeach; ?>
  1656. <?php endif; ?>
  1657. </select>
  1658. </label>
  1659. <?php endif; ?>
  1660. </div>
  1661. </fieldset>
  1662. <?php
  1663. list( $columns ) = $this->get_column_info();
  1664. foreach ( $columns as $column_name => $column_display_name ) {
  1665. if ( isset( $core_columns[ $column_name ] ) ) {
  1666. continue;
  1667. }
  1668. if ( $bulk ) {
  1669. /**
  1670. * Fires once for each column in Bulk Edit mode.
  1671. *
  1672. * @since 2.7.0
  1673. *
  1674. * @param string $column_name Name of the column to edit.
  1675. * @param string $post_type The post type slug.
  1676. */
  1677. do_action( 'bulk_edit_custom_box', $column_name, $screen->post_type );
  1678. } else {
  1679. /**
  1680. * Fires once for each column in Quick Edit mode.
  1681. *
  1682. * @since 2.7.0
  1683. *
  1684. * @param string $column_name Name of the column to edit.
  1685. * @param string $post_type The post type slug, or current screen name if this is a taxonomy list table.
  1686. * @param string $taxonomy The taxonomy name, if any.
  1687. */
  1688. do_action( 'quick_edit_custom_box', $column_name, $screen->post_type, '' );
  1689. }
  1690. }
  1691. ?>
  1692. <div class="submit inline-edit-save">
  1693. <?php if ( ! $bulk ) : ?>
  1694. <?php wp_nonce_field( 'inlineeditnonce', '_inline_edit', false ); ?>
  1695. <button type="button" class="button button-primary save"><?php _e( 'Update' ); ?></button>
  1696. <?php else : ?>
  1697. <?php submit_button( __( 'Update' ), 'primary', 'bulk_edit', false ); ?>
  1698. <?php endif; ?>
  1699. <button type="button" class="button cancel"><?php _e( 'Cancel' ); ?></button>
  1700. <?php if ( ! $bulk ) : ?>
  1701. <span class="spinner"></span>
  1702. <?php endif; ?>
  1703. <input type="hidden" name="post_view" value="<?php echo esc_attr( $m ); ?>" />
  1704. <input type="hidden" name="screen" value="<?php echo esc_attr( $screen->id ); ?>" />
  1705. <?php if ( ! $bulk && ! post_type_supports( $screen->post_type, 'author' ) ) : ?>
  1706. <input type="hidden" name="post_author" value="<?php echo esc_attr( $post->post_author ); ?>" />
  1707. <?php endif; ?>
  1708. <div class="notice notice-error notice-alt inline hidden">
  1709. <p class="error"></p>
  1710. </div>
  1711. </div>
  1712. </div> <!-- end of .inline-edit-wrapper -->
  1713. </td></tr>
  1714. <?php
  1715. $bulk++;
  1716. endwhile;
  1717. ?>
  1718. </tbody></table>
  1719. </form>
  1720. <?php
  1721. }
  1722. }