| @@ -15,8 +15,9 @@ | ||
| 15 | 15 | use WPDeveloper\BetterDocs\Editors\Elementor\Helper; |
| 16 | 16 | use Elementor\Group_Control_Border; |
| 17 | 17 | use Elementor\Group_Control_Typography; |
| 18 | 18 | use WPDeveloper\BetterDocs\Editors\Elementor\BaseWidget; |
| 19 | +use WPDeveloper\BetterDocs\FrontEnd\WooProductFAQ as WooFAQRenderer; | |
| 19 | 20 | |
| 20 | 21 | class FAQ extends BaseWidget { |
| 21 | 22 | |
| 22 | 23 | public function get_name() { |
| @@ -70,8 +71,34 @@ | ||
| 70 | 71 | 'label_block' => true |
| 71 | 72 | ] |
| 72 | 73 | ); |
| 73 | 74 | |
| 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 | + | |
| 74 | 101 | $this->add_control( |
| 75 | 102 | 'faq_layout_section', |
| 76 | 103 | [ |
| 77 | 104 | 'label' => __( 'FAQ Section', 'betterdocs' ), |
| @@ -114,9 +141,9 @@ | ||
| 114 | 141 | 'h5' => __( 'H5', 'betterdocs' ), |
| 115 | 142 | 'h6' => __( 'H6', 'betterdocs' ) |
| 116 | 143 | ], |
| 117 | 144 | 'condition' => [ |
| 118 | - 'faq_layout_selection' => ['layout-1', 'layout-2', 'layout-3'] | |
| 145 | + 'faq_layout_selection' => [ 'layout-1', 'layout-2', 'layout-3' ] | |
| 119 | 146 | ] |
| 120 | 147 | ] |
| 121 | 148 | ); |
| 122 | 149 | |
| @@ -121,39 +148,80 @@ | ||
| 121 | 148 | ); |
| 122 | 149 | |
| 123 | 150 | $terms = betterdocs()->container->get( Helper::class )->get_faq_terms(); |
| 124 | 151 | |
| 125 | - $this->add_control( | |
| 126 | - 'select_specific_faq', | |
| 127 | - [ | |
| 128 | - 'label' => __( 'Include FAQ Groups', 'betterdocs' ), | |
| 129 | - 'label_block' => true, | |
| 130 | - 'type' => Controls_Manager::SELECT2, | |
| 131 | - 'options' => $terms, | |
| 132 | - 'multiple' => true, | |
| 133 | - 'default' => '', | |
| 134 | - 'select2options' => [ | |
| 135 | - 'placeholder' => __( 'Include FAQ Groups', 'betterdocs' ), | |
| 136 | - 'allowClear' => true | |
| 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 | + ] | |
| 137 | 204 | ] |
| 138 | - ] | |
| 139 | - ); | |
| 205 | + ); | |
| 140 | 206 | |
| 141 | - $this->add_control( | |
| 142 | - 'exclude_specific_faq', | |
| 143 | - [ | |
| 144 | - 'label' => __( 'Exclude FAQ Groups', 'betterdocs' ), | |
| 145 | - 'label_block' => true, | |
| 146 | - 'type' => Controls_Manager::SELECT2, | |
| 147 | - 'options' => $terms, | |
| 148 | - 'multiple' => true, | |
| 149 | - 'default' => '', | |
| 150 | - 'select2options' => [ | |
| 151 | - 'placeholder' => __( 'Exclude FAQ Groups', 'betterdocs' ), | |
| 152 | - 'allowClear' => true | |
| 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 | + ] | |
| 153 | 221 | ] |
| 154 | - ] | |
| 155 | - ); | |
| 222 | + ); | |
| 223 | + } | |
| 156 | 224 | |
| 157 | 225 | $this->end_controls_section(); |
| 158 | 226 | |
| 159 | 227 | /** |
| @@ -267,8 +335,30 @@ | ||
| 267 | 335 | ] |
| 268 | 336 | ] |
| 269 | 337 | ); |
| 270 | 338 | |
| 339 | + $this->add_control( | |
| 340 | + 'faq_box_title_background_color', | |
| 341 | + [ | |
| 342 | + 'label' => esc_html__( 'Background Color', 'betterdocs' ), | |
| 343 | + 'type' => Controls_Manager::COLOR, | |
| 344 | + 'selectors' => [ | |
| 345 | + '{{WRAPPER}} .betterdocs-faq-wrapper .betterdocs-faq-inner-wrapper .betterdocs-faq-title' => 'background-color:{{VALUE}};' | |
| 346 | + ] | |
| 347 | + ] | |
| 348 | + ); | |
| 349 | + | |
| 350 | + $this->add_control( | |
| 351 | + 'faq_box_title_background_color_hover', | |
| 352 | + [ | |
| 353 | + 'label' => esc_html__( 'Background Hover Color', 'betterdocs' ), | |
| 354 | + 'type' => Controls_Manager::COLOR, | |
| 355 | + 'selectors' => [ | |
| 356 | + '{{WRAPPER}} .betterdocs-faq-wrapper .betterdocs-faq-inner-wrapper .betterdocs-faq-title:hover' => 'background-color:{{VALUE}};' | |
| 357 | + ] | |
| 358 | + ] | |
| 359 | + ); | |
| 360 | + | |
| 271 | 361 | $this->add_group_control( |
| 272 | 362 | Group_Control_Typography::get_type(), |
| 273 | 363 | [ |
| 274 | 364 | 'name' => 'faq_box_title_typography', |
| @@ -311,8 +401,44 @@ | ||
| 311 | 401 | ] |
| 312 | 402 | ] |
| 313 | 403 | ); |
| 314 | 404 | |
| 405 | + $this->add_control( | |
| 406 | + 'faq_box_title_background_color_layout_4', | |
| 407 | + [ | |
| 408 | + 'label' => esc_html__( 'Background Color', 'betterdocs' ), | |
| 409 | + 'type' => Controls_Manager::COLOR, | |
| 410 | + 'selectors' => [ | |
| 411 | + '{{WRAPPER}} .betterdocs-faq-wrapper.betterdocs-faq-layout-4 .betterdocs-faq-inner-wrapper .betterdocs-faq-tab-wrapper .betterdocs-faq-tab' => 'background-color:{{VALUE}};', | |
| 412 | + '{{WRAPPER}} .betterdocs-faq-wrapper.betterdocs-faq-layout-4 .betterdocs-faq-inner-wrapper .betterdocs-faq-list-wrapper .betterdocs-faq-tab' => 'background-color:{{VALUE}};' | |
| 413 | + ] | |
| 414 | + ] | |
| 415 | + ); | |
| 416 | + | |
| 417 | + $this->add_control( | |
| 418 | + 'faq_box_title_background_color_hover_layout_4', | |
| 419 | + [ | |
| 420 | + 'label' => esc_html__( 'Background Hover Color', 'betterdocs' ), | |
| 421 | + 'type' => Controls_Manager::COLOR, | |
| 422 | + 'selectors' => [ | |
| 423 | + '{{WRAPPER}} .betterdocs-faq-wrapper.betterdocs-faq-layout-4 .betterdocs-faq-inner-wrapper .betterdocs-faq-tab-wrapper .betterdocs-faq-tab:hover' => 'background-color:{{VALUE}};', | |
| 424 | + '{{WRAPPER}} .betterdocs-faq-wrapper.betterdocs-faq-layout-4 .betterdocs-faq-inner-wrapper .betterdocs-faq-list-wrapper .betterdocs-faq-tab:hover' => 'background-color:{{VALUE}};' | |
| 425 | + ] | |
| 426 | + ] | |
| 427 | + ); | |
| 428 | + | |
| 429 | + $this->add_control( | |
| 430 | + 'faq_box_title_background_color_active_layout_4', | |
| 431 | + [ | |
| 432 | + 'label' => esc_html__( 'Active Background Color', 'betterdocs' ), | |
| 433 | + 'type' => Controls_Manager::COLOR, | |
| 434 | + 'selectors' => [ | |
| 435 | + '{{WRAPPER}} .betterdocs-faq-wrapper.betterdocs-faq-layout-4 .betterdocs-faq-inner-wrapper .betterdocs-faq-tab-wrapper .betterdocs-faq-tab.active' => 'background-color:{{VALUE}};', | |
| 436 | + '{{WRAPPER}} .betterdocs-faq-wrapper.betterdocs-faq-layout-4 .betterdocs-faq-inner-wrapper .betterdocs-faq-list-wrapper .betterdocs-faq-tab.active' => 'background-color:{{VALUE}};' | |
| 437 | + ] | |
| 438 | + ] | |
| 439 | + ); | |
| 440 | + | |
| 315 | 441 | $this->add_group_control( |
| 316 | 442 | Group_Control_Typography::get_type(), |
| 317 | 443 | [ |
| 318 | 444 | 'name' => 'faq_box_title_typography_layout_4', |
| @@ -802,12 +928,46 @@ | ||
| 802 | 928 | |
| 803 | 929 | protected function render_callback() { |
| 804 | 930 | $control_values = $this->get_settings_for_display(); |
| 805 | 931 | |
| 806 | - $specific_faqs = ! empty( $control_values['select_specific_faq'] ) ? implode( ',', $control_values['select_specific_faq'] ) : ''; | |
| 932 | + $taxonomy = sanitize_key( $control_values['faq_taxonomy'] ?? 'betterdocs_faq_category' ); | |
| 807 | 933 | |
| 808 | - $faqs_exclude = ! empty( $control_values['exclude_specific_faq'] ) ? implode( ',', $control_values['exclude_specific_faq'] ) : ''; | |
| 934 | + if ( $taxonomy === 'product_assignments' ) { | |
| 935 | + if ( ElementorPlugin::instance()->editor->is_edit_mode() ) { | |
| 936 | + // Editor preview: show all product FAQ terms so the layout can be styled. | |
| 937 | + betterdocs()->views->get( | |
| 938 | + 'layouts/faq', | |
| 939 | + [ | |
| 940 | + 'enable' => true, | |
| 941 | + 'have_posts' => true, | |
| 942 | + 'layout' => $control_values['faq_layout_selection'], | |
| 943 | + 'shortcode_attr' => [ | |
| 944 | + 'group_exclude' => '', | |
| 945 | + 'groups' => '', | |
| 946 | + 'class' => 'betterdocs-faq-' . $control_values['faq_layout_selection'], | |
| 947 | + 'faq_heading' => $control_values['faq_layout_section'], | |
| 948 | + 'faq_section_title_tag' => $control_values['faq_section_title_tag'], | |
| 949 | + 'faq_group_title_tag' => $control_values['faq_group_title_tag'], | |
| 950 | + 'faq_taxonomy' => 'betterdocs_product_faq_category', | |
| 951 | + ] | |
| 952 | + ] | |
| 953 | + ); | |
| 954 | + $this->render_editor_script(); | |
| 955 | + } else { | |
| 956 | + $this->render_product_assignments( $control_values['faq_layout_selection'] ); | |
| 957 | + } | |
| 809 | 958 | |
| 959 | + return; | |
| 960 | + } | |
| 961 | + | |
| 962 | + if ( $taxonomy === 'betterdocs_product_faq_category' ) { | |
| 963 | + $specific_faqs = ! empty( $control_values['select_specific_product_faq'] ) ? implode( ',', $control_values['select_specific_product_faq'] ) : ''; | |
| 964 | + $faqs_exclude = ! empty( $control_values['exclude_specific_product_faq'] ) ? implode( ',', $control_values['exclude_specific_product_faq'] ) : ''; | |
| 965 | + } else { | |
| 966 | + $specific_faqs = ! empty( $control_values['select_specific_faq'] ) ? implode( ',', $control_values['select_specific_faq'] ) : ''; | |
| 967 | + $faqs_exclude = ! empty( $control_values['exclude_specific_faq'] ) ? implode( ',', $control_values['exclude_specific_faq'] ) : ''; | |
| 968 | + } | |
| 969 | + | |
| 810 | 970 | betterdocs()->views->get( |
| 811 | 971 | 'layouts/faq', |
| 812 | 972 | [ |
| 813 | 973 | 'enable' => true, |
| @@ -818,9 +978,10 @@ | ||
| 818 | 978 | 'class' => 'betterdocs-faq-' . $control_values['faq_layout_selection'], |
| 819 | 979 | 'groups' => $specific_faqs, |
| 820 | 980 | 'faq_heading' => $control_values['faq_layout_section'], |
| 821 | 981 | 'faq_section_title_tag' => $control_values['faq_section_title_tag'], |
| 822 | - 'faq_group_title_tag' => $control_values['faq_group_title_tag'] | |
| 982 | + 'faq_group_title_tag' => $control_values['faq_group_title_tag'], | |
| 983 | + 'faq_taxonomy' => $taxonomy | |
| 823 | 984 | ] |
| 824 | 985 | ] |
| 825 | 986 | ); |
| 826 | 987 | |
| @@ -826,8 +987,31 @@ | ||
| 826 | 987 | |
| 827 | 988 | if ( ElementorPlugin::instance()->editor->is_edit_mode() ) { |
| 828 | 989 | $this->render_editor_script(); |
| 829 | 990 | } |
| 991 | + } | |
| 992 | + | |
| 993 | + private function render_product_assignments( $layout ) { | |
| 994 | + if ( ! class_exists( 'WooCommerce' ) || ! is_product() ) { | |
| 995 | + return; | |
| 996 | + } | |
| 997 | + | |
| 998 | + // In theme-builder templates, the widget can render outside the loop, | |
| 999 | + // so get_the_ID() can be 0 — fall back to the queried product. | |
| 1000 | + $product_id = get_the_ID(); | |
| 1001 | + if ( ! $product_id ) { | |
| 1002 | + $product_id = get_queried_object_id(); | |
| 1003 | + } | |
| 1004 | + if ( ! $product_id ) { | |
| 1005 | + return; | |
| 1006 | + } | |
| 1007 | + | |
| 1008 | + /** @var WooFAQRenderer $renderer */ | |
| 1009 | + $renderer = betterdocs()->container->get( WooFAQRenderer::class ); | |
| 1010 | + | |
| 1011 | + // This widget is the explicit placement; don't also inject into the tab/summary. | |
| 1012 | + $renderer->suppress_auto_placement(); | |
| 1013 | + $renderer->render_for_product( $product_id, $layout ); | |
| 830 | 1014 | } |
| 831 | 1015 | |
| 832 | 1016 | protected function render_editor_script() { |
| 833 | 1017 | ?> |