Ei kuvausta

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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034
  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 $label Link text.
  221. * @param string $class Optional. Class attribute. Default empty string.
  222. * @return string The formatted link string.
  223. */
  224. protected function get_edit_link( $args, $label, $class = '' ) {
  225. $url = add_query_arg( $args, 'edit.php' );
  226. $class_html = '';
  227. $aria_current = '';
  228. if ( ! empty( $class ) ) {
  229. $class_html = sprintf(
  230. ' class="%s"',
  231. esc_attr( $class )
  232. );
  233. if ( 'current' === $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. $label
  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, eg.
  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
  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, $level, $pagenum, $per_page, &$to_display ) {
  776. if ( ! isset( $children_pages[ $parent ] ) ) {
  777. return;
  778. }
  779. $start = ( $pagenum - 1 ) * $per_page;
  780. $end = $start + $per_page;
  781. foreach ( $children_pages[ $parent ] 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 ] ); // Required in order to keep track of orphans.
  815. }
  816. /**
  817. * Handles the checkbox column output.
  818. *
  819. * @since 4.3.0
  820. *
  821. * @param WP_Post $post The current WP_Post object.
  822. */
  823. public function column_cb( $post ) {
  824. $show = current_user_can( 'edit_post', $post->ID );
  825. /**
  826. * Filters whether to show the bulk edit checkbox for a post in its list table.
  827. *
  828. * By default the checkbox is only shown if the current user can edit the post.
  829. *
  830. * @since 5.7.0
  831. *
  832. * @param bool $show Whether to show the checkbox.
  833. * @param WP_Post $post The current WP_Post object.
  834. */
  835. if ( apply_filters( 'wp_list_table_show_post_checkbox', $show, $post ) ) :
  836. ?>
  837. <label class="screen-reader-text" for="cb-select-<?php the_ID(); ?>">
  838. <?php
  839. /* translators: %s: Post title. */
  840. printf( __( 'Select %s' ), _draft_or_post_title() );
  841. ?>
  842. </label>
  843. <input id="cb-select-<?php the_ID(); ?>" type="checkbox" name="post[]" value="<?php the_ID(); ?>" />
  844. <div class="locked-indicator">
  845. <span class="locked-indicator-icon" aria-hidden="true"></span>
  846. <span class="screen-reader-text">
  847. <?php
  848. printf(
  849. /* translators: %s: Post title. */
  850. __( '&#8220;%s&#8221; is locked' ),
  851. _draft_or_post_title()
  852. );
  853. ?>
  854. </span>
  855. </div>
  856. <?php
  857. endif;
  858. }
  859. /**
  860. * @since 4.3.0
  861. *
  862. * @param WP_Post $post
  863. * @param string $classes
  864. * @param string $data
  865. * @param string $primary
  866. */
  867. protected function _column_title( $post, $classes, $data, $primary ) {
  868. echo '<td class="' . $classes . ' page-title" ', $data, '>';
  869. echo $this->column_title( $post );
  870. echo $this->handle_row_actions( $post, 'title', $primary );
  871. echo '</td>';
  872. }
  873. /**
  874. * Handles the title column output.
  875. *
  876. * @since 4.3.0
  877. *
  878. * @global string $mode List table view mode.
  879. *
  880. * @param WP_Post $post The current WP_Post object.
  881. */
  882. public function column_title( $post ) {
  883. global $mode;
  884. if ( $this->hierarchical_display ) {
  885. if ( 0 === $this->current_level && (int) $post->post_parent > 0 ) {
  886. // Sent level 0 by accident, by default, or because we don't know the actual level.
  887. $find_main_page = (int) $post->post_parent;
  888. while ( $find_main_page > 0 ) {
  889. $parent = get_post( $find_main_page );
  890. if ( is_null( $parent ) ) {
  891. break;
  892. }
  893. $this->current_level++;
  894. $find_main_page = (int) $parent->post_parent;
  895. if ( ! isset( $parent_name ) ) {
  896. /** This filter is documented in wp-includes/post-template.php */
  897. $parent_name = apply_filters( 'the_title', $parent->post_title, $parent->ID );
  898. }
  899. }
  900. }
  901. }
  902. $can_edit_post = current_user_can( 'edit_post', $post->ID );
  903. if ( $can_edit_post && 'trash' !== $post->post_status ) {
  904. $lock_holder = wp_check_post_lock( $post->ID );
  905. if ( $lock_holder ) {
  906. $lock_holder = get_userdata( $lock_holder );
  907. $locked_avatar = get_avatar( $lock_holder->ID, 18 );
  908. /* translators: %s: User's display name. */
  909. $locked_text = esc_html( sprintf( __( '%s is currently editing' ), $lock_holder->display_name ) );
  910. } else {
  911. $locked_avatar = '';
  912. $locked_text = '';
  913. }
  914. echo '<div class="locked-info"><span class="locked-avatar">' . $locked_avatar . '</span> <span class="locked-text">' . $locked_text . "</span></div>\n";
  915. }
  916. $pad = str_repeat( '&#8212; ', $this->current_level );
  917. echo '<strong>';
  918. $title = _draft_or_post_title();
  919. if ( $can_edit_post && 'trash' !== $post->post_status ) {
  920. printf(
  921. '<a class="row-title" href="%s" aria-label="%s">%s%s</a>',
  922. get_edit_post_link( $post->ID ),
  923. /* translators: %s: Post title. */
  924. esc_attr( sprintf( __( '&#8220;%s&#8221; (Edit)' ), $title ) ),
  925. $pad,
  926. $title
  927. );
  928. } else {
  929. printf(
  930. '<span>%s%s</span>',
  931. $pad,
  932. $title
  933. );
  934. }
  935. _post_states( $post );
  936. if ( isset( $parent_name ) ) {
  937. $post_type_object = get_post_type_object( $post->post_type );
  938. echo ' | ' . $post_type_object->labels->parent_item_colon . ' ' . esc_html( $parent_name );
  939. }
  940. echo "</strong>\n";
  941. if ( 'excerpt' === $mode
  942. && ! is_post_type_hierarchical( $this->screen->post_type )
  943. && current_user_can( 'read_post', $post->ID )
  944. ) {
  945. if ( post_password_required( $post ) ) {
  946. echo '<span class="protected-post-excerpt">' . esc_html( get_the_excerpt() ) . '</span>';
  947. } else {
  948. echo esc_html( get_the_excerpt() );
  949. }
  950. }
  951. get_inline_data( $post );
  952. }
  953. /**
  954. * Handles the post date column output.
  955. *
  956. * @since 4.3.0
  957. *
  958. * @global string $mode List table view mode.
  959. *
  960. * @param WP_Post $post The current WP_Post object.
  961. */
  962. public function column_date( $post ) {
  963. global $mode;
  964. if ( '0000-00-00 00:00:00' === $post->post_date ) {
  965. $t_time = __( 'Unpublished' );
  966. $time_diff = 0;
  967. } else {
  968. $t_time = sprintf(
  969. /* translators: 1: Post date, 2: Post time. */
  970. __( '%1$s at %2$s' ),
  971. /* translators: Post date format. See https://www.php.net/manual/datetime.format.php */
  972. get_the_time( __( 'Y/m/d' ), $post ),
  973. /* translators: Post time format. See https://www.php.net/manual/datetime.format.php */
  974. get_the_time( __( 'g:i a' ), $post )
  975. );
  976. $time = get_post_timestamp( $post );
  977. $time_diff = time() - $time;
  978. }
  979. if ( 'publish' === $post->post_status ) {
  980. $status = __( 'Published' );
  981. } elseif ( 'future' === $post->post_status ) {
  982. if ( $time_diff > 0 ) {
  983. $status = '<strong class="error-message">' . __( 'Missed schedule' ) . '</strong>';
  984. } else {
  985. $status = __( 'Scheduled' );
  986. }
  987. } else {
  988. $status = __( 'Last Modified' );
  989. }
  990. /**
  991. * Filters the status text of the post.
  992. *
  993. * @since 4.8.0
  994. *
  995. * @param string $status The status text.
  996. * @param WP_Post $post Post object.
  997. * @param string $column_name The column name.
  998. * @param string $mode The list display mode ('excerpt' or 'list').
  999. */
  1000. $status = apply_filters( 'post_date_column_status', $status, $post, 'date', $mode );
  1001. if ( $status ) {
  1002. echo $status . '<br />';
  1003. }
  1004. /**
  1005. * Filters the published time of the post.
  1006. *
  1007. * @since 2.5.1
  1008. * @since 5.5.0 Removed the difference between 'excerpt' and 'list' modes.
  1009. * The published time and date are both displayed now,
  1010. * which is equivalent to the previous 'excerpt' mode.
  1011. *
  1012. * @param string $t_time The published time.
  1013. * @param WP_Post $post Post object.
  1014. * @param string $column_name The column name.
  1015. * @param string $mode The list display mode ('excerpt' or 'list').
  1016. */
  1017. echo apply_filters( 'post_date_column_time', $t_time, $post, 'date', $mode );
  1018. }
  1019. /**
  1020. * Handles the comments column output.
  1021. *
  1022. * @since 4.3.0
  1023. *
  1024. * @param WP_Post $post The current WP_Post object.
  1025. */
  1026. public function column_comments( $post ) {
  1027. ?>
  1028. <div class="post-com-count-wrapper">
  1029. <?php
  1030. $pending_comments = isset( $this->comment_pending_count[ $post->ID ] ) ? $this->comment_pending_count[ $post->ID ] : 0;
  1031. $this->comments_bubble( $post->ID, $pending_comments );
  1032. ?>
  1033. </div>
  1034. <?php
  1035. }
  1036. /**
  1037. * Handles the post author column output.
  1038. *
  1039. * @since 4.3.0
  1040. *
  1041. * @param WP_Post $post The current WP_Post object.
  1042. */
  1043. public function column_author( $post ) {
  1044. $args = array(
  1045. 'post_type' => $post->post_type,
  1046. 'author' => get_the_author_meta( 'ID' ),
  1047. );
  1048. echo $this->get_edit_link( $args, get_the_author() );
  1049. }
  1050. /**
  1051. * Handles the default column output.
  1052. *
  1053. * @since 4.3.0
  1054. *
  1055. * @param WP_Post $post The current WP_Post object.
  1056. * @param string $column_name The current column name.
  1057. */
  1058. public function column_default( $post, $column_name ) {
  1059. if ( 'categories' === $column_name ) {
  1060. $taxonomy = 'category';
  1061. } elseif ( 'tags' === $column_name ) {
  1062. $taxonomy = 'post_tag';
  1063. } elseif ( 0 === strpos( $column_name, 'taxonomy-' ) ) {
  1064. $taxonomy = substr( $column_name, 9 );
  1065. } else {
  1066. $taxonomy = false;
  1067. }
  1068. if ( $taxonomy ) {
  1069. $taxonomy_object = get_taxonomy( $taxonomy );
  1070. $terms = get_the_terms( $post->ID, $taxonomy );
  1071. if ( is_array( $terms ) ) {
  1072. $term_links = array();
  1073. foreach ( $terms as $t ) {
  1074. $posts_in_term_qv = array();
  1075. if ( 'post' !== $post->post_type ) {
  1076. $posts_in_term_qv['post_type'] = $post->post_type;
  1077. }
  1078. if ( $taxonomy_object->query_var ) {
  1079. $posts_in_term_qv[ $taxonomy_object->query_var ] = $t->slug;
  1080. } else {
  1081. $posts_in_term_qv['taxonomy'] = $taxonomy;
  1082. $posts_in_term_qv['term'] = $t->slug;
  1083. }
  1084. $label = esc_html( sanitize_term_field( 'name', $t->name, $t->term_id, $taxonomy, 'display' ) );
  1085. $term_links[] = $this->get_edit_link( $posts_in_term_qv, $label );
  1086. }
  1087. /**
  1088. * Filters the links in `$taxonomy` column of edit.php.
  1089. *
  1090. * @since 5.2.0
  1091. *
  1092. * @param string[] $term_links Array of term editing links.
  1093. * @param string $taxonomy Taxonomy name.
  1094. * @param WP_Term[] $terms Array of term objects appearing in the post row.
  1095. */
  1096. $term_links = apply_filters( 'post_column_taxonomy_links', $term_links, $taxonomy, $terms );
  1097. /* translators: Used between list items, there is a space after the comma. */
  1098. echo implode( __( ', ' ), $term_links );
  1099. } else {
  1100. echo '<span aria-hidden="true">&#8212;</span><span class="screen-reader-text">' . $taxonomy_object->labels->no_terms . '</span>';
  1101. }
  1102. return;
  1103. }
  1104. if ( is_post_type_hierarchical( $post->post_type ) ) {
  1105. /**
  1106. * Fires in each custom column on the Posts list table.
  1107. *
  1108. * This hook only fires if the current post type is hierarchical,
  1109. * such as pages.
  1110. *
  1111. * @since 2.5.0
  1112. *
  1113. * @param string $column_name The name of the column to display.
  1114. * @param int $post_id The current post ID.
  1115. */
  1116. do_action( 'manage_pages_custom_column', $column_name, $post->ID );
  1117. } else {
  1118. /**
  1119. * Fires in each custom column in the Posts list table.
  1120. *
  1121. * This hook only fires if the current post type is non-hierarchical,
  1122. * such as posts.
  1123. *
  1124. * @since 1.5.0
  1125. *
  1126. * @param string $column_name The name of the column to display.
  1127. * @param int $post_id The current post ID.
  1128. */
  1129. do_action( 'manage_posts_custom_column', $column_name, $post->ID );
  1130. }
  1131. /**
  1132. * Fires for each custom column of a specific post type in the Posts list table.
  1133. *
  1134. * The dynamic portion of the hook name, `$post->post_type`, refers to the post type.
  1135. *
  1136. * Possible hook names include:
  1137. *
  1138. * - `manage_post_posts_custom_column`
  1139. * - `manage_page_posts_custom_column`
  1140. *
  1141. * @since 3.1.0
  1142. *
  1143. * @param string $column_name The name of the column to display.
  1144. * @param int $post_id The current post ID.
  1145. */
  1146. do_action( "manage_{$post->post_type}_posts_custom_column", $column_name, $post->ID );
  1147. }
  1148. /**
  1149. * @global WP_Post $post Global post object.
  1150. *
  1151. * @param int|WP_Post $post
  1152. * @param int $level
  1153. */
  1154. public function single_row( $post, $level = 0 ) {
  1155. $global_post = get_post();
  1156. $post = get_post( $post );
  1157. $this->current_level = $level;
  1158. $GLOBALS['post'] = $post;
  1159. setup_postdata( $post );
  1160. $classes = 'iedit author-' . ( get_current_user_id() === (int) $post->post_author ? 'self' : 'other' );
  1161. $lock_holder = wp_check_post_lock( $post->ID );
  1162. if ( $lock_holder ) {
  1163. $classes .= ' wp-locked';
  1164. }
  1165. if ( $post->post_parent ) {
  1166. $count = count( get_post_ancestors( $post->ID ) );
  1167. $classes .= ' level-' . $count;
  1168. } else {
  1169. $classes .= ' level-0';
  1170. }
  1171. ?>
  1172. <tr id="post-<?php echo $post->ID; ?>" class="<?php echo implode( ' ', get_post_class( $classes, $post->ID ) ); ?>">
  1173. <?php $this->single_row_columns( $post ); ?>
  1174. </tr>
  1175. <?php
  1176. $GLOBALS['post'] = $global_post;
  1177. }
  1178. /**
  1179. * Gets the name of the default primary column.
  1180. *
  1181. * @since 4.3.0
  1182. *
  1183. * @return string Name of the default primary column, in this case, 'title'.
  1184. */
  1185. protected function get_default_primary_column_name() {
  1186. return 'title';
  1187. }
  1188. /**
  1189. * Generates and displays row action links.
  1190. *
  1191. * @since 4.3.0
  1192. *
  1193. * @param WP_Post $post Post being acted upon.
  1194. * @param string $column_name Current column name.
  1195. * @param string $primary Primary column name.
  1196. * @return string Row actions output for posts, or an empty string
  1197. * if the current column is not the primary column.
  1198. */
  1199. protected function handle_row_actions( $post, $column_name, $primary ) {
  1200. if ( $primary !== $column_name ) {
  1201. return '';
  1202. }
  1203. $post_type_object = get_post_type_object( $post->post_type );
  1204. $can_edit_post = current_user_can( 'edit_post', $post->ID );
  1205. $actions = array();
  1206. $title = _draft_or_post_title();
  1207. if ( $can_edit_post && 'trash' !== $post->post_status ) {
  1208. $actions['edit'] = sprintf(
  1209. '<a href="%s" aria-label="%s">%s</a>',
  1210. get_edit_post_link( $post->ID ),
  1211. /* translators: %s: Post title. */
  1212. esc_attr( sprintf( __( 'Edit &#8220;%s&#8221;' ), $title ) ),
  1213. __( 'Edit' )
  1214. );
  1215. if ( 'wp_block' !== $post->post_type ) {
  1216. $actions['inline hide-if-no-js'] = sprintf(
  1217. '<button type="button" class="button-link editinline" aria-label="%s" aria-expanded="false">%s</button>',
  1218. /* translators: %s: Post title. */
  1219. esc_attr( sprintf( __( 'Quick edit &#8220;%s&#8221; inline' ), $title ) ),
  1220. __( 'Quick&nbsp;Edit' )
  1221. );
  1222. }
  1223. }
  1224. if ( current_user_can( 'delete_post', $post->ID ) ) {
  1225. if ( 'trash' === $post->post_status ) {
  1226. $actions['untrash'] = sprintf(
  1227. '<a href="%s" aria-label="%s">%s</a>',
  1228. wp_nonce_url( admin_url( sprintf( $post_type_object->_edit_link . '&amp;action=untrash', $post->ID ) ), 'untrash-post_' . $post->ID ),
  1229. /* translators: %s: Post title. */
  1230. esc_attr( sprintf( __( 'Restore &#8220;%s&#8221; from the Trash' ), $title ) ),
  1231. __( 'Restore' )
  1232. );
  1233. } elseif ( EMPTY_TRASH_DAYS ) {
  1234. $actions['trash'] = sprintf(
  1235. '<a href="%s" class="submitdelete" aria-label="%s">%s</a>',
  1236. get_delete_post_link( $post->ID ),
  1237. /* translators: %s: Post title. */
  1238. esc_attr( sprintf( __( 'Move &#8220;%s&#8221; to the Trash' ), $title ) ),
  1239. _x( 'Trash', 'verb' )
  1240. );
  1241. }
  1242. if ( 'trash' === $post->post_status || ! EMPTY_TRASH_DAYS ) {
  1243. $actions['delete'] = sprintf(
  1244. '<a href="%s" class="submitdelete" aria-label="%s">%s</a>',
  1245. get_delete_post_link( $post->ID, '', true ),
  1246. /* translators: %s: Post title. */
  1247. esc_attr( sprintf( __( 'Delete &#8220;%s&#8221; permanently' ), $title ) ),
  1248. __( 'Delete Permanently' )
  1249. );
  1250. }
  1251. }
  1252. if ( is_post_type_viewable( $post_type_object ) ) {
  1253. if ( in_array( $post->post_status, array( 'pending', 'draft', 'future' ), true ) ) {
  1254. if ( $can_edit_post ) {
  1255. $preview_link = get_preview_post_link( $post );
  1256. $actions['view'] = sprintf(
  1257. '<a href="%s" rel="bookmark" aria-label="%s">%s</a>',
  1258. esc_url( $preview_link ),
  1259. /* translators: %s: Post title. */
  1260. esc_attr( sprintf( __( 'Preview &#8220;%s&#8221;' ), $title ) ),
  1261. __( 'Preview' )
  1262. );
  1263. }
  1264. } elseif ( 'trash' !== $post->post_status ) {
  1265. $actions['view'] = sprintf(
  1266. '<a href="%s" rel="bookmark" aria-label="%s">%s</a>',
  1267. get_permalink( $post->ID ),
  1268. /* translators: %s: Post title. */
  1269. esc_attr( sprintf( __( 'View &#8220;%s&#8221;' ), $title ) ),
  1270. __( 'View' )
  1271. );
  1272. }
  1273. }
  1274. if ( 'wp_block' === $post->post_type ) {
  1275. $actions['export'] = sprintf(
  1276. '<button type="button" class="wp-list-reusable-blocks__export button-link" data-id="%s" aria-label="%s">%s</button>',
  1277. $post->ID,
  1278. /* translators: %s: Post title. */
  1279. esc_attr( sprintf( __( 'Export &#8220;%s&#8221; as JSON' ), $title ) ),
  1280. __( 'Export as JSON' )
  1281. );
  1282. }
  1283. if ( is_post_type_hierarchical( $post->post_type ) ) {
  1284. /**
  1285. * Filters the array of row action links on the Pages list table.
  1286. *
  1287. * The filter is evaluated only for hierarchical post types.
  1288. *
  1289. * @since 2.8.0
  1290. *
  1291. * @param string[] $actions An array of row action links. Defaults are
  1292. * 'Edit', 'Quick Edit', 'Restore', 'Trash',
  1293. * 'Delete Permanently', 'Preview', and 'View'.
  1294. * @param WP_Post $post The post object.
  1295. */
  1296. $actions = apply_filters( 'page_row_actions', $actions, $post );
  1297. } else {
  1298. /**
  1299. * Filters the array of row action links on the Posts list table.
  1300. *
  1301. * The filter is evaluated only for non-hierarchical post types.
  1302. *
  1303. * @since 2.8.0
  1304. *
  1305. * @param string[] $actions An array of row action links. Defaults are
  1306. * 'Edit', 'Quick Edit', 'Restore', 'Trash',
  1307. * 'Delete Permanently', 'Preview', and 'View'.
  1308. * @param WP_Post $post The post object.
  1309. */
  1310. $actions = apply_filters( 'post_row_actions', $actions, $post );
  1311. }
  1312. return $this->row_actions( $actions );
  1313. }
  1314. /**
  1315. * Outputs the hidden row displayed when inline editing
  1316. *
  1317. * @since 3.1.0
  1318. *
  1319. * @global string $mode List table view mode.
  1320. */
  1321. public function inline_edit() {
  1322. global $mode;
  1323. $screen = $this->screen;
  1324. $post = get_default_post_to_edit( $screen->post_type );
  1325. $post_type_object = get_post_type_object( $screen->post_type );
  1326. $taxonomy_names = get_object_taxonomies( $screen->post_type );
  1327. $hierarchical_taxonomies = array();
  1328. $flat_taxonomies = array();
  1329. foreach ( $taxonomy_names as $taxonomy_name ) {
  1330. $taxonomy = get_taxonomy( $taxonomy_name );
  1331. $show_in_quick_edit = $taxonomy->show_in_quick_edit;
  1332. /**
  1333. * Filters whether the current taxonomy should be shown in the Quick Edit panel.
  1334. *
  1335. * @since 4.2.0
  1336. *
  1337. * @param bool $show_in_quick_edit Whether to show the current taxonomy in Quick Edit.
  1338. * @param string $taxonomy_name Taxonomy name.
  1339. * @param string $post_type Post type of current Quick Edit post.
  1340. */
  1341. if ( ! apply_filters( 'quick_edit_show_taxonomy', $show_in_quick_edit, $taxonomy_name, $screen->post_type ) ) {
  1342. continue;
  1343. }
  1344. if ( $taxonomy->hierarchical ) {
  1345. $hierarchical_taxonomies[] = $taxonomy;
  1346. } else {
  1347. $flat_taxonomies[] = $taxonomy;
  1348. }
  1349. }
  1350. $m = ( isset( $mode ) && 'excerpt' === $mode ) ? 'excerpt' : 'list';
  1351. $can_publish = current_user_can( $post_type_object->cap->publish_posts );
  1352. $core_columns = array(
  1353. 'cb' => true,
  1354. 'date' => true,
  1355. 'title' => true,
  1356. 'categories' => true,
  1357. 'tags' => true,
  1358. 'comments' => true,
  1359. 'author' => true,
  1360. );
  1361. ?>
  1362. <form method="get">
  1363. <table style="display: none"><tbody id="inlineedit">
  1364. <?php
  1365. $hclass = count( $hierarchical_taxonomies ) ? 'post' : 'page';
  1366. $inline_edit_classes = "inline-edit-row inline-edit-row-$hclass";
  1367. $bulk_edit_classes = "bulk-edit-row bulk-edit-row-$hclass bulk-edit-{$screen->post_type}";
  1368. $quick_edit_classes = "quick-edit-row quick-edit-row-$hclass inline-edit-{$screen->post_type}";
  1369. $bulk = 0;
  1370. while ( $bulk < 2 ) :
  1371. $classes = $inline_edit_classes . ' ';
  1372. $classes .= $bulk ? $bulk_edit_classes : $quick_edit_classes;
  1373. ?>
  1374. <tr id="<?php echo $bulk ? 'bulk-edit' : 'inline-edit'; ?>" class="<?php echo $classes; ?>" style="display: none">
  1375. <td colspan="<?php echo $this->get_column_count(); ?>" class="colspanchange">
  1376. <fieldset class="inline-edit-col-left">
  1377. <legend class="inline-edit-legend"><?php echo $bulk ? __( 'Bulk Edit' ) : __( 'Quick Edit' ); ?></legend>
  1378. <div class="inline-edit-col">
  1379. <?php if ( post_type_supports( $screen->post_type, 'title' ) ) : ?>
  1380. <?php if ( $bulk ) : ?>
  1381. <div id="bulk-title-div">
  1382. <div id="bulk-titles"></div>
  1383. </div>
  1384. <?php else : // $bulk ?>
  1385. <label>
  1386. <span class="title"><?php _e( 'Title' ); ?></span>
  1387. <span class="input-text-wrap"><input type="text" name="post_title" class="ptitle" value="" /></span>
  1388. </label>
  1389. <?php if ( is_post_type_viewable( $screen->post_type ) ) : ?>
  1390. <label>
  1391. <span class="title"><?php _e( 'Slug' ); ?></span>
  1392. <span class="input-text-wrap"><input type="text" name="post_name" value="" /></span>
  1393. </label>
  1394. <?php endif; // is_post_type_viewable() ?>
  1395. <?php endif; // $bulk ?>
  1396. <?php endif; // post_type_supports( ... 'title' ) ?>
  1397. <?php if ( ! $bulk ) : ?>
  1398. <fieldset class="inline-edit-date">
  1399. <legend><span class="title"><?php _e( 'Date' ); ?></span></legend>
  1400. <?php touch_time( 1, 1, 0, 1 ); ?>
  1401. </fieldset>
  1402. <br class="clear" />
  1403. <?php endif; // $bulk ?>
  1404. <?php
  1405. if ( post_type_supports( $screen->post_type, 'author' ) ) {
  1406. $authors_dropdown = '';
  1407. if ( current_user_can( $post_type_object->cap->edit_others_posts ) ) {
  1408. $users_opt = array(
  1409. 'hide_if_only_one_author' => false,
  1410. 'who' => 'authors',
  1411. 'name' => 'post_author',
  1412. 'class' => 'authors',
  1413. 'multi' => 1,
  1414. 'echo' => 0,
  1415. 'show' => 'display_name_with_login',
  1416. );
  1417. if ( $bulk ) {
  1418. $users_opt['show_option_none'] = __( '&mdash; No Change &mdash;' );
  1419. }
  1420. /**
  1421. * Filters the arguments used to generate the Quick Edit authors drop-down.
  1422. *
  1423. * @since 5.6.0
  1424. *
  1425. * @see wp_dropdown_users()
  1426. *
  1427. * @param array $users_opt An array of arguments passed to wp_dropdown_users().
  1428. * @param bool $bulk A flag to denote if it's a bulk action.
  1429. */
  1430. $users_opt = apply_filters( 'quick_edit_dropdown_authors_args', $users_opt, $bulk );
  1431. $authors = wp_dropdown_users( $users_opt );
  1432. if ( $authors ) {
  1433. $authors_dropdown = '<label class="inline-edit-author">';
  1434. $authors_dropdown .= '<span class="title">' . __( 'Author' ) . '</span>';
  1435. $authors_dropdown .= $authors;
  1436. $authors_dropdown .= '</label>';
  1437. }
  1438. } // current_user_can( 'edit_others_posts' )
  1439. if ( ! $bulk ) {
  1440. echo $authors_dropdown;
  1441. }
  1442. } // post_type_supports( ... 'author' )
  1443. ?>
  1444. <?php if ( ! $bulk && $can_publish ) : ?>
  1445. <div class="inline-edit-group wp-clearfix">
  1446. <label class="alignleft">
  1447. <span class="title"><?php _e( 'Password' ); ?></span>
  1448. <span class="input-text-wrap"><input type="text" name="post_password" class="inline-edit-password-input" value="" /></span>
  1449. </label>
  1450. <span class="alignleft inline-edit-or">
  1451. <?php
  1452. /* translators: Between password field and private checkbox on post quick edit interface. */
  1453. _e( '&ndash;OR&ndash;' );
  1454. ?>
  1455. </span>
  1456. <label class="alignleft inline-edit-private">
  1457. <input type="checkbox" name="keep_private" value="private" />
  1458. <span class="checkbox-title"><?php _e( 'Private' ); ?></span>
  1459. </label>
  1460. </div>
  1461. <?php endif; ?>
  1462. </div>
  1463. </fieldset>
  1464. <?php if ( count( $hierarchical_taxonomies ) && ! $bulk ) : ?>
  1465. <fieldset class="inline-edit-col-center inline-edit-categories">
  1466. <div class="inline-edit-col">
  1467. <?php foreach ( $hierarchical_taxonomies as $taxonomy ) : ?>
  1468. <span class="title inline-edit-categories-label"><?php echo esc_html( $taxonomy->labels->name ); ?></span>
  1469. <input type="hidden" name="<?php echo ( 'category' === $taxonomy->name ) ? 'post_category[]' : 'tax_input[' . esc_attr( $taxonomy->name ) . '][]'; ?>" value="0" />
  1470. <ul class="cat-checklist <?php echo esc_attr( $taxonomy->name ); ?>-checklist">
  1471. <?php wp_terms_checklist( null, array( 'taxonomy' => $taxonomy->name ) ); ?>
  1472. </ul>
  1473. <?php endforeach; // $hierarchical_taxonomies as $taxonomy ?>
  1474. </div>
  1475. </fieldset>
  1476. <?php endif; // count( $hierarchical_taxonomies ) && ! $bulk ?>
  1477. <fieldset class="inline-edit-col-right">
  1478. <div class="inline-edit-col">
  1479. <?php
  1480. if ( post_type_supports( $screen->post_type, 'author' ) && $bulk ) {
  1481. echo $authors_dropdown;
  1482. }
  1483. ?>
  1484. <?php if ( post_type_supports( $screen->post_type, 'page-attributes' ) ) : ?>
  1485. <?php if ( $post_type_object->hierarchical ) : ?>
  1486. <label>
  1487. <span class="title"><?php _e( 'Parent' ); ?></span>
  1488. <?php
  1489. $dropdown_args = array(
  1490. 'post_type' => $post_type_object->name,
  1491. 'selected' => $post->post_parent,
  1492. 'name' => 'post_parent',
  1493. 'show_option_none' => __( 'Main Page (no parent)' ),
  1494. 'option_none_value' => 0,
  1495. 'sort_column' => 'menu_order, post_title',
  1496. );
  1497. if ( $bulk ) {
  1498. $dropdown_args['show_option_no_change'] = __( '&mdash; No Change &mdash;' );
  1499. }
  1500. /**
  1501. * Filters the arguments used to generate the Quick Edit page-parent drop-down.
  1502. *
  1503. * @since 2.7.0
  1504. * @since 5.6.0 The `$bulk` parameter was added.
  1505. *
  1506. * @see wp_dropdown_pages()
  1507. *
  1508. * @param array $dropdown_args An array of arguments passed to wp_dropdown_pages().
  1509. * @param bool $bulk A flag to denote if it's a bulk action.
  1510. */
  1511. $dropdown_args = apply_filters( 'quick_edit_dropdown_pages_args', $dropdown_args, $bulk );
  1512. wp_dropdown_pages( $dropdown_args );
  1513. ?>
  1514. </label>
  1515. <?php endif; // hierarchical ?>
  1516. <?php if ( ! $bulk ) : ?>
  1517. <label>
  1518. <span class="title"><?php _e( 'Order' ); ?></span>
  1519. <span class="input-text-wrap"><input type="text" name="menu_order" class="inline-edit-menu-order-input" value="<?php echo $post->menu_order; ?>" /></span>
  1520. </label>
  1521. <?php endif; // ! $bulk ?>
  1522. <?php endif; // post_type_supports( ... 'page-attributes' ) ?>
  1523. <?php if ( 0 < count( get_page_templates( null, $screen->post_type ) ) ) : ?>
  1524. <label>
  1525. <span class="title"><?php _e( 'Template' ); ?></span>
  1526. <select name="page_template">
  1527. <?php if ( $bulk ) : ?>
  1528. <option value="-1"><?php _e( '&mdash; No Change &mdash;' ); ?></option>
  1529. <?php endif; // $bulk ?>
  1530. <?php
  1531. /** This filter is documented in wp-admin/includes/meta-boxes.php */
  1532. $default_title = apply_filters( 'default_page_template_title', __( 'Default template' ), 'quick-edit' );
  1533. ?>
  1534. <option value="default"><?php echo esc_html( $default_title ); ?></option>
  1535. <?php page_template_dropdown( '', $screen->post_type ); ?>
  1536. </select>
  1537. </label>
  1538. <?php endif; ?>
  1539. <?php if ( count( $flat_taxonomies ) && ! $bulk ) : ?>
  1540. <?php foreach ( $flat_taxonomies as $taxonomy ) : ?>
  1541. <?php if ( current_user_can( $taxonomy->cap->assign_terms ) ) : ?>
  1542. <?php $taxonomy_name = esc_attr( $taxonomy->name ); ?>
  1543. <label class="inline-edit-tags">
  1544. <span class="title"><?php echo esc_html( $taxonomy->labels->name ); ?></span>
  1545. <textarea data-wp-taxonomy="<?php echo $taxonomy_name; ?>" cols="22" rows="1" name="tax_input[<?php echo $taxonomy_name; ?>]" class="tax_input_<?php echo $taxonomy_name; ?>"></textarea>
  1546. </label>
  1547. <?php endif; // current_user_can( 'assign_terms' ) ?>
  1548. <?php endforeach; // $flat_taxonomies as $taxonomy ?>
  1549. <?php endif; // count( $flat_taxonomies ) && ! $bulk ?>
  1550. <?php if ( post_type_supports( $screen->post_type, 'comments' ) || post_type_supports( $screen->post_type, 'trackbacks' ) ) : ?>
  1551. <?php if ( $bulk ) : ?>
  1552. <div class="inline-edit-group wp-clearfix">
  1553. <?php if ( post_type_supports( $screen->post_type, 'comments' ) ) : ?>
  1554. <label class="alignleft">
  1555. <span class="title"><?php _e( 'Comments' ); ?></span>
  1556. <select name="comment_status">
  1557. <option value=""><?php _e( '&mdash; No Change &mdash;' ); ?></option>
  1558. <option value="open"><?php _e( 'Allow' ); ?></option>
  1559. <option value="closed"><?php _e( 'Do not allow' ); ?></option>
  1560. </select>
  1561. </label>
  1562. <?php endif; ?>
  1563. <?php if ( post_type_supports( $screen->post_type, 'trackbacks' ) ) : ?>
  1564. <label class="alignright">
  1565. <span class="title"><?php _e( 'Pings' ); ?></span>
  1566. <select name="ping_status">
  1567. <option value=""><?php _e( '&mdash; No Change &mdash;' ); ?></option>
  1568. <option value="open"><?php _e( 'Allow' ); ?></option>
  1569. <option value="closed"><?php _e( 'Do not allow' ); ?></option>
  1570. </select>
  1571. </label>
  1572. <?php endif; ?>
  1573. </div>
  1574. <?php else : // $bulk ?>
  1575. <div class="inline-edit-group wp-clearfix">
  1576. <?php if ( post_type_supports( $screen->post_type, 'comments' ) ) : ?>
  1577. <label class="alignleft">
  1578. <input type="checkbox" name="comment_status" value="open" />
  1579. <span class="checkbox-title"><?php _e( 'Allow Comments' ); ?></span>
  1580. </label>
  1581. <?php endif; ?>
  1582. <?php if ( post_type_supports( $screen->post_type, 'trackbacks' ) ) : ?>
  1583. <label class="alignleft">
  1584. <input type="checkbox" name="ping_status" value="open" />
  1585. <span class="checkbox-title"><?php _e( 'Allow Pings' ); ?></span>
  1586. </label>
  1587. <?php endif; ?>
  1588. </div>
  1589. <?php endif; // $bulk ?>
  1590. <?php endif; // post_type_supports( ... comments or pings ) ?>
  1591. <div class="inline-edit-group wp-clearfix">
  1592. <label class="inline-edit-status alignleft">
  1593. <span class="title"><?php _e( 'Status' ); ?></span>
  1594. <select name="_status">
  1595. <?php if ( $bulk ) : ?>
  1596. <option value="-1"><?php _e( '&mdash; No Change &mdash;' ); ?></option>
  1597. <?php endif; // $bulk ?>
  1598. <?php if ( $can_publish ) : // Contributors only get "Unpublished" and "Pending Review". ?>
  1599. <option value="publish"><?php _e( 'Published' ); ?></option>
  1600. <option value="future"><?php _e( 'Scheduled' ); ?></option>
  1601. <?php if ( $bulk ) : ?>
  1602. <option value="private"><?php _e( 'Private' ); ?></option>
  1603. <?php endif; // $bulk ?>
  1604. <?php endif; ?>
  1605. <option value="pending"><?php _e( 'Pending Review' ); ?></option>
  1606. <option value="draft"><?php _e( 'Draft' ); ?></option>
  1607. </select>
  1608. </label>
  1609. <?php if ( 'post' === $screen->post_type && $can_publish && current_user_can( $post_type_object->cap->edit_others_posts ) ) : ?>
  1610. <?php if ( $bulk ) : ?>
  1611. <label class="alignright">
  1612. <span class="title"><?php _e( 'Sticky' ); ?></span>
  1613. <select name="sticky">
  1614. <option value="-1"><?php _e( '&mdash; No Change &mdash;' ); ?></option>
  1615. <option value="sticky"><?php _e( 'Sticky' ); ?></option>
  1616. <option value="unsticky"><?php _e( 'Not Sticky' ); ?></option>
  1617. </select>
  1618. </label>
  1619. <?php else : // $bulk ?>
  1620. <label class="alignleft">
  1621. <input type="checkbox" name="sticky" value="sticky" />
  1622. <span class="checkbox-title"><?php _e( 'Make this post sticky' ); ?></span>
  1623. </label>
  1624. <?php endif; // $bulk ?>
  1625. <?php endif; // 'post' && $can_publish && current_user_can( 'edit_others_posts' ) ?>
  1626. </div>
  1627. <?php if ( $bulk && current_theme_supports( 'post-formats' ) && post_type_supports( $screen->post_type, 'post-formats' ) ) : ?>
  1628. <?php $post_formats = get_theme_support( 'post-formats' ); ?>
  1629. <label class="alignleft">
  1630. <span class="title"><?php _ex( 'Format', 'post format' ); ?></span>
  1631. <select name="post_format">
  1632. <option value="-1"><?php _e( '&mdash; No Change &mdash;' ); ?></option>
  1633. <option value="0"><?php echo get_post_format_string( 'standard' ); ?></option>
  1634. <?php if ( is_array( $post_formats[0] ) ) : ?>
  1635. <?php foreach ( $post_formats[0] as $format ) : ?>
  1636. <option value="<?php echo esc_attr( $format ); ?>"><?php echo esc_html( get_post_format_string( $format ) ); ?></option>
  1637. <?php endforeach; ?>
  1638. <?php endif; ?>
  1639. </select>
  1640. </label>
  1641. <?php endif; ?>
  1642. </div>
  1643. </fieldset>
  1644. <?php
  1645. list( $columns ) = $this->get_column_info();
  1646. foreach ( $columns as $column_name => $column_display_name ) {
  1647. if ( isset( $core_columns[ $column_name ] ) ) {
  1648. continue;
  1649. }
  1650. if ( $bulk ) {
  1651. /**
  1652. * Fires once for each column in Bulk Edit mode.
  1653. *
  1654. * @since 2.7.0
  1655. *
  1656. * @param string $column_name Name of the column to edit.
  1657. * @param string $post_type The post type slug.
  1658. */
  1659. do_action( 'bulk_edit_custom_box', $column_name, $screen->post_type );
  1660. } else {
  1661. /**
  1662. * Fires once for each column in Quick Edit mode.
  1663. *
  1664. * @since 2.7.0
  1665. *
  1666. * @param string $column_name Name of the column to edit.
  1667. * @param string $post_type The post type slug, or current screen name if this is a taxonomy list table.
  1668. * @param string $taxonomy The taxonomy name, if any.
  1669. */
  1670. do_action( 'quick_edit_custom_box', $column_name, $screen->post_type, '' );
  1671. }
  1672. }
  1673. ?>
  1674. <div class="submit inline-edit-save">
  1675. <button type="button" class="button cancel alignleft"><?php _e( 'Cancel' ); ?></button>
  1676. <?php if ( ! $bulk ) : ?>
  1677. <?php wp_nonce_field( 'inlineeditnonce', '_inline_edit', false ); ?>
  1678. <button type="button" class="button button-primary save alignright"><?php _e( 'Update' ); ?></button>
  1679. <span class="spinner"></span>
  1680. <?php else : ?>
  1681. <?php submit_button( __( 'Update' ), 'primary alignright', 'bulk_edit', false ); ?>
  1682. <?php endif; ?>
  1683. <input type="hidden" name="post_view" value="<?php echo esc_attr( $m ); ?>" />
  1684. <input type="hidden" name="screen" value="<?php echo esc_attr( $screen->id ); ?>" />
  1685. <?php if ( ! $bulk && ! post_type_supports( $screen->post_type, 'author' ) ) : ?>
  1686. <input type="hidden" name="post_author" value="<?php echo esc_attr( $post->post_author ); ?>" />
  1687. <?php endif; ?>
  1688. <br class="clear" />
  1689. <div class="notice notice-error notice-alt inline hidden">
  1690. <p class="error"></p>
  1691. </div>
  1692. </div>
  1693. </td></tr>
  1694. <?php
  1695. $bulk++;
  1696. endwhile;
  1697. ?>
  1698. </tbody></table>
  1699. </form>
  1700. <?php
  1701. }
  1702. }