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/Settings.php +197 -70 4.7.04.9.2 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 }
@@ -258,8 +283,12 @@
258 283 'enable_breadcrumb_category' => true,
259 284 'enable_breadcrumb_title' => true,
260 285 'enable_sidebar_cat_list' => true,
261 286 'enable_print_icon' => true,
287 + 'print_enable_logo' => false,
288 + 'print_logo' => array(),
289 + 'print_enable_footer' => false,
290 + 'print_footer_text' => '',
262 291 'enable_tags' => true,
263 292 'email_feedback' => true,
264 293 'feedback_link_text' => __( 'Still stuck? How can we help?', 'betterdocs' ),
265 294 'reaction_feedback_text' => __( 'Thanks for your feedback', 'betterdocs' ),
@@ -272,8 +301,9 @@
272 301 'enable_credit' => false,
273 302 'enable_archive_sidebar' => true,
274 303 'archive_nested_subcategory' => true,
275 304 'archive_enable_pagination' => false,
305 + 'archive_lazy_load_descendants' => false,
276 306 'enable_content_restriction' => false,
277 307 'enable_reporting' => false,
278 308 'enable_sample_data' => false,
279 309 'reporting_day' => 'monday',
@@ -295,8 +325,19 @@
295 325 'ai_edit_actions' => AIEdit::default_actions(),
296 326 'enable_article_summary' => false,
297 327 'article_summary_model' => 'gpt-4o-mini',
298 328 'article_summary_max_token' => 1500,
329 + // Multi-platform AI (content suite). `ai_platform` selects the active
330 + // provider; `ai_model` is the single global model; keys are stored
331 + // per platform so switching never loses a saved key. OpenAI reuses
332 + // `ai_autowrite_api_key` above — it has always held an OpenAI key,
333 + // so nothing needs migrating.
334 + 'ai_platform' => 'openai',
335 + 'ai_model' => 'gpt-4o-mini',
336 + 'ai_api_key_gemini' => '',
337 + 'ai_api_key_claude' => '',
338 + 'ai_api_key_deepseek' => '',
339 + 'ai_api_key_openrouter' => '',
299 340 'enable_estimated_reading_time' => true,
300 341 'enable_encyclopedia' => false,
301 342 'enable_glossaries' => false,
302 343 'show_glossary_suggestions' => true,
@@ -306,9 +347,21 @@
306 347 'singular_estimated_reading_time_text' => __( 'min read', 'betterdocs' ),
307 348 'betterdocs_access_control_repeater' => array(),
308 349 'internal_knowledge_base_type' => 'basic',
309 350 'betterdocs_access_control_repeater_kb' => array(),
310 - 'enable_git_integration' => false
351 + 'enable_git_integration' => false,
352 + /**
353 + * MCP master switch. Off by default; the toggle lives on the
354 + * BetterDocs → MCP page, not in the settings tree, and writes
355 + * through POST betterdocs/v1/settings.
356 + *
357 + * The key has to be listed here or `get()` cannot see it at all:
358 + * it answers `$default` for anything absent from the defaults
359 + * array, whatever the stored option holds.
360 + *
361 + * @since 4.9.0
362 + */
363 + 'enable_mcp' => ''
311 364 );
312 365
313 366 $_default = apply_filters( 'betterdocs_default_settings', $_default );
314 367 // $_default = apply_filters_deprecated(
@@ -609,9 +662,9 @@
609 662 // slips into storage, mask(mask) === mask, so an equality-only guard would faithfully
610 663 // preserve the corrupted value forever (round-2 follow-up #1). When the stored value is
611 664 // itself mask-shaped it is unrecoverable — clear it so the UI and the GeoIP/GA4 status
612 665 // 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' );
666 + $sensitive_api_keys = self::sensitive_api_key_fields();
614 667 foreach ( $sensitive_api_keys as $api_key_field ) {
615 668 if ( ! isset( $settings[ $api_key_field ] ) ) {
616 669 continue;
617 670 }
@@ -636,9 +689,9 @@
636 689 array(
637 690 'tab' => 'tab-betterdocs-ai',
638 691 'context' => 'write_with_ai',
639 692 'token_key' => 'ai_autowrite_max_token',
640 - 'model_key' => 'write_with_ai_model',
693 + 'model_key' => 'ai_model',
641 694 'label' => __( 'Write with AI', 'betterdocs' )
642 695 ),
643 696 array(
644 697 'tab' => 'tab-betterdocs-ai',
@@ -643,9 +696,9 @@
643 696 array(
644 697 'tab' => 'tab-betterdocs-ai',
645 698 'context' => 'article_summary',
646 699 'token_key' => 'article_summary_max_token',
647 - 'model_key' => 'article_summary_model',
700 + 'model_key' => 'ai_model',
648 701 'label' => __( 'AI Doc Summarizer', 'betterdocs' )
649 702 )
650 703 );
651 704 foreach ( $token_pairs as $pair ) {
@@ -1017,8 +1070,17 @@
1017 1070 'enable_disable_text_active' => true,
1018 1071 'default' => 1,
1019 1072 'priority' => 1
1020 1073 ),
1074 + 'archive_lazy_load_descendants' => array(
1075 + 'name' => 'archive_lazy_load_descendants',
1076 + 'type' => 'toggle',
1077 + 'label' => __( 'Lazy Load Sidebar Docs & Subcategories', 'betterdocs' ),
1078 + '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' ),
1079 + 'enable_disable_text_active' => true,
1080 + 'default' => false,
1081 + 'priority' => 2
1082 + ),
1021 1083 'nested_subcategory' => array(
1022 1084 'name' => 'nested_subcategory',
1023 1085 'type' => 'toggle',
1024 1086 'label' => __( 'Nested Sub Category', 'betterdocs' ),
@@ -1023,9 +1085,9 @@
1023 1085 'type' => 'toggle',
1024 1086 'label' => __( 'Nested Sub Category', 'betterdocs' ),
1025 1087 'enable_disable_text_active' => true,
1026 1088 'default' => '',
1027 - 'priority' => 2
1089 + 'priority' => 3
1028 1090 ),
1029 1091 'column_number' => array(
1030 1092 'name' => 'column_number',
1031 1093 'type' => 'number',
@@ -1031,9 +1093,9 @@
1031 1093 'type' => 'number',
1032 1094 'label' => __( 'Number Of Columns', 'betterdocs' ),
1033 1095 'label_subtitle' => __( 'This setting is not applicable for sleek layout.', 'betterdocs' ),
1034 1096 'default' => 3,
1035 - 'priority' => 3
1097 + 'priority' => 4
1036 1098 ),
1037 1099 'posts_number' => apply_filters( 'betterdocs_posts_number', array(
1038 1100 'name' => 'posts_number',
1039 1101 'type' => 'number',
@@ -1039,9 +1101,9 @@
1039 1101 'type' => 'number',
1040 1102 'label' => __( 'Number Of Docs', 'betterdocs' ),
1041 1103 'label_subtitle' => __( 'This setting is not applicable for handbook layout.', 'betterdocs' ),
1042 1104 'default' => 10,
1043 - 'priority' => 4
1105 + 'priority' => 5
1044 1106 ) ),
1045 1107 'post_count' => array(
1046 1108 'name' => 'post_count',
1047 1109 'type' => 'toggle',
@@ -1047,9 +1109,9 @@
1047 1109 'type' => 'toggle',
1048 1110 'label' => __( 'Doc Count', 'betterdocs' ),
1049 1111 'enable_disable_text_active' => true,
1050 1112 'default' => 1,
1051 - 'priority' => 5
1113 + 'priority' => 6
1052 1114 ),
1053 1115 'count_text' => array(
1054 1116 'name' => 'count_text',
1055 1117 'type' => 'text',
@@ -1054,9 +1116,9 @@
1054 1116 'name' => 'count_text',
1055 1117 'type' => 'text',
1056 1118 'label' => __( 'Count Text', 'betterdocs' ),
1057 1119 'default' => __( 'Docs', 'betterdocs' ),
1058 - 'priority' => 6
1120 + 'priority' => 7
1059 1121 ),
1060 1122 'count_text_singular' => array(
1061 1123 'name' => 'count_text_singular',
1062 1124 'type' => 'text',
@@ -1061,9 +1123,9 @@
1061 1123 'name' => 'count_text_singular',
1062 1124 'type' => 'text',
1063 1125 'label' => __( 'Count Text Singular', 'betterdocs' ),
1064 1126 'default' => __( 'Doc', 'betterdocs' ),
1065 - 'priority' => 7
1127 + 'priority' => 8
1066 1128 ),
1067 1129 'exploremore_btn' => array(
1068 1130 'name' => 'exploremore_btn',
1069 1131 'type' => 'toggle',
@@ -1069,9 +1131,9 @@
1069 1131 'type' => 'toggle',
1070 1132 'label' => __( 'Explore More Button', 'betterdocs' ),
1071 1133 'enable_disable_text_active' => true,
1072 1134 'default' => true,
1073 - 'priority' => 8
1135 + 'priority' => 9
1074 1136 ),
1075 1137 'exploremore_btn_txt' => array(
1076 1138 'name' => 'exploremore_btn_txt',
1077 1139 'type' => 'text',
@@ -1076,9 +1138,9 @@
1076 1138 'name' => 'exploremore_btn_txt',
1077 1139 'type' => 'text',
1078 1140 'label' => __( 'Explore More Button Text', 'betterdocs' ),
1079 1141 'default' => __( 'Explore More', 'betterdocs' ),
1080 - 'priority' => 9,
1142 + 'priority' => 10,
1081 1143 'rules' => Rules::is( 'exploremore_btn', true )
1082 1144 ),
1083 1145 'betterdocs_popular_docs_text' => array(
1084 1146 'name' => 'betterdocs_popular_docs_text',
@@ -1084,9 +1146,9 @@
1084 1146 'name' => 'betterdocs_popular_docs_text',
1085 1147 'type' => 'text',
1086 1148 'label' => __( 'Popular Docs Text', 'betterdocs' ),
1087 1149 'default' => __( 'Popular Docs', 'betterdocs' ),
1088 - 'priority' => 10,
1150 + 'priority' => 11,
1089 1151 'is_pro' => true
1090 1152 ),
1091 1153 'betterdocs_popular_docs_number' => array(
1092 1154 'name' => 'betterdocs_popular_docs_number',
@@ -1092,9 +1154,9 @@
1092 1154 'name' => 'betterdocs_popular_docs_number',
1093 1155 'type' => 'number',
1094 1156 'label' => __( 'Popular Docs Number', 'betterdocs' ),
1095 1157 'default' => 10,
1096 - 'priority' => 11,
1158 + 'priority' => 12,
1097 1159 'is_pro' => true
1098 1160 )
1099 1161 )
1100 1162 ),
@@ -1338,8 +1400,44 @@
1338 1400 'enable_disable_text_active' => true,
1339 1401 'default' => 1,
1340 1402 'priority' => 3
1341 1403 ),
1404 + 'print_enable_logo' => array(
1405 + 'name' => 'print_enable_logo',
1406 + 'type' => 'toggle',
1407 + 'label' => __( 'Logo on Printed Doc', 'betterdocs' ),
1408 + 'label_subtitle' => __( 'Show a logo at the top of the printed / PDF page', 'betterdocs' ),
1409 + 'enable_disable_text_active' => true,
1410 + 'default' => 0,
1411 + 'priority' => 4
1412 + ),
1413 + 'print_logo' => array(
1414 + 'name' => 'print_logo',
1415 + 'type' => 'media',
1416 + 'value' => '',
1417 + 'label' => __( 'Print Logo', 'betterdocs' ),
1418 + 'label_subtitle' => __( 'Leave empty to use your site logo, or the site icon when no site logo is set', 'betterdocs' ),
1419 + 'priority' => 5,
1420 + 'rules' => Rules::is( 'print_enable_logo', true )
1421 + ),
1422 + 'print_enable_footer' => array(
1423 + 'name' => 'print_enable_footer',
1424 + 'type' => 'toggle',
1425 + 'label' => __( 'Footer on Printed Doc', 'betterdocs' ),
1426 + 'label_subtitle' => __( 'Show a footer on every page of the printed / PDF document', 'betterdocs' ),
1427 + 'enable_disable_text_active' => true,
1428 + 'default' => 0,
1429 + 'priority' => 6
1430 + ),
1431 + 'print_footer_text' => array(
1432 + 'name' => 'print_footer_text',
1433 + 'type' => 'textarea',
1434 + 'label' => __( 'Print Footer Text', 'betterdocs' ),
1435 + 'label_subtitle' => __( 'Leave empty to use the site name and current year', 'betterdocs' ),
1436 + 'default' => '',
1437 + 'priority' => 7,
1438 + 'rules' => Rules::is( 'print_enable_footer', true )
1439 + ),
1342 1440 'enable_tags' => array(
1343 1441 'name' => 'enable_tags',
1344 1442 'type' => 'toggle',
1345 1443 'label' => __( 'Tags', 'betterdocs' ),
@@ -1344,9 +1442,9 @@
1344 1442 'type' => 'toggle',
1345 1443 'label' => __( 'Tags', 'betterdocs' ),
1346 1444 'enable_disable_text_active' => true,
1347 1445 'default' => 1,
1348 - 'priority' => 4
1446 + 'priority' => 8
1349 1447 ),
1350 1448 'show_last_update_time' => array(
1351 1449 'name' => 'show_last_update_time',
1352 1450 'type' => 'toggle',
@@ -1352,9 +1450,9 @@
1352 1450 'type' => 'toggle',
1353 1451 'label' => __( 'Last Update Time', 'betterdocs' ),
1354 1452 'enable_disable_text_active' => true,
1355 1453 'default' => 1,
1356 - 'priority' => 5
1454 + 'priority' => 9
1357 1455 ),
1358 1456 'enable_navigation' => array(
1359 1457 'name' => 'enable_navigation',
1360 1458 'type' => 'toggle',
@@ -1360,9 +1458,9 @@
1360 1458 'type' => 'toggle',
1361 1459 'label' => __( 'Navigation', 'betterdocs' ),
1362 1460 'enable_disable_text_active' => true,
1363 1461 'default' => 1,
1364 - 'priority' => 6
1462 + 'priority' => 10
1365 1463 ),
1366 1464 'enable_comment' => array(
1367 1465 'name' => 'enable_comment',
1368 1466 'type' => 'toggle',
@@ -1368,9 +1466,9 @@
1368 1466 'type' => 'toggle',
1369 1467 'label' => __( 'Comment', 'betterdocs' ),
1370 1468 'enable_disable_text_active' => true,
1371 1469 'default' => '',
1372 - 'priority' => 7
1470 + 'priority' => 11
1373 1471 ),
1374 1472 'enable_credit' => array(
1375 1473 'name' => 'enable_credit',
1376 1474 'type' => 'toggle',
@@ -1376,9 +1474,9 @@
1376 1474 'type' => 'toggle',
1377 1475 'label' => __( 'Show Powered by BetterDocs', 'betterdocs' ),
1378 1476 'enable_disable_text_active' => true,
1379 1477 'default' => '',
1380 - 'priority' => 8
1478 + 'priority' => 12
1381 1479 ),
1382 1480 'reaction_feedback_text' => array(
1383 1481 'name' => 'reaction_feedback_text',
1384 1482 'type' => 'text',
@@ -1383,9 +1481,9 @@
1383 1481 'name' => 'reaction_feedback_text',
1384 1482 'type' => 'text',
1385 1483 'label' => __( 'Reaction Feedback Text', 'betterdocs' ),
1386 1484 'default' => __( 'Thanks for your feedback.', 'betterdocs' ),
1387 - 'priority' => 9
1485 + 'priority' => 13
1388 1486 ),
1389 1487 'enable_estimated_reading_time' => array(
1390 1488 'name' => 'enable_estimated_reading_time',
1391 1489 'type' => 'toggle',
@@ -1391,9 +1489,9 @@
1391 1489 'type' => 'toggle',
1392 1490 'label' => __( 'Estimated Reading Time', 'betterdocs' ),
1393 1491 'enable_disable_text_active' => true,
1394 1492 'default' => 0,
1395 - 'priority' => 10
1493 + 'priority' => 14
1396 1494 ),
1397 1495 'estimated_reading_time_title' => array(
1398 1496 'name' => 'estimated_reading_time_title',
1399 1497 'type' => 'text',
@@ -1398,9 +1496,9 @@
1398 1496 'name' => 'estimated_reading_time_title',
1399 1497 'type' => 'text',
1400 1498 'label' => __( 'Estimated Reading Time Title', 'betterdocs' ),
1401 1499 'default' => '',
1402 - 'priority' => 11,
1500 + 'priority' => 15,
1403 1501 'rules' => Rules::is( 'enable_estimated_reading_time', true )
1404 1502 ),
1405 1503 'estimated_reading_time_text' => array(
1406 1504 'name' => 'estimated_reading_time_text',
@@ -1406,9 +1504,9 @@
1406 1504 'name' => 'estimated_reading_time_text',
1407 1505 'type' => 'text',
1408 1506 'label' => __( 'Estimated Reading Time Text', 'betterdocs' ),
1409 1507 'default' => __( 'min read', 'betterdocs' ),
1410 - 'priority' => 12,
1508 + 'priority' => 16,
1411 1509 'rules' => Rules::is( 'enable_estimated_reading_time', true )
1412 1510 ),
1413 1511 'singular_estimated_reading_time_text' => array(
1414 1512 'name' => 'singular_estimated_reading_time_text',
@@ -1414,9 +1512,9 @@
1414 1512 'name' => 'singular_estimated_reading_time_text',
1415 1513 'type' => 'text',
1416 1514 'label' => __( 'Estimated Reading Time Text Singular', 'betterdocs' ),
1417 1515 'default' => __( 'min read', 'betterdocs' ),
1418 - 'priority' => 13,
1516 + 'priority' => 17,
1419 1517 'rules' => Rules::is( 'enable_estimated_reading_time', true )
1420 1518 )
1421 1519 )
1422 1520 ),
@@ -2296,18 +2394,84 @@
2296 2394 'open-ai-settings' => array(
2297 2395 'id' => 'open-ai-settings',
2298 2396 'name' => 'open-ai-settings',
2299 2397 'type' => 'section',
2300 - 'label' => __( 'API Settings', 'betterdocs' ),
2398 + 'label' => __( 'AI Platform & API', 'betterdocs' ),
2301 2399 'priority' => 1,
2302 2400 'fields' => array(
2401 + 'ai_platform' => array(
2402 + 'name' => 'ai_platform',
2403 + 'type' => 'select',
2404 + 'label' => __( 'AI Platform', 'betterdocs' ),
2405 + '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' ),
2406 + 'priority' => 1,
2407 + 'multiple' => false,
2408 + 'default' => 'openai',
2409 + // DeepSeek and OpenRouter are temporarily hidden from the
2410 + // dropdown. Filter the options here, NOT ModelRegistry::platforms() —
2411 + // sensitive_api_key_fields() loops the registry to mask each
2412 + // platform's key field, so trimming the registry would silently
2413 + // un-mask those keys. Re-enable later by dropping the array_diff_key.
2414 + 'options' => GlobalFields::normalize_fields(
2415 + array_diff_key( ModelRegistry::platforms(), array_flip( array( 'deepseek', 'openrouter' ) ) )
2416 + )
2417 + ),
2418 + // OpenAI's key field keeps its original name so installs
2419 + // that already saved a Write with AI key keep it.
2303 2420 'ai_autowrite_api_key' => array(
2304 2421 'name' => 'ai_autowrite_api_key',
2305 2422 '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/' ) ),
2423 + 'label' => __( 'OpenAI API Key', 'betterdocs' ),
2424 + '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 2425 'default' => '',
2309 - 'priority' => 1
2426 + 'priority' => 2,
2427 + 'rules' => Rules::is( 'ai_platform', 'openai' )
2428 + ),
2429 + 'ai_api_key_gemini' => array(
2430 + 'name' => 'ai_api_key_gemini',
2431 + 'type' => 'text',
2432 + 'label' => __( 'Google Gemini API Key', 'betterdocs' ),
2433 + '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' ) ),
2434 + 'default' => '',
2435 + 'priority' => 2,
2436 + 'rules' => Rules::is( 'ai_platform', 'gemini' )
2437 + ),
2438 + 'ai_api_key_claude' => array(
2439 + 'name' => 'ai_api_key_claude',
2440 + 'type' => 'text',
2441 + 'label' => __( 'Anthropic Claude API Key', 'betterdocs' ),
2442 + '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' ) ),
2443 + 'default' => '',
2444 + 'priority' => 2,
2445 + 'rules' => Rules::is( 'ai_platform', 'claude' )
2446 + ),
2447 + 'ai_api_key_deepseek' => array(
2448 + 'name' => 'ai_api_key_deepseek',
2449 + 'type' => 'text',
2450 + 'label' => __( 'DeepSeek API Key', 'betterdocs' ),
2451 + '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' ) ),
2452 + 'default' => '',
2453 + 'priority' => 2,
2454 + 'rules' => Rules::is( 'ai_platform', 'deepseek' )
2455 + ),
2456 + 'ai_api_key_openrouter' => array(
2457 + 'name' => 'ai_api_key_openrouter',
2458 + 'type' => 'text',
2459 + 'label' => __( 'OpenRouter API Key', 'betterdocs' ),
2460 + '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' ) ),
2461 + 'default' => '',
2462 + 'priority' => 2,
2463 + 'rules' => Rules::is( 'ai_platform', 'openrouter' )
2464 + ),
2465 + 'ai_model' => array(
2466 + 'name' => 'ai_model',
2467 + 'type' => 'platform_model_select',
2468 + 'label' => __( 'Model', 'betterdocs' ),
2469 + 'label_subtitle' => __( 'Available models depend on the selected platform.', 'betterdocs' ),
2470 + 'priority' => 10,
2471 + 'default' => 'gpt-4o-mini',
2472 + 'catalogue' => ModelRegistry::catalogue(),
2473 + 'defaults' => ModelRegistry::defaults()
2310 2474 )
2311 2475 )
2312 2476 ),
2313 2477 'write-with-ai' => array(
@@ -2372,9 +2536,10 @@
2372 2536 'priority' => 6,
2373 2537 'label' => __( 'Write Glossaries with AI', 'betterdocs' ),
2374 2538 'label_subtitle' => __( 'Generate AI based Glossary definitions from the Glossaries admin page', 'betterdocs' ),
2375 2539 'enable_disable_text_active' => true,
2376 - 'default' => true
2540 + 'default' => true,
2541 + 'is_pro' => true
2377 2542 ),
2378 2543 'enable_docs_ai_suite' => array(
2379 2544 'name' => 'enable_docs_ai_suite',
2380 2545 'type' => 'toggle',
@@ -2383,27 +2548,8 @@
2383 2548 'label_subtitle' => __( 'Add BetterDocs AI actions for suggesting categories, tags and excerpts inside the native Docs editor panels.', 'betterdocs' ),
2384 2549 'enable_disable_text_active' => true,
2385 2550 'default' => true
2386 2551 ),
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 2552 'ai_autowrite_max_token' => array(
2407 2553 'name' => 'ai_autowrite_max_token',
2408 2554 'type' => 'min_token_number',
2409 2555 'label' => __( 'Set Max Tokens', 'betterdocs' ),
@@ -2410,9 +2556,9 @@
2410 2556 'label_subtitle' => sprintf( // translators: %s is a link to more information about token limits.
2411 2557 __( '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 2558 'default' => 2500,
2413 2559 'priority' => 10,
2414 - 'model_field' => 'write_with_ai_model',
2560 + 'model_field' => 'ai_model',
2415 2561 'context' => 'write_with_ai',
2416 2562 'min_token_map' => AIHelper::get_min_tokens_map( 'write_with_ai' ),
2417 2563 'classes' => 'wprf-type-text'
2418 2564 )
@@ -2485,31 +2631,12 @@
2485 2631 'label_subtitle' => sprintf( // translators: %s is a link to more information about token limits.
2486 2632 __( '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 2633 'default' => 1500,
2488 2634 'priority' => 5,
2489 - 'model_field' => 'article_summary_model',
2635 + 'model_field' => 'ai_model',
2490 2636 'context' => 'article_summary',
2491 2637 'min_token_map' => AIHelper::get_min_tokens_map( 'article_summary' ),
2492 2638 '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 2639 )
2513 2640 )
2514 2641 )
2515 2642 )