PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.9.5
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.9.5
4.9.5 4.9.4 4.9.3 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 All 203 releases
← All changes | includes/Core/Settings.php +446 -93 4.7.0 → 4.9.5 View file →
@@ -9,8 +9,10 @@
9 9 use WP_Error;
10 10 use WP_User;
11 11 use WPDeveloper\BetterDocs\Admin\Builder\GlobalFields;
12 12 use WPDeveloper\BetterDocs\Admin\Builder\Rules;
13 +use WPDeveloper\BetterDocs\AI\ModelRegistry;
14 +use WPDeveloper\BetterDocs\AI\ProviderFactory;
13 15 use WPDeveloper\BetterDocs\REST\AIEdit;
14 16 use WPDeveloper\BetterDocs\Utils\AIHelper;
15 17 use WPDeveloper\BetterDocs\Utils\Base;
16 18 use WPDeveloper\BetterDocs\Utils\Database;
@@ -77,8 +79,31 @@
77 79 return esc_url( admin_url( 'admin.php?page=betterdocs-settings' ) );
78 80 }
79 81
80 82 /**
83 + * Settings keys holding secret API keys. These are masked before reaching
84 + * the browser, stripped for non-admins, and never persisted as their mask.
85 + *
86 + * Covers the AI Chatbot key plus every content-suite platform key. OpenAI's
87 + * is `ai_autowrite_api_key` (see ProviderFactory::key_field_for), hence the
88 + * dedupe.
89 + *
90 + * @return array<int,string>
91 + */
92 + public static function sensitive_api_key_fields() {
93 + $fields = array(
94 + ProviderFactory::OPENAI_KEY_FIELD,
95 + 'ai_chatbot_api_key',
96 + );
97 + foreach ( array_keys( ModelRegistry::platforms() ) as $platform ) {
98 + $fields[] = ProviderFactory::key_field_for( $platform );
99 + }
100 + // Add-ons (e.g. the AI Chatbot) register their own per-platform keys here
101 + // so they are masked in the browser and stripped for non-admins.
102 + return apply_filters( 'betterdocs_sensitive_api_key_fields', array_values( array_unique( $fields ) ) );
103 + }
104 +
105 + /**
81 106 * This method is responsible for enqueueing scripts in settings panel
82 107 *
83 108 * @param string $hook
84 109 *
@@ -95,9 +120,9 @@
95 120
96 121 $settings = GlobalFields::normalize( $this->settings_args() );
97 122
98 123 // Mask sensitive API keys before they reach the browser. Non-admins still get them stripped entirely below.
99 - $sensitive_api_keys = array( 'ai_autowrite_api_key', 'ai_chatbot_api_key', 'ga4_api_secret', 'maxmind_license_key', 'maxmind_account_id' );
124 + $sensitive_api_keys = self::sensitive_api_key_fields();
100 125 foreach ( $sensitive_api_keys as $api_key_field ) {
101 126 if ( ! empty( $settings[ 'values' ][ $api_key_field ] ) ) {
102 127 $settings[ 'values' ][ $api_key_field ] = Helper::mask_api_key( $settings[ 'values' ][ $api_key_field ] );
103 128 }
@@ -214,8 +239,12 @@
214 239 'category_slug' => 'docs-category',
215 240 'tag_slug' => 'docs-tag',
216 241 'permalink_structure' => 'docs/',
217 242 'enable_faq_schema' => false,
243 + 'analytics_from' => 'everyone',
244 + 'unique_visitor_count' => true,
245 + 'exclude_bot_analytics' => true,
246 + 'child_category_exclude' => false,
218 247 'live_search' => true,
219 248 'advance_search' => false,
220 249 'popular_keyword_limit' => 5,
221 250 'search_letter_limit' => 3,
@@ -258,8 +287,12 @@
258 287 'enable_breadcrumb_category' => true,
259 288 'enable_breadcrumb_title' => true,
260 289 'enable_sidebar_cat_list' => true,
261 290 'enable_print_icon' => true,
291 + 'print_enable_logo' => false,
292 + 'print_logo' => array(),
293 + 'print_enable_footer' => false,
294 + 'print_footer_text' => '',
262 295 'enable_tags' => true,
263 296 'email_feedback' => true,
264 297 'feedback_link_text' => __( 'Still stuck? How can we help?', 'betterdocs' ),
265 298 'reaction_feedback_text' => __( 'Thanks for your feedback', 'betterdocs' ),
@@ -272,8 +305,9 @@
272 305 'enable_credit' => false,
273 306 'enable_archive_sidebar' => true,
274 307 'archive_nested_subcategory' => true,
275 308 'archive_enable_pagination' => false,
309 + 'archive_lazy_load_descendants' => false,
276 310 'enable_content_restriction' => false,
277 311 'enable_reporting' => false,
278 312 'enable_sample_data' => false,
279 313 'reporting_day' => 'monday',
@@ -295,8 +329,19 @@
295 329 'ai_edit_actions' => AIEdit::default_actions(),
296 330 'enable_article_summary' => false,
297 331 'article_summary_model' => 'gpt-4o-mini',
298 332 'article_summary_max_token' => 1500,
333 + // Multi-platform AI (content suite). `ai_platform` selects the active
334 + // provider; `ai_model` is the single global model; keys are stored
335 + // per platform so switching never loses a saved key. OpenAI reuses
336 + // `ai_autowrite_api_key` above — it has always held an OpenAI key,
337 + // so nothing needs migrating.
338 + 'ai_platform' => 'openai',
339 + 'ai_model' => 'gpt-4o-mini',
340 + 'ai_api_key_gemini' => '',
341 + 'ai_api_key_claude' => '',
342 + 'ai_api_key_deepseek' => '',
343 + 'ai_api_key_openrouter' => '',
299 344 'enable_estimated_reading_time' => true,
300 345 'enable_encyclopedia' => false,
301 346 'enable_glossaries' => false,
302 347 'show_glossary_suggestions' => true,
@@ -306,9 +351,40 @@
306 351 'singular_estimated_reading_time_text' => __( 'min read', 'betterdocs' ),
307 352 'betterdocs_access_control_repeater' => array(),
308 353 'internal_knowledge_base_type' => 'basic',
309 354 'betterdocs_access_control_repeater_kb' => array(),
310 - 'enable_git_integration' => false
355 + 'enable_git_integration' => false,
356 + 'enable_ai_actions' => true,
357 + 'enable_markdown_endpoint' => true,
358 + 'ai_actions_button_label' => __( 'Copy page', 'betterdocs' ),
359 + 'ai_actions_prompt_template' => __( 'Read from {URL} so I can ask questions about it.', 'betterdocs' ),
360 + 'ai_actions_copy_page' => true,
361 + 'ai_actions_view_markdown' => true,
362 + 'ai_actions_chatgpt' => true,
363 + 'ai_actions_claude' => true,
364 + 'ai_actions_gemini' => true,
365 + 'ai_actions_perplexity' => false,
366 + 'ai_actions_grok' => false,
367 + 'enable_listen' => true,
368 + 'listen_button_label' => __( 'Listen', 'betterdocs' ),
369 + 'listen_show_speed' => true,
370 + // Words a synthetic voice gets through in a minute at rate 1.
371 + // Deliberately lower than the 200 the reading-time pill assumes — that
372 + // number describes silent reading. Only the player's duration estimate
373 + // uses this; see Core\Listen::WORDS_PER_MINUTE.
374 + 'listen_words_per_minute' => 180,
375 + /**
376 + * MCP master switch. Off by default; the toggle lives on the
377 + * BetterDocs → MCP page, not in the settings tree, and writes
378 + * through POST betterdocs/v1/settings.
379 + *
380 + * The key has to be listed here or `get()` cannot see it at all:
381 + * it answers `$default` for anything absent from the defaults
382 + * array, whatever the stored option holds.
383 + *
384 + * @since 4.9.0
385 + */
386 + 'enable_mcp' => ''
311 387 );
312 388
313 389 $_default = apply_filters( 'betterdocs_default_settings', $_default );
314 390 // $_default = apply_filters_deprecated(
@@ -609,9 +685,9 @@
609 685 // slips into storage, mask(mask) === mask, so an equality-only guard would faithfully
610 686 // preserve the corrupted value forever (round-2 follow-up #1). When the stored value is
611 687 // itself mask-shaped it is unrecoverable — clear it so the UI and the GeoIP/GA4 status
612 688 // surfaces honestly report a missing key instead of failing downstream with 401s.
613 - $sensitive_api_keys = array( 'ai_autowrite_api_key', 'ai_chatbot_api_key', 'ga4_api_secret', 'maxmind_license_key', 'maxmind_account_id' );
689 + $sensitive_api_keys = self::sensitive_api_key_fields();
614 690 foreach ( $sensitive_api_keys as $api_key_field ) {
615 691 if ( ! isset( $settings[ $api_key_field ] ) ) {
616 692 continue;
617 693 }
@@ -636,9 +712,9 @@
636 712 array(
637 713 'tab' => 'tab-betterdocs-ai',
638 714 'context' => 'write_with_ai',
639 715 'token_key' => 'ai_autowrite_max_token',
640 - 'model_key' => 'write_with_ai_model',
716 + 'model_key' => 'ai_model',
641 717 'label' => __( 'Write with AI', 'betterdocs' )
642 718 ),
643 719 array(
644 720 'tab' => 'tab-betterdocs-ai',
@@ -643,9 +719,9 @@
643 719 array(
644 720 'tab' => 'tab-betterdocs-ai',
645 721 'context' => 'article_summary',
646 722 'token_key' => 'article_summary_max_token',
647 - 'model_key' => 'article_summary_model',
723 + 'model_key' => 'ai_model',
648 724 'label' => __( 'AI Doc Summarizer', 'betterdocs' )
649 725 )
650 726 );
651 727 foreach ( $token_pairs as $pair ) {
@@ -680,21 +756,38 @@
680 756 betterdocs()->kbmigration->migrate();
681 757 }
682 758 $_settings = wp_parse_args( $_normalized_settings, $_old_settings );
683 759
684 - // Check if there are actual changes before saving.
685 - // update_option returns false when values serialize identically, which can happen
686 - // due to object caching or type normalization even when user made changes.
687 - $_has_changes = $_settings != $_old_settings;
760 + // Detect whether this save actually changes the effective settings.
761 + //
762 + // The stored option and the submitted payload are normalized differently:
763 + // an optional field can be ABSENT from storage yet arrive as '' (e.g.
764 + // Feedback URL), and array fields can be stored empty ( [] ) while their
765 + // normalized/default form is non-empty (e.g. Instant Answer's
766 + // display_ia_texonomy defaults to ['all']). Comparing the raw arrays
767 + // ( $_settings != $_old_settings ) therefore reported a phantom change on
768 + // every save, leaving the tab perpetually "dirty" and always toasting
769 + // "Changes Saved Successfully." instead of "There are no changes to be
770 + // saved." — see WPDevelopers/betterdocs-pro#78.
771 + //
772 + // Compare like-for-like instead: fill defaults on both sides and run both
773 + // through the same normalization, so semantically-equal states (absent vs
774 + // '', [] vs ['all'], 'on' vs true) collapse to identical values and only a
775 + // real edit registers. This is also more reliable than update_option()'s
776 + // return, which is false whenever values serialize identically under object
777 + // caching or type coercion even when the user did change something (#49).
778 + $_defaults = array_merge( $this->get_default(), $this->get_pro_defaults() );
779 + $_old_normalized = $this->get_normalized_values( wp_parse_args( $_old_settings, $_defaults ), $_defaults );
780 + $_new_normalized = $this->get_normalized_values( wp_parse_args( $_settings, $_defaults ), $_defaults );
781 + $_has_changes = $_new_normalized != $_old_normalized;
688 782
689 783 $_saved = $this->database->save( $this->base_key, $_settings );
690 784
691 785 do_action_ref_array( 'betterdocs::settings::saved', array( $_saved, $_settings, $_old_settings, &$this ) );
692 786
693 - // Return true if save succeeded OR if there were changes to attempt saving.
694 - // This handles cases where update_option returns false due to identical serialization
695 - // (e.g., object caching, type coercion during serialization).
696 - return $_saved || $_has_changes;
787 + // The success / no-changes toast reflects whether the user made a real
788 + // change, not update_option()'s (unreliable) return value.
789 + return $_has_changes;
697 790 }
698 791
699 792 public function views( $hook ) {
700 793 return betterdocs()->views->get( 'admin/settings' );
@@ -892,10 +985,9 @@
892 985 'guests' => __( 'Guests Only', 'betterdocs' ),
893 986 'registered_users' => __( 'Registered Users Only', 'betterdocs' )
894 987 ) ),
895 988 'default' => 'everyone',
896 - 'priority' => 13,
897 - 'is_pro' => true
989 + 'priority' => 13
898 990 ),
899 991 'unique_visitor_count' => array(
900 992 'name' => 'unique_visitor_count',
901 993 'type' => 'toggle',
@@ -900,11 +992,10 @@
900 992 'name' => 'unique_visitor_count',
901 993 'type' => 'toggle',
902 994 'label' => __( 'Unique Visitor Count', 'betterdocs' ),
903 995 'enable_disable_text_active' => true,
904 - 'default' => false,
905 - 'priority' => 14,
906 - 'is_pro' => true
996 + 'default' => true,
997 + 'priority' => 14
907 998 ),
908 999 'exclude_bot_analytics' => array(
909 1000 'name' => 'exclude_bot_analytics',
910 1001 'type' => 'toggle',
@@ -909,11 +1000,10 @@
909 1000 'name' => 'exclude_bot_analytics',
910 1001 'type' => 'toggle',
911 1002 'label' => __( 'Exclude Bot Analytics', 'betterdocs' ),
912 1003 'enable_disable_text_active' => true,
913 - 'default' => false,
914 - 'priority' => 15,
915 - 'is_pro' => true
1004 + 'default' => true,
1005 + 'priority' => 15
916 1006 )
917 1007
918 1008 )
919 1009 ) )
@@ -1017,8 +1107,17 @@
1017 1107 'enable_disable_text_active' => true,
1018 1108 'default' => 1,
1019 1109 'priority' => 1
1020 1110 ),
1111 + 'archive_lazy_load_descendants' => array(
1112 + 'name' => 'archive_lazy_load_descendants',
1113 + 'type' => 'toggle',
1114 + 'label' => __( 'Lazy Load Sidebar Docs & Subcategories', 'betterdocs' ),
1115 + 'label_subtitle' => __( 'Applies to the Sidebar layout wherever it appears — single docs, category archives, and the Sidebar widget/block. Off by default: enable to render only the active category upfront and fetch the rest on click (or on scroll for the Memphis layout), cutting initial HTML size on large knowledge bases.', 'betterdocs' ),
1116 + 'enable_disable_text_active' => true,
1117 + 'default' => false,
1118 + 'priority' => 2
1119 + ),
1021 1120 'nested_subcategory' => array(
1022 1121 'name' => 'nested_subcategory',
1023 1122 'type' => 'toggle',
1024 1123 'label' => __( 'Nested Sub Category', 'betterdocs' ),
@@ -1023,9 +1122,9 @@
1023 1122 'type' => 'toggle',
1024 1123 'label' => __( 'Nested Sub Category', 'betterdocs' ),
1025 1124 'enable_disable_text_active' => true,
1026 1125 'default' => '',
1027 - 'priority' => 2
1126 + 'priority' => 3
1028 1127 ),
1029 1128 'column_number' => array(
1030 1129 'name' => 'column_number',
1031 1130 'type' => 'number',
@@ -1031,9 +1130,9 @@
1031 1130 'type' => 'number',
1032 1131 'label' => __( 'Number Of Columns', 'betterdocs' ),
1033 1132 'label_subtitle' => __( 'This setting is not applicable for sleek layout.', 'betterdocs' ),
1034 1133 'default' => 3,
1035 - 'priority' => 3
1134 + 'priority' => 4
1036 1135 ),
1037 1136 'posts_number' => apply_filters( 'betterdocs_posts_number', array(
1038 1137 'name' => 'posts_number',
1039 1138 'type' => 'number',
@@ -1039,9 +1138,9 @@
1039 1138 'type' => 'number',
1040 1139 'label' => __( 'Number Of Docs', 'betterdocs' ),
1041 1140 'label_subtitle' => __( 'This setting is not applicable for handbook layout.', 'betterdocs' ),
1042 1141 'default' => 10,
1043 - 'priority' => 4
1142 + 'priority' => 5
1044 1143 ) ),
1045 1144 'post_count' => array(
1046 1145 'name' => 'post_count',
1047 1146 'type' => 'toggle',
@@ -1047,9 +1146,9 @@
1047 1146 'type' => 'toggle',
1048 1147 'label' => __( 'Doc Count', 'betterdocs' ),
1049 1148 'enable_disable_text_active' => true,
1050 1149 'default' => 1,
1051 - 'priority' => 5
1150 + 'priority' => 6
1052 1151 ),
1053 1152 'count_text' => array(
1054 1153 'name' => 'count_text',
1055 1154 'type' => 'text',
@@ -1054,9 +1153,9 @@
1054 1153 'name' => 'count_text',
1055 1154 'type' => 'text',
1056 1155 'label' => __( 'Count Text', 'betterdocs' ),
1057 1156 'default' => __( 'Docs', 'betterdocs' ),
1058 - 'priority' => 6
1157 + 'priority' => 7
1059 1158 ),
1060 1159 'count_text_singular' => array(
1061 1160 'name' => 'count_text_singular',
1062 1161 'type' => 'text',
@@ -1061,9 +1160,9 @@
1061 1160 'name' => 'count_text_singular',
1062 1161 'type' => 'text',
1063 1162 'label' => __( 'Count Text Singular', 'betterdocs' ),
1064 1163 'default' => __( 'Doc', 'betterdocs' ),
1065 - 'priority' => 7
1164 + 'priority' => 8
1066 1165 ),
1067 1166 'exploremore_btn' => array(
1068 1167 'name' => 'exploremore_btn',
1069 1168 'type' => 'toggle',
@@ -1069,9 +1168,9 @@
1069 1168 'type' => 'toggle',
1070 1169 'label' => __( 'Explore More Button', 'betterdocs' ),
1071 1170 'enable_disable_text_active' => true,
1072 1171 'default' => true,
1073 - 'priority' => 8
1172 + 'priority' => 9
1074 1173 ),
1075 1174 'exploremore_btn_txt' => array(
1076 1175 'name' => 'exploremore_btn_txt',
1077 1176 'type' => 'text',
@@ -1076,9 +1175,9 @@
1076 1175 'name' => 'exploremore_btn_txt',
1077 1176 'type' => 'text',
1078 1177 'label' => __( 'Explore More Button Text', 'betterdocs' ),
1079 1178 'default' => __( 'Explore More', 'betterdocs' ),
1080 - 'priority' => 9,
1179 + 'priority' => 10,
1081 1180 'rules' => Rules::is( 'exploremore_btn', true )
1082 1181 ),
1083 1182 'betterdocs_popular_docs_text' => array(
1084 1183 'name' => 'betterdocs_popular_docs_text',
@@ -1084,9 +1183,9 @@
1084 1183 'name' => 'betterdocs_popular_docs_text',
1085 1184 'type' => 'text',
1086 1185 'label' => __( 'Popular Docs Text', 'betterdocs' ),
1087 1186 'default' => __( 'Popular Docs', 'betterdocs' ),
1088 - 'priority' => 10,
1187 + 'priority' => 11,
1089 1188 'is_pro' => true
1090 1189 ),
1091 1190 'betterdocs_popular_docs_number' => array(
1092 1191 'name' => 'betterdocs_popular_docs_number',
@@ -1092,9 +1191,9 @@
1092 1191 'name' => 'betterdocs_popular_docs_number',
1093 1192 'type' => 'number',
1094 1193 'label' => __( 'Popular Docs Number', 'betterdocs' ),
1095 1194 'default' => 10,
1096 - 'priority' => 11,
1195 + 'priority' => 12,
1097 1196 'is_pro' => true
1098 1197 )
1099 1198 )
1100 1199 ),
@@ -1127,11 +1226,10 @@
1127 1226 'name' => 'child_category_exclude',
1128 1227 'type' => 'toggle',
1129 1228 'label' => __( 'Exclude Child Terms In Category Search', 'betterdocs' ),
1130 1229 'enable_disable_text_active' => true,
1131 - 'default' => '',
1132 - 'priority' => 3,
1133 - 'is_pro' => true
1230 + 'default' => false,
1231 + 'priority' => 3
1134 1232 ) ),
1135 1233 'popular_keyword_limit' => apply_filters( 'betterdocs_popular_keyword_limit_settings', array(
1136 1234 'name' => 'popular_keyword_limit',
1137 1235 'type' => 'number',
@@ -1338,8 +1436,44 @@
1338 1436 'enable_disable_text_active' => true,
1339 1437 'default' => 1,
1340 1438 'priority' => 3
1341 1439 ),
1440 + 'print_enable_logo' => array(
1441 + 'name' => 'print_enable_logo',
1442 + 'type' => 'toggle',
1443 + 'label' => __( 'Logo on Printed Doc', 'betterdocs' ),
1444 + 'label_subtitle' => __( 'Show a logo at the top of the printed / PDF page', 'betterdocs' ),
1445 + 'enable_disable_text_active' => true,
1446 + 'default' => 0,
1447 + 'priority' => 4
1448 + ),
1449 + 'print_logo' => array(
1450 + 'name' => 'print_logo',
1451 + 'type' => 'media',
1452 + 'value' => '',
1453 + 'label' => __( 'Print Logo', 'betterdocs' ),
1454 + 'label_subtitle' => __( 'Leave empty to use your site logo, or the site icon when no site logo is set', 'betterdocs' ),
1455 + 'priority' => 5,
1456 + 'rules' => Rules::is( 'print_enable_logo', true )
1457 + ),
1458 + 'print_enable_footer' => array(
1459 + 'name' => 'print_enable_footer',
1460 + 'type' => 'toggle',
1461 + 'label' => __( 'Footer on Printed Doc', 'betterdocs' ),
1462 + 'label_subtitle' => __( 'Show a footer on every page of the printed / PDF document', 'betterdocs' ),
1463 + 'enable_disable_text_active' => true,
1464 + 'default' => 0,
1465 + 'priority' => 6
1466 + ),
1467 + 'print_footer_text' => array(
1468 + 'name' => 'print_footer_text',
1469 + 'type' => 'textarea',
1470 + 'label' => __( 'Print Footer Text', 'betterdocs' ),
1471 + 'label_subtitle' => __( 'Leave empty to use the site name and current year', 'betterdocs' ),
1472 + 'default' => '',
1473 + 'priority' => 7,
1474 + 'rules' => Rules::is( 'print_enable_footer', true )
1475 + ),
1342 1476 'enable_tags' => array(
1343 1477 'name' => 'enable_tags',
1344 1478 'type' => 'toggle',
1345 1479 'label' => __( 'Tags', 'betterdocs' ),
@@ -1344,9 +1478,9 @@
1344 1478 'type' => 'toggle',
1345 1479 'label' => __( 'Tags', 'betterdocs' ),
1346 1480 'enable_disable_text_active' => true,
1347 1481 'default' => 1,
1348 - 'priority' => 4
1482 + 'priority' => 8
1349 1483 ),
1350 1484 'show_last_update_time' => array(
1351 1485 'name' => 'show_last_update_time',
1352 1486 'type' => 'toggle',
@@ -1352,9 +1486,9 @@
1352 1486 'type' => 'toggle',
1353 1487 'label' => __( 'Last Update Time', 'betterdocs' ),
1354 1488 'enable_disable_text_active' => true,
1355 1489 'default' => 1,
1356 - 'priority' => 5
1490 + 'priority' => 9
1357 1491 ),
1358 1492 'enable_navigation' => array(
1359 1493 'name' => 'enable_navigation',
1360 1494 'type' => 'toggle',
@@ -1360,9 +1494,9 @@
1360 1494 'type' => 'toggle',
1361 1495 'label' => __( 'Navigation', 'betterdocs' ),
1362 1496 'enable_disable_text_active' => true,
1363 1497 'default' => 1,
1364 - 'priority' => 6
1498 + 'priority' => 10
1365 1499 ),
1366 1500 'enable_comment' => array(
1367 1501 'name' => 'enable_comment',
1368 1502 'type' => 'toggle',
@@ -1368,9 +1502,9 @@
1368 1502 'type' => 'toggle',
1369 1503 'label' => __( 'Comment', 'betterdocs' ),
1370 1504 'enable_disable_text_active' => true,
1371 1505 'default' => '',
1372 - 'priority' => 7
1506 + 'priority' => 11
1373 1507 ),
1374 1508 'enable_credit' => array(
1375 1509 'name' => 'enable_credit',
1376 1510 'type' => 'toggle',
@@ -1376,9 +1510,9 @@
1376 1510 'type' => 'toggle',
1377 1511 'label' => __( 'Show Powered by BetterDocs', 'betterdocs' ),
1378 1512 'enable_disable_text_active' => true,
1379 1513 'default' => '',
1380 - 'priority' => 8
1514 + 'priority' => 12
1381 1515 ),
1382 1516 'reaction_feedback_text' => array(
1383 1517 'name' => 'reaction_feedback_text',
1384 1518 'type' => 'text',
@@ -1383,9 +1517,9 @@
1383 1517 'name' => 'reaction_feedback_text',
1384 1518 'type' => 'text',
1385 1519 'label' => __( 'Reaction Feedback Text', 'betterdocs' ),
1386 1520 'default' => __( 'Thanks for your feedback.', 'betterdocs' ),
1387 - 'priority' => 9
1521 + 'priority' => 13
1388 1522 ),
1389 1523 'enable_estimated_reading_time' => array(
1390 1524 'name' => 'enable_estimated_reading_time',
1391 1525 'type' => 'toggle',
@@ -1391,9 +1525,9 @@
1391 1525 'type' => 'toggle',
1392 1526 'label' => __( 'Estimated Reading Time', 'betterdocs' ),
1393 1527 'enable_disable_text_active' => true,
1394 1528 'default' => 0,
1395 - 'priority' => 10
1529 + 'priority' => 14
1396 1530 ),
1397 1531 'estimated_reading_time_title' => array(
1398 1532 'name' => 'estimated_reading_time_title',
1399 1533 'type' => 'text',
@@ -1398,9 +1532,9 @@
1398 1532 'name' => 'estimated_reading_time_title',
1399 1533 'type' => 'text',
1400 1534 'label' => __( 'Estimated Reading Time Title', 'betterdocs' ),
1401 1535 'default' => '',
1402 - 'priority' => 11,
1536 + 'priority' => 15,
1403 1537 'rules' => Rules::is( 'enable_estimated_reading_time', true )
1404 1538 ),
1405 1539 'estimated_reading_time_text' => array(
1406 1540 'name' => 'estimated_reading_time_text',
@@ -1406,9 +1540,9 @@
1406 1540 'name' => 'estimated_reading_time_text',
1407 1541 'type' => 'text',
1408 1542 'label' => __( 'Estimated Reading Time Text', 'betterdocs' ),
1409 1543 'default' => __( 'min read', 'betterdocs' ),
1410 - 'priority' => 12,
1544 + 'priority' => 16,
1411 1545 'rules' => Rules::is( 'enable_estimated_reading_time', true )
1412 1546 ),
1413 1547 'singular_estimated_reading_time_text' => array(
1414 1548 'name' => 'singular_estimated_reading_time_text',
@@ -1414,9 +1548,9 @@
1414 1548 'name' => 'singular_estimated_reading_time_text',
1415 1549 'type' => 'text',
1416 1550 'label' => __( 'Estimated Reading Time Text Singular', 'betterdocs' ),
1417 1551 'default' => __( 'min read', 'betterdocs' ),
1418 - 'priority' => 13,
1552 + 'priority' => 17,
1419 1553 'rules' => Rules::is( 'enable_estimated_reading_time', true )
1420 1554 )
1421 1555 )
1422 1556 ),
@@ -1683,8 +1817,202 @@
1683 1817 'enable_disable_text_active' => true,
1684 1818 'default' => false
1685 1819 )
1686 1820 ) )
1821 + ),
1822 + 'layout_single_doc_ai_actions' => array(
1823 + 'id' => 'layout_single_doc_ai_actions',
1824 + 'name' => 'layout_single_doc_ai_actions',
1825 + 'type' => 'section',
1826 + 'label' => __( 'AI Actions', 'betterdocs' ),
1827 + 'priority' => 8,
1828 + 'fields' => apply_filters( 'betterdocs_single_doc_ai_actions', array(
1829 + 'enable_ai_actions' => array(
1830 + 'name' => 'enable_ai_actions',
1831 + 'type' => 'toggle',
1832 + 'priority' => 1,
1833 + 'label' => __( 'Enable AI Actions', 'betterdocs' ),
1834 + 'label_subtitle' => __( 'Show a "Copy page" button beside the doc title so readers can copy the page as Markdown or open it in an AI assistant.', 'betterdocs' ),
1835 + 'enable_disable_text_active' => true,
1836 + 'default' => true
1837 + ),
1838 + 'enable_markdown_endpoint' => array(
1839 + 'name' => 'enable_markdown_endpoint',
1840 + 'type' => 'toggle',
1841 + 'priority' => 2,
1842 + 'label' => __( 'Serve Docs as Markdown', 'betterdocs' ),
1843 + // Plain text, not `<doc-url>.md`: the subtitle is rendered as HTML, so an
1844 + // angle-bracketed placeholder is swallowed as an unknown tag and the
1845 + // sentence read "Publish every doc at .md".
1846 + 'label_subtitle' => __( 'Publish every doc as Markdown at its address plus .md (for example /docs/getting-started.md), so AI assistants can read a clean copy. The Listen player uses it too. Required by "Copy page", "View as Markdown", and the Claude and Google AI Studio actions.', 'betterdocs' ),
1847 + 'enable_disable_text_active' => true,
1848 + 'default' => true
1849 + ),
1850 + // AI Actions and the Markdown endpoint are deliberately independent: the
1851 + // `.md` copies also serve AI assistants directly and the Listen player.
1852 + // So switching AI Actions off leaves Markdown on — say so where the
1853 + // admin is looking, rather than leaving a lone "Enabled" toggle under a
1854 + // feature that reads as switched off.
1855 + 'ai_actions_markdown_notice' => array(
1856 + 'name' => 'ai_actions_markdown_notice',
1857 + 'type' => 'html',
1858 + 'priority' => 3,
1859 + 'html' => sprintf(
1860 + '<div class="betterdocs-ai-actions-markdown-notice" style="margin:8px 0 0;padding:12px 16px;background:#f0f6ff;border-left:4px solid #5a6bff;border-radius:4px;font-size:13px;color:#202223;line-height:1.5;">%s</div>',
1861 + esc_html__( 'AI Actions is off, but your docs are still published as Markdown (.md) for AI assistants and the Listen player. Turn off "Serve Docs as Markdown" above if you don\'t want that.', 'betterdocs' )
1862 + ),
1863 + 'rules' => Rules::logicalRule( array(
1864 + Rules::is( 'enable_ai_actions', false ),
1865 + Rules::is( 'enable_markdown_endpoint', true )
1866 + ), 'and' )
1867 + ),
1868 + 'ai_actions_button_label' => array(
1869 + 'name' => 'ai_actions_button_label',
1870 + 'type' => 'text',
1871 + 'priority' => 4,
1872 + 'label' => __( 'Button Label', 'betterdocs' ),
1873 + 'default' => __( 'Copy page', 'betterdocs' ),
1874 + 'rules' => Rules::is( 'enable_ai_actions', true )
1875 + ),
1876 + 'ai_actions_prompt_template' => array(
1877 + 'name' => 'ai_actions_prompt_template',
1878 + 'type' => 'text',
1879 + 'priority' => 5,
1880 + 'label' => __( 'AI Prompt Template', 'betterdocs' ),
1881 + 'label_subtitle' => __( 'Sent to the AI assistant when a reader opens this doc there. Use {URL} for the doc address.', 'betterdocs' ),
1882 + 'default' => __( 'Read from {URL} so I can ask questions about it.', 'betterdocs' ),
1883 + 'rules' => Rules::is( 'enable_ai_actions', true )
1884 + ),
1885 + 'ai_actions_copy_page' => array(
1886 + 'name' => 'ai_actions_copy_page',
1887 + 'type' => 'toggle',
1888 + 'priority' => 6,
1889 + 'label' => __( 'Copy Page', 'betterdocs' ),
1890 + 'enable_disable_text_active' => true,
1891 + 'default' => true,
1892 + 'rules' => Rules::logicalRule( array(
1893 + Rules::is( 'enable_ai_actions', true ),
1894 + Rules::is( 'enable_markdown_endpoint', true )
1895 + ), 'and' )
1896 + ),
1897 + 'ai_actions_view_markdown' => array(
1898 + 'name' => 'ai_actions_view_markdown',
1899 + 'type' => 'toggle',
1900 + 'priority' => 7,
1901 + 'label' => __( 'View as Markdown', 'betterdocs' ),
1902 + 'enable_disable_text_active' => true,
1903 + 'default' => true,
1904 + 'rules' => Rules::logicalRule( array(
1905 + Rules::is( 'enable_ai_actions', true ),
1906 + Rules::is( 'enable_markdown_endpoint', true )
1907 + ), 'and' )
1908 + ),
1909 + 'ai_actions_chatgpt' => array(
1910 + 'name' => 'ai_actions_chatgpt',
1911 + 'type' => 'toggle',
1912 + 'priority' => 8,
1913 + 'label' => __( 'Open in ChatGPT', 'betterdocs' ),
1914 + 'enable_disable_text_active' => true,
1915 + 'default' => true,
1916 + 'rules' => Rules::is( 'enable_ai_actions', true )
1917 + ),
1918 + 'ai_actions_claude' => array(
1919 + 'name' => 'ai_actions_claude',
1920 + 'type' => 'toggle',
1921 + 'priority' => 9,
1922 + 'label' => __( 'Open in Claude', 'betterdocs' ),
1923 + 'enable_disable_text_active' => true,
1924 + 'default' => true,
1925 + 'rules' => Rules::logicalRule( array(
1926 + Rules::is( 'enable_ai_actions', true ),
1927 + Rules::is( 'enable_markdown_endpoint', true )
1928 + ), 'and' )
1929 + ),
1930 + 'ai_actions_gemini' => array(
1931 + 'name' => 'ai_actions_gemini',
1932 + 'type' => 'toggle',
1933 + 'priority' => 10,
1934 + 'label' => __( 'Open in Google AI Studio', 'betterdocs' ),
1935 + 'label_subtitle' => __( 'Gemini has no way to receive a prompt from a link, so this opens Google AI Studio instead. A Google account is required.', 'betterdocs' ),
1936 + 'enable_disable_text_active' => true,
1937 + 'default' => true,
1938 + 'rules' => Rules::logicalRule( array(
1939 + Rules::is( 'enable_ai_actions', true ),
1940 + Rules::is( 'enable_markdown_endpoint', true )
1941 + ), 'and' )
1942 + ),
1943 + 'ai_actions_perplexity' => array(
1944 + 'name' => 'ai_actions_perplexity',
1945 + 'type' => 'toggle',
1946 + 'priority' => 11,
1947 + 'label' => __( 'Open in Perplexity', 'betterdocs' ),
1948 + 'enable_disable_text_active' => true,
1949 + 'default' => false,
1950 + 'rules' => Rules::logicalRule( array(
1951 + Rules::is( 'enable_ai_actions', true ),
1952 + Rules::is( 'enable_markdown_endpoint', true )
1953 + ), 'and' )
1954 + ),
1955 + 'ai_actions_grok' => array(
1956 + 'name' => 'ai_actions_grok',
1957 + 'type' => 'toggle',
1958 + 'priority' => 12,
1959 + 'label' => __( 'Open in Grok', 'betterdocs' ),
1960 + 'enable_disable_text_active' => true,
1961 + 'default' => false,
1962 + 'rules' => Rules::logicalRule( array(
1963 + Rules::is( 'enable_ai_actions', true ),
1964 + Rules::is( 'enable_markdown_endpoint', true )
1965 + ), 'and' )
1966 + )
1967 + ) )
1968 + ),
1969 + 'layout_single_doc_listen' => array(
1970 + 'id' => 'layout_single_doc_listen',
1971 + 'name' => 'layout_single_doc_listen',
1972 + 'type' => 'section',
1973 + 'label' => __( 'Listen', 'betterdocs' ),
1974 + 'priority' => 9,
1975 + 'fields' => apply_filters( 'betterdocs_single_doc_listen', array(
1976 + 'enable_listen' => array(
1977 + 'name' => 'enable_listen',
1978 + 'type' => 'toggle',
1979 + 'priority' => 1,
1980 + 'label' => __( 'Enable Listen', 'betterdocs' ),
1981 + 'label_subtitle' => __( 'Show a "Listen" button beside the reading time. Pressed, it turns into a small audio player that reads the doc aloud using the visitor\'s browser — no audio files and no third-party service.', 'betterdocs' ),
1982 + 'enable_disable_text_active' => true,
1983 + 'default' => true
1984 + ),
1985 + 'listen_button_label' => array(
1986 + 'name' => 'listen_button_label',
1987 + 'type' => 'text',
1988 + 'priority' => 2,
1989 + 'label' => __( 'Button Label', 'betterdocs' ),
1990 + 'default' => __( 'Listen', 'betterdocs' ),
1991 + 'rules' => Rules::is( 'enable_listen', true )
1992 + ),
1993 + 'listen_show_speed' => array(
1994 + 'name' => 'listen_show_speed',
1995 + 'type' => 'toggle',
1996 + 'priority' => 3,
1997 + 'label' => __( 'Speed Control', 'betterdocs' ),
1998 + 'label_subtitle' => __( 'Let readers cycle the playback speed between 0.75× and 2×.', 'betterdocs' ),
1999 + 'enable_disable_text_active' => true,
2000 + 'default' => true,
2001 + 'rules' => Rules::is( 'enable_listen', true )
2002 + ),
2003 + 'listen_words_per_minute' => array(
2004 + 'name' => 'listen_words_per_minute',
2005 + 'type' => 'number',
2006 + 'priority' => 4,
2007 + 'label' => __( 'Words Per Minute', 'betterdocs' ),
2008 + 'label_subtitle' => __( 'Used to estimate the total playing time the player shows. Browser voices read at roughly 180 words a minute at normal speed; raise this if the countdown finishes early.', 'betterdocs' ),
2009 + 'default' => 180,
2010 + 'min' => 60,
2011 + 'max' => 400,
2012 + 'rules' => Rules::is( 'enable_listen', true )
2013 + )
2014 + ) )
1687 2015 )
1688 2016 )
1689 2017 )
1690 2018 )
@@ -2296,18 +2624,84 @@
2296 2624 'open-ai-settings' => array(
2297 2625 'id' => 'open-ai-settings',
2298 2626 'name' => 'open-ai-settings',
2299 2627 'type' => 'section',
2300 - 'label' => __( 'API Settings', 'betterdocs' ),
2628 + 'label' => __( 'AI Platform & API', 'betterdocs' ),
2301 2629 'priority' => 1,
2302 2630 'fields' => array(
2631 + 'ai_platform' => array(
2632 + 'name' => 'ai_platform',
2633 + 'type' => 'select',
2634 + 'label' => __( 'AI Platform', 'betterdocs' ),
2635 + 'label_subtitle' => __( 'Choose which AI platform powers Write with AI, AI Edit, the Doc Summarizer and Quality Score. The model list and API key field below adapt to your choice.', 'betterdocs' ),
2636 + 'priority' => 1,
2637 + 'multiple' => false,
2638 + 'default' => 'openai',
2639 + // DeepSeek and OpenRouter are temporarily hidden from the
2640 + // dropdown. Filter the options here, NOT ModelRegistry::platforms() —
2641 + // sensitive_api_key_fields() loops the registry to mask each
2642 + // platform's key field, so trimming the registry would silently
2643 + // un-mask those keys. Re-enable later by dropping the array_diff_key.
2644 + 'options' => GlobalFields::normalize_fields(
2645 + array_diff_key( ModelRegistry::platforms(), array_flip( array( 'deepseek', 'openrouter' ) ) )
2646 + )
2647 + ),
2648 + // OpenAI's key field keeps its original name so installs
2649 + // that already saved a Write with AI key keep it.
2303 2650 'ai_autowrite_api_key' => array(
2304 2651 'name' => 'ai_autowrite_api_key',
2305 2652 'type' => 'text',
2306 - 'label' => __( 'API Key', 'betterdocs' ),
2307 - 'label_subtitle' => sprintf( /* translators: %s is a link to the documentation about generating an OpenAI API key. */__( 'Check out this <a target="_blank" href="%s">documentation</a> to find out how to generate your OpenAI API Key.', 'betterdocs' ), esc_url( 'https://betterdocs.co/docs/write-with-ai/' ) ),
2653 + 'label' => __( 'OpenAI API Key', 'betterdocs' ),
2654 + 'label_subtitle' => sprintf( /* translators: %s: documentation URL */ __( 'Check out this <a target="_blank" href="%s">documentation</a> to generate your OpenAI API key.', 'betterdocs' ), esc_url( 'https://betterdocs.co/docs/write-with-ai/' ) ),
2308 2655 'default' => '',
2309 - 'priority' => 1
2656 + 'priority' => 2,
2657 + 'rules' => Rules::is( 'ai_platform', 'openai' )
2658 + ),
2659 + 'ai_api_key_gemini' => array(
2660 + 'name' => 'ai_api_key_gemini',
2661 + 'type' => 'text',
2662 + 'label' => __( 'Google Gemini API Key', 'betterdocs' ),
2663 + 'label_subtitle' => sprintf( /* translators: %s: Google AI Studio URL */ __( 'Generate a key from <a target="_blank" href="%s">Google AI Studio</a>.', 'betterdocs' ), esc_url( 'https://aistudio.google.com/app/apikey' ) ),
2664 + 'default' => '',
2665 + 'priority' => 2,
2666 + 'rules' => Rules::is( 'ai_platform', 'gemini' )
2667 + ),
2668 + 'ai_api_key_claude' => array(
2669 + 'name' => 'ai_api_key_claude',
2670 + 'type' => 'text',
2671 + 'label' => __( 'Anthropic Claude API Key', 'betterdocs' ),
2672 + 'label_subtitle' => sprintf( /* translators: %s: Anthropic Console URL */ __( 'Generate a key from the <a target="_blank" href="%s">Anthropic Console</a>.', 'betterdocs' ), esc_url( 'https://console.anthropic.com/settings/keys' ) ),
2673 + 'default' => '',
2674 + 'priority' => 2,
2675 + 'rules' => Rules::is( 'ai_platform', 'claude' )
2676 + ),
2677 + 'ai_api_key_deepseek' => array(
2678 + 'name' => 'ai_api_key_deepseek',
2679 + 'type' => 'text',
2680 + 'label' => __( 'DeepSeek API Key', 'betterdocs' ),
2681 + 'label_subtitle' => sprintf( /* translators: %s: DeepSeek platform URL */ __( 'Generate a key from the <a target="_blank" href="%s">DeepSeek platform</a>.', 'betterdocs' ), esc_url( 'https://platform.deepseek.com/api_keys' ) ),
2682 + 'default' => '',
2683 + 'priority' => 2,
2684 + 'rules' => Rules::is( 'ai_platform', 'deepseek' )
2685 + ),
2686 + 'ai_api_key_openrouter' => array(
2687 + 'name' => 'ai_api_key_openrouter',
2688 + 'type' => 'text',
2689 + 'label' => __( 'OpenRouter API Key', 'betterdocs' ),
2690 + 'label_subtitle' => sprintf( /* translators: %s: OpenRouter keys URL */ __( 'Generate a key from <a target="_blank" href="%s">OpenRouter</a>.', 'betterdocs' ), esc_url( 'https://openrouter.ai/keys' ) ),
2691 + 'default' => '',
2692 + 'priority' => 2,
2693 + 'rules' => Rules::is( 'ai_platform', 'openrouter' )
2694 + ),
2695 + 'ai_model' => array(
2696 + 'name' => 'ai_model',
2697 + 'type' => 'platform_model_select',
2698 + 'label' => __( 'Model', 'betterdocs' ),
2699 + 'label_subtitle' => __( 'Available models depend on the selected platform.', 'betterdocs' ),
2700 + 'priority' => 10,
2701 + 'default' => 'gpt-4o-mini',
2702 + 'catalogue' => ModelRegistry::catalogue(),
2703 + 'defaults' => ModelRegistry::defaults()
2310 2704 )
2311 2705 )
2312 2706 ),
2313 2707 'write-with-ai' => array(
@@ -2372,9 +2766,10 @@
2372 2766 'priority' => 6,
2373 2767 'label' => __( 'Write Glossaries with AI', 'betterdocs' ),
2374 2768 'label_subtitle' => __( 'Generate AI based Glossary definitions from the Glossaries admin page', 'betterdocs' ),
2375 2769 'enable_disable_text_active' => true,
2376 - 'default' => true
2770 + 'default' => true,
2771 + 'is_pro' => true
2377 2772 ),
2378 2773 'enable_docs_ai_suite' => array(
2379 2774 'name' => 'enable_docs_ai_suite',
2380 2775 'type' => 'toggle',
@@ -2383,27 +2778,8 @@
2383 2778 'label_subtitle' => __( 'Add BetterDocs AI actions for suggesting categories, tags and excerpts inside the native Docs editor panels.', 'betterdocs' ),
2384 2779 'enable_disable_text_active' => true,
2385 2780 'default' => true
2386 2781 ),
2387 - 'write_with_ai_model' => array(
2388 - 'name' => 'write_with_ai_model',
2389 - 'type' => 'select',
2390 - 'label' => __( 'OpenAI Model*', 'betterdocs' ),
2391 - 'priority' => 20,
2392 - 'multiple' => false,
2393 - 'default' => 'gpt-4o-mini',
2394 - 'options' => GlobalFields::normalize_fields( array(
2395 - 'gpt-4o-mini' => 'GPT-4o Mini',
2396 - 'gpt-4o' => 'GPT-4o',
2397 - 'gpt-4.1-nano' => 'GPT-4.1 Nano',
2398 - 'gpt-4.1-mini' => 'GPT-4.1 Mini',
2399 - 'gpt-4.1' => 'GPT-4.1',
2400 - 'gpt-5-nano' => 'GPT-5 Nano',
2401 - 'gpt-5-mini' => 'GPT-5 Mini',
2402 - 'gpt-5' => 'GPT-5',
2403 - 'gpt-5.5' => 'GPT-5.5'
2404 - ) )
2405 - ),
2406 2782 'ai_autowrite_max_token' => array(
2407 2783 'name' => 'ai_autowrite_max_token',
2408 2784 'type' => 'min_token_number',
2409 2785 'label' => __( 'Set Max Tokens', 'betterdocs' ),
@@ -2410,9 +2786,9 @@
2410 2786 'label_subtitle' => sprintf( // translators: %s is a link to more information about token limits.
2411 2787 __( 'Documentation will be generated based on the Token Limits you have set. For more information on Token Limits, you can check out this <a target="_blank" href="%s">link</a>.', 'betterdocs' ), esc_url( 'https://platform.openai.com/account/limits' ) ),
2412 2788 'default' => 2500,
2413 2789 'priority' => 10,
2414 - 'model_field' => 'write_with_ai_model',
2790 + 'model_field' => 'ai_model',
2415 2791 'context' => 'write_with_ai',
2416 2792 'min_token_map' => AIHelper::get_min_tokens_map( 'write_with_ai' ),
2417 2793 'classes' => 'wprf-type-text'
2418 2794 )
@@ -2485,31 +2861,12 @@
2485 2861 'label_subtitle' => sprintf( // translators: %s is a link to more information about token limits.
2486 2862 __( 'Single Doc summarizer will be generated based on the token limits you have set. For more information on Token Limits, you can check out this <a target="_blank" href="%s">link</a>.', 'betterdocs' ), esc_url( 'https://platform.openai.com/account/limits' ) ),
2487 2863 'default' => 1500,
2488 2864 'priority' => 5,
2489 - 'model_field' => 'article_summary_model',
2865 + 'model_field' => 'ai_model',
2490 2866 'context' => 'article_summary',
2491 2867 'min_token_map' => AIHelper::get_min_tokens_map( 'article_summary' ),
2492 2868 'classes' => 'wprf-type-text'
2493 - ),
2494 - 'article_summary_model' => array(
2495 - 'name' => 'article_summary_model',
2496 - 'type' => 'select',
2497 - 'label' => __( 'OpenAI Model*', 'betterdocs' ),
2498 - 'priority' => 10,
2499 - 'multiple' => false,
2500 - 'default' => 'gpt-4o-mini',
2501 - 'options' => GlobalFields::normalize_fields( array(
2502 - 'gpt-4o-mini' => 'GPT-4o Mini',
2503 - 'gpt-4o' => 'GPT-4o',
2504 - 'gpt-4.1-nano' => 'GPT-4.1 Nano',
2505 - 'gpt-4.1-mini' => 'GPT-4.1 Mini',
2506 - 'gpt-4.1' => 'GPT-4.1',
2507 - 'gpt-5-nano' => 'GPT-5 Nano',
2508 - 'gpt-5-mini' => 'GPT-5 Mini',
2509 - 'gpt-5' => 'GPT-5',
2510 - 'gpt-5.5' => 'GPT-5.5'
2511 - ) )
2512 2869 )
2513 2870 )
2514 2871 )
2515 2872 )
@@ -2616,15 +2973,11 @@
2616 2973 return array(
2617 2974 'multiple_kb' => false,
2618 2975 'enable_glossaries' => false,
2619 2976 'enable_encyclopedia' => false,
2620 - 'analytics_from' => false,
2621 - 'unique_visitor_count' => false,
2622 - 'exclude_bot_analytics' => false,
2623 2977 'show_attachment' => false,
2624 2978 'show_related_docs' => false,
2625 2979 'advance_search' => false,
2626 - 'child_category_exclude' => false,
2627 2980 'kb_based_search' => false,
2628 2981 'enable_disable' => false,
2629 2982 'enable_content_restriction' => false
2630 2983 );