PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 3.8.9
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v3.8.9
4.9.2 4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 All 200 releases
← All changes | includes/Editors/Elementor/Widget/Basic/FAQ.php +33 -233 4.8.23.8.9 View file →
@@ -14,12 +14,10 @@
14 14 use Elementor\Controls_Manager;
15 15 use WPDeveloper\BetterDocs\Editors\Elementor\Helper;
16 16 use Elementor\Group_Control_Border;
17 17 use Elementor\Group_Control_Typography;
18 -use WPDeveloper\BetterDocs\Editors\Elementor\BaseWidget;
19 -use WPDeveloper\BetterDocs\FrontEnd\WooProductFAQ as WooFAQRenderer;
20 18
21 -class FAQ extends BaseWidget {
19 +class FAQ extends Widget_Base {
22 20
23 21 public function get_name() {
24 22 return 'betterdocs-faq';
25 23 }
@@ -71,34 +69,8 @@
71 69 'label_block' => true
72 70 ]
73 71 );
74 72
75 - if ( class_exists( 'WooCommerce' ) ) {
76 - $this->add_control(
77 - 'faq_taxonomy',
78 - [
79 - 'label' => __( 'FAQ Type', 'betterdocs' ),
80 - 'type' => Controls_Manager::SELECT,
81 - 'options' => [
82 - 'betterdocs_faq_category' => __( 'General FAQ', 'betterdocs' ),
83 - 'betterdocs_product_faq_category' => __( 'Product FAQ', 'betterdocs' ),
84 - 'product_assignments' => __( 'Product Assignments', 'betterdocs' ),
85 - ],
86 - 'default' => 'betterdocs_faq_category',
87 - ]
88 - );
89 -
90 - $this->add_control(
91 - 'faq_product_assignments_note',
92 - [
93 - 'type' => Controls_Manager::RAW_HTML,
94 - 'raw' => __( 'Groups are resolved automatically per product: a per-product assignment is shown if set, otherwise the groups assigned to the product’s categories. Manage assignments on each group in FAQ Builder → WooCommerce → Product FAQ Groups.', 'betterdocs' ),
95 - 'content_classes' => 'elementor-control-field-description',
96 - 'condition' => [ 'faq_taxonomy' => 'product_assignments' ],
97 - ]
98 - );
99 - }
100 -
101 73 $this->add_control(
102 74 'faq_layout_section',
103 75 [
104 76 'label' => __( 'FAQ Section', 'betterdocs' ),
@@ -109,120 +81,42 @@
109 81 'default' => __( 'Frequently Asked Questions', 'betterdocs' )
110 82 ]
111 83 );
112 84
85 + $terms = betterdocs()->container->get( Helper::class )->get_faq_terms();
86 +
113 87 $this->add_control(
114 - 'faq_section_title_tag',
88 + 'select_specific_faq',
115 89 [
116 - 'label' => __( 'Section Title Tag', 'betterdocs' ),
117 - 'type' => Controls_Manager::SELECT,
118 - 'default' => 'h2',
119 - 'options' => [
120 - 'h1' => __( 'H1', 'betterdocs' ),
121 - 'h2' => __( 'H2', 'betterdocs' ),
122 - 'h3' => __( 'H3', 'betterdocs' ),
123 - 'h4' => __( 'H4', 'betterdocs' ),
124 - 'h5' => __( 'H5', 'betterdocs' ),
125 - 'h6' => __( 'H6', 'betterdocs' )
90 + 'label' => __( 'Include FAQ Groups', 'betterdocs' ),
91 + 'label_block' => true,
92 + 'type' => Controls_Manager::SELECT2,
93 + 'options' => $terms,
94 + 'multiple' => true,
95 + 'default' => '',
96 + 'select2options' => [
97 + 'placeholder' => __( 'Include FAQ Groups', 'betterdocs' ),
98 + 'allowClear' => true
126 99 ]
127 100 ]
128 101 );
129 102
130 103 $this->add_control(
131 - 'faq_group_title_tag',
104 + 'exclude_specific_faq',
132 105 [
133 - 'label' => __( 'Group Title Tag', 'betterdocs' ),
134 - 'type' => Controls_Manager::SELECT,
135 - 'default' => 'h3',
136 - 'options' => [
137 - 'h1' => __( 'H1', 'betterdocs' ),
138 - 'h2' => __( 'H2', 'betterdocs' ),
139 - 'h3' => __( 'H3', 'betterdocs' ),
140 - 'h4' => __( 'H4', 'betterdocs' ),
141 - 'h5' => __( 'H5', 'betterdocs' ),
142 - 'h6' => __( 'H6', 'betterdocs' )
143 - ],
144 - 'condition' => [
145 - 'faq_layout_selection' => [ 'layout-1', 'layout-2', 'layout-3' ]
106 + 'label' => __( 'Exclude FAQ Groups', 'betterdocs' ),
107 + 'label_block' => true,
108 + 'type' => Controls_Manager::SELECT2,
109 + 'options' => $terms,
110 + 'multiple' => true,
111 + 'default' => '',
112 + 'select2options' => [
113 + 'placeholder' => __( 'Exclude FAQ Groups', 'betterdocs' ),
114 + 'allowClear' => true
146 115 ]
147 116 ]
148 117 );
149 118
150 - $terms = betterdocs()->container->get( Helper::class )->get_faq_terms();
151 -
152 - // General FAQ include/exclude — shown always when WooCommerce is off,
153 - // or conditioned on FAQ Type = General FAQ when WooCommerce is on.
154 - $general_include_args = [
155 - 'label' => __( 'Include FAQ Groups', 'betterdocs' ),
156 - 'label_block' => true,
157 - 'type' => Controls_Manager::SELECT2,
158 - 'options' => $terms,
159 - 'multiple' => true,
160 - 'default' => '',
161 - 'select2options' => [
162 - 'placeholder' => __( 'Include FAQ Groups', 'betterdocs' ),
163 - 'allowClear' => true
164 - ]
165 - ];
166 - $general_exclude_args = [
167 - 'label' => __( 'Exclude FAQ Groups', 'betterdocs' ),
168 - 'label_block' => true,
169 - 'type' => Controls_Manager::SELECT2,
170 - 'options' => $terms,
171 - 'multiple' => true,
172 - 'default' => '',
173 - 'select2options' => [
174 - 'placeholder' => __( 'Exclude FAQ Groups', 'betterdocs' ),
175 - 'allowClear' => true
176 - ]
177 - ];
178 -
179 - if ( class_exists( 'WooCommerce' ) ) {
180 - $general_include_args['condition'] = [ 'faq_taxonomy' => 'betterdocs_faq_category' ];
181 - $general_exclude_args['condition'] = [ 'faq_taxonomy' => 'betterdocs_faq_category' ];
182 - }
183 -
184 - $this->add_control( 'select_specific_faq', $general_include_args );
185 - $this->add_control( 'exclude_specific_faq', $general_exclude_args );
186 -
187 - if ( class_exists( 'WooCommerce' ) ) {
188 - $product_terms = betterdocs()->query->get_faq_terms( [], 'betterdocs_product_faq_category' );
189 -
190 - $this->add_control(
191 - 'select_specific_product_faq',
192 - [
193 - 'label' => __( 'Include Product FAQ Groups', 'betterdocs' ),
194 - 'label_block' => true,
195 - 'type' => Controls_Manager::SELECT2,
196 - 'options' => $product_terms,
197 - 'multiple' => true,
198 - 'default' => '',
199 - 'condition' => [ 'faq_taxonomy' => 'betterdocs_product_faq_category' ],
200 - 'select2options' => [
201 - 'placeholder' => __( 'Include Product FAQ Groups', 'betterdocs' ),
202 - 'allowClear' => true
203 - ]
204 - ]
205 - );
206 -
207 - $this->add_control(
208 - 'exclude_specific_product_faq',
209 - [
210 - 'label' => __( 'Exclude Product FAQ Groups', 'betterdocs' ),
211 - 'label_block' => true,
212 - 'type' => Controls_Manager::SELECT2,
213 - 'options' => $product_terms,
214 - 'multiple' => true,
215 - 'default' => '',
216 - 'condition' => [ 'faq_taxonomy' => 'betterdocs_product_faq_category' ],
217 - 'select2options' => [
218 - 'placeholder' => __( 'Exclude Product FAQ Groups', 'betterdocs' ),
219 - 'allowClear' => true
220 - ]
221 - ]
222 - );
223 - }
224 -
225 119 $this->end_controls_section();
226 120
227 121 /**
228 122 * ----------------------------------------------------------
@@ -319,9 +213,9 @@
319 213 [
320 214 'label' => esc_html__( 'Title Color', 'betterdocs' ),
321 215 'type' => Controls_Manager::COLOR,
322 216 'selectors' => [
323 - '{{WRAPPER}} .betterdocs-faq-wrapper .betterdocs-faq-inner-wrapper .betterdocs-faq-title .betterdocs-faq-title-tag' => 'color:{{VALUE}};'
217 + '{{WRAPPER}} .betterdocs-faq-wrapper .betterdocs-faq-inner-wrapper .betterdocs-faq-title h2' => 'color:{{VALUE}};'
324 218 ]
325 219 ]
326 220 );
327 221
@@ -330,9 +224,9 @@
330 224 [
331 225 'label' => esc_html__( 'Title Hover Color', 'betterdocs' ),
332 226 'type' => Controls_Manager::COLOR,
333 227 'selectors' => [
334 - '{{WRAPPER}} .betterdocs-faq-wrapper .betterdocs-faq-inner-wrapper .betterdocs-faq-title .betterdocs-faq-title-tag:hover' => 'color:{{VALUE}};'
228 + '{{WRAPPER}} .betterdocs-faq-wrapper .betterdocs-faq-inner-wrapper .betterdocs-faq-title h2:hover' => 'color:{{VALUE}};'
335 229 ]
336 230 ]
337 231 );
338 232
@@ -339,9 +233,9 @@
339 233 $this->add_group_control(
340 234 Group_Control_Typography::get_type(),
341 235 [
342 236 'name' => 'faq_box_title_typography',
343 - 'selector' => '{{WRAPPER}} .betterdocs-faq-wrapper .betterdocs-faq-inner-wrapper .betterdocs-faq-title .betterdocs-faq-title-tag'
237 + 'selector' => '{{WRAPPER}} .betterdocs-faq-wrapper .betterdocs-faq-inner-wrapper .betterdocs-faq-title h2'
344 238 ]
345 239 );
346 240
347 241 $this->end_controls_section();
@@ -867,49 +761,15 @@
867 761
868 762 $this->end_controls_section();
869 763 }
870 764
871 - protected function render_callback() {
765 + protected function render() {
872 766 $control_values = $this->get_settings_for_display();
873 767
874 - $taxonomy = sanitize_key( $control_values['faq_taxonomy'] ?? 'betterdocs_faq_category' );
768 + $specific_faqs = ! empty( $control_values['select_specific_faq'] ) ? implode( ',', $control_values['select_specific_faq'] ) : '';
875 769
876 - if ( $taxonomy === 'product_assignments' ) {
877 - if ( ElementorPlugin::instance()->editor->is_edit_mode() ) {
878 - // Editor preview: show all product FAQ terms so the layout can be styled.
879 - betterdocs()->views->get(
880 - 'layouts/faq',
881 - [
882 - 'enable' => true,
883 - 'have_posts' => true,
884 - 'layout' => $control_values['faq_layout_selection'],
885 - 'shortcode_attr' => [
886 - 'group_exclude' => '',
887 - 'groups' => '',
888 - 'class' => 'betterdocs-faq-' . $control_values['faq_layout_selection'],
889 - 'faq_heading' => $control_values['faq_layout_section'],
890 - 'faq_section_title_tag' => $control_values['faq_section_title_tag'],
891 - 'faq_group_title_tag' => $control_values['faq_group_title_tag'],
892 - 'faq_taxonomy' => 'betterdocs_product_faq_category',
893 - ]
894 - ]
895 - );
896 - $this->render_editor_script();
897 - } else {
898 - $this->render_product_assignments( $control_values['faq_layout_selection'] );
899 - }
770 + $faqs_exclude = ! empty( $control_values['exclude_specific_faq'] ) ? implode( ',', $control_values['exclude_specific_faq'] ) : '';
900 771
901 - return;
902 - }
903 -
904 - if ( $taxonomy === 'betterdocs_product_faq_category' ) {
905 - $specific_faqs = ! empty( $control_values['select_specific_product_faq'] ) ? implode( ',', $control_values['select_specific_product_faq'] ) : '';
906 - $faqs_exclude = ! empty( $control_values['exclude_specific_product_faq'] ) ? implode( ',', $control_values['exclude_specific_product_faq'] ) : '';
907 - } else {
908 - $specific_faqs = ! empty( $control_values['select_specific_faq'] ) ? implode( ',', $control_values['select_specific_faq'] ) : '';
909 - $faqs_exclude = ! empty( $control_values['exclude_specific_faq'] ) ? implode( ',', $control_values['exclude_specific_faq'] ) : '';
910 - }
911 -
912 772 betterdocs()->views->get(
913 773 'layouts/faq',
914 774 [
915 775 'enable' => true,
@@ -915,15 +775,12 @@
915 775 'enable' => true,
916 776 'have_posts' => true,
917 777 'layout' => $control_values['faq_layout_selection'],
918 778 'shortcode_attr' => [
919 - 'group_exclude' => $faqs_exclude,
920 - 'class' => 'betterdocs-faq-' . $control_values['faq_layout_selection'],
921 - 'groups' => $specific_faqs,
922 - 'faq_heading' => $control_values['faq_layout_section'],
923 - 'faq_section_title_tag' => $control_values['faq_section_title_tag'],
924 - 'faq_group_title_tag' => $control_values['faq_group_title_tag'],
925 - 'faq_taxonomy' => $taxonomy
779 + 'group_exclude' => $faqs_exclude,
780 + 'class' => 'betterdocs-faq-' . $control_values['faq_layout_selection'],
781 + 'groups' => $specific_faqs,
782 + 'faq_heading' => $control_values['faq_layout_section']
926 783 ]
927 784 ]
928 785 );
929 786
@@ -931,69 +788,12 @@
931 788 $this->render_editor_script();
932 789 }
933 790 }
934 791
935 - private function render_product_assignments( $layout ) {
936 - if ( ! class_exists( 'WooCommerce' ) || ! is_product() ) {
937 - return;
938 - }
939 -
940 - // In theme-builder templates, the widget can render outside the loop,
941 - // so get_the_ID() can be 0 — fall back to the queried product.
942 - $product_id = get_the_ID();
943 - if ( ! $product_id ) {
944 - $product_id = get_queried_object_id();
945 - }
946 - if ( ! $product_id ) {
947 - return;
948 - }
949 -
950 - /** @var WooFAQRenderer $renderer */
951 - $renderer = betterdocs()->container->get( WooFAQRenderer::class );
952 -
953 - // This widget is the explicit placement; don't also inject into the tab/summary.
954 - $renderer->suppress_auto_placement();
955 - $renderer->render_for_product( $product_id, $layout );
956 - }
957 -
958 792 protected function render_editor_script() {
959 793 ?>
960 794 <script>
961 795 jQuery(document).ready(function($) {
962 - const $tabs = $(".betterdocs-faq-tab");
963 - const $mobileTabs = $(".betterdocs-faq-list-wrapper .betterdocs-faq-tab");
964 - const $contents = $(".betterdocs-faq-list-content");
965 -
966 - $tabs.on("click", function () {
967 - const termId = $(this).data("term-id");
968 -
969 - // Remove active class from all tabs and hide all contents
970 - $tabs.removeClass("active");
971 - $contents.hide().removeClass("active");
972 -
973 - // Reset all icons to default state
974 - $(".betterdocs-faq-iconplus").show();
975 - $(".betterdocs-faq-iconminus").hide();
976 -
977 - // Add active class to the clicked tab and show the corresponding content
978 - $(this).addClass("active");
979 - $contents.filter(`[data-term-id="${termId}"]`).show().addClass("active");
980 -
981 - // Toggle icons for the active tab
982 - $(this).find(".betterdocs-faq-iconplus").hide();
983 - $(this).find(".betterdocs-faq-iconminus").show();
984 - });
985 -
986 - // Trigger click on the first tab to show the first content by default
987 - if ($tabs.length > 0) {
988 - $tabs.first().trigger("click").addClass("active");
989 -
990 - const $mobileFirstTab = $mobileTabs.first();
991 - $mobileFirstTab.addClass("active");
992 - $mobileFirstTab.find(".betterdocs-faq-iconplus").hide();
993 - $mobileFirstTab.find(".betterdocs-faq-iconminus").show();
994 - }
995 -
996 796 $('.betterdocs-faq-post').on('click', function(e) {
997 797 var current_node = $(this);
998 798 var active_list = $('.betterdocs-faq-group.active');
999 799