PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 3.5.9
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v3.5.9
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.5.9, at includes/Admin/PostsTable.php

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