| @@ -1,12 +1,19 @@ | ||
| 1 | 1 | <?php |
| 2 | +namespace WPDeveloper\BetterDocs\Core; | |
| 2 | 3 | |
| 3 | -namespace WPDeveloper\BetterDocs\Core; | |
| 4 | +if ( ! defined( 'ABSPATH' ) ) { | |
| 5 | + exit; | |
| 6 | +} | |
| 4 | 7 | |
| 8 | + | |
| 5 | 9 | use WP_Error; |
| 6 | 10 | use WP_User; |
| 7 | 11 | use WPDeveloper\BetterDocs\Admin\Builder\GlobalFields; |
| 8 | 12 | use WPDeveloper\BetterDocs\Admin\Builder\Rules; |
| 13 | +use WPDeveloper\BetterDocs\AI\ModelRegistry; | |
| 14 | +use WPDeveloper\BetterDocs\AI\ProviderFactory; | |
| 15 | +use WPDeveloper\BetterDocs\REST\AIEdit; | |
| 9 | 16 | use WPDeveloper\BetterDocs\Utils\AIHelper; |
| 10 | 17 | use WPDeveloper\BetterDocs\Utils\Base; |
| 11 | 18 | use WPDeveloper\BetterDocs\Utils\Database; |
| 12 | 19 | use WPDeveloper\BetterDocs\Utils\Helper; |
| @@ -72,8 +79,31 @@ | ||
| 72 | 79 | return esc_url( admin_url( 'admin.php?page=betterdocs-settings' ) ); |
| 73 | 80 | } |
| 74 | 81 | |
| 75 | 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 | + /** | |
| 76 | 106 | * This method is responsible for enqueueing scripts in settings panel |
| 77 | 107 | * |
| 78 | 108 | * @param string $hook |
| 79 | 109 | * |
| @@ -90,9 +120,9 @@ | ||
| 90 | 120 | |
| 91 | 121 | $settings = GlobalFields::normalize( $this->settings_args() ); |
| 92 | 122 | |
| 93 | 123 | // Mask sensitive API keys before they reach the browser. Non-admins still get them stripped entirely below. |
| 94 | - $sensitive_api_keys = array( 'ai_autowrite_api_key', 'ai_chatbot_api_key' ); | |
| 124 | + $sensitive_api_keys = self::sensitive_api_key_fields(); | |
| 95 | 125 | foreach ( $sensitive_api_keys as $api_key_field ) { |
| 96 | 126 | if ( ! empty( $settings[ 'values' ][ $api_key_field ] ) ) { |
| 97 | 127 | $settings[ 'values' ][ $api_key_field ] = Helper::mask_api_key( $settings[ 'values' ][ $api_key_field ] ); |
| 98 | 128 | } |
| @@ -198,8 +228,9 @@ | ||
| 198 | 228 | */ |
| 199 | 229 | public function get_default() { |
| 200 | 230 | $_default = array( |
| 201 | 231 | 'multiple_kb' => '', |
| 232 | + 'enable_ai_sample_docs' => true, | |
| 202 | 233 | 'enable_export_faq' => true, |
| 203 | 234 | 'builtin_doc_page' => true, |
| 204 | 235 | 'breadcrumb_doc_title' => __( 'Docs', 'betterdocs' ), |
| 205 | 236 | 'enable_category_hierarchy_slugs' => false, |
| @@ -252,8 +283,12 @@ | ||
| 252 | 283 | 'enable_breadcrumb_category' => true, |
| 253 | 284 | 'enable_breadcrumb_title' => true, |
| 254 | 285 | 'enable_sidebar_cat_list' => true, |
| 255 | 286 | 'enable_print_icon' => true, |
| 287 | + 'print_enable_logo' => false, | |
| 288 | + 'print_logo' => array(), | |
| 289 | + 'print_enable_footer' => false, | |
| 290 | + 'print_footer_text' => '', | |
| 256 | 291 | 'enable_tags' => true, |
| 257 | 292 | 'email_feedback' => true, |
| 258 | 293 | 'feedback_link_text' => __( 'Still stuck? How can we help?', 'betterdocs' ), |
| 259 | 294 | 'reaction_feedback_text' => __( 'Thanks for your feedback', 'betterdocs' ), |
| @@ -266,8 +301,9 @@ | ||
| 266 | 301 | 'enable_credit' => false, |
| 267 | 302 | 'enable_archive_sidebar' => true, |
| 268 | 303 | 'archive_nested_subcategory' => true, |
| 269 | 304 | 'archive_enable_pagination' => false, |
| 305 | + 'archive_lazy_load_descendants' => false, | |
| 270 | 306 | 'enable_content_restriction' => false, |
| 271 | 307 | 'enable_reporting' => false, |
| 272 | 308 | 'enable_sample_data' => false, |
| 273 | 309 | 'reporting_day' => 'monday', |
| @@ -274,14 +310,34 @@ | ||
| 274 | 310 | 'reporting_email' => get_option( 'admin_email' ), |
| 275 | 311 | 'enable_write_with_ai' => true, |
| 276 | 312 | 'enable_faq_write_with_ai' => true, |
| 277 | 313 | 'enable_glossaries_write_with_ai' => true, |
| 314 | + 'enable_docs_ai_suite' => true, | |
| 278 | 315 | 'write_with_ai_model' => 'gpt-4o-mini', |
| 279 | 316 | 'ai_autowrite_api_key' => '', |
| 280 | 317 | 'ai_autowrite_max_token' => 2500, |
| 318 | + 'write_with_ai_instructions' => array( | |
| 319 | + array( | |
| 320 | + 'id' => 'default', | |
| 321 | + 'title' => __( 'Default/Core', 'betterdocs' ), | |
| 322 | + 'content' => WriteWithAI::default_instruction_content() | |
| 323 | + ) | |
| 324 | + ), | |
| 325 | + 'ai_edit_actions' => AIEdit::default_actions(), | |
| 281 | 326 | 'enable_article_summary' => false, |
| 282 | 327 | 'article_summary_model' => 'gpt-4o-mini', |
| 283 | 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' => '', | |
| 284 | 340 | 'enable_estimated_reading_time' => true, |
| 285 | 341 | 'enable_encyclopedia' => false, |
| 286 | 342 | 'enable_glossaries' => false, |
| 287 | 343 | 'show_glossary_suggestions' => true, |
| @@ -291,9 +347,21 @@ | ||
| 291 | 347 | 'singular_estimated_reading_time_text' => __( 'min read', 'betterdocs' ), |
| 292 | 348 | 'betterdocs_access_control_repeater' => array(), |
| 293 | 349 | 'internal_knowledge_base_type' => 'basic', |
| 294 | 350 | 'betterdocs_access_control_repeater_kb' => array(), |
| 295 | - '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' => '' | |
| 296 | 364 | ); |
| 297 | 365 | |
| 298 | 366 | $_default = apply_filters( 'betterdocs_default_settings', $_default ); |
| 299 | 367 | // $_default = apply_filters_deprecated( |
| @@ -425,9 +493,10 @@ | ||
| 425 | 493 | wp_send_json_error(); |
| 426 | 494 | } |
| 427 | 495 | |
| 428 | 496 | if ( isset( $_POST[ 'mode' ] ) ) { |
| 429 | - if ( $this->save( 'dark_mode', rest_sanitize_boolean( $_POST[ 'mode' ] ) ) ) { // phpcs:ignore | |
| 497 | + $mode = sanitize_text_field( wp_unslash( $_POST[ 'mode' ] ) ); | |
| 498 | + if ( $this->save( 'dark_mode', rest_sanitize_boolean( $mode ) ) ) { | |
| 430 | 499 | wp_send_json_success(); |
| 431 | 500 | } |
| 432 | 501 | } |
| 433 | 502 | |
| @@ -585,19 +654,27 @@ | ||
| 585 | 654 | } |
| 586 | 655 | |
| 587 | 656 | $_old_settings = $this->database->get( $this->base_key, $this->get_default() ); |
| 588 | 657 | |
| 589 | - // The frontend only ever sees masked API keys. If a submitted value matches the mask of | |
| 590 | - // the stored value, the user did not change it — restore the original so the mask string | |
| 591 | - // is never persisted. | |
| 592 | - $sensitive_api_keys = array( 'ai_autowrite_api_key', 'ai_chatbot_api_key' ); | |
| 658 | + // The frontend only ever sees masked API keys. A submitted value that still looks like a | |
| 659 | + // mask (contains a run of asterisks — no real key does) means "unchanged": restore the | |
| 660 | + // stored original so a mask string is never persisted. Guarding on the mask SHAPE rather | |
| 661 | + // than strict equality with mask(stored) closes the corruption loop QA hit: once a mask | |
| 662 | + // slips into storage, mask(mask) === mask, so an equality-only guard would faithfully | |
| 663 | + // preserve the corrupted value forever (round-2 follow-up #1). When the stored value is | |
| 664 | + // itself mask-shaped it is unrecoverable — clear it so the UI and the GeoIP/GA4 status | |
| 665 | + // surfaces honestly report a missing key instead of failing downstream with 401s. | |
| 666 | + $sensitive_api_keys = self::sensitive_api_key_fields(); | |
| 593 | 667 | foreach ( $sensitive_api_keys as $api_key_field ) { |
| 594 | 668 | if ( ! isset( $settings[ $api_key_field ] ) ) { |
| 595 | 669 | continue; |
| 596 | 670 | } |
| 597 | - $stored = isset( $_old_settings[ $api_key_field ] ) ? $_old_settings[ $api_key_field ] : ''; | |
| 598 | - if ( '' !== $stored && trim( (string) $settings[ $api_key_field ] ) === Helper::mask_api_key( $stored ) ) { | |
| 599 | - $settings[ $api_key_field ] = $stored; | |
| 671 | + $incoming = trim( (string) $settings[ $api_key_field ] ); | |
| 672 | + $stored = isset( $_old_settings[ $api_key_field ] ) ? (string) $_old_settings[ $api_key_field ] : ''; | |
| 673 | + $stored_is_mask = '' !== $stored && preg_match( '/\*{4,}/', $stored ); | |
| 674 | + | |
| 675 | + if ( '' !== $incoming && preg_match( '/\*{4,}/', $incoming ) ) { | |
| 676 | + $settings[ $api_key_field ] = $stored_is_mask ? '' : $stored; | |
| 600 | 677 | } |
| 601 | 678 | } |
| 602 | 679 | |
| 603 | 680 | // Minimum-token policy: reject saves where a feature's max_token field is below the |
| @@ -612,9 +689,9 @@ | ||
| 612 | 689 | array( |
| 613 | 690 | 'tab' => 'tab-betterdocs-ai', |
| 614 | 691 | 'context' => 'write_with_ai', |
| 615 | 692 | 'token_key' => 'ai_autowrite_max_token', |
| 616 | - 'model_key' => 'write_with_ai_model', | |
| 693 | + 'model_key' => 'ai_model', | |
| 617 | 694 | 'label' => __( 'Write with AI', 'betterdocs' ) |
| 618 | 695 | ), |
| 619 | 696 | array( |
| 620 | 697 | 'tab' => 'tab-betterdocs-ai', |
| @@ -619,9 +696,9 @@ | ||
| 619 | 696 | array( |
| 620 | 697 | 'tab' => 'tab-betterdocs-ai', |
| 621 | 698 | 'context' => 'article_summary', |
| 622 | 699 | 'token_key' => 'article_summary_max_token', |
| 623 | - 'model_key' => 'article_summary_model', | |
| 700 | + 'model_key' => 'ai_model', | |
| 624 | 701 | 'label' => __( 'AI Doc Summarizer', 'betterdocs' ) |
| 625 | 702 | ) |
| 626 | 703 | ); |
| 627 | 704 | foreach ( $token_pairs as $pair ) { |
| @@ -993,8 +1070,17 @@ | ||
| 993 | 1070 | 'enable_disable_text_active' => true, |
| 994 | 1071 | 'default' => 1, |
| 995 | 1072 | 'priority' => 1 |
| 996 | 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 | + ), | |
| 997 | 1083 | 'nested_subcategory' => array( |
| 998 | 1084 | 'name' => 'nested_subcategory', |
| 999 | 1085 | 'type' => 'toggle', |
| 1000 | 1086 | 'label' => __( 'Nested Sub Category', 'betterdocs' ), |
| @@ -999,9 +1085,9 @@ | ||
| 999 | 1085 | 'type' => 'toggle', |
| 1000 | 1086 | 'label' => __( 'Nested Sub Category', 'betterdocs' ), |
| 1001 | 1087 | 'enable_disable_text_active' => true, |
| 1002 | 1088 | 'default' => '', |
| 1003 | - 'priority' => 2 | |
| 1089 | + 'priority' => 3 | |
| 1004 | 1090 | ), |
| 1005 | 1091 | 'column_number' => array( |
| 1006 | 1092 | 'name' => 'column_number', |
| 1007 | 1093 | 'type' => 'number', |
| @@ -1007,9 +1093,9 @@ | ||
| 1007 | 1093 | 'type' => 'number', |
| 1008 | 1094 | 'label' => __( 'Number Of Columns', 'betterdocs' ), |
| 1009 | 1095 | 'label_subtitle' => __( 'This setting is not applicable for sleek layout.', 'betterdocs' ), |
| 1010 | 1096 | 'default' => 3, |
| 1011 | - 'priority' => 3 | |
| 1097 | + 'priority' => 4 | |
| 1012 | 1098 | ), |
| 1013 | 1099 | 'posts_number' => apply_filters( 'betterdocs_posts_number', array( |
| 1014 | 1100 | 'name' => 'posts_number', |
| 1015 | 1101 | 'type' => 'number', |
| @@ -1015,9 +1101,9 @@ | ||
| 1015 | 1101 | 'type' => 'number', |
| 1016 | 1102 | 'label' => __( 'Number Of Docs', 'betterdocs' ), |
| 1017 | 1103 | 'label_subtitle' => __( 'This setting is not applicable for handbook layout.', 'betterdocs' ), |
| 1018 | 1104 | 'default' => 10, |
| 1019 | - 'priority' => 4 | |
| 1105 | + 'priority' => 5 | |
| 1020 | 1106 | ) ), |
| 1021 | 1107 | 'post_count' => array( |
| 1022 | 1108 | 'name' => 'post_count', |
| 1023 | 1109 | 'type' => 'toggle', |
| @@ -1023,9 +1109,9 @@ | ||
| 1023 | 1109 | 'type' => 'toggle', |
| 1024 | 1110 | 'label' => __( 'Doc Count', 'betterdocs' ), |
| 1025 | 1111 | 'enable_disable_text_active' => true, |
| 1026 | 1112 | 'default' => 1, |
| 1027 | - 'priority' => 5 | |
| 1113 | + 'priority' => 6 | |
| 1028 | 1114 | ), |
| 1029 | 1115 | 'count_text' => array( |
| 1030 | 1116 | 'name' => 'count_text', |
| 1031 | 1117 | 'type' => 'text', |
| @@ -1030,9 +1116,9 @@ | ||
| 1030 | 1116 | 'name' => 'count_text', |
| 1031 | 1117 | 'type' => 'text', |
| 1032 | 1118 | 'label' => __( 'Count Text', 'betterdocs' ), |
| 1033 | 1119 | 'default' => __( 'Docs', 'betterdocs' ), |
| 1034 | - 'priority' => 6 | |
| 1120 | + 'priority' => 7 | |
| 1035 | 1121 | ), |
| 1036 | 1122 | 'count_text_singular' => array( |
| 1037 | 1123 | 'name' => 'count_text_singular', |
| 1038 | 1124 | 'type' => 'text', |
| @@ -1037,9 +1123,9 @@ | ||
| 1037 | 1123 | 'name' => 'count_text_singular', |
| 1038 | 1124 | 'type' => 'text', |
| 1039 | 1125 | 'label' => __( 'Count Text Singular', 'betterdocs' ), |
| 1040 | 1126 | 'default' => __( 'Doc', 'betterdocs' ), |
| 1041 | - 'priority' => 7 | |
| 1127 | + 'priority' => 8 | |
| 1042 | 1128 | ), |
| 1043 | 1129 | 'exploremore_btn' => array( |
| 1044 | 1130 | 'name' => 'exploremore_btn', |
| 1045 | 1131 | 'type' => 'toggle', |
| @@ -1045,9 +1131,9 @@ | ||
| 1045 | 1131 | 'type' => 'toggle', |
| 1046 | 1132 | 'label' => __( 'Explore More Button', 'betterdocs' ), |
| 1047 | 1133 | 'enable_disable_text_active' => true, |
| 1048 | 1134 | 'default' => true, |
| 1049 | - 'priority' => 8 | |
| 1135 | + 'priority' => 9 | |
| 1050 | 1136 | ), |
| 1051 | 1137 | 'exploremore_btn_txt' => array( |
| 1052 | 1138 | 'name' => 'exploremore_btn_txt', |
| 1053 | 1139 | 'type' => 'text', |
| @@ -1052,9 +1138,9 @@ | ||
| 1052 | 1138 | 'name' => 'exploremore_btn_txt', |
| 1053 | 1139 | 'type' => 'text', |
| 1054 | 1140 | 'label' => __( 'Explore More Button Text', 'betterdocs' ), |
| 1055 | 1141 | 'default' => __( 'Explore More', 'betterdocs' ), |
| 1056 | - 'priority' => 9, | |
| 1142 | + 'priority' => 10, | |
| 1057 | 1143 | 'rules' => Rules::is( 'exploremore_btn', true ) |
| 1058 | 1144 | ), |
| 1059 | 1145 | 'betterdocs_popular_docs_text' => array( |
| 1060 | 1146 | 'name' => 'betterdocs_popular_docs_text', |
| @@ -1060,9 +1146,9 @@ | ||
| 1060 | 1146 | 'name' => 'betterdocs_popular_docs_text', |
| 1061 | 1147 | 'type' => 'text', |
| 1062 | 1148 | 'label' => __( 'Popular Docs Text', 'betterdocs' ), |
| 1063 | 1149 | 'default' => __( 'Popular Docs', 'betterdocs' ), |
| 1064 | - 'priority' => 10, | |
| 1150 | + 'priority' => 11, | |
| 1065 | 1151 | 'is_pro' => true |
| 1066 | 1152 | ), |
| 1067 | 1153 | 'betterdocs_popular_docs_number' => array( |
| 1068 | 1154 | 'name' => 'betterdocs_popular_docs_number', |
| @@ -1068,9 +1154,9 @@ | ||
| 1068 | 1154 | 'name' => 'betterdocs_popular_docs_number', |
| 1069 | 1155 | 'type' => 'number', |
| 1070 | 1156 | 'label' => __( 'Popular Docs Number', 'betterdocs' ), |
| 1071 | 1157 | 'default' => 10, |
| 1072 | - 'priority' => 11, | |
| 1158 | + 'priority' => 12, | |
| 1073 | 1159 | 'is_pro' => true |
| 1074 | 1160 | ) |
| 1075 | 1161 | ) |
| 1076 | 1162 | ), |
| @@ -1097,8 +1183,9 @@ | ||
| 1097 | 1183 | 'default' => '', |
| 1098 | 1184 | 'priority' => 2, |
| 1099 | 1185 | 'is_pro' => true |
| 1100 | 1186 | ) ), |
| 1187 | + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- legacy public filter name for category-exclude settings field, retained for back-compat. | |
| 1101 | 1188 | 'child_category_exclude' => apply_filters( 'child_category_exclude', array( |
| 1102 | 1189 | 'name' => 'child_category_exclude', |
| 1103 | 1190 | 'type' => 'toggle', |
| 1104 | 1191 | 'label' => __( 'Exclude Child Terms In Category Search', 'betterdocs' ), |
| @@ -1250,8 +1337,9 @@ | ||
| 1250 | 1337 | ) |
| 1251 | 1338 | ) |
| 1252 | 1339 | ) |
| 1253 | 1340 | ), |
| 1341 | + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- legacy public filter name for single-doc setting section, retained for back-compat. | |
| 1254 | 1342 | 'layout_single_doc' => apply_filters( 'single_doc_setting_section', array( |
| 1255 | 1343 | 'id' => 'layout_single_doc', |
| 1256 | 1344 | 'name' => 'layout_single_doc', |
| 1257 | 1345 | 'type' => 'section', |
| @@ -1312,8 +1400,44 @@ | ||
| 1312 | 1400 | 'enable_disable_text_active' => true, |
| 1313 | 1401 | 'default' => 1, |
| 1314 | 1402 | 'priority' => 3 |
| 1315 | 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 | + ), | |
| 1316 | 1440 | 'enable_tags' => array( |
| 1317 | 1441 | 'name' => 'enable_tags', |
| 1318 | 1442 | 'type' => 'toggle', |
| 1319 | 1443 | 'label' => __( 'Tags', 'betterdocs' ), |
| @@ -1318,9 +1442,9 @@ | ||
| 1318 | 1442 | 'type' => 'toggle', |
| 1319 | 1443 | 'label' => __( 'Tags', 'betterdocs' ), |
| 1320 | 1444 | 'enable_disable_text_active' => true, |
| 1321 | 1445 | 'default' => 1, |
| 1322 | - 'priority' => 4 | |
| 1446 | + 'priority' => 8 | |
| 1323 | 1447 | ), |
| 1324 | 1448 | 'show_last_update_time' => array( |
| 1325 | 1449 | 'name' => 'show_last_update_time', |
| 1326 | 1450 | 'type' => 'toggle', |
| @@ -1326,9 +1450,9 @@ | ||
| 1326 | 1450 | 'type' => 'toggle', |
| 1327 | 1451 | 'label' => __( 'Last Update Time', 'betterdocs' ), |
| 1328 | 1452 | 'enable_disable_text_active' => true, |
| 1329 | 1453 | 'default' => 1, |
| 1330 | - 'priority' => 5 | |
| 1454 | + 'priority' => 9 | |
| 1331 | 1455 | ), |
| 1332 | 1456 | 'enable_navigation' => array( |
| 1333 | 1457 | 'name' => 'enable_navigation', |
| 1334 | 1458 | 'type' => 'toggle', |
| @@ -1334,9 +1458,9 @@ | ||
| 1334 | 1458 | 'type' => 'toggle', |
| 1335 | 1459 | 'label' => __( 'Navigation', 'betterdocs' ), |
| 1336 | 1460 | 'enable_disable_text_active' => true, |
| 1337 | 1461 | 'default' => 1, |
| 1338 | - 'priority' => 6 | |
| 1462 | + 'priority' => 10 | |
| 1339 | 1463 | ), |
| 1340 | 1464 | 'enable_comment' => array( |
| 1341 | 1465 | 'name' => 'enable_comment', |
| 1342 | 1466 | 'type' => 'toggle', |
| @@ -1342,9 +1466,9 @@ | ||
| 1342 | 1466 | 'type' => 'toggle', |
| 1343 | 1467 | 'label' => __( 'Comment', 'betterdocs' ), |
| 1344 | 1468 | 'enable_disable_text_active' => true, |
| 1345 | 1469 | 'default' => '', |
| 1346 | - 'priority' => 7 | |
| 1470 | + 'priority' => 11 | |
| 1347 | 1471 | ), |
| 1348 | 1472 | 'enable_credit' => array( |
| 1349 | 1473 | 'name' => 'enable_credit', |
| 1350 | 1474 | 'type' => 'toggle', |
| @@ -1350,9 +1474,9 @@ | ||
| 1350 | 1474 | 'type' => 'toggle', |
| 1351 | 1475 | 'label' => __( 'Show Powered by BetterDocs', 'betterdocs' ), |
| 1352 | 1476 | 'enable_disable_text_active' => true, |
| 1353 | 1477 | 'default' => '', |
| 1354 | - 'priority' => 8 | |
| 1478 | + 'priority' => 12 | |
| 1355 | 1479 | ), |
| 1356 | 1480 | 'reaction_feedback_text' => array( |
| 1357 | 1481 | 'name' => 'reaction_feedback_text', |
| 1358 | 1482 | 'type' => 'text', |
| @@ -1357,9 +1481,9 @@ | ||
| 1357 | 1481 | 'name' => 'reaction_feedback_text', |
| 1358 | 1482 | 'type' => 'text', |
| 1359 | 1483 | 'label' => __( 'Reaction Feedback Text', 'betterdocs' ), |
| 1360 | 1484 | 'default' => __( 'Thanks for your feedback.', 'betterdocs' ), |
| 1361 | - 'priority' => 9 | |
| 1485 | + 'priority' => 13 | |
| 1362 | 1486 | ), |
| 1363 | 1487 | 'enable_estimated_reading_time' => array( |
| 1364 | 1488 | 'name' => 'enable_estimated_reading_time', |
| 1365 | 1489 | 'type' => 'toggle', |
| @@ -1365,9 +1489,9 @@ | ||
| 1365 | 1489 | 'type' => 'toggle', |
| 1366 | 1490 | 'label' => __( 'Estimated Reading Time', 'betterdocs' ), |
| 1367 | 1491 | 'enable_disable_text_active' => true, |
| 1368 | 1492 | 'default' => 0, |
| 1369 | - 'priority' => 10 | |
| 1493 | + 'priority' => 14 | |
| 1370 | 1494 | ), |
| 1371 | 1495 | 'estimated_reading_time_title' => array( |
| 1372 | 1496 | 'name' => 'estimated_reading_time_title', |
| 1373 | 1497 | 'type' => 'text', |
| @@ -1372,9 +1496,9 @@ | ||
| 1372 | 1496 | 'name' => 'estimated_reading_time_title', |
| 1373 | 1497 | 'type' => 'text', |
| 1374 | 1498 | 'label' => __( 'Estimated Reading Time Title', 'betterdocs' ), |
| 1375 | 1499 | 'default' => '', |
| 1376 | - 'priority' => 11, | |
| 1500 | + 'priority' => 15, | |
| 1377 | 1501 | 'rules' => Rules::is( 'enable_estimated_reading_time', true ) |
| 1378 | 1502 | ), |
| 1379 | 1503 | 'estimated_reading_time_text' => array( |
| 1380 | 1504 | 'name' => 'estimated_reading_time_text', |
| @@ -1380,9 +1504,9 @@ | ||
| 1380 | 1504 | 'name' => 'estimated_reading_time_text', |
| 1381 | 1505 | 'type' => 'text', |
| 1382 | 1506 | 'label' => __( 'Estimated Reading Time Text', 'betterdocs' ), |
| 1383 | 1507 | 'default' => __( 'min read', 'betterdocs' ), |
| 1384 | - 'priority' => 12, | |
| 1508 | + 'priority' => 16, | |
| 1385 | 1509 | 'rules' => Rules::is( 'enable_estimated_reading_time', true ) |
| 1386 | 1510 | ), |
| 1387 | 1511 | 'singular_estimated_reading_time_text' => array( |
| 1388 | 1512 | 'name' => 'singular_estimated_reading_time_text', |
| @@ -1388,9 +1512,9 @@ | ||
| 1388 | 1512 | 'name' => 'singular_estimated_reading_time_text', |
| 1389 | 1513 | 'type' => 'text', |
| 1390 | 1514 | 'label' => __( 'Estimated Reading Time Text Singular', 'betterdocs' ), |
| 1391 | 1515 | 'default' => __( 'min read', 'betterdocs' ), |
| 1392 | - 'priority' => 13, | |
| 1516 | + 'priority' => 17, | |
| 1393 | 1517 | 'rules' => Rules::is( 'enable_estimated_reading_time', true ) |
| 1394 | 1518 | ) |
| 1395 | 1519 | ) |
| 1396 | 1520 | ), |
| @@ -2270,18 +2394,84 @@ | ||
| 2270 | 2394 | 'open-ai-settings' => array( |
| 2271 | 2395 | 'id' => 'open-ai-settings', |
| 2272 | 2396 | 'name' => 'open-ai-settings', |
| 2273 | 2397 | 'type' => 'section', |
| 2274 | - 'label' => __( 'API Settings', 'betterdocs' ), | |
| 2398 | + 'label' => __( 'AI Platform & API', 'betterdocs' ), | |
| 2275 | 2399 | 'priority' => 1, |
| 2276 | 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. | |
| 2277 | 2420 | 'ai_autowrite_api_key' => array( |
| 2278 | 2421 | 'name' => 'ai_autowrite_api_key', |
| 2279 | 2422 | 'type' => 'text', |
| 2280 | - 'label' => __( 'API Key', 'betterdocs' ), | |
| 2281 | - '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/' ) ), | |
| 2282 | 2425 | 'default' => '', |
| 2283 | - '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() | |
| 2284 | 2474 | ) |
| 2285 | 2475 | ) |
| 2286 | 2476 | ), |
| 2287 | 2477 | 'write-with-ai' => array( |
| @@ -2290,8 +2480,39 @@ | ||
| 2290 | 2480 | 'type' => 'section', |
| 2291 | 2481 | 'label' => __( 'Write with AI', 'betterdocs' ), |
| 2292 | 2482 | 'priority' => 5, |
| 2293 | 2483 | 'fields' => array( |
| 2484 | + 'write-with-ai-subtabs' => array( | |
| 2485 | + 'id' => 'write-with-ai-subtabs', | |
| 2486 | + 'name' => 'write_with_ai_subtabs', | |
| 2487 | + 'label' => __( 'Write with AI', 'betterdocs' ), | |
| 2488 | + 'classes' => 'tab-nested-layout', | |
| 2489 | + 'type' => 'tab', | |
| 2490 | + 'active' => 'wwa-configuration', | |
| 2491 | + 'completionTrack' => true, | |
| 2492 | + 'sidebar' => false, | |
| 2493 | + 'save' => false, | |
| 2494 | + 'title' => false, | |
| 2495 | + 'config' => array( | |
| 2496 | + 'active' => 'wwa-configuration', | |
| 2497 | + 'sidebar' => false, | |
| 2498 | + 'title' => false | |
| 2499 | + ), | |
| 2500 | + 'submit' => array( | |
| 2501 | + 'show' => false | |
| 2502 | + ), | |
| 2503 | + 'step' => array( | |
| 2504 | + 'show' => false | |
| 2505 | + ), | |
| 2506 | + 'priority' => 5, | |
| 2507 | + 'fields' => array( | |
| 2508 | + 'wwa-configuration' => array( | |
| 2509 | + 'id' => 'wwa-configuration', | |
| 2510 | + 'name' => 'wwa-configuration', | |
| 2511 | + 'type' => 'section', | |
| 2512 | + 'label' => __( 'Configuration', 'betterdocs' ), | |
| 2513 | + 'priority' => 1, | |
| 2514 | + 'fields' => array( | |
| 2294 | 2515 | 'enable_write_with_ai' => array( |
| 2295 | 2516 | 'name' => 'enable_write_with_ai', |
| 2296 | 2517 | 'type' => 'toggle', |
| 2297 | 2518 | 'priority' => 0, |
| @@ -2315,29 +2536,20 @@ | ||
| 2315 | 2536 | 'priority' => 6, |
| 2316 | 2537 | 'label' => __( 'Write Glossaries with AI', 'betterdocs' ), |
| 2317 | 2538 | 'label_subtitle' => __( 'Generate AI based Glossary definitions from the Glossaries admin page', 'betterdocs' ), |
| 2318 | 2539 | 'enable_disable_text_active' => true, |
| 2540 | + 'default' => true, | |
| 2541 | + 'is_pro' => true | |
| 2542 | + ), | |
| 2543 | + 'enable_docs_ai_suite' => array( | |
| 2544 | + 'name' => 'enable_docs_ai_suite', | |
| 2545 | + 'type' => 'toggle', | |
| 2546 | + 'priority' => 7, | |
| 2547 | + 'label' => __( 'Enhance Docs with AI', 'betterdocs' ), | |
| 2548 | + 'label_subtitle' => __( 'Add BetterDocs AI actions for suggesting categories, tags and excerpts inside the native Docs editor panels.', 'betterdocs' ), | |
| 2549 | + 'enable_disable_text_active' => true, | |
| 2319 | 2550 | 'default' => true |
| 2320 | 2551 | ), |
| 2321 | - 'write_with_ai_model' => array( | |
| 2322 | - 'name' => 'write_with_ai_model', | |
| 2323 | - 'type' => 'select', | |
| 2324 | - 'label' => __( 'OpenAI Model*', 'betterdocs' ), | |
| 2325 | - 'priority' => 20, | |
| 2326 | - 'multiple' => false, | |
| 2327 | - 'default' => 'gpt-4o-mini', | |
| 2328 | - 'options' => GlobalFields::normalize_fields( array( | |
| 2329 | - 'gpt-4o-mini' => 'GPT-4o Mini', | |
| 2330 | - 'gpt-4o' => 'GPT-4o', | |
| 2331 | - 'gpt-4.1-nano' => 'GPT-4.1 Nano', | |
| 2332 | - 'gpt-4.1-mini' => 'GPT-4.1 Mini', | |
| 2333 | - 'gpt-4.1' => 'GPT-4.1', | |
| 2334 | - 'gpt-5-nano' => 'GPT-5 Nano', | |
| 2335 | - 'gpt-5-mini' => 'GPT-5 Mini', | |
| 2336 | - 'gpt-5' => 'GPT-5', | |
| 2337 | - 'gpt-5.5' => 'GPT-5.5' | |
| 2338 | - ) ) | |
| 2339 | - ), | |
| 2340 | 2552 | 'ai_autowrite_max_token' => array( |
| 2341 | 2553 | 'name' => 'ai_autowrite_max_token', |
| 2342 | 2554 | 'type' => 'min_token_number', |
| 2343 | 2555 | 'label' => __( 'Set Max Tokens', 'betterdocs' ), |
| @@ -2344,13 +2556,57 @@ | ||
| 2344 | 2556 | 'label_subtitle' => sprintf( // translators: %s is a link to more information about token limits. |
| 2345 | 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' ) ), |
| 2346 | 2558 | 'default' => 2500, |
| 2347 | 2559 | 'priority' => 10, |
| 2348 | - 'model_field' => 'write_with_ai_model', | |
| 2560 | + 'model_field' => 'ai_model', | |
| 2349 | 2561 | 'context' => 'write_with_ai', |
| 2350 | 2562 | 'min_token_map' => AIHelper::get_min_tokens_map( 'write_with_ai' ), |
| 2351 | 2563 | 'classes' => 'wprf-type-text' |
| 2352 | 2564 | ) |
| 2565 | + ) | |
| 2566 | + ), | |
| 2567 | + 'wwa-personalize' => array( | |
| 2568 | + 'id' => 'wwa-personalize', | |
| 2569 | + 'name' => 'wwa-personalize', | |
| 2570 | + 'type' => 'section', | |
| 2571 | + 'label' => __( 'Write AI Personalize', 'betterdocs' ), | |
| 2572 | + 'priority' => 5, | |
| 2573 | + 'fields' => array( | |
| 2574 | + 'write_with_ai_instructions' => array( | |
| 2575 | + 'name' => 'write_with_ai_instructions', | |
| 2576 | + 'type' => 'wwa_instructions', | |
| 2577 | + 'label' => __( 'Instructions', 'betterdocs' ), | |
| 2578 | + 'label_subtitle' => __( 'Add reusable instruction sets to steer how BetterDocs AI writes. The "Default/Core" set is always applied; extra sets can be picked in the Write with AI popup.', 'betterdocs' ), | |
| 2579 | + 'priority' => 1, | |
| 2580 | + 'default' => array( | |
| 2581 | + array( | |
| 2582 | + 'id' => 'default', | |
| 2583 | + 'title' => __( 'Default/Core', 'betterdocs' ), | |
| 2584 | + 'content' => WriteWithAI::default_instruction_content() | |
| 2585 | + ) | |
| 2586 | + ) | |
| 2587 | + ) | |
| 2588 | + ) | |
| 2589 | + ), | |
| 2590 | + 'edit-ai-personalize' => array( | |
| 2591 | + 'id' => 'edit-ai-personalize', | |
| 2592 | + 'name' => 'edit-ai-personalize', | |
| 2593 | + 'type' => 'section', | |
| 2594 | + 'label' => __( 'Edit AI Personalize', 'betterdocs' ), | |
| 2595 | + 'priority' => 7, | |
| 2596 | + 'fields' => array( | |
| 2597 | + 'ai_edit_actions' => array( | |
| 2598 | + 'name' => 'ai_edit_actions', | |
| 2599 | + 'type' => 'ai_edit_actions', | |
| 2600 | + 'label' => __( 'Actions', 'betterdocs' ), | |
| 2601 | + 'label_subtitle' => __( 'Choose which Edit with AI actions appear in the editor. Toggle actions on or off, edit each action\'s instruction, or add your own custom actions. All predefined actions are enabled by default.', 'betterdocs' ), | |
| 2602 | + 'priority' => 1, | |
| 2603 | + 'default' => AIEdit::default_actions() | |
| 2604 | + ) | |
| 2605 | + ) | |
| 2606 | + ) | |
| 2607 | + ) | |
| 2608 | + ) | |
| 2353 | 2609 | ) |
| 2354 | 2610 | ), |
| 2355 | 2611 | 'article-summary' => array( |
| 2356 | 2612 | 'id' => 'article-summary', |
| @@ -2375,31 +2631,12 @@ | ||
| 2375 | 2631 | 'label_subtitle' => sprintf( // translators: %s is a link to more information about token limits. |
| 2376 | 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' ) ), |
| 2377 | 2633 | 'default' => 1500, |
| 2378 | 2634 | 'priority' => 5, |
| 2379 | - 'model_field' => 'article_summary_model', | |
| 2635 | + 'model_field' => 'ai_model', | |
| 2380 | 2636 | 'context' => 'article_summary', |
| 2381 | 2637 | 'min_token_map' => AIHelper::get_min_tokens_map( 'article_summary' ), |
| 2382 | 2638 | 'classes' => 'wprf-type-text' |
| 2383 | - ), | |
| 2384 | - 'article_summary_model' => array( | |
| 2385 | - 'name' => 'article_summary_model', | |
| 2386 | - 'type' => 'select', | |
| 2387 | - 'label' => __( 'OpenAI Model*', 'betterdocs' ), | |
| 2388 | - 'priority' => 10, | |
| 2389 | - 'multiple' => false, | |
| 2390 | - 'default' => 'gpt-4o-mini', | |
| 2391 | - 'options' => GlobalFields::normalize_fields( array( | |
| 2392 | - 'gpt-4o-mini' => 'GPT-4o Mini', | |
| 2393 | - 'gpt-4o' => 'GPT-4o', | |
| 2394 | - 'gpt-4.1-nano' => 'GPT-4.1 Nano', | |
| 2395 | - 'gpt-4.1-mini' => 'GPT-4.1 Mini', | |
| 2396 | - 'gpt-4.1' => 'GPT-4.1', | |
| 2397 | - 'gpt-5-nano' => 'GPT-5 Nano', | |
| 2398 | - 'gpt-5-mini' => 'GPT-5 Mini', | |
| 2399 | - 'gpt-5' => 'GPT-5', | |
| 2400 | - 'gpt-5.5' => 'GPT-5.5' | |
| 2401 | - ) ) | |
| 2402 | 2639 | ) |
| 2403 | 2640 | ) |
| 2404 | 2641 | ) |
| 2405 | 2642 | ) |