PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.7.0
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.7.0
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.7.0, at includes/Core/Settings.php

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