PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.9.2
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.9.2
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/Admin.php +100 -7 4.9.14.9.2 View file →
@@ -19,19 +19,26 @@
19 19 use WPDeveloper\BetterDocs\Dependencies\DI\Container;
20 20
21 21 class Admin extends Base {
22 22 /**
23 - * Per-user flag recording that this administrator has opened the MCP screen.
23 + * Per-user flag recording that this administrator has opened the Content IQ
24 + * screen — the discovery badge (ADR-063) now flags Content Intelligence, the
25 + * headline feature, rather than MCP.
24 26 *
25 27 * Stores the timestamp of the first visit, but only its **presence** is read:
26 - * absent means "this user has not seen MCP yet", which is what puts the
27 - * one-time discovery badge on the menu (ADR-063). Per user on purpose — two
28 - * administrators each get their own first look, and neither clears the other's.
28 + * absent means "this user has not seen Content IQ yet", which is what puts the
29 + * one-time discovery badge on the menu. Per user on purpose — two administrators
30 + * each get their own first look, and neither clears the other's. A deliberately
31 + * fresh meta key (not the old `betterdocs_mcp_seen`) so a user who already
32 + * dismissed the MCP badge still gets this one for the new feature.
29 33 *
34 + * The private `*_mcp_*` helper names below are kept as-is to hold the diff to
35 + * the target slug + this key; they now paint the Content IQ item.
36 + *
30 37 * @var string
31 38 * @since 4.9.0
32 39 */
33 - const MCP_SEEN_META = 'betterdocs_mcp_seen';
40 + const MCP_SEEN_META = 'betterdocs_content_iq_seen';
34 41
35 42 /**
36 43 * Whether this request painted the MCP discovery badge onto the menu.
37 44 *
@@ -170,8 +177,10 @@
170 177 add_filter( 'admin_body_class', array( $this, 'body_classes' ) );
171 178 add_filter( 'parent_file', array( $type, 'highlight_admin_menu' ) );
172 179 add_filter( 'submenu_file', array( $type, 'highlight_admin_submenu' ), 10, 2 );
173 180 add_filter( 'betterdocs_admin_menu', array( $this, 'quick_setup_menu' ), 10, 1 );
181 + // Runs last so it also orders items Pro/add-ons append through this filter.
182 + add_filter( 'betterdocs_admin_menu', array( $this, 'order_admin_menu' ), 999, 1 );
174 183
175 184 /**
176 185 * Remove Comments Column from List Table.
177 186 */
@@ -597,8 +606,9 @@
597 606 'betterdocs-settings',
598 607 'betterdocs-admin',
599 608 'betterdocs-dashboard',
600 609 'betterdocs-analytics',
610 + 'betterdocs-content-iq',
601 611 'betterdocs-glossaries',
602 612 'betterdocs-faq',
603 613 'betterdocs-doc-categories',
604 614 'betterdocs-doc-tags',
@@ -876,8 +886,9 @@
876 886 'analytics_older' => version_compare( betterdocs()->pro_version(), '3.3.4', '<=' ),
877 887 'betterdocs_ChatBot_plugin' => is_plugin_active( 'betterdocs-ai-chatbot/betterdocs-ai-chatbot.php' ),
878 888 'api_docs_teaser' => betterdocs()->show_api_docs_teaser(),
879 889 'glossaries_teaser' => betterdocs()->show_glossary_teaser(),
890 + 'content_intelligence_teaser' => betterdocs()->show_content_intelligence_teaser(),
880 891 'is_woocommerce_active' => class_exists( 'WooCommerce' ),
881 892 'total_doc_category_terms' => wp_count_terms( 'doc_category' ),
882 893 'current_admin_language' => Helper::get_current_admin_language(),
883 894 'is_multilingual' => Helper::is_multilingual_active(),
@@ -1096,9 +1107,9 @@
1096 1107 }
1097 1108
1098 1109 if ( isset( $submenu[ $this->slug ] ) && is_array( $submenu[ $this->slug ] ) ) {
1099 1110 foreach ( $submenu[ $this->slug ] as &$sub_item ) {
1100 - if ( isset( $sub_item[2] ) && 'betterdocs-mcp' === $sub_item[2] ) {
1111 + if ( isset( $sub_item[2] ) && 'betterdocs-content-iq' === $sub_item[2] ) {
1101 1112 $sub_item[0] .= self::mcp_submenu_pill();
1102 1113 break;
1103 1114 }
1104 1115 }
@@ -1183,9 +1194,9 @@
1183 1194 public static function is_mcp_screen_request() {
1184 1195 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only screen detection; see mark_mcp_seen().
1185 1196 $page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : '';
1186 1197
1187 - if ( 'betterdocs-mcp' !== $page ) {
1198 + if ( 'betterdocs-content-iq' !== $page ) {
1188 1199 return false;
1189 1200 }
1190 1201
1191 1202 return current_user_can( 'manage_options' );
@@ -1390,8 +1401,18 @@
1390 1401 'output',
1391 1402 ),
1392 1403 $parent_slug
1393 1404 ),
1405 + 'content_intelligence' => $this->normalize_menu(
1406 + __( 'Content IQ', 'betterdocs' ),
1407 + 'betterdocs-content-iq',
1408 + 'read_docs_analytics',
1409 + array(
1410 + $this,
1411 + 'output',
1412 + ),
1413 + $parent_slug
1414 + ),
1394 1415 'faq' => $this->normalize_menu(
1395 1416 __( 'FAQ Builder', 'betterdocs' ),
1396 1417 'betterdocs-faq',
1397 1418 'read_faq_builder',
@@ -1402,8 +1423,17 @@
1402 1423 $parent_slug
1403 1424 ),
1404 1425 );
1405 1426
1427 + // Content Intelligence ships in Pro, which overwrites that same key in place.
1428 + // Unlike API Docs it has to sit directly after Analytics, and `menus()` walks
1429 + // this array in insertion order — so the slot is declared inside the literal
1430 + // above and only withdrawn here. Appending it after the fact, api_docs-style,
1431 + // would park it at the bottom of the menu.
1432 + if ( ! ( betterdocs()->show_content_intelligence_teaser() || betterdocs()->has_content_intelligence() ) ) {
1433 + unset( $betterdocs_admin_pages['content_intelligence'] );
1434 + }
1435 +
1406 1436 // Glossaries is Pro. Reserve this same 'glossaries' slot for Free's locked
1407 1437 // teaser so the item keeps this position; once Pro is active the real
1408 1438 // screen overwrites the key in place (same pattern as API Docs below).
1409 1439 if ( betterdocs()->show_glossary_teaser() || ( betterdocs()->is_pro_active() && betterdocs()->settings->get( 'enable_glossaries' ) == true ) ) {
@@ -1450,8 +1480,70 @@
1450 1480
1451 1481 return apply_filters( 'betterdocs_admin_menu', $betterdocs_admin_pages, array( $this, 'output' ), $parent_slug );
1452 1482 }
1453 1483
1484 + /**
1485 + * Put the BetterDocs submenu in a deliberate order.
1486 + *
1487 + * Order used to be an accident of *when* each item was added: Free declares
1488 + * most of them inline, and reserves in-place slots for `glossaries` /
1489 + * `api_docs` so Pro can overwrite the key without moving it. Anything added
1490 + * purely through this filter, though, could only land at the end — which is
1491 + * why Multiple KB (Pro, priority 100) and AI Chatbot Logs sat after
1492 + * everything else regardless of where they belong.
1493 + *
1494 + * Sorting here, at priority 999, fixes that for every source at once: Free's
1495 + * own entries, Pro's, and any add-on's. Knowledge Base now follows Tags (it
1496 + * is the third taxonomy-ish thing, so it belongs with Categories and Tags
1497 + * rather than past Analytics), and API Docs follows Knowledge Base.
1498 + *
1499 + * Keys not listed keep their relative order and are appended, so an add-on
1500 + * that registers something unknown to this list is never dropped.
1501 + *
1502 + * @param array $pages Menu pages keyed by slug id.
1503 + * @return array
1504 + */
1505 + public function order_admin_menu( $pages ) {
1506 + if ( ! is_array( $pages ) ) {
1507 + return $pages;
1508 + }
1509 +
1510 + $order = array(
1511 + 'betterdocs',
1512 + 'dashboard',
1513 + 'all_docs',
1514 + 'add_new',
1515 + 'categories',
1516 + 'tags',
1517 + 'multiple_kb',
1518 + 'api_docs',
1519 + 'settings',
1520 + 'mcp',
1521 + 'analytics',
1522 + // Content IQ reads as a second Analytics screen, so it has to stay
1523 + // pinned directly behind it. Without this entry it would fall into
1524 + // the unknown-key bucket below and be appended to the bottom of the
1525 + // menu — the exact placement the menu literal avoids by declaring
1526 + // the slot inline rather than filtering it in api_docs-style.
1527 + 'content_intelligence',
1528 + 'faq',
1529 + 'glossaries',
1530 + 'ai_chatbot',
1531 + 'ai_chatbot_logs',
1532 + );
1533 +
1534 + $ordered = array();
1535 + foreach ( $order as $key ) {
1536 + if ( array_key_exists( $key, $pages ) ) {
1537 + $ordered[ $key ] = $pages[ $key ];
1538 + unset( $pages[ $key ] );
1539 + }
1540 + }
1541 +
1542 + // `$pages` now holds only unknown keys, still in their original order.
1543 + return array_merge( $ordered, $pages );
1544 + }
1545 +
1454 1546 public function add_custom_classes_to_menu_items() {
1455 1547 global $menu, $submenu;
1456 1548
1457 1549 $menu_items = array(
@@ -1462,8 +1554,9 @@
1462 1554 'betterdocs-doc-tags' => 'betterdocs-tags',
1463 1555 'betterdocs-settings' => 'betterdocs-settings',
1464 1556 'betterdocs-mcp' => 'betterdocs-mcp',
1465 1557 'betterdocs-analytics' => 'betterdocs-analytics',
1558 + 'betterdocs-content-iq' => 'betterdocs-content-iq',
1466 1559 'betterdocs-faq' => 'betterdocs-faq',
1467 1560 'betterdocs-glossaries' => 'betterdocs-glossaries',
1468 1561 'betterdocs-ai-chatbot' => 'betterdocs-ai-chatbot',
1469 1562 'betterdocs-api-docs' => 'betterdocs-api-docs',