PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 3.8.9
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v3.8.9
4.9.2 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 All 200 releases
← All changes | includes/Core/Glossaries.php +23 -327 4.8.23.8.9 View file →
@@ -1,20 +1,11 @@
1 1 <?php
2 +
2 3 namespace WPDeveloper\BetterDocs\Core;
3 4
4 -if ( ! defined( 'ABSPATH' ) ) {
5 - exit;
6 -}
7 -
8 -// Glossary-by-term and glossary admin filters require tax_query and meta_key.
9 -// phpcs:disable WordPress.DB.SlowDBQuery.slow_db_query_tax_query
10 -// phpcs:disable WordPress.DB.SlowDBQuery.slow_db_query_meta_query
11 -// phpcs:disable WordPress.DB.SlowDBQuery.slow_db_query_meta_key
12 -
13 5 use WP_Query;
14 6 use WP_Error;
15 7 use WPDeveloper\BetterDocs\Utils\Base;
16 -use WPDeveloper\BetterDocs\Utils\Helper;
17 8
18 9 class Glossaries extends Base {
19 10 /**
20 11 * REST API namespace
@@ -37,55 +28,21 @@
37 28 add_action( 'created_glossaries', [ $this, 'action_created_betterdocs_glossaries' ], 10, 2 );
38 29 add_action( 'rest_api_init', [ $this, 'register_api_endpoint' ] );
39 30 add_action( 'rest_api_init', [ $this, 'register_glossary_rest_fields' ] );
40 31 add_action( 'rest_glossaries_query', array( $this, 'glossaries_orderby_meta' ), 10, 2 );
41 - add_action( 'rest_glossaries_query', array( $this, 'disable_language_filtering_for_admin_rest' ), 5, 2 );
42 - // Ensure meta fields are properly exposed in REST API
43 - add_filter( 'rest_prepare_glossaries', array( $this, 'add_meta_to_rest_response' ), 10, 3 );
44 32 // Enqueue Scripts
45 33 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] );
46 - // Ensure existing glossaries have proper status
47 - add_action( 'admin_init', [ $this, 'ensure_glossaries_have_status' ] );
48 34 }
49 35
50 36 public function register_post() {
51 - // Register term meta fields for glossaries taxonomy
52 - // Force register without duplicate checks to ensure they're properly registered
53 37 register_term_meta(
54 38 $this->category,
55 39 'status',
56 40 [
57 41 'show_in_rest' => true,
58 - 'single' => true,
59 - 'type' => 'string', // Changed to string to match React expectation
60 - 'default' => '1',
61 - 'sanitize_callback' => 'sanitize_text_field'
42 + 'single' => true
62 43 ]
63 44 );
64 -
65 - register_term_meta(
66 - $this->category,
67 - 'order',
68 - [
69 - 'show_in_rest' => true,
70 - 'single' => true,
71 - 'type' => 'string', // Changed to string for consistency
72 - 'default' => '0',
73 - 'sanitize_callback' => 'sanitize_text_field'
74 - ]
75 - );
76 -
77 - register_term_meta(
78 - $this->category,
79 - 'glossary_term_description',
80 - [
81 - 'show_in_rest' => true,
82 - 'single' => true,
83 - 'type' => 'string',
84 - 'default' => '',
85 - 'sanitize_callback' => 'wp_kses_post'
86 - ]
87 - );
88 45 }
89 46
90 47 public function enqueue( $hook ) {
91 48 if ( $hook === 'betterdocs_page_betterdocs-glossaries' ) {
@@ -100,14 +57,12 @@
100 57 betterdocs()->assets->localize(
101 58 'betterdocs-admin-glossaries',
102 59 'betterdocs',
103 60 [
104 - 'dir_url' => BETTERDOCS_ABSURL,
105 - 'rest_url' => esc_url_raw( rest_url() ),
106 - 'free_version' => betterdocs()->version,
107 - 'nonce' => wp_create_nonce( 'wp_rest' ),
108 - 'current_language' => Helper::get_current_language(),
109 - 'is_multilingual' => Helper::is_multilingual_active()
61 + 'dir_url' => BETTERDOCS_ABSURL,
62 + 'rest_url' => esc_url_raw( rest_url() ),
63 + 'free_version' => betterdocs()->version,
64 + 'nonce' => wp_create_nonce( 'wp_rest' )
110 65 ]
111 66 );
112 67 }
113 68 }
@@ -115,40 +70,9 @@
115 70 public function output() {
116 71 betterdocs()->views->get( 'admin/glossaries' );
117 72 }
118 73
119 - /**
120 - * Ensure existing glossaries have proper status meta
121 - * This fixes the issue where existing glossaries appear disabled
122 - */
123 - public function ensure_glossaries_have_status() {
124 - // Run this every time in admin to ensure status is properly set
125 - // Get all glossaries terms
126 - $all_terms = get_terms( array(
127 - 'taxonomy' => 'glossaries',
128 - 'hide_empty' => false,
129 - 'suppress_filters' => true, // phpcs:ignore WordPressVIPMinimum.Hooks.PreGetPosts.PreGetPosts,WordPressVIPMinimum.Performance.WPQueryParams.SuppressFilters_suppress_filters -- intentional WPML/Polylang language bypass.
130 - ) );
131 74
132 - if ( ! empty( $all_terms ) && ! is_wp_error( $all_terms ) ) {
133 - foreach ( $all_terms as $term ) {
134 - $current_status = get_term_meta( $term->term_id, 'status', true );
135 -
136 - // If status is empty or not set, set it to enabled ('1')
137 - if ( empty( $current_status ) || $current_status === '' ) {
138 - update_term_meta( $term->term_id, 'status', '1' );
139 - }
140 -
141 - // Also ensure order meta exists
142 - $current_order = get_term_meta( $term->term_id, 'order', true );
143 - if ( empty( $current_order ) || $current_order === '' ) {
144 - update_term_meta( $term->term_id, 'order', '0' );
145 - }
146 - }
147 - }
148 - }
149 -
150 -
151 75 /**
152 76 * Default the taxonomy's terms' order if it's not set.
153 77 *
154 78 * @param string $tax_slug The taxonomy's slug.
@@ -155,10 +79,9 @@
155 79 */
156 80 public function action_created_betterdocs_glossaries( $term_id ) {
157 81 $order = $this->get_max_taxonomy_order( 'glossaries' );
158 82 // update_term_meta( $term_id, 'order', $order++ );
159 - update_term_meta( $term_id, 'status', '1' ); // Set as string
160 - update_term_meta( $term_id, 'order', '0' ); // Also set order
83 + update_term_meta( $term_id, 'status', 1 );
161 84 }
162 85
163 86 /**
164 87 * Default the taxonomy's terms' order if it's not set.
@@ -188,14 +111,13 @@
188 111 */
189 112 private function get_max_taxonomy_order( $tax_slug ) {
190 113 global $wpdb;
191 114
192 - // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- live max-order needed when assigning new terms; cache would be stale.
193 115 $max_term_order = $wpdb->get_col(
194 116 $wpdb->prepare(
195 117 "SELECT MAX( CAST( tm.meta_value AS UNSIGNED ) )
196 118 FROM $wpdb->terms t
197 - JOIN $wpdb->term_taxonomy tt ON t.term_id = tt.term_id AND tt.taxonomy = %s
119 + JOIN $wpdb->term_taxonomy tt ON t.term_id = tt.term_id AND tt.taxonomy = '%s'
198 120 JOIN $wpdb->termmeta tm ON tm.term_id = t.term_id WHERE tm.meta_key = 'order'",
199 121 $tax_slug
200 122 )
201 123 );
@@ -372,29 +294,10 @@
372 294 return current_user_can( 'edit_others_posts' );
373 295 }
374 296 ]
375 297 );
376 -
377 298 register_rest_route(
378 299 $this->namespace,
379 - '/glossary/check_existing',
380 - [
381 - 'methods' => [ 'POST' ],
382 - 'callback' => [ $this, 'check_existing_glossaries' ],
383 - 'permission_callback' => function () {
384 - return current_user_can( 'edit_others_posts' );
385 - },
386 - 'args' => array(
387 - 'terms' => array(
388 - 'type' => 'array',
389 - 'required' => true,
390 - 'items' => array( 'type' => 'string' ),
391 - ),
392 - ),
393 - ]
394 - );
395 - register_rest_route(
396 - $this->namespace,
397 300 '/glossary/get_glossaries',
398 301 [
399 302 'methods' => [ 'GET' ],
400 303 'callback' => [ $this, 'get_glossaries' ],
@@ -424,9 +327,8 @@
424 327 public function create_glossaries( $params ) {
425 328 $title = $params->get_param( 'title' );
426 329 $description = $params->get_param( 'description' );
427 330 $slug = $params->get_param( 'slug' );
428 - $language = $params->get_param( 'language' );
429 331
430 332 // Create the term
431 333 $new_term = wp_insert_term(
432 334 $title,
@@ -436,9 +338,9 @@
436 338 ]
437 339 );
438 340
439 341 if ( is_wp_error( $new_term ) ) {
440 - return ['status' => 'failed', 'data' => $new_term];
342 + return $new_term;
441 343 }
442 344
443 345 // Set the custom field description
444 346 $term_id = $new_term['term_id'];
@@ -443,83 +345,20 @@
443 345 // Set the custom field description
444 346 $term_id = $new_term['term_id'];
445 347 update_term_meta( $term_id, 'glossary_term_description', $description ); //phpcs:ignore inline styles are need for the front-end
446 348
447 - // Set language for multilingual plugins
448 - if ( $language && Helper::is_multilingual_active() ) {
449 - // WPML Support
450 - if ( is_plugin_active( 'sitepress-multilingual-cms/sitepress.php' ) ) {
451 - global $sitepress;
452 - if ( $sitepress && method_exists( $sitepress, 'set_element_language_details' ) ) {
453 - $sitepress->set_element_language_details( $term_id, 'tax_glossaries', null, $language );
454 - }
455 - }
456 - // Polylang Support
457 - elseif ( function_exists( 'pll_set_term_language' ) ) {
458 - pll_set_term_language( $term_id, $language );
459 - }
460 - }
461 -
462 - $new_term = $this->glossary_term_in_rest_api_schema($term_id, $description);
463 - return ['status' => 'success', 'data' => $new_term];
349 + return true;
464 350 }
465 351
466 - /**
467 - * Form Glossary Term In Rest Api Schema Format
468 - *
469 - * @param int $term_id
470 - * @param string $meta_description
471 - * @return array
472 - */
473 - public function glossary_term_in_rest_api_schema( $term_id, $meta_description = '' ) {
474 - $term = get_term_by('id', $term_id, 'glossaries');
475 - return [
476 - 'count' => $term->count,
477 - 'description' => $term->description,
478 - 'glossary_term_description' => $meta_description, //get the current description
479 - 'id' => $term->term_id,
480 - 'link' => get_permalink($term->term_id),
481 - 'meta' => [
482 - 'status' => get_term_meta( $term->term_id, 'status', true )
483 - ],
484 - 'name' => $term->name,
485 - 'parent' => $term->parent,
486 - 'slug' => $term->slug,
487 - 'taxonomy' => $term->taxonomy
488 - ];
489 - }
352 + public function update_glossaries( $request ) {
353 + $params = $request->get_params();
490 354
491 - public function update_glossaries( $request ) {
492 355 $term_id = $request->get_param( 'term_id' );
493 356 $title = $request->get_param( 'title' );
494 357 $description = $request->get_param( 'description' );
495 358 $description = ( $description !== 'undefined' ) ? $description : '';
496 359 $slug = $request->get_param( 'slug' );
497 - $language = $request->get_param( 'language' );
498 360
499 - // Verify that we're updating the correct language version of the term
500 - if ( $language && Helper::is_multilingual_active() ) {
501 - $term_language = null;
502 -
503 - // WPML Support
504 - if ( is_plugin_active( 'sitepress-multilingual-cms/sitepress.php' ) ) {
505 - global $sitepress;
506 - if ( $sitepress && method_exists( $sitepress, 'get_element_language_details' ) ) {
507 - $lang_details = $sitepress->get_element_language_details( $term_id, 'tax_glossaries' );
508 - $term_language = $lang_details ? $lang_details->language_code : null;
509 - }
510 - }
511 - // Polylang Support
512 - elseif ( function_exists( 'pll_get_term_language' ) ) {
513 - $term_language = pll_get_term_language( $term_id );
514 - }
515 -
516 - // Only update if the term belongs to the current language
517 - if ( $term_language && $term_language !== $language ) {
518 - return ['status' => 'failed', 'data' => new \WP_Error( 'wrong_language', 'Cannot update term from different language' )];
519 - }
520 - }
521 -
522 361 // Check if there's old data in the default description field and transfer it to the custom field
523 362 $old_description = get_term_field( 'description', $term_id, 'glossaries' );
524 363 if ( ! empty( $old_description ) && empty( get_term_meta( $term_id, 'glossary_term_description', true ) ) ) {
525 364 update_term_meta( $term_id, 'glossary_term_description', wp_kses_post( $old_description ) );
@@ -536,20 +375,19 @@
536 375 ]
537 376 );
538 377
539 378 if ( is_wp_error( $update ) ) {
540 - return ['status' => 'failed', 'data' => $update];
379 + return $update;
541 380 } else {
542 381 // Update the custom field description
543 382 update_term_meta( $term_id, 'glossary_term_description', $description );
544 - return ['status' => 'success', 'data' => $this->glossary_term_in_rest_api_schema($term_id, $description)];
383 + return true;
545 384 }
546 385 }
547 386
548 387 public function save_glossary_term_meta( $term_id, $tt_id ) {
549 388 if ( isset( $_POST['glossary_term_description'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
550 - $description = wp_kses_post( wp_unslash( $_POST['glossary_term_description'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
551 - update_term_meta( $term_id, 'glossary_term_description', $description );
389 + update_term_meta( $term_id, 'glossary_term_description', wp_kses_post( $_POST['glossary_term_description'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
552 390 }
553 391 }
554 392
555 393 public function register_glossary_rest_fields() {
@@ -723,21 +561,9 @@
723 561
724 562 public function update_glossary_status( $params ) {
725 563 $term_id = $params->get_param( 'term_id' );
726 564 $status = $params->get_param( 'status' );
727 -
728 - // Ensure status is a string ('0' or '1')
729 - $status = $status ? '1' : '0';
730 -
731 - $result = update_term_meta( $term_id, 'status', $status );
732 -
733 - // Return success response with updated status
734 - return array(
735 - 'success' => $result !== false,
736 - 'term_id' => $term_id,
737 - 'status' => $status,
738 - 'message' => $result !== false ? 'Status updated successfully' : 'Failed to update status'
739 - );
565 + return update_term_meta( $term_id, 'status', $status );
740 566 }
741 567
742 568 public function fetch_faq_posts( $params ) {
743 569 $faq = [];
@@ -743,27 +569,17 @@
743 569 $faq = [];
744 570 $type = $params->get_param( 'type' );
745 571
746 572 if ( $type == 'category' ) {
747 - $term_args = [
748 - 'taxonomy' => 'glossaries',
749 - 'hide_empty' => false,
750 - ];
573 + $taxonomy_objects = get_terms(
574 + [
575 + 'taxonomy' => 'glossaries',
576 + 'hide_empty' => false,
577 + ]
578 + );
751 579
752 - // Add language filtering if multilingual plugin is active and we should apply filtering
753 - $current_language = Helper::get_current_language();
754 - if ( $current_language && Helper::is_multilingual_active() && Helper::should_apply_language_filtering() ) {
755 - // For WPML and Polylang, use 'lang' parameter
756 - if ( is_plugin_active( 'sitepress-multilingual-cms/sitepress.php' ) || function_exists( 'pll_current_language' ) ) {
757 - $term_args['lang'] = $current_language;
758 - }
759 - }
760 -
761 - $taxonomy_objects = get_terms( $term_args );
762 -
763 580 if ( $taxonomy_objects && ! is_wp_error( $taxonomy_objects ) ) :
764 581 foreach ( $taxonomy_objects as $term ) :
765 - // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query -- core glossary-by-term query; tax filtering is required functionality.
766 582 $args = [
767 583 'post_type' => 'betterdocs_faq',
768 584 'post_status' => 'publish',
769 585 'post_per_page' => -1,
@@ -800,9 +616,8 @@
800 616
801 617 public function glossary_search( $request ) {
802 618
803 619 $title = $request['title'];
804 - $lang = $request['lang'] ?? null;
805 620
806 621 // Perform the taxonomy search
807 622 $taxonomy_args = array(
808 623 'name__like' => $title,
@@ -809,17 +624,8 @@
809 624 'taxonomy' => 'glossaries',
810 625 'hide_empty' => false
811 626 );
812 627
813 - // Add language filtering if multilingual plugin is active
814 - $language_to_use = $lang ?: Helper::get_current_language();
815 - if ( $language_to_use && Helper::is_multilingual_active() ) {
816 - // For WPML and Polylang, use 'lang' parameter
817 - if ( is_plugin_active( 'sitepress-multilingual-cms/sitepress.php' ) || function_exists( 'pll_current_language' ) ) {
818 - $taxonomy_args['lang'] = $language_to_use;
819 - }
820 - }
821 -
822 628 $taxonomies = get_terms( $taxonomy_args );
823 629
824 630 if ( ! empty( $taxonomies ) ) {
825 631 $result = array();
@@ -840,85 +646,11 @@
840 646 return new WP_Error( 'taxonomy_not_found', 'Taxonomy not found.', array( 'status' => 404 ) );
841 647 }
842 648 }
843 649
844 - public function disable_language_filtering_for_admin_rest( $args, $request ) {
845 - // Check if this is an admin REST request for glossaries management
846 - if ( is_user_logged_in() && current_user_can( 'edit_others_posts' ) ) {
847 - // Check if language parameter is explicitly provided in the request
848 - $lang_param = $request->get_param( 'lang' );
849 -
850 - if ( $lang_param ) {
851 - // If language is specified, use it for filtering
852 - $args['lang'] = $lang_param;
853 - } else {
854 - // If no language specified, remove language filtering to show all
855 - unset( $args['lang'] );
856 - // Add a temporary filter to bypass language restrictions
857 - add_filter( 'get_terms', array( $this, 'ensure_all_glossaries_in_admin' ), 10, 3 );
858 - }
859 - }
860 -
861 - return $args;
862 - }
863 -
864 - public function ensure_all_glossaries_in_admin( $terms, $taxonomies, $args ) {
865 - // Only apply to glossaries taxonomy in admin context
866 - if ( in_array( 'glossaries', (array) $taxonomies ) && is_user_logged_in() && current_user_can( 'edit_others_posts' ) ) {
867 - // Remove this filter to prevent infinite loops
868 - remove_filter( 'get_terms', array( $this, 'ensure_all_glossaries_in_admin' ), 10 );
869 -
870 - // Get all glossaries terms without language filtering
871 - $all_args = $args;
872 - unset( $all_args['lang'] );
873 - $all_args['suppress_filters'] = true; // phpcs:ignore WordPressVIPMinimum.Hooks.PreGetPosts.PreGetPosts,WordPressVIPMinimum.Performance.WPQueryParams.SuppressFilters_suppress_filters -- admin glossary management requires bypassing all language filters.
874 -
875 - $all_terms = get_terms( $all_args );
876 -
877 - // Re-add the filter for future calls
878 - add_filter( 'get_terms', array( $this, 'ensure_all_glossaries_in_admin' ), 10, 3 );
879 -
880 - return is_wp_error( $all_terms ) ? $terms : $all_terms;
881 - }
882 -
883 - return $terms;
884 - }
885 -
886 - /**
887 - * Add meta fields to REST API response
888 - */
889 - public function add_meta_to_rest_response( $response, $term, $request ) {
890 - // Ensure meta fields are properly included
891 - $meta = get_term_meta( $term->term_id );
892 -
893 - // Format meta data as expected by React component
894 - $response->data['meta'] = array();
895 -
896 - if ( isset( $meta['status'] ) ) {
897 - $response->data['meta']['status'] = $meta['status'];
898 - } else {
899 - $response->data['meta']['status'] = array( '1' ); // Default to enabled
900 - }
901 -
902 - if ( isset( $meta['order'] ) ) {
903 - $response->data['meta']['order'] = $meta['order'];
904 - } else {
905 - $response->data['meta']['order'] = array( '0' ); // Default order
906 - }
907 -
908 - if ( isset( $meta['glossary_term_description'] ) ) {
909 - $response->data['meta']['glossary_term_description'] = $meta['glossary_term_description'];
910 - } else {
911 - $response->data['meta']['glossary_term_description'] = array( '' );
912 - }
913 -
914 - return $response;
915 - }
916 -
917 650 public function glossaries_orderby_meta( $args, $request ) {
918 651 if ( $args['taxonomy'] === 'glossaries' ) {
919 652 $args['orderby'] = 'meta_value_num';
920 - // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- ordering by status meta is required UX.
921 653 $args['meta_key'] = 'status';
922 654 }
923 655 return $args;
924 656 }
@@ -926,44 +658,8 @@
926 658 public function get_glossary_count( $request ) {
927 659 $options = get_option( 'store_glossary_count' );
928 660 return rest_ensure_response( $options );
929 661 }
930 -
931 - /**
932 - * Given a list of candidate glossary term names, return which ones already
933 - * exist in the `glossaries` taxonomy. Used by the bulk Define-with-AI flow
934 - * to surface conflicts before generation rather than at save time.
935 - */
936 - public function check_existing_glossaries( $request ) {
937 - $terms = $request->get_param( 'terms' );
938 - if ( ! is_array( $terms ) ) {
939 - return rest_ensure_response( array( 'existing' => array() ) );
940 - }
941 -
942 - $existing = array();
943 - $seen = array();
944 -
945 - foreach ( $terms as $term ) {
946 - if ( ! is_string( $term ) ) {
947 - continue;
948 - }
949 - $trimmed = trim( $term );
950 - if ( $trimmed === '' ) {
951 - continue;
952 - }
953 - $key = strtolower( $trimmed );
954 - if ( isset( $seen[ $key ] ) ) {
955 - continue;
956 - }
957 - $seen[ $key ] = true;
958 -
959 - if ( term_exists( $trimmed, 'glossaries' ) ) {
960 - $existing[] = $trimmed;
961 - }
962 - }
963 -
964 - return rest_ensure_response( array( 'existing' => $existing ) );
965 - }
966 662 public function get_glossaries( $request ) {
967 663 $taxo = get_taxonomies(
968 664 array(
969 665 'name' => array(
@@ -972,7 +668,7 @@
972 668 ),
973 669 'objects'
974 670 );
975 671
976 - return rest_ensure_response( $taxo );
672 + return rest_ensuree_response( $taxo );
977 673 }
978 674 }