PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.5.2
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.5.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 3.5.2 All 199 releases
betterdocs / includes / Core / Settings.php

Settings.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.5.2, at includes/Core/Settings.php

3,025 lines 196.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WPDeveloper\BetterDocs\Core;
4
5 use WP_Error;
6 use WP_User;
7 use WPDeveloper\BetterDocs\Admin\Builder\GlobalFields;
8 use WPDeveloper\BetterDocs\Admin\Builder\Rules;
9 use WPDeveloper\BetterDocs\Utils\AIHelper;
10 use WPDeveloper\BetterDocs\Utils\Base;
11 use WPDeveloper\BetterDocs\Utils\Database;
12 use WPDeveloper\BetterDocs\Utils\Helper;
13
14 class Settings extends Base {
15 public $base_key = 'betterdocs_settings';
16
17 /**
18 * Database class
19 * @var Database
20 */
21 protected $database;
22
23 private $deprecated = array();
24
25 private $cannot_be_empty = array(
26 'breadcrumb_doc_title',
27 'docs_slug',
28 'category_slug',
29 'tag_slug',
30 'permalink_structure',
31 'docs_page'
32 );
33
34 public function __construct( Database $database ) {
35 $this->database = $database;
36 $this->deprecated = $this->deprecated_settings();
37
38 add_filter( 'betterdocs_settings_tabs', array( $this, 'import_export_settings' ) );
39 add_filter( 'betterdocs_settings_tabs', array( $this, 'maybe_remove_git_tab' ), 20 );
40
41 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_old' ), 99 );
42 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ), 99 );
43
44 // if ( isset( $_GET['page'] ) && $_GET['page'] === 'betterdocs-settings' && ! has_action( 'betterdocs_settings_header' ) ) {
45 // add_action( 'betterdocs_settings_header', [ $this, 'header' ] );
46 // }
47
48 add_action( 'wp_ajax_betterdocs_dark_mode', array( $this, 'dark_mode' ) );
49 add_filter( 'betterdocs_settings_tab_advance', array( $this, 'hide_roles_management' ), 11, 1 );
50 add_action( 'betterdocs::settings::saved', array( $this, 'fallback_slugs' ), 99, 3 );
51 }
52
53 public function fallback_slugs( $_saved, $_settings, $_old_settings = array() ) {
54 $_default = $this->get_default();
55 foreach ( $this->cannot_be_empty as $key ) {
56 if ( 'docs_page' === $key && ! $_settings[ 'builtin_doc_page' ] && empty( $_settings[ $key ] ) ) {
57 $this->save( 'builtin_doc_page', true );
58 continue;
59 }
60
61 if ( empty( $_settings[ $key ] ) ) {
62 $this->save( $key, $_default[ $key ] );
63 }
64 }
65 }
66
67 /**
68 * Get the settings URL for admin
69 * @return string
70 */
71 public function url() {
72 return esc_url( admin_url( 'admin.php?page=betterdocs-settings' ) );
73 }
74
75 /**
76 * This method is responsible for enqueueing scripts in settings panel
77 *
78 * @param string $hook
79 *
80 * @return void
81 * @since 2.5.0
82 */
83 public function enqueue( $hook ) {
84 if ( ! betterdocs()->is_betterdocs_screen( $hook ) ) {
85 return;
86 }
87
88 wp_enqueue_media();
89 wp_enqueue_script( 'betterdocs-admin' );
90
91 $settings = GlobalFields::normalize( $this->settings_args() );
92
93 // 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' );
95 foreach ( $sensitive_api_keys as $api_key_field ) {
96 if ( ! empty( $settings[ 'values' ][ $api_key_field ] ) ) {
97 $settings[ 'values' ][ $api_key_field ] = Helper::mask_api_key( $settings[ 'values' ][ $api_key_field ] );
98 }
99 }
100
101 if ( ! current_user_can( 'edit_docs_settings' ) ) {
102 foreach ( $sensitive_api_keys as $api_key_field ) {
103 if ( isset( $settings[ 'values' ][ $api_key_field ] ) ) {
104 unset( $settings[ 'values' ][ $api_key_field ] );
105 }
106 }
107 }
108
109 // Inject raw git settings to bypass get() fallback (e.g., '' -> 'docs')
110 if ( isset( $settings[ 'values' ] ) ) {
111 $settings[ 'values' ][ 'git_provider' ] = $this->get_raw_field( 'git_provider', 'github' );
112 $settings[ 'values' ][ 'git_repository_url' ] = $this->get_raw_field( 'git_repository_url', '' );
113 $settings[ 'values' ][ 'git_branch' ] = $this->get_raw_field( 'git_branch', '' );
114 $settings[ 'values' ][ 'git_docs_directory' ] = $this->get_raw_field( 'git_docs_directory', '' );
115 }
116
117 betterdocs()->assets->localize( 'betterdocs-admin', 'betterdocsAdminSettings', $settings );
118 betterdocs()->assets->enqueue( 'betterdocs-icons', 'admin/btd-icon/style.css' );
119 }
120
121 public function enqueue_old( $hook ) {
122 // FREE & Pro Version Compatibility Check
123 if ( ! betterdocs()->is_betterdocs_screen( $hook ) ) {
124 return;
125 }
126
127 $this->enqueue( 'betterdocs_page_betterdocs-settings' );
128 }
129
130 /**
131 * This method is responsible for printing header in dashboard settings page.
132 *
133 * @param string $hook
134 *
135 * @return void
136 * @since 2.5.0
137 */
138 public function header( $hook ) {
139 if ( 'settings' !== $hook ) {
140 return;
141 }
142
143 betterdocs()->views->get( 'admin/template-parts/settings-header' );
144 betterdocs()->views->get( 'admin/template-parts/settings-header-2' );
145 }
146
147 /**
148 * A list of deprecated settings keys.
149 *
150 * @return array
151 * @since 2.5.0
152 */
153 public function deprecated_settings() {
154 return array();
155 }
156
157 /**
158 * Dynamic migration caller.
159 *
160 * @return void
161 * @since 2.5.0
162 */
163 public function migration( $version ) {
164 if ( $version > 250 ) {
165 for ( $v = 250; $v <= $version; ++$v ) {
166 $_func = "v{$v}";
167 if ( method_exists( $this, $_func ) ) {
168 call_user_func( array( $this, $_func ) );
169 }
170 }
171 }
172 }
173
174 /**
175 * Migration for version 2.5.0
176 *
177 * @return void
178 * @since 2.5.0
179 */
180 public function v250() {
181 if ( $this->get( 'alphabetically_order_term', false ) ) {
182 $this->save( 'terms_orderby', 'name' );
183 }
184
185 if ( $orderby = $this->get( 'alphabetically_order_post', false ) ) {
186 if ( '1' === $orderby ) {
187 $this->save( 'alphabetically_order_post', 'title' );
188 }
189 }
190 }
191
192 /**
193 * A list of default settings data.
194 *
195 * @return array
196 * @since 1.0.0
197 *
198 */
199 public function get_default() {
200 $_default = array(
201 'multiple_kb' => '',
202 'enable_export_faq' => true,
203 'builtin_doc_page' => true,
204 'breadcrumb_doc_title' => __( 'Docs', 'betterdocs' ),
205 'enable_category_hierarchy_slugs' => false,
206 'docs_slug' => 'docs',
207 'docs_page' => 0,
208 'category_slug' => 'docs-category',
209 'tag_slug' => 'docs-tag',
210 'permalink_structure' => 'docs/',
211 'enable_faq_schema' => false,
212 'live_search' => true,
213 'advance_search' => false,
214 'popular_keyword_limit' => 5,
215 'search_letter_limit' => 3,
216 'search_placeholder' => __( 'Search...', 'betterdocs' ),
217 'search_not_found_text' => __( 'Sorry, no docs were found.', 'betterdocs' ),
218 'search_result_image' => true,
219 'search_modal_search_type' => 'all',
220 'masonry_layout' => true,
221 'docs_list_icon' => array(),
222 'category_title_link' => false,
223 'terms_orderby' => 'betterdocs_order',
224 'alphabetically_order_term' => false,
225 'terms_order' => 'ASC',
226 'alphabetically_order_post' => 'betterdocs_order',
227 'docs_order' => 'ASC',
228 'nested_subcategory' => false,
229 'column_number' => 3,
230 'posts_number' => 10,
231 'post_count' => true,
232 'count_text' => __( 'Docs', 'betterdocs' ),
233 'count_text_singular' => __( 'Doc', 'betterdocs' ),
234 'exploremore_btn' => true,
235 'exploremore_btn_txt' => __( 'Explore More', 'betterdocs' ),
236 'doc_single' => 1,
237 'enable_toc' => true,
238 'toc_title' => __( 'Table of Contents', 'betterdocs' ),
239 'toc_hierarchy' => true,
240 'toc_list_number' => false,
241 'toc_dynamic_title' => false,
242 'enable_sticky_toc' => true,
243 'sticky_toc_offset' => 100,
244 'collapsible_toc_mobile' => false,
245 'supported_heading_tag' => array( '1', '2', '3', '4', '5', '6' ),
246 'enable_post_title' => true,
247 'title_link_ctc' => true,
248 'enable_breadcrumb' => true,
249 'breadcrumb_home_text' => __( 'Home', 'betterdocs' ),
250 'enable_breadcrumb_home_text' => true,
251 'breadcrumb_home_url' => get_home_url(),
252 'enable_breadcrumb_category' => true,
253 'enable_breadcrumb_title' => true,
254 'enable_sidebar_cat_list' => true,
255 'enable_print_icon' => true,
256 'enable_tags' => true,
257 'email_feedback' => true,
258 'feedback_link_text' => __( 'Still stuck? How can we help?', 'betterdocs' ),
259 'reaction_feedback_text' => __( 'Thanks for your feedback', 'betterdocs' ),
260 'feedback_url' => '',
261 'feedback_form_title' => __( 'How can we help?', 'betterdocs' ),
262 'email_address' => get_option( 'admin_email' ),
263 'show_last_update_time' => true,
264 'enable_navigation' => true,
265 'enable_comment' => true,
266 'enable_credit' => false,
267 'enable_archive_sidebar' => true,
268 'archive_nested_subcategory' => true,
269 'archive_enable_pagination' => false,
270 'enable_content_restriction' => false,
271 'enable_reporting' => false,
272 'enable_sample_data' => false,
273 'reporting_day' => 'monday',
274 'reporting_email' => get_option( 'admin_email' ),
275 'enable_write_with_ai' => true,
276 'enable_faq_write_with_ai' => true,
277 'enable_glossaries_write_with_ai' => true,
278 'write_with_ai_model' => 'gpt-4o-mini',
279 'ai_autowrite_api_key' => '',
280 'ai_autowrite_max_token' => 2500,
281 'enable_article_summary' => false,
282 'article_summary_model' => 'gpt-4o-mini',
283 'article_summary_max_token' => 1500,
284 'enable_estimated_reading_time' => true,
285 'enable_encyclopedia' => false,
286 'enable_glossaries' => false,
287 'show_glossary_suggestions' => true,
288 'enable_ai_chatbot' => false,
289 'estimated_reading_time_title' => '',
290 'estimated_reading_time_text' => __( 'min read', 'betterdocs' ),
291 'singular_estimated_reading_time_text' => __( 'min read', 'betterdocs' ),
292 'betterdocs_access_control_repeater' => array(),
293 'internal_knowledge_base_type' => 'basic',
294 'betterdocs_access_control_repeater_kb' => array(),
295 'enable_git_integration' => false
296 );
297
298 $_default = apply_filters( 'betterdocs_default_settings', $_default );
299 // $_default = apply_filters_deprecated(
300 // 'betterdocs_option_default_settings', [$_default], '2.5.0',
301 // 'betterdocs_default_settings', 'betterdocs_option_default_settings will be removed from v3.5.0.'
302 // );
303
304 return $_default;
305 }
306
307 /**
308 * A list of default settings for pro plugins.
309 *
310 * @return array
311 * @since 2.5.0
312 */
313 public function get_pro_defaults() {
314 return array();
315 }
316
317 public function is_elementor_pro() {
318 return is_plugin_active( 'elementor-pro/elementor-pro.php' );
319 }
320
321 /**
322 * Get full site editor links for docs page.
323 *
324 * @return string
325 * @since 3.5.8
326 */
327 public function gutenberg_link() {
328 if ( betterdocs()->helper->current_theme_is_fse_theme() ) {
329 return admin_url( 'site-editor.php?postType=wp_template&postId=betterdocs/betterdocs//archive-docs' );
330 } else {
331 return 'https://betterdocs.co/docs/betterdocs-provides-full-site-editor-support/';
332 }
333 }
334
335 /**
336 * Get elementor theme builder link for docs page.
337 *
338 * @return string
339 * @since 3.5.8
340 */
341 public function elementor_link() {
342 if ( $this->is_elementor_pro() ) {
343 return admin_url( 'admin.php?page=elementor-app#/site-editor/templates/doc-archive' );
344 } else {
345 return 'https://betterdocs.co/docs/docs-page-with-elementor/';
346 }
347 }
348
349 public function customizer_link() {
350 $query[ 'autofocus[panel]' ] = 'betterdocs_customize_options';
351 $query[ 'return' ] = admin_url( 'edit.php?post_type=docs' );
352 $builtin_doc_page = betterdocs()->settings->get( 'builtin_doc_page' );
353 $docs_slug = betterdocs()->settings->get( 'docs_slug' );
354 $docs_page = betterdocs()->settings->get( 'builtin_doc_page' );
355
356 if ( 1 == $builtin_doc_page && $docs_slug ) {
357 $query[ 'url' ] = site_url( '/' . $docs_slug );
358 } elseif ( 1 != $builtin_doc_page && $docs_page ) {
359 $post_info = get_post( $docs_page );
360 $query[ 'url' ] = site_url( '/' . $post_info->post_name );
361 }
362
363 return add_query_arg( $query, admin_url( 'customize.php' ) );
364 }
365
366 public function design_tab() {
367 $settings = array();
368
369 $settings[ 'gutenberg_link' ] = array(
370 'name' => 'gutenberg_link',
371 'type' => 'action',
372 'action' => 'betterdocs_settings_gutenberg_link',
373 'button' => betterdocs()->helper->current_theme_is_fse_theme() ? __( 'Design with Gutenberg', 'betterdocs' ) : __( 'Learn More', 'betterdocs' ),
374 'url' => $this->gutenberg_link(),
375 'customizer_img' => betterdocs()->assets->icon( 'customizer/gutenberg-preview.png', true ),
376 'priority' => 1
377 );
378
379 $settings[ 'elementor_link' ] = array(
380 'name' => 'elementor_link',
381 'type' => 'action',
382 'action' => 'betterdocs_settings_elementor_link',
383 'button' => $this->is_elementor_pro() ? __( 'Design with Elementor', 'betterdocs' ) : __( 'Learn More', 'betterdocs' ),
384 'url' => $this->elementor_link(),
385 'customizer_img' => betterdocs()->assets->icon( 'customizer/elementor-preview.png', true ),
386 'priority' => 2
387 );
388
389 if ( ! betterdocs()->helper->current_theme_is_fse_theme() ) {
390 $settings[ 'customizer_link' ] = array(
391 'name' => 'customizer_link',
392 'type' => 'action',
393 'action' => 'betterdocs_settings_customizer_link',
394 'button' => __( 'Customize in BetterDocs', 'betterdocs' ),
395 'url' => $this->customizer_link(),
396 'customizer_img' => betterdocs()->assets->icon( 'customizer/customizer-preview.png', true ),
397 'priority' => 3
398 );
399 }
400
401 return $settings;
402 }
403
404 /**
405 * Get All Roles
406 * dynamically
407 *
408 * @return array
409 */
410 public function get_roles() {
411 $roles = wp_roles()->role_names;
412 unset( $roles[ 'subscriber' ] );
413
414 return $roles;
415 }
416
417 /**
418 * Set dark mode
419 *
420 * @return void
421 * @since 1.0.0
422 */
423 public function dark_mode() {
424 if ( ! check_ajax_referer( 'doc_cat_order_nonce', 'nonce', false ) ) {
425 wp_send_json_error();
426 }
427
428 if ( isset( $_POST[ 'mode' ] ) ) {
429 if ( $this->save( 'dark_mode', rest_sanitize_boolean( $_POST[ 'mode' ] ) ) ) { // phpcs:ignore
430 wp_send_json_success();
431 }
432 }
433
434 wp_send_json_error();
435 }
436
437 public function get_normalized_value( $key, $value, $default = null ) {
438 $_origin_value = $_value = $value;
439
440 if ( in_array( $_value, array( 'on', 'off', '1', 'false', 'true' ), true ) ) {
441 switch ( $_value ) {
442 case 'on':
443 case 'ON':
444 case '1':
445 case 'true':
446 $_value = true;
447 break;
448 case 'off':
449 case 'OFF':
450 case '':
451 case 'false':
452 $_value = false;
453 break;
454 }
455 }
456
457 $this->type_validation( $_value, $default );
458
459 return $_value;
460 }
461
462 public function get_normalized_values( $values, $default_values = array() ) {
463 if ( empty( $values ) ) {
464 return array();
465 }
466
467 $_settings = array();
468 foreach ( $values as $key => $value ) {
469 $_default_value = isset( $default_values[ $key ] ) ? $default_values[ $key ] : null;
470 $_settings[ $key ] = $this->get_normalized_value( $key, $value, $_default_value );
471 }
472
473 return $_settings;
474 }
475
476 public function get_all( $raw = false ) {
477 $_default_settings = $raw ? array() : array_merge( $this->get_default(), $this->get_pro_defaults() );
478 $_settings = $this->database->get( $this->base_key, $_default_settings );
479
480 return $this->get_normalized_values( $_settings, $_default_settings );
481 }
482
483 public function type_validation( &$value, $defaultValue = null ) {
484 if ( null !== $defaultValue ) {
485 /**
486 * Check if value is not in same type
487 */
488 $_default_type = gettype( $defaultValue );
489
490 if ( ! ( is_scalar( $defaultValue ) && is_scalar( $value ) ) && empty( $value ) ) {
491 $value = $defaultValue;
492 }
493
494 settype( $value, $_default_type );
495 }
496 }
497
498 /**
499 * Get settings value by key
500 *
501 * @param string $key
502 * @param mixed $default
503 * @param bool $get_all
504 *
505 * @return mixed
506 * @since 2.5.0
507 *
508 */
509 public function get( $key, $default = null ) {
510 $_default_settings = array_merge( $this->get_default(), $this->get_pro_defaults() );
511 $_settings = $this->database->get( $this->base_key, $_default_settings );
512
513 $_value = $default;
514 switch ( true ) {
515 // Check if it's a PRO Option
516 case ! isset( $_default_settings[ $key ] ):
517 $_value = $default;
518 break;
519 // Check if it's a FREE Option and not in DB.
520 case ! isset( $_settings[ $key ] ) && isset( $_default_settings[ $key ] ):
521 $_value = null !== $default ? $default : $_default_settings[ $key ];
522 break;
523 // Check if it's a FREE Option
524 case isset( $_settings[ $key ] ) && isset( $_default_settings[ $key ] ):
525 $_value = $_settings[ $key ];
526 break;
527 }
528
529 $_value = $this->get_normalized_value( $key, $_value, isset( $_default_settings[ $key ] ) ? $_default_settings[ $key ] : null );
530
531 if ( gettype( $_value ) === 'string' ) {
532 if ( empty( $_value ) && null != $default ) {
533 $_value = $default;
534 } elseif ( empty( $_value ) && null === $default && isset( $_default_settings[ $key ] ) ) {
535 $_value = $_default_settings[ $key ];
536 }
537 }
538
539 return $_value;
540 }
541
542 public function get_raw_field( $key, $default = null ) {
543 $_settings = $this->database->get( $this->base_key, array() );
544
545 if ( isset( $_settings[ $key ] ) ) {
546 return $this->get_normalized_value( $key, $_settings[ $key ], $default );
547 }
548
549 return $default;
550 }
551
552 public function save( $key, $value ) {
553 $_settings = $this->database->get( $this->base_key, array() );
554 $_settings[ $key ] = $value;
555
556 return $this->database->save( $this->base_key, $_settings );
557 }
558
559 public function save_settings( $settings ) {
560 $existing_plugins = betterdocs()->kbmigration->knowledge_base_plugins();
561 if ( ! current_user_can( 'edit_docs_settings' ) ) {
562 return new WP_Error( 'unauthorized_action', __( 'You don\'t have any rights for saving settings.', 'betterdocs' ) );
563 }
564
565 // The frontend sends the currently-active settings tab as a transport-only hint so
566 // tab-scoped validation (the AI min-token floor below) only runs when that tab is being
567 // saved. It must never be persisted into the settings option.
568 $active_tab = '';
569 if ( array_key_exists( '_active_tab', $settings ) ) {
570 $active_tab = (string) $settings[ '_active_tab' ];
571 unset( $settings[ '_active_tab' ] );
572 }
573
574 // Quickbuilder sends back the full UI-loaded payload. To avoid overwriting values that we
575 // asynchronously updated in the DB via AJAX (like Git integration fields), we strictly remove them
576 // from the incoming save payload so that the freshly loaded `$_old_settings` values persist.
577 $exclude_ajax_keys = array( 'git_provider', 'git_repository_url', 'git_branch', 'git_docs_directory' );
578 foreach ( $exclude_ajax_keys as $key ) {
579 if ( array_key_exists( $key, $settings ) ) {
580 unset( $settings[ $key ] );
581 }
582 if ( isset( $settings[ 'values' ] ) && is_array( $settings[ 'values' ] ) && array_key_exists( $key, $settings[ 'values' ] ) ) {
583 unset( $settings[ 'values' ][ $key ] );
584 }
585 }
586
587 $_old_settings = $this->database->get( $this->base_key, $this->get_default() );
588
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' );
593 foreach ( $sensitive_api_keys as $api_key_field ) {
594 if ( ! isset( $settings[ $api_key_field ] ) ) {
595 continue;
596 }
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;
600 }
601 }
602
603 // Minimum-token policy: reject saves where a feature's max_token field is below the
604 // per-model floor defined by AIHelper::get_min_tokens(). The model may be unchanged in
605 // this submission, so fall back to the stored value when it isn't part of the payload.
606 // Both token fields live under the AI Content Suite tab, so enforce the floor only when
607 // that tab is the one being saved — saving any other settings page never trips it (e.g.
608 // old users whose stored value predates the floor). When the active-tab hint is absent
609 // (stale cached JS, the quick-setup wizard, or any programmatic caller) the loop is
610 // skipped and the save is allowed through.
611 $token_pairs = array(
612 array(
613 'tab' => 'tab-betterdocs-ai',
614 'context' => 'write_with_ai',
615 'token_key' => 'ai_autowrite_max_token',
616 'model_key' => 'write_with_ai_model',
617 'label' => __( 'Write with AI', 'betterdocs' )
618 ),
619 array(
620 'tab' => 'tab-betterdocs-ai',
621 'context' => 'article_summary',
622 'token_key' => 'article_summary_max_token',
623 'model_key' => 'article_summary_model',
624 'label' => __( 'AI Doc Summarizer', 'betterdocs' )
625 )
626 );
627 foreach ( $token_pairs as $pair ) {
628 if ( $active_tab !== $pair[ 'tab' ] ) {
629 continue;
630 }
631 if ( ! array_key_exists( $pair[ 'token_key' ], $settings ) ) {
632 continue;
633 }
634 $incoming_tokens = (int) $settings[ $pair[ 'token_key' ] ];
635 $incoming_model = isset( $settings[ $pair[ 'model_key' ] ] )
636 ? $settings[ $pair[ 'model_key' ] ]
637 : ( isset( $_old_settings[ $pair[ 'model_key' ] ] ) ? $_old_settings[ $pair[ 'model_key' ] ] : '' );
638 $min = AIHelper::get_min_tokens( $pair[ 'context' ], $incoming_model );
639 if ( $min > 0 && $incoming_tokens < $min ) {
640 return new WP_Error(
641 'min_tokens_below_threshold',
642 sprintf(
643 /* translators: 1: feature label, 2: minimum tokens, 3: model id */
644 __( '%1$s max tokens must be at least %2$d for %3$s.', 'betterdocs' ),
645 $pair[ 'label' ],
646 $min,
647 $incoming_model
648 )
649 );
650 }
651 }
652
653 // @todo: sanitize the data before inject in DB.
654 $_normalized_settings = $this->get_normalized_values( $settings );
655 if ( $existing_plugins && isset( $_normalized_settings[ 'migration_step' ] ) && true == $_normalized_settings[ 'migration_step' ] ) {
656 betterdocs()->kbmigration->migrate();
657 }
658 $_settings = wp_parse_args( $_normalized_settings, $_old_settings );
659
660 // Check if there are actual changes before saving.
661 // update_option returns false when values serialize identically, which can happen
662 // due to object caching or type normalization even when user made changes.
663 $_has_changes = $_settings != $_old_settings;
664
665 $_saved = $this->database->save( $this->base_key, $_settings );
666
667 do_action_ref_array( 'betterdocs::settings::saved', array( $_saved, $_settings, $_old_settings, &$this ) );
668
669 // Return true if save succeeded OR if there were changes to attempt saving.
670 // This handles cases where update_option returns false due to identical serialization
671 // (e.g., object caching, type coercion during serialization).
672 return $_saved || $_has_changes;
673 }
674
675 public function views( $hook ) {
676 return betterdocs()->views->get( 'admin/settings' );
677 }
678
679 public function save_default_settings() {
680 $_settings = $this->get_all();
681
682 return $this->database->save( $this->base_key, $_settings );
683 }
684
685 public function get_pages() {
686 $_pages = betterdocs()->query->get_posts( array(
687 'post_type' => 'page',
688 'numberposts' => -1,
689 'post_status' => 'publish',
690 'posts_per_page' => -1
691 ) );
692
693 $__pages = array();
694
695 if ( ! empty( $_pages ) ) {
696 $__pages[ 0 ] = __( 'Select a Page', 'betterdocs' );
697 foreach ( $_pages->posts as $page ) {
698 $__pages[ $page->ID ] = esc_html( $page->post_title );
699 }
700 }
701
702 return $__pages;
703 }
704
705 public function settings_args() {
706 $wp_roles = $this->normalize_options( $this->get_roles() );
707
708 $roles_for_ikb = $this->normalize_options( array_merge( array( 'all' => __( 'All logged in users', 'betterdocs' ) ), wp_roles()->role_names ) );
709
710 unset( $roles_for_ikb[ 'administrator' ] );
711
712 $settings = array(
713 'id' => 'betterdocs_settings_metabox_wrapper',
714 'title' => __( 'betterdocs', 'betterdocs' ),
715 'object_types' => array( 'betterdocs' ),
716 'context' => 'normal',
717 'priority' => 'high',
718 'show_header' => false,
719 'tabnumber' => false,
720 'is_pro_active' => betterdocs()->is_pro_active(),
721 'logoURL' => betterdocs()->assets->icon( 'betterdocs-icon.svg', true ),
722 'layout' => 'vertical',
723 'config' => array(
724 'active' => 'tab-general',
725 'sidebar' => true,
726 'title' => false
727 ),
728 'submit' => array(
729 'show' => true,
730 'label' => __( 'Save', 'betterdocs' ),
731 'loadingLabel' => __( 'Saving...', 'betterdocs' ),
732 'class' => 'save-settings',
733 'rules' => Rules::logicalRule( array(
734 Rules::is( 'config.active', 'tab-design', true ),
735 Rules::is( 'config.active', 'tab-shortcodes', true ),
736 Rules::is( 'config.active', 'tab-instant-answer', true ),
737 Rules::is( 'config.active', 'tab-import-export', true ),
738 Rules::is( 'config.active', 'tab-migration', true )
739 ), 'and' )
740 ),
741 'values' => betterdocs()->is_pro_active() ? $this->get_all() : array_merge( $this->get_all(), $this->pro_settings_default_values() ),
742 'tabs' => apply_filters( 'betterdocs_settings_tabs', array(
743 'tab-general' => apply_filters( 'betterdocs_settings_tab_general', array(
744 'id' => 'tab-general',
745 'label' => __( 'General', 'betterdocs' ),
746 'classes' => 'tab-general',
747 'priority' => 10,
748 'fields' => array(
749 'title-general' => apply_filters( 'betterdocs_encyclopedia_settings', array(
750 'name' => 'title-general-tab',
751 'type' => 'section',
752 'label' => __( 'General Settings', 'betterdocs' ),
753 'priority' => 10,
754 'fields' => array(
755 'multiple_kb' => apply_filters( 'betterdocs_multi_kb_settings', array(
756 'name' => 'multiple_kb',
757 'type' => 'toggle',
758 'label' => __( 'Multiple Knowledge Base', 'betterdocs' ),
759 'enable_disable_text_active' => true,
760 'default' => '',
761 'priority' => 1,
762 'is_pro' => true
763 ) ),
764 'builtin_doc_page' => array(
765 'name' => 'builtin_doc_page',
766 'type' => 'toggle',
767 'label' => __( 'Built-in Documentation Page', 'betterdocs' ),
768 'enable_disable_text_active' => true,
769 'default' => 1,
770 'priority' => 2
771 ),
772 'enable_category_hierarchy_slugs' => array(
773 'name' => 'enable_category_hierarchy_slugs',
774 'type' => 'toggle',
775 'label' => __( 'Enable Category Hierarchy Slug', 'betterdocs' ),
776 'enable_disable_text_active' => true,
777 'default' => false,
778 'priority' => 3,
779 'label_subtitle' => __( "Shows Hierarchy Based Permalink Slugs For Doc Categories & Single Doc Page", 'betterdocs' )
780 ),
781 'breadcrumb_doc_title' => array(
782 'name' => 'breadcrumb_doc_title',
783 'type' => 'text',
784 'label' => __( 'Documentation Page Title', 'betterdocs' ),
785 'default' => __( 'Docs', 'betterdocs' ),
786 'priority' => 4,
787 'rules' => Rules::is( 'builtin_doc_page', true )
788 ),
789 'docs_slug' => array(
790 'name' => 'docs_slug',
791 'type' => 'text',
792 'label' => __( 'BetterDocs Root Slug', 'betterdocs' ),
793 'default' => 'docs',
794 'priority' => 5,
795 'rules' => Rules::is( 'builtin_doc_page', true )
796 ),
797 'docs_page' => array(
798 'name' => 'docs_page',
799 'label' => __( 'Docs Page', 'betterdocs' ),
800 'type' => 'select',
801 'default' => 0,
802 'priority' => 6,
803 'search' => true,
804 'options' => $this->normalize_options( $this->get_pages() ),
805 'label_subtitle' => __( 'You will need to insert BetterDocs Shortcode inside the page. This page will be used as docs permalink.', 'betterdocs' ),
806 'rules' => Rules::is( 'builtin_doc_page', false )
807 ),
808
809 'category_slug' => array(
810 'name' => 'category_slug',
811 'type' => 'text',
812 'label' => __( 'Custom Category Slug', 'betterdocs' ),
813 'default' => 'docs-category',
814 'priority' => 7
815 ),
816 'tag_slug' => array(
817 'name' => 'tag_slug',
818 'type' => 'text',
819 'label' => __( 'Custom Tag Slug', 'betterdocs' ),
820 'default' => 'docs-tag',
821 'priority' => 8
822 ),
823 'permalink_structure' => array(
824 'name' => 'permalink_structure',
825 'type' => 'permalink_structure',
826 'label' => __( 'Single Docs Permalink', 'betterdocs' ),
827 'default' => PostType::permalink_structure(),
828 'priority' => 9,
829 'tags' => $this->normalize_options( array(
830 '%doc_category%' => '%doc_category%',
831 '%knowledge_base%' => '%knowledge_base%'
832 ) ),
833 'label_subtitle' => __( 'Make sure to keep Docs Root Slug in the Single Docs Permalink. You are not able to keep it blank. You can use the available tags from below.', 'betterdocs' )
834 ),
835 'enable_glossaries' => array(
836 'name' => 'enable_glossaries',
837 'type' => 'toggle',
838 'label' => __( 'Show Glossary', 'betterdocs' ),
839 'label_subtitle' => __( 'Enable the glossary feature to allow users to look up definitions for terms used within your encyclopedia or glossaries themselves.', 'betterdocs' ),
840 'enable_disable_text_active' => true,
841 'default' => false,
842 'priority' => 10,
843 'is_pro' => true
844 ),
845 'enable_encyclopedia' => array(
846 'name' => 'enable_encyclopedia',
847 'type' => 'toggle',
848 'label' => __( 'Built-in Encyclopedia Page', 'betterdocs' ),
849 'enable_disable_text_active' => true,
850 'default' => false,
851 'priority' => 11,
852 'is_pro' => true
853 ),
854 'enable_faq_schema' => array(
855 'name' => 'enable_faq_schema',
856 'type' => 'toggle',
857 'label' => __( 'FAQ Schema', 'betterdocs' ),
858 'enable_disable_text_active' => true,
859 'default' => '',
860 'priority' => 12
861 ),
862 'analytics_from' => array(
863 'name' => 'analytics_from',
864 'type' => 'select',
865 'label' => __( 'Analytics From', 'betterdocs' ),
866 'options' => $this->normalize_options( array(
867 'everyone' => __( 'Everyone', 'betterdocs' ),
868 'guests' => __( 'Guests Only', 'betterdocs' ),
869 'registered_users' => __( 'Registered Users Only', 'betterdocs' )
870 ) ),
871 'default' => 'everyone',
872 'priority' => 13,
873 'is_pro' => true
874 ),
875 'unique_visitor_count' => array(
876 'name' => 'unique_visitor_count',
877 'type' => 'toggle',
878 'label' => __( 'Unique Visitor Count', 'betterdocs' ),
879 'enable_disable_text_active' => true,
880 'default' => false,
881 'priority' => 14,
882 'is_pro' => true
883 ),
884 'exclude_bot_analytics' => array(
885 'name' => 'exclude_bot_analytics',
886 'type' => 'toggle',
887 'label' => __( 'Exclude Bot Analytics', 'betterdocs' ),
888 'enable_disable_text_active' => true,
889 'default' => false,
890 'priority' => 15,
891 'is_pro' => true
892 )
893
894 )
895 ) )
896 )
897 ) ),
898 'tab-layout' => apply_filters( 'betterdocs_settings_tab_layout', array(
899 'id' => 'tab-layout',
900 'label' => __( 'Layout', 'betterdocs' ),
901 'classes' => 'tab-layout',
902 'priority' => 20,
903 'fields' => array(
904 'title-layout' => array(
905 'name' => 'title-layout-tab',
906 'type' => 'section',
907 'label' => __( 'Layout Settings', 'betterdocs' ),
908 'priority' => 20,
909 'fields' => array(
910 'tab-sidebar-layout' => apply_filters( 'betterdocs_settings_tab_sidebar_layout', array(
911 'id' => 'tab-sidebar-layout',
912 'name' => 'tab_sidebar_layout',
913 'label' => __( 'Layout Settings', 'betterdocs' ),
914 'classes' => 'tab-layout',
915 'type' => 'tab',
916 'active' => 'layout_documentation_page',
917 'completionTrack' => true,
918 'sidebar' => false,
919 'save' => false,
920 'title' => false,
921 'config' => array(
922 'active' => 'layout_documentation_page',
923 'sidebar' => false,
924 'title' => false
925 ),
926 'submit' => array(
927 'show' => false
928 ),
929 'step' => array(
930 'show' => false
931 ),
932 'priority' => 20,
933 'fields' => array(
934 'layout_documentation_page' => array(
935 'id' => 'layout_documentation_page',
936 'name' => 'layout_documentation_page',
937 'type' => 'section',
938 'label' => __( 'Documentation Page', 'betterdocs' ),
939 'priority' => 1,
940 'fields' => array(
941 'tab-nested-layout-1' => array(
942 'id' => 'tab-nested-layout-1',
943 'name' => 'tab_nested_layout_1',
944 'label' => __( 'Documentation Page', 'betterdocs' ),
945 'classes' => 'tab-nested-layout',
946 'type' => 'tab',
947 'active' => 'layout_documentation_page_general',
948 'completionTrack' => true,
949 'sidebar' => false,
950 'save' => false,
951 'title' => false,
952 'config' => array(
953 'active' => 'layout_documentation_page_general',
954 'sidebar' => false,
955 'title' => false
956 ),
957 'submit' => array(
958 'show' => false
959 ),
960 'step' => array(
961 'show' => false
962 ),
963 'priority' => 1,
964 'fields' => array(
965 'layout_documentation_page_general' => array(
966 'id' => 'layout_documentation_page_general',
967 'name' => 'layout_documentation_page_general',
968 'type' => 'section',
969 'label' => __( 'General', 'betterdocs' ),
970 'priority' => 1,
971 'fields' => array(
972 'docs_list_icon' => array(
973 'name' => 'docs_list_icon',
974 'type' => 'media',
975 'value' => '',
976 'label' => __( 'Docs List Icon', 'betterdocs' ),
977 'label_subtitle' => __( 'Upload your own preferred document list icon', 'betterdocs' ),
978 'priority' => 0
979 ),
980 'category_title_link' => array(
981 'name' => 'category_title_link',
982 'type' => 'toggle',
983 'label' => __( 'Category Title Link', 'betterdocs' ),
984 'label_subtitle' => __( 'This setting is applicable for category grid layout', 'betterdocs' ),
985 'enable_disable_text_active' => true,
986 'default' => false,
987 'priority' => 0
988 ),
989 'masonry_layout' => array(
990 'name' => 'masonry_layout',
991 'type' => 'toggle',
992 'label' => __( 'Masonry', 'betterdocs' ),
993 'enable_disable_text_active' => true,
994 'default' => 1,
995 'priority' => 1
996 ),
997 'nested_subcategory' => array(
998 'name' => 'nested_subcategory',
999 'type' => 'toggle',
1000 'label' => __( 'Nested Sub Category', 'betterdocs' ),
1001 'enable_disable_text_active' => true,
1002 'default' => '',
1003 'priority' => 2
1004 ),
1005 'column_number' => array(
1006 'name' => 'column_number',
1007 'type' => 'number',
1008 'label' => __( 'Number Of Columns', 'betterdocs' ),
1009 'label_subtitle' => __( 'This setting is not applicable for sleek layout.', 'betterdocs' ),
1010 'default' => 3,
1011 'priority' => 3
1012 ),
1013 'posts_number' => apply_filters( 'betterdocs_posts_number', array(
1014 'name' => 'posts_number',
1015 'type' => 'number',
1016 'label' => __( 'Number Of Docs', 'betterdocs' ),
1017 'label_subtitle' => __( 'This setting is not applicable for handbook layout.', 'betterdocs' ),
1018 'default' => 10,
1019 'priority' => 4
1020 ) ),
1021 'post_count' => array(
1022 'name' => 'post_count',
1023 'type' => 'toggle',
1024 'label' => __( 'Doc Count', 'betterdocs' ),
1025 'enable_disable_text_active' => true,
1026 'default' => 1,
1027 'priority' => 5
1028 ),
1029 'count_text' => array(
1030 'name' => 'count_text',
1031 'type' => 'text',
1032 'label' => __( 'Count Text', 'betterdocs' ),
1033 'default' => __( 'Docs', 'betterdocs' ),
1034 'priority' => 6
1035 ),
1036 'count_text_singular' => array(
1037 'name' => 'count_text_singular',
1038 'type' => 'text',
1039 'label' => __( 'Count Text Singular', 'betterdocs' ),
1040 'default' => __( 'Doc', 'betterdocs' ),
1041 'priority' => 7
1042 ),
1043 'exploremore_btn' => array(
1044 'name' => 'exploremore_btn',
1045 'type' => 'toggle',
1046 'label' => __( 'Explore More Button', 'betterdocs' ),
1047 'enable_disable_text_active' => true,
1048 'default' => true,
1049 'priority' => 8
1050 ),
1051 'exploremore_btn_txt' => array(
1052 'name' => 'exploremore_btn_txt',
1053 'type' => 'text',
1054 'label' => __( 'Explore More Button Text', 'betterdocs' ),
1055 'default' => __( 'Explore More', 'betterdocs' ),
1056 'priority' => 9,
1057 'rules' => Rules::is( 'exploremore_btn', true )
1058 ),
1059 'betterdocs_popular_docs_text' => array(
1060 'name' => 'betterdocs_popular_docs_text',
1061 'type' => 'text',
1062 'label' => __( 'Popular Docs Text', 'betterdocs' ),
1063 'default' => __( 'Popular Docs', 'betterdocs' ),
1064 'priority' => 10,
1065 'is_pro' => true
1066 ),
1067 'betterdocs_popular_docs_number' => array(
1068 'name' => 'betterdocs_popular_docs_number',
1069 'type' => 'number',
1070 'label' => __( 'Popular Docs Number', 'betterdocs' ),
1071 'default' => 10,
1072 'priority' => 11,
1073 'is_pro' => true
1074 )
1075 )
1076 ),
1077 'layout_documentation_page_search' => array(
1078 'id' => 'layout_documentation_page_search',
1079 'name' => 'layout_documentation_page_search',
1080 'type' => 'section',
1081 'label' => __( 'Search', 'betterdocs' ),
1082 'priority' => 2,
1083 'fields' => array(
1084 'live_search' => array(
1085 'name' => 'live_search',
1086 'type' => 'toggle',
1087 'label' => __( 'Live Search', 'betterdocs' ),
1088 'enable_disable_text_active' => true,
1089 'default' => 1,
1090 'priority' => 1
1091 ),
1092 'advance_search' => apply_filters( 'betterdocs_advance_search_settings', array(
1093 'name' => 'advance_search',
1094 'type' => 'toggle',
1095 'label' => __( 'Advanced Search', 'betterdocs' ),
1096 'enable_disable_text_active' => true,
1097 'default' => '',
1098 'priority' => 2,
1099 'is_pro' => true
1100 ) ),
1101 'child_category_exclude' => apply_filters( 'child_category_exclude', array(
1102 'name' => 'child_category_exclude',
1103 'type' => 'toggle',
1104 'label' => __( 'Exclude Child Terms In Category Search', 'betterdocs' ),
1105 'enable_disable_text_active' => true,
1106 'default' => '',
1107 'priority' => 3,
1108 'is_pro' => true
1109 ) ),
1110 'popular_keyword_limit' => apply_filters( 'betterdocs_popular_keyword_limit_settings', array(
1111 'name' => 'popular_keyword_limit',
1112 'type' => 'number',
1113 'label' => __( 'Minimum amount of Keywords Search', 'betterdocs' ),
1114 'default' => 5,
1115 'priority' => 4,
1116 'is_pro' => true
1117 ) ),
1118 'search_letter_limit' => array(
1119 'name' => 'search_letter_limit',
1120 'type' => 'number',
1121 'label' => __( 'Minimum Character Limit For Search Result', 'betterdocs' ),
1122 'priority' => 5,
1123 'default' => 3
1124 ),
1125 'search_placeholder' => array(
1126 'name' => 'search_placeholder',
1127 'type' => 'text',
1128 'label' => __( 'Search Placeholder', 'betterdocs' ),
1129 'default' => __( 'Search..', 'betterdocs' ),
1130 'priority' => 6
1131 ),
1132 'search_button_text' => apply_filters( 'betterdocs_search_button_text', array(
1133 'name' => 'search_button_text',
1134 'type' => 'text',
1135 'label' => __( 'Search Button Text', 'betterdocs' ),
1136 'priority' => 7,
1137 'default' => __( 'Search', 'betterdocs' )
1138 ) ),
1139 'search_not_found_text' => array(
1140 'name' => 'search_not_found_text',
1141 'type' => 'text',
1142 'label' => __( 'Search Not Found Text', 'betterdocs' ),
1143 'default' => 'Sorry, no docs were found.',
1144 'priority' => 8
1145 ),
1146 'search_result_image' => array(
1147 'name' => 'search_result_image',
1148 'type' => 'toggle',
1149 'label' => __( 'Search Result Image', 'betterdocs' ),
1150 'enable_disable_text_active' => true,
1151 'default' => 1,
1152 'priority' => 9
1153 ),
1154 'kb_based_search' => apply_filters( 'betterdocs_kb_based_search_settings', array(
1155 'name' => 'kb_based_search',
1156 'type' => 'toggle',
1157 'label' => __( 'KB Based Search', 'betterdocs' ),
1158 'enable_disable_text_active' => true,
1159 'default' => '',
1160 'priority' => 10,
1161 'is_pro' => true,
1162 'rules' => Rules::is( 'multiple_kb', true )
1163 ) ),
1164 'search_modal_search_type' => array(
1165 'name' => 'search_modal_search_type',
1166 'type' => 'select',
1167 'label' => __( 'Search Modal Source', 'betterdocs' ),
1168 'label_subtitle' => __( 'Choose the content type to be shown in the search modal. <br> <b>Note:</b> This is only applicable on Modal Search.', 'betterdocs' ),
1169 'default' => 'all',
1170 'options' => $this->normalize_options( array(
1171 'all' => __( 'Both (Docs + FAQs)', 'betterdocs' ),
1172 'docs' => __( 'Docs only', 'betterdocs' ),
1173 'faq' => __( 'FAQs only', 'betterdocs' )
1174 ) ),
1175 'priority' => 11
1176 )
1177 )
1178 ),
1179 'layout_documentation_page_order_by' => array(
1180 'id' => 'layout_documentation_page_order_by',
1181 'name' => 'layout_documentation_page_order_by',
1182 'type' => 'section',
1183 'label' => __( 'Order By', 'betterdocs' ),
1184 'priority' => 3,
1185 'fields' => array(
1186 'terms_orderby' => array(
1187 'name' => 'terms_orderby',
1188 'type' => 'select',
1189 'label' => __( 'Terms Order By', 'betterdocs' ),
1190 'default' => 'betterdocs_order',
1191 'options' => $this->normalize_options( apply_filters( 'betterdocs_terms_orderby_options', array(
1192 'none' => __( 'No order', 'betterdocs' ),
1193 'name' => __( 'Name', 'betterdocs' ),
1194 'slug' => __( 'Slug', 'betterdocs' ),
1195 'term_group' => __( 'Term Group', 'betterdocs' ),
1196 'term_id' => __( 'Term ID', 'betterdocs' ),
1197 'id' => __( 'ID', 'betterdocs' ),
1198 'description' => __( 'Description', 'betterdocs' ),
1199 'parent' => __( 'Parent', 'betterdocs' ),
1200 'betterdocs_order' => __( 'BetterDocs Order', 'betterdocs' )
1201 ) ) ),
1202 'priority' => 1
1203 ),
1204 'terms_order' => array(
1205 'name' => 'terms_order',
1206 'type' => 'select',
1207 'label' => __( 'Terms Order', 'betterdocs' ),
1208 'default' => 'ASC',
1209 'options' => $this->normalize_options( array(
1210 'ASC' => 'Ascending',
1211 'DESC' => 'Descending'
1212 ) ),
1213 'priority' => 3,
1214 'rules' => Rules::includes( 'terms_orderby', 'betterdocs_order', true )
1215 ),
1216 'alphabetically_order_post' => array(
1217 'name' => 'alphabetically_order_post',
1218 'type' => 'select',
1219 'label' => __( 'Docs Order By', 'betterdocs' ),
1220 'default' => 'betterdocs_order',
1221 'options' => $this->normalize_options( array(
1222 'none' => __( 'No order', 'betterdocs' ),
1223 'ID' => __( 'Docs ID', 'betterdocs' ),
1224 'author' => __( 'Docs Author', 'betterdocs' ),
1225 'title' => __( 'Title', 'betterdocs' ),
1226 'date' => __( 'Date', 'betterdocs' ),
1227 'modified' => __( 'Last Modified Date', 'betterdocs' ),
1228 'parent' => __( 'Parent Id', 'betterdocs' ),
1229 'rand' => __( 'Random', 'betterdocs' ),
1230 'comment_count' => __( 'Comment Count', 'betterdocs' ),
1231 'menu_order' => __( 'Menu Order', 'betterdocs' ),
1232 'betterdocs_order' => __( 'BetterDocs Order', 'betterdocs' )
1233 ) ),
1234 'priority' => 4
1235 ),
1236 'docs_order' => array(
1237 'name' => 'docs_order',
1238 'type' => 'select',
1239 'label' => __( 'Docs Order', 'betterdocs' ),
1240 'default' => 'ASC',
1241 'options' => $this->normalize_options( array(
1242 'ASC' => 'Ascending',
1243 'DESC' => 'Descending'
1244 ) ),
1245 'priority' => 5,
1246 'rules' => Rules::includes( 'alphabetically_order_post', 'betterdocs_order', true )
1247 )
1248 )
1249 )
1250 )
1251 )
1252 )
1253 ),
1254 'layout_single_doc' => apply_filters( 'single_doc_setting_section', array(
1255 'id' => 'layout_single_doc',
1256 'name' => 'layout_single_doc',
1257 'type' => 'section',
1258 'label' => __( 'Single Doc', 'betterdocs' ),
1259 'priority' => 2,
1260 'fields' => array(
1261 'tab-nested-layout-2' => array(
1262 'id' => 'tab-nested-layout-2',
1263 'name' => 'tab_nested_layout_2',
1264 'label' => __( 'Single Doc', 'betterdocs' ),
1265 'classes' => 'tab-nested-layout',
1266 'type' => 'tab',
1267 'active' => 'layout_single_doc_general',
1268 'completionTrack' => true,
1269 'sidebar' => false,
1270 'save' => false,
1271 'title' => false,
1272 'config' => array(
1273 'active' => 'layout_single_doc_general',
1274 'sidebar' => false,
1275 'title' => false
1276 ),
1277 'submit' => array(
1278 'show' => false
1279 ),
1280 'step' => array(
1281 'show' => false
1282 ),
1283 'priority' => 20,
1284 'fields' => array(
1285 'layout_single_doc_general' => array(
1286 'id' => 'layout_single_doc_general',
1287 'name' => 'layout_single_doc_general',
1288 'type' => 'section',
1289 'label' => __( 'General', 'betterdocs' ),
1290 'priority' => 5,
1291 'fields' => array(
1292 'enable_post_title' => array(
1293 'name' => 'enable_post_title',
1294 'type' => 'toggle',
1295 'label' => __( 'Doc Title', 'betterdocs' ),
1296 'enable_disable_text_active' => true,
1297 'default' => 1,
1298 'priority' => 1
1299 ),
1300 'enable_sidebar_cat_list' => array(
1301 'name' => 'enable_sidebar_cat_list',
1302 'type' => 'toggle',
1303 'label' => __( 'Sidebar Category List', 'betterdocs' ),
1304 'enable_disable_text_active' => true,
1305 'default' => 1,
1306 'priority' => 2
1307 ),
1308 'enable_print_icon' => array(
1309 'name' => 'enable_print_icon',
1310 'type' => 'toggle',
1311 'label' => __( 'Print Icon', 'betterdocs' ),
1312 'enable_disable_text_active' => true,
1313 'default' => 1,
1314 'priority' => 3
1315 ),
1316 'enable_tags' => array(
1317 'name' => 'enable_tags',
1318 'type' => 'toggle',
1319 'label' => __( 'Tags', 'betterdocs' ),
1320 'enable_disable_text_active' => true,
1321 'default' => 1,
1322 'priority' => 4
1323 ),
1324 'show_last_update_time' => array(
1325 'name' => 'show_last_update_time',
1326 'type' => 'toggle',
1327 'label' => __( 'Last Update Time', 'betterdocs' ),
1328 'enable_disable_text_active' => true,
1329 'default' => 1,
1330 'priority' => 5
1331 ),
1332 'enable_navigation' => array(
1333 'name' => 'enable_navigation',
1334 'type' => 'toggle',
1335 'label' => __( 'Navigation', 'betterdocs' ),
1336 'enable_disable_text_active' => true,
1337 'default' => 1,
1338 'priority' => 6
1339 ),
1340 'enable_comment' => array(
1341 'name' => 'enable_comment',
1342 'type' => 'toggle',
1343 'label' => __( 'Comment', 'betterdocs' ),
1344 'enable_disable_text_active' => true,
1345 'default' => '',
1346 'priority' => 7
1347 ),
1348 'enable_credit' => array(
1349 'name' => 'enable_credit',
1350 'type' => 'toggle',
1351 'label' => __( 'Show Powered by BetterDocs', 'betterdocs' ),
1352 'enable_disable_text_active' => true,
1353 'default' => '',
1354 'priority' => 8
1355 ),
1356 'reaction_feedback_text' => array(
1357 'name' => 'reaction_feedback_text',
1358 'type' => 'text',
1359 'label' => __( 'Reaction Feedback Text', 'betterdocs' ),
1360 'default' => __( 'Thanks for your feedback.', 'betterdocs' ),
1361 'priority' => 9
1362 ),
1363 'enable_estimated_reading_time' => array(
1364 'name' => 'enable_estimated_reading_time',
1365 'type' => 'toggle',
1366 'label' => __( 'Estimated Reading Time', 'betterdocs' ),
1367 'enable_disable_text_active' => true,
1368 'default' => 0,
1369 'priority' => 10
1370 ),
1371 'estimated_reading_time_title' => array(
1372 'name' => 'estimated_reading_time_title',
1373 'type' => 'text',
1374 'label' => __( 'Estimated Reading Time Title', 'betterdocs' ),
1375 'default' => '',
1376 'priority' => 11,
1377 'rules' => Rules::is( 'enable_estimated_reading_time', true )
1378 ),
1379 'estimated_reading_time_text' => array(
1380 'name' => 'estimated_reading_time_text',
1381 'type' => 'text',
1382 'label' => __( 'Estimated Reading Time Text', 'betterdocs' ),
1383 'default' => __( 'min read', 'betterdocs' ),
1384 'priority' => 12,
1385 'rules' => Rules::is( 'enable_estimated_reading_time', true )
1386 ),
1387 'singular_estimated_reading_time_text' => array(
1388 'name' => 'singular_estimated_reading_time_text',
1389 'type' => 'text',
1390 'label' => __( 'Estimated Reading Time Text Singular', 'betterdocs' ),
1391 'default' => __( 'min read', 'betterdocs' ),
1392 'priority' => 13,
1393 'rules' => Rules::is( 'enable_estimated_reading_time', true )
1394 )
1395 )
1396 ),
1397 'layout_single_doc_TOC' => array(
1398 'id' => 'layout_single_doc_TOC',
1399 'name' => 'layout_single_doc_TOC',
1400 'type' => 'section',
1401 'label' => __( 'TOC', 'betterdocs' ),
1402 'priority' => 5,
1403 'fields' => array(
1404 'enable_toc' => array(
1405 'name' => 'enable_toc',
1406 'type' => 'toggle',
1407 'label' => __( 'Table of Contents', 'betterdocs' ),
1408 'enable_disable_text_active' => true,
1409 'default' => 1,
1410 'priority' => 1
1411 ),
1412 'toc_title' => array(
1413 'name' => 'toc_title',
1414 'type' => 'text',
1415 'label' => __( 'TOC Title', 'betterdocs' ),
1416 'default' => __( 'Table of Contents', 'betterdocs' ),
1417 'priority' => 2,
1418 'rules' => Rules::is( 'enable_toc', true )
1419
1420 ),
1421 'toc_hierarchy' => array(
1422 'name' => 'toc_hierarchy',
1423 'type' => 'toggle',
1424 'label' => __( 'TOC Hierarchy', 'betterdocs' ),
1425 'enable_disable_text_active' => true,
1426 'default' => 1,
1427 'priority' => 3,
1428 'rules' => Rules::is( 'enable_toc', true )
1429 ),
1430 'toc_list_number' => array(
1431 'name' => 'toc_list_number',
1432 'type' => 'toggle',
1433 'label' => __( 'TOC List Number', 'betterdocs' ),
1434 'enable_disable_text_active' => true,
1435 'default' => 1,
1436 'priority' => 4,
1437 'rules' => Rules::is( 'enable_toc', true )
1438 ),
1439 'toc_dynamic_title' => array(
1440 'name' => 'toc_dynamic_title',
1441 'type' => 'toggle',
1442 'label' => __( 'Show TOC Title in Anchor Links', 'betterdocs' ),
1443 'enable_disable_text_active' => true,
1444 'default' => 0,
1445 'priority' => 5,
1446 'rules' => Rules::is( 'enable_toc', true )
1447 ),
1448 'enable_sticky_toc' => array(
1449 'name' => 'enable_sticky_toc',
1450 'type' => 'toggle',
1451 'label' => __( 'Sticky TOC', 'betterdocs' ),
1452 'enable_disable_text_active' => true,
1453 'default' => 1,
1454 'priority' => 6,
1455 'rules' => Rules::is( 'enable_toc', true )
1456 ),
1457 'collapsible_toc_mobile' => array(
1458 'name' => 'collapsible_toc_mobile',
1459 'type' => 'toggle',
1460 'label' => __( 'Collapsible TOC on small devices', 'betterdocs' ),
1461 'enable_disable_text_active' => true,
1462 'default' => '',
1463 'priority' => 7,
1464 'rules' => Rules::is( 'enable_toc', true )
1465 ),
1466 'title_link_ctc' => array(
1467 'name' => 'title_link_ctc',
1468 'type' => 'toggle',
1469 'label' => __( 'Title Link Copy To Clipboard', 'betterdocs' ),
1470 'enable_disable_text_active' => true,
1471 'default' => 1,
1472 'priority' => 8
1473 ),
1474 'supported_heading_tag' => array(
1475 'name' => 'supported_heading_tag',
1476 'label' => __( 'TOC Supported Heading Tag', 'betterdocs' ),
1477 'type' => 'checkbox-select',
1478 'multiple' => true,
1479 'priority' => 10,
1480 'default' => array(
1481 '1',
1482 '2',
1483 '3',
1484 '4',
1485 '5',
1486 '6'
1487 ),
1488 'options' => $this->normalize_options( array(
1489 '1' => 'H1',
1490 '2' => 'H2',
1491 '3' => 'H3',
1492 '4' => 'H4',
1493 '5' => 'H5',
1494 '6' => 'H6'
1495 ) ),
1496 'priority' => 9,
1497 'rules' => Rules::is( 'enable_toc', true )
1498 ),
1499 'sticky_toc_offset' => array(
1500 'name' => 'sticky_toc_offset',
1501 'type' => 'number',
1502 'label' => __( 'Content Offset', 'betterdocs' ),
1503 'default' => 100,
1504 'priority' => 10,
1505 'label_subtitle' => __( 'content offset from top on scroll.', 'betterdocs' ),
1506 'rules' => Rules::is( 'enable_toc', true )
1507 )
1508 )
1509 ),
1510 'layout_single_doc_breadcrumb' => array(
1511 'id' => 'layout_single_doc_breadcrumb',
1512 'name' => 'layout_single_doc_breadcrumb',
1513 'type' => 'section',
1514 'label' => __( 'Breadcrumb', 'betterdocs' ),
1515 'priority' => 5,
1516 'fields' => array(
1517 'enable_breadcrumb' => array(
1518 'name' => 'enable_breadcrumb',
1519 'type' => 'toggle',
1520 'label' => __( 'Breadcrumb', 'betterdocs' ),
1521 'enable_disable_text_active' => true,
1522 'default' => 1,
1523 'priority' => 1
1524 ),
1525 'enable_breadcrumb_home_text' => array(
1526 'name' => 'enable_breadcrumb_home_text',
1527 'type' => 'toggle',
1528 'label' => __( 'Enable Breadcrumb Home Text', 'betterdocs' ),
1529 'enable_disable_text_active' => true,
1530 'default' => true,
1531 'priority' => 2,
1532 'rules' => Rules::is( 'enable_breadcrumb', true )
1533 ),
1534 'breadcrumb_home_text' => array(
1535 'name' => 'breadcrumb_home_text',
1536 'type' => 'text',
1537 'label' => __( 'Breadcrumb Home Text', 'betterdocs' ),
1538 'default' => __( 'Home', 'betterdocs' ),
1539 'priority' => 3,
1540 'rules' => Rules::logicalRule(
1541 array(
1542 Rules::is( 'enable_breadcrumb', true ),
1543 Rules::is( 'enable_breadcrumb_home_text', true )
1544 ),
1545 'and'
1546 )
1547 ),
1548 'breadcrumb_home_url' => array(
1549 'name' => 'breadcrumb_home_url',
1550 'type' => 'text',
1551 'label' => __( 'Breadcrumb Home URL', 'betterdocs' ),
1552 'priority' => 4,
1553 'default' => get_home_url(),
1554 'rules' => Rules::is( 'enable_breadcrumb', true )
1555 ),
1556 'enable_breadcrumb_category' => array(
1557 'name' => 'enable_breadcrumb_category',
1558 'type' => 'toggle',
1559 'label' => __( 'Category on Breadcrumb', 'betterdocs' ),
1560 'enable_disable_text_active' => true,
1561 'default' => 1,
1562 'priority' => 5,
1563 'rules' => Rules::is( 'enable_breadcrumb', true )
1564 ),
1565 'enable_breadcrumb_title' => array(
1566 'name' => 'enable_breadcrumb_title',
1567 'type' => 'toggle',
1568 'label' => __( 'Title on Breadcrumb', 'betterdocs' ),
1569 'enable_disable_text_active' => true,
1570 'default' => 1,
1571 'priority' => 6,
1572 'rules' => Rules::is( 'enable_breadcrumb', true )
1573 )
1574 )
1575 ),
1576 'layout_single_doc_email_feedback' => array(
1577 'id' => 'layout_single_doc_email_feedback',
1578 'name' => 'layout_single_doc_email_feedback',
1579 'type' => 'section',
1580 'label' => __( 'Email Feedback', 'betterdocs' ),
1581 'priority' => 5,
1582 'fields' => array(
1583 'email_feedback' => array(
1584 'name' => 'email_feedback',
1585 'type' => 'toggle',
1586 'label' => __( 'Email Feedback', 'betterdocs' ),
1587 'enable_disable_text_active' => true,
1588 'default' => 1,
1589 'priority' => 1
1590 ),
1591 'feedback_link_text' => array(
1592 'name' => 'feedback_link_text',
1593 'type' => 'text',
1594 'label' => __( 'Feedback Link Text', 'betterdocs' ),
1595 'default' => __( 'Still stuck? How can we help?', 'betterdocs' ),
1596 'priority' => 2,
1597 'rules' => Rules::is( 'email_feedback', true )
1598 ),
1599 'feedback_form_title' => array(
1600 'name' => 'feedback_form_title',
1601 'type' => 'text',
1602 'label' => __( 'Feedback Form Title', 'betterdocs' ),
1603 'default' => __( 'Still stuck? How can we help?', 'betterdocs' ),
1604 'priority' => 3,
1605 'rules' => Rules::is( 'email_feedback', true )
1606 ),
1607 'email_address' => array(
1608 'name' => 'email_address',
1609 'type' => 'text',
1610 'label' => __( 'Email Address', 'betterdocs' ),
1611 'default' => get_option( 'admin_email' ),
1612 'priority' => 4,
1613 'label_subtitle' => __( 'The email address where the Feedback form will be sent', 'betterdocs' ),
1614 'rules' => Rules::is( 'email_feedback', true )
1615 ),
1616 'feedback_url' => array(
1617 'name' => 'feedback_url',
1618 'type' => 'text',
1619 'label' => __( 'Feedback URL', 'betterdocs' ),
1620 'default' => '',
1621 'priority' => 5,
1622 'rules' => Rules::is( 'email_feedback', true )
1623 )
1624 )
1625 ),
1626 array(
1627 'id' => 'layout_single_doc_attachments',
1628 'name' => 'layout_single_doc_attachments',
1629 'type' => 'section',
1630 'label' => __( 'Attachments', 'betterdocs' ),
1631 'priority' => 6,
1632 'fields' => apply_filters( 'betterdocs_single_doc_attachments', array(
1633 'show_attachment' => array(
1634 'name' => 'show_attachment',
1635 'type' => 'toggle',
1636 'is_pro' => true,
1637 'priority' => 1,
1638 'label' => __( 'Show Attachment', 'betterdocs' ),
1639 'enable_disable_text_active' => true,
1640 'default' => false
1641 )
1642 ) )
1643 ),
1644 array(
1645 'id' => 'layout_single_doc_related_docs',
1646 'name' => 'layout_single_doc_related_docs',
1647 'type' => 'section',
1648 'label' => __( 'Related Docs', 'betterdocs' ),
1649 'priority' => 7,
1650 'fields' => apply_filters( 'betterdocs_single_doc_related_docs', array(
1651 'show_related_docs' => array(
1652 'name' => 'show_related_docs',
1653 'type' => 'toggle',
1654 'is_pro' => true,
1655 'priority' => 1,
1656 'label' => __( 'Show Related Docs', 'betterdocs' ),
1657 'enable_disable_text_active' => true,
1658 'default' => false
1659 )
1660 ) )
1661 )
1662 )
1663 )
1664 )
1665 ) ),
1666 'layout_archive_page' => array(
1667 'id' => 'layout_archive_page',
1668 'name' => 'layout_archive_page',
1669 'type' => 'section',
1670 'label' => __( 'Archive Page', 'betterdocs' ),
1671 'priority' => 3,
1672 'fields' => array(
1673 'enable_archive_sidebar' => array(
1674 'name' => 'enable_archive_sidebar',
1675 'type' => 'toggle',
1676 'label' => __( 'Sidebar Category List', 'betterdocs' ),
1677 'enable_disable_text_active' => true,
1678 'default' => 1,
1679 'priority' => 31
1680 ),
1681 'archive_nested_subcategory' => array(
1682 'name' => 'archive_nested_subcategory',
1683 'type' => 'toggle',
1684 'label' => __( 'Nested Subcategory', 'betterdocs' ),
1685 'enable_disable_text_active' => true,
1686 'default' => 1,
1687 'priority' => 32
1688 ),
1689 'archive_enable_pagination' => array(
1690 'name' => 'archive_enable_pagination',
1691 'type' => 'toggle',
1692 'label' => __( 'Enable Pagination', 'betterdocs' ),
1693 'enable_disable_text_active' => true,
1694 'default' => false,
1695 'priority' => 33
1696 )
1697 )
1698 )
1699 )
1700 ) )
1701 )
1702 )
1703 )
1704 ) ),
1705 'tab-design' => apply_filters( 'betterdocs_settings_tab_design', array(
1706 'id' => 'tab-design',
1707 'label' => __( 'Design', 'betterdocs' ),
1708 'priority' => 30,
1709 'fields' => array(
1710 'title-design' => array(
1711 'name' => 'title-design-tab',
1712 'type' => 'section',
1713 'label' => __( 'Design', 'betterdocs' ),
1714 'priority' => 30,
1715 'fields' => $this->design_tab()
1716 )
1717 )
1718 ) ),
1719 'tab-shortcodes' => apply_filters( 'betterdocs_settings_tab_shortcodes', array(
1720 'label' => __( 'Shortcodes', 'betterdocs' ),
1721 'id' => 'tab-shortcodes',
1722 'classes' => 'tab-shortcodes',
1723 'priority' => 40,
1724 'fields' => array(
1725 'title-shortcodes' => array(
1726 'name' => 'title-shortcodes-tab',
1727 'type' => 'section',
1728 'label' => __( 'Shortcodes', 'betterdocs' ),
1729 'priority' => 40,
1730 'searchable' => true,
1731 'searchPlaceholder' => __( 'Search for shortcode', 'betterdocs' ),
1732 'searchNotFoundMessage' => '<img src="' . betterdocs()->assets->icon( 'not-found.svg', true ) . '"/><p>' . __( 'No Shortcodes Found with these keywords', 'betterdocs' ) . '</p>',
1733 'fields' => apply_filters( 'betterdocs_shortcode_fields', array(
1734 'search_form' => array(
1735 'name' => 'search_form',
1736 'type' => 'copy-to-clipboard',
1737 'label' => __( 'Search Form', 'betterdocs' ),
1738 'default' => '[betterdocs_search_form]',
1739 'readOnly' => true,
1740 'priority' => 1,
1741 'description' => __( '[betterdocs_search_form placeholder="Search..." heading="Heading" subheading="Subheading" category_search="true" search_button="true" popular_search="true"]', 'betterdocs' ),
1742 'descriptionLabel' => __( 'Example with parameters:', 'betterdocs' ),
1743 'descriptionCopyable' => true
1744 ),
1745 'feedback_form' => array(
1746 'name' => 'feedback_form',
1747 'type' => 'copy-to-clipboard',
1748 'label' => __( 'Feedback Form', 'betterdocs' ),
1749 'default' => '[betterdocs_feedback_form]',
1750 'readOnly' => true,
1751 'priority' => 2,
1752 'description' => __( '[betterdocs_feedback_form button_text="Send"]', 'betterdocs' ),
1753 'descriptionLabel' => __( 'Example with parameters:', 'betterdocs' ),
1754 'descriptionCopyable' => true
1755 ),
1756 'category_grid' => array(
1757 'name' => 'category_grid',
1758 'type' => 'copy-to-clipboard',
1759 'label' => __( 'Category Grid- Layout 1', 'betterdocs' ),
1760 'default' => '[betterdocs_category_grid]',
1761 'readOnly' => true,
1762 'priority' => 3,
1763 'description' => __( '[betterdocs_category_grid show_count="true" show_icon="true" masonry="true" column="3" posts_per_page="5" nested_subcategory="true" terms="term_ID, term_ID" terms_orderby="" terms_order="" multiple_knowledge_base="" kb_slug="" title_tag="h2" orderby="" order="" ]', 'betterdocs' ),
1764 'descriptionLabel' => __( 'Example with parameters:', 'betterdocs' ),
1765 'descriptionCopyable' => true
1766 ),
1767 'category_box' => array(
1768 'name' => 'category_box',
1769 'type' => 'copy-to-clipboard',
1770 'label' => __( 'Category Box- Layout 2', 'betterdocs' ),
1771 'default' => '[betterdocs_category_box]',
1772 'readOnly' => true,
1773 'priority' => 4,
1774 'description' => __( '[betterdocs_category_box orderby="" column="" nested_subcategory="" terms="" terms_orderby="" show_icon="" kb_slug="" title_tag="h2" multiple_knowledge_base="false" border_bottom="false"]', 'betterdocs' ),
1775 'descriptionLabel' => __( 'Example with parameters:', 'betterdocs' ),
1776 'descriptionCopyable' => true
1777 ),
1778 'category_list' => array(
1779 'name' => 'category_list',
1780 'type' => 'copy-to-clipboard',
1781 'label' => __( 'Category List', 'betterdocs' ),
1782 'default' => '[betterdocs_category_list]',
1783 'readOnly' => true,
1784 'priority' => 5,
1785 'description' => __( '[betterdocs_category_list orderby="" order="" posts_per_page="" nested_subcategory="" terms="" terms_orderby="" terms_order="" kb_slug="" multiple_knowledge_base="false" title_tag="h2"]', 'betterdocs' ),
1786 'descriptionLabel' => __( 'Example with parameters:', 'betterdocs' ),
1787 'descriptionCopyable' => true
1788 ),
1789 'faq_modern_layout' => array(
1790 'name' => 'faq_modern_layout',
1791 'type' => 'copy-to-clipboard',
1792 'label' => __( 'FAQ Layout - 1', 'betterdocs' ),
1793 'default' => '[betterdocs_faq_list_modern]',
1794 'readOnly' => true,
1795 'priority' => 13,
1796 'description' => __( '[betterdocs_faq_list_modern groups="group_id" class="" group_exclude="group_id" faq_heading="Frequently Asked Questions"]', 'betterdocs' ),
1797 'descriptionLabel' => __( 'Example with parameters:', 'betterdocs' ),
1798 'descriptionCopyable' => true
1799 ),
1800 'faq_classic_layout' => array(
1801 'name' => 'faq_classic_layout',
1802 'type' => 'copy-to-clipboard',
1803 'label' => __( 'FAQ Layout - 2', 'betterdocs' ),
1804 'default' => '[betterdocs_faq_list_classic]',
1805 'readOnly' => true,
1806 'priority' => 14,
1807 'description' => __( '[betterdocs_faq_list_classic groups="group_id" class="" group_exclude="group_id" faq_heading="Frequently Asked Questions"]', 'betterdocs' ),
1808 'descriptionLabel' => __( 'Example with parameters:', 'betterdocs' ),
1809 'descriptionCopyable' => true
1810 ),
1811 'betterdocs_faq_list_layout_3' => array(
1812 'name' => 'betterdocs_faq_list_layout_3',
1813 'type' => 'copy-to-clipboard',
1814 'label' => __( 'FAQ Layout - 3', 'betterdocs' ),
1815 'default' => '[betterdocs_faq_list_layout_3 class="faq-doc betterdocs-faq-layout-3"]',
1816 'readOnly' => true,
1817 'priority' => 15,
1818 'description' => __( '[betterdocs_faq_list_layout_3 class="faq-doc betterdocs-faq-layout-3" groups="group_id" class="" group_exclude="group_id" faq_heading="Frequently Asked Questions"]', 'betterdocs' ),
1819 'descriptionLabel' => __( 'Example with parameters:', 'betterdocs' ),
1820 'descriptionCopyable' => true
1821 ),
1822 'betterdocs_faq_tab' => array(
1823 'name' => 'betterdocs_faq_tab',
1824 'type' => 'copy-to-clipboard',
1825 'label' => __( 'FAQ Layout - 4', 'betterdocs' ),
1826 'default' => '[betterdocs_faq_tab class="faq-doc betterdocs-faq-layout-4"]',
1827 'readOnly' => true,
1828 'priority' => 16,
1829 'description' => __( '[betterdocs_faq_tab class="faq-doc betterdocs-faq-layout-4" groups="group_id" group_exclude="group_id" faq_heading="Frequently Asked Questions"]', 'betterdocs' ),
1830 'descriptionLabel' => __( 'Example with parameters:', 'betterdocs' ),
1831 'descriptionCopyable' => true
1832 )
1833 ) )
1834 )
1835 )
1836 ) ),
1837 'tab-advance-settings' => apply_filters( 'betterdocs_settings_tab_advance', array(
1838 'id' => 'tab-advance-settings',
1839 'label' => __( 'Access & Restrictions', 'betterdocs' ),
1840 'classes' => 'tab-layout',
1841 'priority' => 50,
1842 'fields' => array(
1843 'title_advance_settings' => array(
1844 'name' => 'title-advance-settings-tab',
1845 'type' => 'section',
1846 'label' => __( 'Access & Restrictions', 'betterdocs' ),
1847 'priority' => 50,
1848 'fields' => array(
1849 'tab-advanced-settings' => array(
1850 'id' => 'tab-advanced-settings',
1851 'name' => 'tab-advanced-settings',
1852 'label' => __( 'Access & Restrictions', 'betterdocs' ),
1853 'classes' => 'tab-layout',
1854 'type' => 'tab',
1855 'active' => 'global_role_management',
1856 'completionTrack' => true,
1857 'sidebar' => false,
1858 'save' => false,
1859 'title' => false,
1860 'config' => array(
1861 'active' => '',
1862 'sidebar' => false,
1863 'title' => false
1864 ),
1865 'submit' => array(
1866 'show' => false
1867 ),
1868 'step' => array(
1869 'show' => false
1870 ),
1871 'priority' => 50,
1872 'fields' => array(
1873 'global_role_management' => array(
1874 'id' => 'global_role_management',
1875 'name' => 'global_role_management',
1876 'type' => 'section',
1877 'label' => __( 'Global Role Management', 'betterdocs' ),
1878 'priority' => 1,
1879 'fields' => array(
1880 'article_roles' => array(
1881 'name' => 'article_roles',
1882 'type' => 'checkbox-select',
1883 'label' => __( 'Who Can Write Docs?', 'betterdocs' ),
1884 'priority' => 1,
1885 'multiple' => true,
1886 'search' => true,
1887 'is_pro' => true,
1888 'default' => array( 'administrator' ),
1889 'options' => $wp_roles
1890 ),
1891 'settings_roles' => array(
1892 'name' => 'settings_roles',
1893 'type' => 'checkbox-select',
1894 'label' => __( 'Who Can Edit Settings?', 'betterdocs' ),
1895 'priority' => 2,
1896 'multiple' => true,
1897 'is_pro' => true,
1898 'search' => true,
1899 'default' => array( 'administrator' ),
1900 'options' => $wp_roles
1901 ),
1902 'analytics_roles' => array(
1903 'name' => 'analytics_roles',
1904 'type' => 'checkbox-select',
1905 'label' => __( 'Who Can Check Analytics?', 'betterdocs' ),
1906 'priority' => 3,
1907 'multiple' => true,
1908 'is_pro' => true,
1909 'search' => true,
1910 'default' => array( 'administrator' ),
1911 'options' => $wp_roles
1912 ),
1913 'faq_roles' => array(
1914 'name' => 'faq_roles',
1915 'type' => 'checkbox-select',
1916 'label' => __( 'Who Can Check FAQ Builder?', 'betterdocs' ),
1917 'priority' => 4,
1918 'multiple' => true,
1919 'is_pro' => true,
1920 'search' => true,
1921 'default' => array( 'administrator' ),
1922 'options' => $wp_roles
1923 )
1924
1925 )
1926 ),
1927 'internal_knowledge_base' => array(
1928 'id' => 'internal_knowledge_base',
1929 'name' => 'internal_knowledge_base',
1930 'type' => 'section',
1931 'label' => __( 'Internal Knowledge Base', 'betterdocs' ),
1932 'priority' => 1,
1933 'fields' => apply_filters( 'betterdocs_settings_content_restriction_fields', array(
1934 'enable_content_restriction' => array(
1935 'name' => 'enable_content_restriction',
1936 'type' => 'toggle',
1937 'is_pro' => true,
1938 'priority' => 5,
1939 'label' => __( 'Internal Knowledge Base', 'betterdocs' ),
1940 'enable_disable_text_active' => true,
1941 'default' => array( 'all' )
1942 ),
1943 'internal_knowledge_base_type' => array(
1944 'name' => 'internal_knowledge_base_type',
1945 'type' => 'radio-card',
1946 'label' => __( 'Choose a Rule Type', 'betterdocs' ),
1947 'label_subtitle' => __( 'Choose a rule type: apply access globally (Basic), or configure detailed restrictions (Advanced) for docs, categories, and KBs.', 'betterdocs' ),
1948 'priority' => 6,
1949 'multiple' => true,
1950 'search' => true,
1951 'default' => array( 'all' ),
1952 'placeholder' => __( 'Select any', 'betterdocs' ),
1953 'options' => array(
1954 array(
1955 'label' => 'Basic',
1956 'value' => 'basic'
1957 ),
1958 array(
1959 'label' => 'Advanced',
1960 'value' => 'advanced'
1961 )
1962 ),
1963 'filterValue' => 'all',
1964 'rules' => Rules::is( 'enable_content_restriction', true )
1965 ),
1966 'content_visibility' => array(
1967 'name' => 'content_visibility',
1968 'type' => 'checkbox-select',
1969 'label' => __( 'Restrict Access to', 'betterdocs' ),
1970 'label_subtitle' => __( 'Only selected User Roles will be able to view your Knowledge Base', 'betterdocs' ),
1971 'is_pro' => true,
1972 'priority' => 7,
1973 'multiple' => true,
1974 'search' => true,
1975 'default' => array( 'all' ),
1976 'placeholder' => __( 'Select any', 'betterdocs' ),
1977 'options' => $roles_for_ikb,
1978 'rules' => Rules::logicalRule(
1979 array(
1980 Rules::is( 'enable_content_restriction', true ),
1981 Rules::is( 'internal_knowledge_base_type', 'basic' )
1982 ),
1983 'and'
1984 ),
1985 'filterValue' => 'all'
1986 ),
1987 'restrict_template' => array(
1988 'name' => 'restrict_template',
1989 'type' => 'checkbox-select',
1990 'label' => __( 'Restriction on Docs', 'betterdocs' ),
1991 'label_subtitle' => __( 'Selected Docs pages will be restricted', 'betterdocs' ),
1992 'is_pro' => true,
1993 'priority' => 8,
1994 'multiple' => true,
1995 'search' => true,
1996 'default' => array( 'all' ),
1997 'placeholder' => __( 'Select any', 'betterdocs' ),
1998 'options' => $this->get_texanomy(),
1999 'rules' => Rules::logicalRule(
2000 array(
2001 Rules::is( 'enable_content_restriction', true ),
2002 Rules::is( 'internal_knowledge_base_type', 'basic' )
2003 ),
2004 'and'
2005 ),
2006 'filterValue' => 'all'
2007 ),
2008 'restrict_category' => array(
2009 'name' => 'restrict_category',
2010 'type' => 'checkbox-select',
2011 'label' => __( 'Restriction on Docs Categories', 'betterdocs' ),
2012 'label_subtitle' => __( 'Selected Docs categories will be restricted', 'betterdocs' ),
2013 'is_pro' => true,
2014 'priority' => 9,
2015 'multiple' => true,
2016 'search' => true,
2017 'default' => array( 'all' ),
2018 'placeholder' => __( 'Select any', 'betterdocs' ),
2019 'options' => $this->get_terms( 'doc_category' ),
2020 'rules' => Rules::logicalRule(
2021 array(
2022 Rules::is( 'enable_content_restriction', true ),
2023 Rules::is( 'internal_knowledge_base_type', 'basic' )
2024 ),
2025 'and'
2026 ),
2027 'filterValue' => 'all'
2028 ),
2029 'restricted_redirect_url' => array(
2030 'name' => 'restricted_redirect_url',
2031 'type' => 'text',
2032 'label' => __( 'Redirect URL', 'betterdocs' ),
2033 'label_subtitle' => __( 'Set a custom URL to redirect users without permissions when they try to access internal knowledge base. By default, restricted content will redirect to the "404 not found" page', 'betterdocs' ),
2034 'default' => '',
2035 'placeholder' => 'https://',
2036 'is_pro' => true,
2037 'priority' => 20,
2038 'rules' => Rules::logicalRule(
2039 array(
2040 Rules::is( 'enable_content_restriction', true )
2041 ),
2042 'and'
2043 )
2044 )
2045 ) )
2046 )
2047 )
2048 )
2049 )
2050 )
2051 )
2052 ) ),
2053 'tab-email-reporting' => apply_filters( 'betterdocs_settings_tab_email_reporting', array(
2054 'id' => 'tab-email-reporting',
2055 'label' => __( 'Email Reporting', 'betterdocs' ),
2056 'priority' => 60,
2057 'fields' => array(
2058 'title-email-reporting' => array(
2059 'name' => 'title-email-reporting-tab',
2060 'type' => 'section',
2061 'label' => __( 'Email Reporting', 'betterdocs' ),
2062 'priority' => 60,
2063 'fields' => array(
2064 'enable_reporting' => array(
2065 'name' => 'enable_reporting',
2066 'label' => __( 'Email Reporting', 'betterdocs' ),
2067 'enable_disable_text_active' => true,
2068 'type' => 'toggle',
2069 'priority' => 1,
2070 'default' => 0
2071 ),
2072 'reporting_frequency' => apply_filters( 'betterdocs_reporting_frequency_settings', array(
2073 'name' => 'reporting_frequency',
2074 'type' => 'select',
2075 'label' => __( 'Reporting Frequency', 'betterdocs' ),
2076 'default' => 'betterdocs_weekly',
2077 'priority' => 2,
2078 'is_pro' => true,
2079 'options' => $this->normalize_options( array(
2080 'betterdocs_daily' => __( 'Once Daily', 'betterdocs' ),
2081 'betterdocs_weekly' => __( 'Once Weekly', 'betterdocs' ),
2082 'betterdocs_monthly' => __( 'Once Monthly', 'betterdocs' )
2083 ) ),
2084 'rules' => Rules::is( 'enable_reporting', true )
2085 ) ),
2086 'reporting_day' => array(
2087 'name' => 'reporting_day',
2088 'type' => 'select',
2089 'label' => __( 'Reporting Day', 'betterdocs' ),
2090 'default' => 'monday',
2091 'rules' => Rules::logicalRule( array(
2092 Rules::is( 'enable_reporting', true ),
2093 Rules::is( 'reporting_frequency', 'betterdocs_weekly' )
2094 ), 'and' ),
2095 'priority' => 3,
2096 'options' => $this->normalize_options( array(
2097 'sunday' => __( 'Sunday', 'betterdocs' ),
2098 'monday' => __( 'Monday', 'betterdocs' ),
2099 'tuesday' => __( 'Tuesday', 'betterdocs' ),
2100 'wednesday' => __( 'Wednesday', 'betterdocs' ),
2101 'thursday' => __( 'Thursday', 'betterdocs' ),
2102 'friday' => __( 'Friday', 'betterdocs' ),
2103 'saturday' => __( 'Saturday', 'betterdocs' )
2104 ) ),
2105 'label_subtitle' => __( 'This is only applicable for the “Weekly” report', 'betterdocs' )
2106 ),
2107 'select_reporting_data' => apply_filters( 'betterdocs_select_reporting_data_settings', array(
2108 'name' => 'select_reporting_data',
2109 'type' => 'checkbox-select',
2110 'label' => __( 'Select Reporting Data', 'betterdocs' ),
2111 'priority' => 4,
2112 'multiple' => true,
2113 'options' => $this->normalize_options( array(
2114 'overview' => 'Overview',
2115 'top-docs' => 'Top Docs',
2116 'most-search' => 'Most Searched Keywords'
2117 ) ),
2118 'default' => array( 'overview', 'top-docs', 'most-search' ),
2119 'is_pro' => true,
2120 'rules' => Rules::is( 'enable_reporting', true )
2121 ) ),
2122 'reporting_email' => array(
2123 'name' => 'reporting_email',
2124 'type' => 'text',
2125 'label' => __( 'Reporting Email', 'betterdocs' ),
2126 'default' => get_option( 'admin_email' ),
2127 'priority' => 5,
2128 'rules' => Rules::is( 'enable_reporting', true )
2129 ),
2130 'reporting_subject' => apply_filters( 'betterdocs_reporting_subject_settings', array(
2131 'name' => 'reporting_subject',
2132 'type' => 'textarea',
2133 'label' => __( 'Reporting Email Subject', 'betterdocs' ),
2134 'default' => wp_sprintf( // Translators: %s is replaced with the website name.
2135 __( 'Your Documentation Performance of %s Website', 'betterdocs' ), get_bloginfo( 'name' ) ),
2136 'priority' => 6,
2137 'is_pro' => true,
2138 'rules' => Rules::is( 'enable_reporting', true )
2139 ) ),
2140 'test_report' => array(
2141 'name' => 'test_report',
2142 'label' => __( 'Reporting Test', 'betterdocs' ),
2143 'text' => __( 'Test Report', 'betterdocs' ),
2144 'type' => 'button',
2145 'priority' => 7,
2146 'rules' => Rules::is( 'enable_reporting', true ),
2147 'ajax' => array(
2148 'on' => 'click',
2149 'api' => '/betterdocs/v1/reporting-test',
2150 'data' => array(
2151 'enable_reporting' => '@enable_reporting',
2152 'select_reporting_data' => '@select_reporting_data',
2153 'reporting_subject' => '@reporting_subject',
2154 'reporting_email' => '@reporting_email',
2155 'reporting_day' => '@reporting_day',
2156 'reporting_frequency' => '@reporting_frequency'
2157 ),
2158 'swal' => array(
2159 'text' => __( 'Successfully Sent a Test Report in Your Email.', 'betterdocs' ),
2160 'icon' => 'success',
2161 'autoClose' => 2000
2162 )
2163 )
2164 )
2165 )
2166 )
2167 )
2168 ) ),
2169 'tab-git-sync' => apply_filters( 'betterdocs_settings_tab_github_integration', array(
2170 'id' => 'tab-git-sync',
2171 'label' => __( 'Git Sync', 'betterdocs' ),
2172 'priority' => 60,
2173 'fields' => array(
2174 'title-git-sync' => array(
2175 'name' => 'title-git-sync-tab',
2176 'type' => 'section',
2177 'label' => __( 'Git Sync Settings', 'betterdocs' ),
2178 'priority' => 60,
2179 'fields' => array(
2180 'enable_git_integration' => array(
2181 'name' => 'enable_git_integration',
2182 'type' => 'toggle',
2183 'label' => __( 'Enable Git Sync', 'betterdocs' ),
2184 'enable_disable_text_active' => true,
2185 'default' => false,
2186 'priority' => 1,
2187 'is_pro' => true,
2188 'label_subtitle' => __( 'Enable bidirectional synchronization between BetterDocs and Git repositories', 'betterdocs' )
2189 )
2190 )
2191 )
2192 )
2193 ) ),
2194 'tab-instant-answer' => apply_filters( 'betterdocs_settings_tab_instant_answer', array(
2195 'id' => 'tab-instant-answer',
2196 'name' => 'tab-instant-answer',
2197 'type' => 'section',
2198 'label' => __( 'Instant Answer', 'betterdocs' ),
2199 'save' => false,
2200 'priority' => 70,
2201 'fields' => array(
2202 'title-instant-answer' => array(
2203 'name' => 'title-instant-answer-tab',
2204 'type' => 'section',
2205 'label' => __( 'Instant Answer', 'betterdocs' ),
2206 'priority' => 80,
2207 'save' => false,
2208 'showSubmit' => false,
2209 'fields' => apply_filters( 'betterdocs_instant_answer_fields', array(
2210 'enable_disable_wrapper' => array(
2211 'name' => 'enable_disable_wrapper',
2212 'type' => 'section',
2213 'priority' => 0,
2214 'save' => false,
2215 'fields' => array(
2216 'enable_disable' => array(
2217 'name' => 'enable_disable',
2218 'type' => 'toggle',
2219 'priority' => 100,
2220 'description' => __( 'Enable Instant Answer', 'betterdocs' ),
2221 'enable_disable_text_active' => false,
2222 'default' => false,
2223 'is_pro' => true
2224 )
2225 )
2226 )
2227 ) )
2228 )
2229 )
2230 ) ),
2231 'tab-betterdocs-ai' => array(
2232 'id' => 'tab-betterdocs-ai',
2233 'name' => 'tab-betterdocs-ai',
2234 'type' => 'section',
2235 'label' => __( 'AI Content Suite', 'betterdocs' ),
2236 'priority' => 74,
2237 'fields' => array(
2238 'sections-betterdocs-ai' => array(
2239 'name' => 'sections-betterdocs-ai',
2240 'type' => 'section',
2241 'label' => __( 'AI Content Suite', 'betterdocs' ),
2242 'priority' => 10,
2243 'fields' => array(
2244 'all-betterdocs-ai' => array(
2245 'id' => 'all-tab-betterdocs-ai',
2246 'name' => 'all-tab-betterdocs-ai',
2247 'label' => __( 'AI Content Suite Settings', 'betterdocs' ),
2248 'classes' => 'tab-layout',
2249 'type' => 'tab',
2250 'active' => 'open-ai-settings',
2251 'completionTrack' => true,
2252 'sidebar' => false,
2253 'save' => false,
2254 'title' => false,
2255 'config' => array(
2256 'active' => 'open-ai-settings',
2257 'sidebar' => false,
2258 'title' => false
2259 ),
2260 'submit' => array(
2261 'show' => false
2262 ),
2263 'step' => array(
2264 'show' => false
2265 ),
2266 'priority' => 20,
2267 'fields' => apply_filters(
2268 'betterdocs_migration_tab_sections',
2269 array(
2270 'open-ai-settings' => array(
2271 'id' => 'open-ai-settings',
2272 'name' => 'open-ai-settings',
2273 'type' => 'section',
2274 'label' => __( 'API Settings', 'betterdocs' ),
2275 'priority' => 1,
2276 'fields' => array(
2277 'ai_autowrite_api_key' => array(
2278 'name' => 'ai_autowrite_api_key',
2279 '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/' ) ),
2282 'default' => '',
2283 'priority' => 1
2284 )
2285 )
2286 ),
2287 'write-with-ai' => array(
2288 'id' => 'write-with-ai',
2289 'name' => 'write-with-ai',
2290 'type' => 'section',
2291 'label' => __( 'Write with AI', 'betterdocs' ),
2292 'priority' => 5,
2293 'fields' => array(
2294 'enable_write_with_ai' => array(
2295 'name' => 'enable_write_with_ai',
2296 'type' => 'toggle',
2297 'priority' => 0,
2298 'label' => __( 'Write Docs with AI', 'betterdocs' ),
2299 'label_subtitle' => __( 'Generate AI based Documentation in your Gutenberg Editor', 'betterdocs' ),
2300 'enable_disable_text_active' => true,
2301 'default' => true
2302 ),
2303 'enable_faq_write_with_ai' => array(
2304 'name' => 'enable_faq_write_with_ai',
2305 'type' => 'toggle',
2306 'priority' => 5,
2307 'label' => __( 'Write FAQ with AI', 'betterdocs' ),
2308 'label_subtitle' => __( 'Generate AI based FAQ in your Editor', 'betterdocs' ),
2309 'enable_disable_text_active' => true,
2310 'default' => true
2311 ),
2312 'enable_glossaries_write_with_ai' => array(
2313 'name' => 'enable_glossaries_write_with_ai',
2314 'type' => 'toggle',
2315 'priority' => 6,
2316 'label' => __( 'Write Glossaries with AI', 'betterdocs' ),
2317 'label_subtitle' => __( 'Generate AI based Glossary definitions from the Glossaries admin page', 'betterdocs' ),
2318 'enable_disable_text_active' => true,
2319 'default' => true
2320 ),
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 'ai_autowrite_max_token' => array(
2341 'name' => 'ai_autowrite_max_token',
2342 'type' => 'min_token_number',
2343 'label' => __( 'Set Max Tokens', 'betterdocs' ),
2344 'label_subtitle' => sprintf( // translators: %s is a link to more information about token limits.
2345 __( '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 'default' => 2500,
2347 'priority' => 10,
2348 'model_field' => 'write_with_ai_model',
2349 'context' => 'write_with_ai',
2350 'min_token_map' => AIHelper::get_min_tokens_map( 'write_with_ai' ),
2351 'classes' => 'wprf-type-text'
2352 )
2353 )
2354 ),
2355 'article-summary' => array(
2356 'id' => 'article-summary',
2357 'name' => 'article-summary',
2358 'type' => 'section',
2359 'label' => __( 'AI Doc Summarizer', 'betterdocs' ),
2360 'priority' => 10,
2361 'fields' => array(
2362 'enable_article_summary' => array(
2363 'name' => 'enable_article_summary',
2364 'type' => 'toggle',
2365 'priority' => 1,
2366 'label' => __( 'AI Powered Doc Summarizer', 'betterdocs' ),
2367 'label_subtitle' => __( 'Enable AI-powered article summaries on single doc pages. Requires OpenAI API key.', 'betterdocs' ),
2368 'enable_disable_text_active' => true,
2369 'default' => false
2370 ),
2371 'article_summary_max_token' => array(
2372 'name' => 'article_summary_max_token',
2373 'type' => 'min_token_number',
2374 'label' => __( 'Set Max Tokens', 'betterdocs' ),
2375 'label_subtitle' => sprintf( // translators: %s is a link to more information about token limits.
2376 __( '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 'default' => 1500,
2378 'priority' => 5,
2379 'model_field' => 'article_summary_model',
2380 'context' => 'article_summary',
2381 'min_token_map' => AIHelper::get_min_tokens_map( 'article_summary' ),
2382 '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 )
2403 )
2404 )
2405 )
2406 )
2407 )
2408 )
2409 )
2410 )
2411 ),
2412 'tab-ai-chatbot' => apply_filters( 'betterdocs_settings_tab_ai_chatbot', array(
2413 'id' => 'tab-ai-chatbot',
2414 'name' => 'tab-ai-chatbot',
2415 'type' => 'section',
2416 'label' => __( 'AI Chatbot', 'betterdocs' ),
2417 'submit' => array(
2418 'show' => false
2419 ),
2420 'save' => false,
2421 'priority' => 75,
2422 'fields' => array(
2423 'title-ai-chatbot-tab' => array(
2424 'name' => 'title-ai-chatbot-tab',
2425 'type' => 'section',
2426 'label' => __( 'AI Chatbot', 'betterdocs' ),
2427 'priority' => 80,
2428 'submit' => array(
2429 'show' => false
2430 ),
2431 'save' => false,
2432 'fields' => apply_filters( 'betterdocs_ai_chatbot_fields', array(
2433 'ai_chatbot_fields' => array(
2434 'name' => 'ai_chatbot_fields',
2435 'type' => 'section',
2436 'priority' => 0,
2437 'fields' => array(
2438 'enable_ai_chatbot' => array(
2439 'name' => 'enable_ai_chatbot',
2440 'label' => __( 'AI Chatbot', 'betterdocs' ),
2441 'description' => __( 'Enable AI Chatbot', 'betterdocs' ),
2442 'type' => 'toggle',
2443 'priority' => 5,
2444 'default' => 0,
2445 'is_pro' => true,
2446 'is_license_active' => false,
2447 'disabled' => ! betterdocs()->is_pro_active() || ! defined( 'BETTERDOCS_CHATBOT_FILE' )
2448 ),
2449 'instant_answer_warning' => array(
2450 'name' => 'instant_answer_warning',
2451 'type' => 'html',
2452 'priority' => 6,
2453 'html' => sprintf(
2454 '<div class="betterdocs-ia-warning" style="margin:8px 0 0;padding:12px 16px;background:#fffaeb;border-left:4px solid #fdb022;border-radius:4px;display:flex;align-items:flex-start;gap:10px;"><span style="font-size:18px;line-height:1;flex-shrink:0;color:#b54708;">&#9888;</span><div style="font-size:13px;color:#202223;line-height:1.5;">%1$s<br><a href="?page=betterdocs-settings&amp;tab=tab-instant-answer" style="display:inline-block;margin-top:6px;color:#5a6bff;text-decoration:none;font-weight:500;">%2$s &rarr;</a></div></div>',
2455 esc_html__( 'Instant Answer is currently disabled. The AI Chatbot won\'t appear on your site until Instant Answer is enabled.', 'betterdocs' ),
2456 esc_html__( 'Enable Instant Answer', 'betterdocs' )
2457 ),
2458 'rules' => Rules::logicalRule( array(
2459 Rules::is( 'enable_ai_chatbot', true ),
2460 Rules::is( 'enable_disable', false ),
2461 ), 'and' ),
2462 )
2463 )
2464 )
2465 ) )
2466 )
2467 )
2468 ) )
2469 ) ),
2470 'TAB_AI_CHATBOT' => get_option( 'enable_ai_chatbot' ),
2471 'CHATBOT_LICENSE' => get_option( 'betterdocs_chatbot_software__license_status' ),
2472 'PRO_ACTIVE' => is_plugin_active( 'betterdocs-pro/betterdocs-pro.php' ),
2473 'PRO_LICENSE' => get_option( 'betterdocs_pro_software__license_status' )
2474 );
2475
2476 $settings = array_merge( $settings, $this->chatbot_active_localize() );
2477
2478 return apply_filters( 'betterdocs_settings_args', $settings );
2479 }
2480
2481 /**
2482 * @return array
2483 */
2484 public function chatbot_active_localize() {
2485 $chatbot_active = is_plugin_active( 'betterdocs-ai-chatbot/betterdocs-ai-chatbot.php' );
2486 $chatbot_license_valid = get_option( 'betterdocs_chatbot_software__license_status' ) === 'valid';
2487 $chatbot_enabled = $this->get( 'enable_ai_chatbot', false );
2488
2489 // AI Search Suggestions are enabled if all conditions are met
2490 $ai_search_suggestions_enabled = betterdocs()->helper->is_ai_chatbot_enabled();
2491
2492 return array(
2493 'CHATBOT_ACTIVE' => $chatbot_active,
2494 'CHATBOT_LICENSE_VALID' => $chatbot_license_valid,
2495 'CHATBOT_ENABLED' => $chatbot_enabled,
2496 'AI_SEARCH_SUGGESTIONS_ENABLED' => $ai_search_suggestions_enabled
2497 );
2498 }
2499
2500 /**
2501 * Call This Function As Helper, When Pro Is Deactivated, To Be Used As Settings Default Values, When Betterdocs Pro Is Deactivated
2502 *
2503 * @return array
2504 */
2505 public function pro_settings_default_values() {
2506 return array(
2507 'multiple_kb' => false,
2508 'enable_glossaries' => false,
2509 'enable_encyclopedia' => false,
2510 'analytics_from' => false,
2511 'unique_visitor_count' => false,
2512 'exclude_bot_analytics' => false,
2513 'show_attachment' => false,
2514 'show_related_docs' => false,
2515 'advance_search' => false,
2516 'child_category_exclude' => false,
2517 'kb_based_search' => false,
2518 'enable_disable' => false,
2519 'enable_content_restriction' => false
2520 );
2521 }
2522
2523 public function import_export_settings( $settings ) {
2524 if ( ! current_user_can( 'import' ) ) {
2525 return $settings;
2526 }
2527
2528 $settings[ 'tab-import-export' ] = apply_filters( 'betterdocs_settings_tab_import_export', array(
2529 'id' => 'tab-import-export',
2530 'name' => 'tab-import-export',
2531 'classes' => 'tab-import-export',
2532 'label' => __( 'Import / Export', 'betterdocs' ),
2533 'priority' => 80,
2534 'fields' => array(
2535 'sections-import-export' => array(
2536 'name' => 'sections-import-export',
2537 'type' => 'section',
2538 'label' => __( 'Import / Export', 'betterdocs' ),
2539 'priority' => 30,
2540 'fields' => array(
2541 'all-tab-import-export' => array(
2542 'id' => 'all-tab-import-export',
2543 'name' => 'all-tab-import-export',
2544 'label' => __( 'Import Export Settings', 'betterdocs' ),
2545 'classes' => 'tab-layout',
2546 'type' => 'tab',
2547 'active' => 'import',
2548 'completionTrack' => true,
2549 'sidebar' => false,
2550 'save' => false,
2551 'title' => false,
2552 'config' => array(
2553 'active' => 'import',
2554 'sidebar' => false,
2555 'title' => false
2556 ),
2557 'submit' => array(
2558 'show' => false
2559 ),
2560 'step' => array(
2561 'show' => false
2562 ),
2563 'priority' => 20,
2564 'fields' => array(
2565 'import' => array(
2566 'id' => 'import',
2567 'name' => 'import',
2568 'type' => 'section',
2569 'label' => __( 'Import', 'betterdocs' ),
2570 'priority' => 1,
2571 'fields' => array(
2572 'import_tab_nested' => array(
2573 'id' => 'import_tab_nested',
2574 'name' => 'import_tab_nested',
2575 'label' => __( 'Import', 'betterdocs' ),
2576 'classes' => 'tab-nested-layout',
2577 'type' => 'tab',
2578 'active' => 'import_docs_nested',
2579 'completionTrack' => true,
2580 'sidebar' => false,
2581 'save' => false,
2582 'title' => false,
2583 'config' => array(
2584 'active' => 'import_docs_nested',
2585 'sidebar' => false,
2586 'title' => false
2587 ),
2588 'submit' => array(
2589 'show' => false
2590 ),
2591 'step' => array(
2592 'show' => false
2593 ),
2594 'priority' => 1,
2595 'fields' => array(
2596 'import_docs_nested' => array(
2597 'id' => 'import_docs_nested',
2598 'name' => 'import_docs_nested',
2599 'type' => 'section',
2600 'label' => __( 'Import Docs', 'betterdocs' ),
2601 'priority' => 1,
2602 'fields' => array(
2603 'import_docs' => array(
2604 'name' => 'import_docs',
2605 'type' => 'importerupload',
2606 'label' => __( 'Import Docs', 'betterdocs' ),
2607 'label_subtitle' => wp_sprintf( // Translators: %1$s is a link to download a sample CSV file.
2608 __( 'To import your Docs, please upload the .xml / .csv file here. <a href="%1$s">Download sample csv file</a>', 'betterdocs' ), betterdocs()->assets->icon( 'BetterDocs-sample-data.csv', true ) ),
2609 'text' => array(
2610 'normal' => __( 'Proceed', 'betterdocs' ),
2611 'saved' => __( 'Proceed', 'betterdocs' ),
2612 'loading' => __( 'Importing...', 'betterdocs' ),
2613 'exists_notice' => __( 'It seems like documentations with same slugs already exist on your website. What would you like to do?', 'betterdocs' )
2614 ),
2615 'ajax' => array(
2616 'on' => 'click',
2617 'api' => '/betterdocs/v1/import-docs',
2618 'swal' => array(
2619 'text' => __( 'Import completed successfully.', 'betterdocs' ),
2620 'icon' => 'success',
2621 'autoClose' => 2000
2622 )
2623 ),
2624 'file_type' => '.xml, .csv',
2625 'priority' => 1
2626 )
2627 )
2628 ),
2629
2630 'import_settings_nested' => array(
2631 'id' => 'import_settings_nested',
2632 'name' => 'import_settings_nested',
2633 'type' => 'section',
2634 'label' => __( 'Import Settings', 'betterdocs' ),
2635 'priority' => 1,
2636 'fields' => array(
2637 'settings_importer' => array(
2638 'name' => 'settings_importer',
2639 'type' => 'settingsuploader',
2640 'label' => __( 'Import Settings', 'betterdocs' ),
2641 'label_subtitle' => __( 'To import BetterDocs Settings, please upload BetterDocs settings you have exported from another website in .json format', 'betterdocs' ),
2642 'reset' => __( 'Change', 'betterdocs' ),
2643 'priority' => 1
2644 ),
2645 'import_settings' => array(
2646 'name' => 'import_settings',
2647 'type' => 'button',
2648 'rules' => Rules::is( 'settings_importer', null, true ),
2649 'text' => array(
2650 'normal' => __( 'Proceed', 'betterdocs' ),
2651 'saved' => __( 'Proceed', 'betterdocs' ),
2652 'loading' => __( 'Importing...', 'betterdocs' )
2653 ),
2654 'ajax' => array(
2655 'on' => 'click',
2656 'api' => '/betterdocs/v1/import-settings',
2657 'data' => array(
2658 'settings' => '@settings_importer'
2659 ),
2660 'swal' => array(
2661 'text' => __( 'Import completed successfully.', 'betterdocs' ),
2662 'icon' => 'success',
2663 'autoClose' => 1000
2664 ),
2665 'reload' => true
2666 ),
2667 'priority' => 2
2668 )
2669 )
2670 )
2671 )
2672 )
2673 )
2674 ),
2675 'export' => array(
2676 'id' => 'export',
2677 'name' => 'export',
2678 'type' => 'section',
2679 'label' => __( 'Export', 'betterdocs' ),
2680 'priority' => 1,
2681 'fields' => array(
2682 'export_tab_nested' => array(
2683 'id' => 'export_tab_nested',
2684 'name' => 'export_tab_nested',
2685 'label' => __( 'Export', 'betterdocs' ),
2686 'classes' => 'tab-nested-layout',
2687 'type' => 'tab',
2688 'active' => 'export_docs_nested',
2689 'completionTrack' => true,
2690 'sidebar' => false,
2691 'save' => false,
2692 'title' => false,
2693 'config' => array(
2694 'active' => 'export_docs_nested',
2695 'sidebar' => false,
2696 'title' => false
2697 ),
2698 'submit' => array(
2699 'show' => false
2700 ),
2701 'step' => array(
2702 'show' => false
2703 ),
2704 'priority' => 1,
2705 'fields' => array(
2706 'export_docs_nested' => array(
2707 'id' => 'export_docs_nested',
2708 'name' => 'export_docs_nested',
2709 'type' => 'section',
2710 'label' => __( 'Export Docs', 'betterdocs' ),
2711 'priority' => 1,
2712 'fields' => apply_filters( 'betterdocs_export_fields', array(
2713 'export_type' => array(
2714 'name' => 'export_type',
2715 'label' => __( 'Select Docs Type', 'betterdocs' ),
2716 'label_subtitle' => __( 'Choose an export type: All Docs, a specific Knowledge Base, or a Doc Category', 'betterdocs' ),
2717 'type' => 'select',
2718 'default' => 'docs',
2719 'priority' => 3,
2720 'search' => true,
2721 'options' => $this->normalize_options( apply_filters( 'betterdocs_export_type_options', array(
2722 'docs' => __( 'Docs', 'betterdocs' ),
2723 'doc_category' => __( 'Docs Category', 'betterdocs' ),
2724 'glossaries' => __( 'Glossaries', 'betterdocs' )
2725 ) ) )
2726 ),
2727 'export_docs' => array(
2728 'name' => 'export_docs',
2729 'type' => 'checkbox-select',
2730 'label' => __( 'Select Docs', 'betterdocs' ),
2731 'label_subtitle' => __( 'Selected docs will be included in the export.', 'betterdocs' ),
2732 'priority' => 4,
2733 'multiple' => true,
2734 'search' => true,
2735 'default' => array( 'all' ),
2736 'placeholder' => __( 'Select any', 'betterdocs' ),
2737 'options' => array_merge( array(
2738 array(
2739 'value' => 'all',
2740 'label' => 'All'
2741 )
2742 ), $this->docs() ),
2743 'filterValue' => 'all',
2744 'rules' => Rules::is( 'export_type', 'docs' )
2745 ),
2746 'export_categories' => array(
2747 'name' => 'export_categories',
2748 'type' => 'checkbox-select',
2749 'label' => __( 'Select Categories', 'betterdocs' ),
2750 'label_subtitle' => __( 'Selected categories and its docs will be included in the export.', 'betterdocs' ),
2751 'priority' => 6,
2752 'multiple' => true,
2753 'search' => true,
2754 'default' => array( 'all' ),
2755 'placeholder' => __( 'Select any', 'betterdocs' ),
2756 'options' => $this->get_terms( 'doc_category', false ),
2757 'filterValue' => 'all',
2758 'rules' => Rules::is( 'export_type', 'doc_category' )
2759 ),
2760 'export_glossaries' => array(
2761 'name' => 'export_glossaries',
2762 'type' => 'checkbox-select',
2763 'label' => __( 'Select Glossaries', 'betterdocs' ),
2764 'label_subtitle' => __( 'Selected glossary terms will be exported.', 'betterdocs' ),
2765 'priority' => 7,
2766 'multiple' => true,
2767 'search' => true,
2768 'default' => array( 'all' ),
2769 'placeholder' => __( 'Select any', 'betterdocs' ),
2770 'options' => $this->get_terms( 'glossaries' ),
2771 'filterValue' => 'all',
2772 'rules' => Rules::is( 'export_type', 'glossaries' )
2773 ),
2774 'file_type' => array(
2775 'name' => 'file_type',
2776 'label' => __( 'Select File Type', 'betterdocs' ),
2777 'label_subtitle' => __( 'Choose a file type', 'betterdocs' ),
2778 'type' => 'select',
2779 'default' => 'xml',
2780 'priority' => 8,
2781 'search' => true,
2782 'options' => $this->normalize_options( apply_filters( 'betterdocs_export_file_type_options', array(
2783 'xml' => __( '.xml', 'betterdocs' ),
2784 'csv' => __( '.csv', 'betterdocs' )
2785 ) ) )
2786 ),
2787 'enable_export_faq' => array(
2788 'name' => 'enable_export_faq',
2789 'type' => 'toggle',
2790 'priority' => 9,
2791 'label' => __( 'Export FAQ', 'betterdocs' ),
2792 'label_subtitle' => __( 'Export FAQ Related Terms & Posts', 'betterdocs' ),
2793 'enable_disable_text_active' => true,
2794 'default' => true,
2795 'rules' => Rules::is( 'export_type', 'glossaries', true )
2796 ),
2797 'export_docs_btn' => array(
2798 'name' => 'export_docs_btn',
2799 'text' => array(
2800 'normal' => __( 'Proceed', 'betterdocs' ),
2801 'saved' => __( 'Proceed', 'betterdocs' ),
2802 'loading' => __( 'Exporting...', 'betterdocs' )
2803 ),
2804 'type' => 'button',
2805 'priority' => 10,
2806 'ajax' => array(
2807 'on' => 'click',
2808 'api' => '/betterdocs/v1/export-docs',
2809 'data' => array(
2810 'export_type' => '@export_type',
2811 'export_docs' => '@export_docs',
2812 'export_kbs' => '@export_kbs',
2813 'export_categories' => '@export_categories',
2814 'export_glossaries' => '@export_glossaries',
2815 'file_type' => '@file_type',
2816 'enable_export_faq' => '@enable_export_faq'
2817 ),
2818 'swal' => array(
2819 'text' => __( 'Exported Successfully.', 'betterdocs' ),
2820 'icon' => 'success',
2821 'autoClose' => 2000
2822 )
2823 )
2824 )
2825 ) )
2826 ),
2827
2828 'export_settings_nested' => array(
2829 'id' => 'export_settings_nested',
2830 'name' => 'export_settings_nested',
2831 'type' => 'section',
2832 'label' => __( 'Export Settings', 'betterdocs' ),
2833 'priority' => 1,
2834 'fields' => array(
2835 'export_settings' => array(
2836 'name' => 'export_settings',
2837 'label' => __( 'Export Settings', 'betterdocs' ),
2838 'label_subtitle' => __( 'Simply click on “Export Settings” button to download your BetterDocs settings in .json format', 'betterdocs' ),
2839 'text' => array(
2840 'normal' => __( 'Export Settings', 'betterdocs' ),
2841 'saved' => __( 'Export Settings', 'betterdocs' ),
2842 'loading' => __( 'Exporting...', 'betterdocs' )
2843 ),
2844 'type' => 'button',
2845 'priority' => 8,
2846 'ajax' => array(
2847 'on' => 'click',
2848 'api' => '/betterdocs/v1/export-settings',
2849 'data' => array(
2850 'betterdocs_settings' => get_option( 'betterdocs_settings' )
2851 ),
2852 'swal' => array(
2853 'text' => __( 'File downloaded successfully.', 'betterdocs' ),
2854 'icon' => 'success',
2855 'autoClose' => 2000
2856 )
2857 )
2858 )
2859 )
2860 )
2861 )
2862 )
2863 )
2864 )
2865 )
2866 )
2867 )
2868 )
2869 )
2870 ) );
2871
2872 return $settings;
2873 }
2874
2875 public function maybe_remove_git_tab( $tabs ) {
2876 $pro_active = is_plugin_active( 'betterdocs-pro/betterdocs-pro.php' );
2877 $pro_has_git = apply_filters( 'betterdocs_pro_has_git_integration', false );
2878
2879 if ( $pro_active && ! $pro_has_git ) {
2880 unset( $tabs[ 'tab-git-sync' ] );
2881 }
2882
2883 return $tabs;
2884 }
2885
2886 public function normalize_options( $options ) {
2887 return GlobalFields::normalize_fields( $options );
2888 }
2889
2890 public function get_texanomy() {
2891 $docs_tax = $this->database->get_cache( 'betterdocs::settings::taxonomies' );
2892
2893 if ( $docs_tax ) {
2894 return $docs_tax;
2895 }
2896
2897 $taxonomies = get_taxonomies( array(
2898 'object_type' => array( 'docs' )
2899 ), 'objects' );
2900
2901 $docs_tax = array(
2902 'all' => 'All Docs Archive',
2903 'docs' => 'Docs Page'
2904 );
2905 foreach ( $taxonomies as $key => $value ) {
2906 $docs_tax[ $key ] = $value->label;
2907 }
2908 unset( $docs_tax[ 'doc_tag' ] );
2909
2910 $docs_tax = $this->normalize_options( $docs_tax );
2911 if ( count( $docs_tax ) > 2 ) {
2912 $this->database->set_cache( 'betterdocs::settings::taxonomies', $docs_tax );
2913 }
2914
2915 return $docs_tax;
2916 }
2917
2918 public function get_terms( $taxonomy, $hide_empty = true ) {
2919 $_cache_key = 'betterdocs::settings::terms::' . trim( $taxonomy );
2920 $docs_tax = $this->database->get_cache( $_cache_key );
2921
2922 if ( $docs_tax ) {
2923 return $docs_tax;
2924 }
2925
2926 $get_terms = get_terms( array(
2927 'taxonomy' => $taxonomy,
2928 'hide_empty' => $hide_empty
2929 ) );
2930
2931 $terms = array(
2932 'all' => 'All'
2933 );
2934
2935 if ( ! empty( $get_terms ) && ! is_wp_error( $get_terms ) ) {
2936 foreach ( $get_terms as $value ) {
2937 if ( isset( $value->slug ) && isset( $value->name ) ) {
2938 $terms[ $value->slug ] = $value->name;
2939 }
2940 }
2941 }
2942
2943 $terms = $this->normalize_options( $terms );
2944 if ( count( $terms ) > 1 ) {
2945 $this->database->set_cache( $_cache_key, $terms );
2946 }
2947
2948 return $terms;
2949 }
2950
2951 public function get_terms_with_ids( $taxonomy ) {
2952 $_cache_key = 'betterdocs::settings::terms::ids' . trim( $taxonomy );
2953 $docs_tax = $this->database->get_cache( $_cache_key );
2954
2955 if ( $docs_tax ) {
2956 return $docs_tax;
2957 }
2958
2959 $get_terms = get_terms( array(
2960 'taxonomy' => $taxonomy,
2961 'hide_empty' => false,
2962 'suppress_filter' => true
2963 ) );
2964
2965 $terms = array();
2966
2967 if ( ! empty( $get_terms ) && ! is_wp_error( $get_terms ) ) {
2968 foreach ( $get_terms as $value ) {
2969 if ( isset( $value->term_id ) && isset( $value->name ) ) {
2970 $terms[ $value->term_id ] = $value->name;
2971 }
2972 }
2973 }
2974
2975 $terms = $this->normalize_options( $terms );
2976 if ( count( $terms ) > 1 ) {
2977 $this->database->set_cache( $_cache_key, $terms );
2978 }
2979
2980 return $terms;
2981 }
2982
2983 /**
2984 * Get all docs
2985 */
2986 public function docs() {
2987 $docs = $this->database->get_cache( 'betterdocs::settings::all_docs' );
2988
2989 if ( $docs ) {
2990 return $docs;
2991 }
2992
2993 $docs = array();
2994
2995 $_docs = get_posts( array(
2996 'post_type' => 'docs',
2997 'numberposts' => -1,
2998 'posts_per_page' => -1
2999 ) );
3000
3001 if ( ! empty( $_docs ) ) {
3002 foreach ( $_docs as $doc ) {
3003 $docs[ $doc->ID ] = betterdocs()->template_helper->kses( $doc->post_title );
3004 }
3005 $docs = GlobalFields::normalize_fields( $docs );
3006 $this->database->set_cache( 'betterdocs::settings::all_docs', $docs );
3007 }
3008
3009 return $docs;
3010 }
3011
3012 public function hide_roles_management( $tabData = array() ) {
3013 global $current_user;
3014
3015 if ( $current_user instanceof WP_User && ! in_array( 'administrator', $current_user->roles ) ) {
3016 unset( $tabData[ 'fields' ][ 'title-advance-settings' ][ 'fields' ][ 'article_roles' ] );
3017 unset( $tabData[ 'fields' ][ 'title-advance-settings' ][ 'fields' ][ 'settings_roles' ] );
3018 unset( $tabData[ 'fields' ][ 'title-advance-settings' ][ 'fields' ][ 'analytics_roles' ] );
3019 unset( $tabData[ 'fields' ][ 'title-advance-settings' ][ 'fields' ][ 'faq_roles' ] );
3020 }
3021
3022 return $tabData;
3023 }
3024 }
3025