PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 3.2.0
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v3.2.0
4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.5.2 All 199 releases
betterdocs / includes / Admin / PostsTable.php

PostsTable.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 3.2.0, at includes/Admin/PostsTable.php

490 lines 20.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace WPDeveloper\BetterDocs\Admin;
3
4 use DateTime;
5 use WP_Post;
6 use WP_Query;
7 use WP_Posts_List_Table;
8
9 class PostsTable extends WP_Posts_List_Table {
10
11 public function __construct( $args = [] ) {
12 parent::__construct( $args );
13
14 $screen = get_current_screen();
15 $this->screen = convert_to_screen( $screen );
16
17 add_filter( 'post_column_taxonomy_links', [$this, 'column_taxonomy_links'], 11, 3 );
18 }
19
20 public function column_taxonomy_links( $term_links, $taxonomy, $terms ) {
21
22 if ( $taxonomy !== 'doc_category' ) {
23 return $term_links;
24 }
25
26 $term_links = [];
27
28 if ( ! empty( $terms ) ) {
29 $taxonomy_object = get_taxonomy( $taxonomy );
30
31 foreach ( $terms as $t ) {
32 $posts_in_term_qv = [];
33
34 if ( $taxonomy_object->query_var ) {
35 $posts_in_term_qv[$taxonomy_object->query_var] = $t->slug;
36 } else {
37 $posts_in_term_qv['taxonomy'] = $taxonomy;
38 $posts_in_term_qv['term'] = $t->slug;
39 }
40
41 $label = esc_html( sanitize_term_field( 'name', $t->name, $t->term_id, $taxonomy, 'display' ) );
42
43 $term_links[] = $this->get_edit_link( $posts_in_term_qv, $label );
44 }
45 }
46
47 return $term_links;
48 }
49
50 /**
51 * Helper to create links to edit.php with params.
52 *
53 * @since 4.4.0
54 *
55 * @param string[] $args Associative array of URL parameters for the link.
56 * @param string $label Link text.
57 * @param string $class Optional. Class attribute. Default empty string.
58 * @return string The formatted link string.
59 */
60 protected function get_edit_link( $args, $label, $class = '' ) {
61 global $current;
62
63 $url = add_query_arg( $args, 'admin.php?page=betterdocs-admin' );
64
65 $class_html = '';
66 $aria_current = '';
67
68 if ( ! empty( $class ) ) {
69 $class_html = sprintf(
70 ' class="%s"',
71 esc_attr( $class )
72 );
73
74 if ( 'current' === $class ) {
75 $aria_current = ' aria-current="page"';
76 }
77 }
78
79 return sprintf(
80 '<a href="%s"%s%s>%s</a>',
81 esc_url( $url ),
82 $class_html,
83 $aria_current,
84 $label
85 );
86 }
87
88 public function docs_wp_query( $per_page ) {
89 $paged = isset( $_REQUEST['paged'] ) ? $_REQUEST['paged'] : 1;
90 $args = [
91 'post_type' => 'docs',
92 'posts_per_page' => $per_page,
93 'orderby' => 'date',
94 'paged' => $paged
95 ];
96
97 if ( isset( $_GET['s'] ) && strlen( $_GET['s'] ) ) {
98 $args['s'] = $_GET['s'];
99 }
100
101 if ( isset( $_GET['order'] ) && $_GET['order'] === 'ASC' ) {
102 $args['order'] = 'ASC';
103 } else {
104 $args['order'] = 'DESC';
105 }
106
107 if ( isset( $_GET['author'] ) && $_GET['author'] != 'all' ) {
108 $args['author'] = $_GET['author'];
109 }
110
111 if ( isset( $_GET['post_status'] ) ) {
112 $args['post_status'] = $_GET['post_status'];
113 }
114
115 if ( isset( $_GET['view'] ) && ! empty( $_GET['view'] ) ) {
116 $args['meta_key'] = '_betterdocs_meta_views';
117 $args['orderby'] = 'meta_value_num';
118 if ( $_GET['view'] === 'most_viewed' ) {
119 $args['order'] = 'DESC';
120 } else if ( $_GET['view'] === 'least_viewed' ) {
121 $args['order'] = 'ASC';
122 }
123 }
124
125 if ( isset( $_GET['date'] ) && ! empty( $_GET['date'] ) ) {
126 $args['orderby'] = $_GET['date'];
127 if ( $_GET['date'] === 'most_recent' ) {
128 $args['order'] = 'DESC';
129 } else if ( $_GET['date'] === 'least_recent' ) {
130 $args['order'] = 'ASC';
131 } else if ( $_GET['date'] === 'custom_date' ) {
132 $after = $before = '';
133 $date_range = explode( '-', $_GET['date_range'] );
134
135 if ( is_array( $date_range ) && array_key_exists( 0, $date_range ) ) {
136 $after = rtrim( $date_range[0] );
137 }
138
139 if ( is_array( $date_range ) && array_key_exists( 1, $date_range ) ) {
140 $before = ltrim( $date_range[1] );
141 }
142
143 $start_date = new DateTime( $after );
144 $end_date = new DateTime( $before );
145
146 if ( $after === $before ) {
147 $start_date = $start_date->format( 'Ymd' );
148 $args['date_query'] = [
149 [
150 'year' => substr( $start_date, 0, 4 ),
151 'month' => substr( $start_date, 4, 2 ),
152 'day' => substr( $start_date, 6, 2 )
153 ]
154 ];
155 } else {
156 $start_date = $start_date->modify( '-1 day' );
157 $end_date = $end_date->modify( '+1 day' );
158 $args['date_query'] = [
159 [
160 'after' => $start_date->format( 'M d, Y' ),
161 'before' => $end_date->format( 'M d, Y' ),
162 'inclusive' => true
163 ]
164 ];
165 }
166 }
167 }
168
169 $args['tax_query'] = [
170 'relation' => 'AND'
171 ];
172
173 if ( isset( $_GET['doc_category'] ) && ! empty( $_GET['doc_category'] ) && ( $_GET['doc_category'] != 'all' ) ) {
174 $args['tax_query'][] = [
175 'taxonomy' => 'doc_category',
176 'field' => 'slug',
177 'operator' => 'IN',
178 'terms' => [$_GET['doc_category']],
179 'include_children' => true
180 ];
181 }
182
183 if ( isset( $_GET['knowledgebase'] ) && ! empty( $_GET['knowledgebase'] ) && ( $_GET['knowledgebase'] != 'all' ) ) {
184 $args['tax_query'][] = [
185 'taxonomy' => 'knowledge_base',
186 'field' => 'slug',
187 'terms' => [$_GET['knowledgebase']],
188 'operator' => 'IN',
189 'include_children' => true
190 ];
191 }
192
193 return new WP_Query( $args );
194 }
195
196 /**
197 * @global string $mode List table view mode.
198 * @global array $avail_post_stati
199 * @global WP_Query $wp_query WordPress Query object.
200 * @global int $per_page
201 */
202 public function prepare_items() {
203 global $mode, $avail_post_stati, $wp_query, $per_page;
204
205 $wp_query = $this->docs_wp_query( $per_page );
206 if ( ! empty( $_REQUEST['mode'] ) ) {
207 $mode = 'excerpt' === $_REQUEST['mode'] ? 'excerpt' : 'list';
208 set_user_setting( 'posts_list_mode', $mode );
209 } else {
210 $mode = get_user_setting( 'posts_list_mode', 'list' );
211 }
212
213 // Is going to call wp().
214 $avail_post_stati = wp_edit_posts_query();
215
216 $this->set_hierarchical_display( is_post_type_hierarchical( $this->screen->post_type ) && 'menu_order title' === $wp_query->query['orderby'] );
217
218 $post_type = $this->screen->post_type;
219 $per_page = $this->get_items_per_page( 'edit_' . $post_type . '_per_page' );
220
221 /** This filter is documented in wp-admin/includes/post.php */
222 $per_page = apply_filters( 'edit_posts_per_page', $per_page, $post_type );
223
224 if ( $this->hierarchical_display ) {
225 $total_items = $wp_query->post_count;
226 } elseif ( $wp_query->found_posts || $this->get_pagenum() === 1 ) {
227 $total_items = $wp_query->found_posts;
228 } else {
229 $post_counts = (array) wp_count_posts( $post_type, 'readable' );
230
231 if ( isset( $_REQUEST['post_status'] ) && in_array( $_REQUEST['post_status'], $avail_post_stati, true ) ) {
232 $total_items = $post_counts[$_REQUEST['post_status']];
233 } elseif ( isset( $_REQUEST['show_sticky'] ) && $_REQUEST['show_sticky'] ) {
234 $total_items = $this->sticky_posts_count;
235 } elseif ( isset( $_GET['author'] ) && get_current_user_id() == $_GET['author'] ) {
236 $total_items = $this->user_posts_count;
237 } else {
238 $total_items = array_sum( $post_counts );
239
240 // Subtract post types that are not included in the admin all list.
241 foreach ( get_post_stati( ['show_in_admin_all_list' => false] ) as $state ) {
242 $total_items -= $post_counts[$state];
243 }
244 }
245 }
246
247 $this->is_trash = isset( $_REQUEST['post_status'] ) && 'trash' === $_REQUEST['post_status'];
248
249 $this->set_pagination_args(
250 [
251 'total_items' => $total_items,
252 'per_page' => $per_page
253 ]
254 );
255 }
256
257 /**
258 * Displays the table.
259 *
260 */
261 public function display() {
262 $singular = $this->_args['singular'];
263 $this->screen->render_screen_reader_content( 'heading_list' );
264 ?>
265 <table class="<?php echo implode( ' ', $this->get_table_classes() ); ?> wp-list-table">
266 <thead>
267 <tr>
268 <?php $this->print_column_headers();?>
269 </tr>
270 </thead>
271
272 <tbody id="the-list"
273 <?php
274 if ( $singular ) {
275 echo " data-wp-lists='list:$singular'";
276 }
277 ?>
278 >
279 <?php $this->display_rows_or_placeholder();?>
280 </tbody>
281
282 </table>
283 <?php
284 $this->display_tablenav( 'bottom' );
285 }
286
287 /**
288 * Generates the table navigation above or below the table
289 *
290 * @param string $which
291 */
292 protected function display_tablenav( $which ) {
293 if ( 'top' === $which ) {
294 wp_nonce_field( 'bulk-' . $this->_args['plural'] );
295 }
296 ?>
297 <div class="tablenav <?php echo esc_attr( $which ); ?>">
298 <?php
299 $this->extra_tablenav( $which );
300 $this->pagination( $which );
301 ?>
302 <br class="clear" />
303 </div>
304 <?php
305 }
306
307 /**
308 * @global WP_Query $wp_query WordPress Query object.
309 * @global int $per_page
310 * @param array $posts
311 * @param int $level
312 */
313 public function display_rows( $posts = [], $level = 0 ) {
314 global $wp_query, $per_page;
315 $wp_query = $this->docs_wp_query( $per_page );
316
317 $posts = $wp_query->posts;
318
319 add_filter( 'the_title', 'esc_html' );
320
321 if ( $this->hierarchical_display ) {
322 $this->_display_rows_hierarchical( $posts, $this->get_pagenum(), $per_page );
323 } else {
324 $this->_display_rows( $posts, $level );
325 }
326 }
327
328 /**
329 * @param array $posts
330 * @param int $level
331 */
332 private function _display_rows( $posts, $level = 0 ) {
333 $post_type = 'docs';
334
335 // Create array of post IDs.
336 $post_ids = [];
337
338 foreach ( $posts as $a_post ) {
339 $post_ids[] = $a_post->ID;
340 }
341
342 if ( post_type_supports( $post_type, 'comments' ) ) {
343 $this->comment_pending_count = get_pending_comments_num( $post_ids );
344 }
345
346 foreach ( $posts as $post ) {
347 $this->single_row( $post, $level );
348 }
349 }
350
351 /**
352 * @since 4.3.0
353 *
354 * @param WP_Post $post
355 */
356 public function custom_column_title( $post ) {
357 echo $this->column_title( $post );
358 }
359
360 /**
361 * Generates and displays row action links.
362 *
363 * @since 4.3.0
364 *
365 * @param WP_Post $post Post being acted upon.
366 * @return string Row actions output for posts, or an empty string
367 * if the current column is not the primary column.
368 */
369 public function custom_row_actions( $post ) {
370 $post_type_object = get_post_type_object( 'docs' );
371 $can_edit_post = current_user_can( 'edit_post', $post->ID );
372 $actions = [];
373 $title = _draft_or_post_title();
374
375 if ( $can_edit_post && 'trash' !== $post->post_status ) {
376 $actions['edit'] = sprintf(
377 '<a href="%s" aria-label="%s">%s</a>',
378 get_edit_post_link( $post->ID ),
379 /* translators: %s: Post title. */
380 esc_attr( sprintf( __( 'Edit &#8220;%s&#8221;' ), $title ) ),
381 __( 'Edit' )
382 );
383
384 if ( 'wp_block' !== $post->post_type ) {
385 $actions['inline hide-if-no-js'] = sprintf(
386 '<button type="button" class="button-link editinline" aria-label="%s" aria-expanded="false">%s</button>',
387 /* translators: %s: Post title. */
388 esc_attr( sprintf( __( 'Quick edit &#8220;%s&#8221; inline' ), $title ) ),
389 __( 'Quick&nbsp;Edit' )
390 );
391 }
392 }
393
394 if ( current_user_can( 'delete_post', $post->ID ) ) {
395 if ( 'trash' === $post->post_status ) {
396 $actions['untrash'] = sprintf(
397 '<a href="%s" aria-label="%s">%s</a>',
398 wp_nonce_url( admin_url( sprintf( $post_type_object->_edit_link . '&amp;action=untrash', $post->ID ) ), 'untrash-post_' . $post->ID ),
399 /* translators: %s: Post title. */
400 esc_attr( sprintf( __( 'Restore &#8220;%s&#8221; from the Trash' ), $title ) ),
401 __( 'Restore' )
402 );
403 } elseif ( EMPTY_TRASH_DAYS ) {
404 $actions['trash'] = sprintf(
405 '<a href="%s" class="submitdelete" aria-label="%s">%s</a>',
406 get_delete_post_link( $post->ID ),
407 /* translators: %s: Post title. */
408 esc_attr( sprintf( __( 'Move &#8220;%s&#8221; to the Trash' ), $title ) ),
409 _x( 'Trash', 'verb' )
410 );
411 }
412 if ( 'trash' === $post->post_status || ! EMPTY_TRASH_DAYS ) {
413 $actions['delete'] = sprintf(
414 '<a href="%s" class="submitdelete" aria-label="%s">%s</a>',
415 get_delete_post_link( $post->ID, '', true ),
416 /* translators: %s: Post title. */
417 esc_attr( sprintf( __( 'Delete &#8220;%s&#8221; permanently' ), $title ) ),
418 __( 'Delete Permanently' )
419 );
420 }
421 }
422
423 if ( is_post_type_viewable( $post_type_object ) ) {
424 if ( in_array( $post->post_status, ['pending', 'draft', 'future'], true ) ) {
425 if ( $can_edit_post ) {
426 $preview_link = get_preview_post_link( $post );
427 $actions['view'] = sprintf(
428 '<a href="%s" rel="bookmark" aria-label="%s">%s</a>',
429 esc_url( $preview_link ),
430 /* translators: %s: Post title. */
431 esc_attr( sprintf( __( 'Preview &#8220;%s&#8221;' ), $title ) ),
432 __( 'Preview' )
433 );
434 }
435 } elseif ( 'trash' !== $post->post_status ) {
436 $actions['view'] = sprintf(
437 '<a href="%s" rel="bookmark" aria-label="%s">%s</a>',
438 get_permalink( $post->ID ),
439 /* translators: %s: Post title. */
440 esc_attr( sprintf( __( 'View &#8220;%s&#8221;' ), $title ) ),
441 __( 'View' )
442 );
443 }
444 }
445
446 if ( 'wp_block' === $post->post_type ) {
447 $actions['export'] = sprintf(
448 '<button type="button" class="wp-list-reusable-blocks__export button-link" data-id="%s" aria-label="%s">%s</button>',
449 $post->ID,
450 /* translators: %s: Post title. */
451 esc_attr( sprintf( __( 'Export &#8220;%s&#8221; as JSON' ), $title ) ),
452 __( 'Export as JSON' )
453 );
454 }
455
456 if ( is_post_type_hierarchical( $post->post_type ) ) {
457 /**
458 * Filters the array of row action links on the Pages list table.
459 *
460 * The filter is evaluated only for hierarchical post types.
461 *
462 * @since 2.8.0
463 *
464 * @param string[] $actions An array of row action links. Defaults are
465 * 'Edit', 'Quick Edit', 'Restore', 'Trash',
466 * 'Delete Permanently', 'Preview', and 'View'.
467 * @param WP_Post $post The post object.
468 */
469 $actions = apply_filters( 'page_row_actions', $actions, $post );
470 } else {
471
472 /**
473 * Filters the array of row action links on the Posts list table.
474 *
475 * The filter is evaluated only for non-hierarchical post types.
476 *
477 * @since 2.8.0
478 *
479 * @param string[] $actions An array of row action links. Defaults are
480 * 'Edit', 'Quick Edit', 'Restore', 'Trash',
481 * 'Delete Permanently', 'Preview', and 'View'.
482 * @param WP_Post $post The post object.
483 */
484 $actions = apply_filters( 'post_row_actions', $actions, $post );
485 }
486
487 return $this->row_actions( $actions );
488 }
489 }
490