PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.9.2
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.9.2
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/BlockEditor/Blocks/FAQ.php +63 -10 4.4.14.9.2 View file →
@@ -1,9 +1,14 @@
1 1 <?php
2 +namespace WPDeveloper\BetterDocs\Editors\BlockEditor\Blocks;
2 3
3 -namespace WPDeveloper\BetterDocs\Editors\BlockEditor\Blocks;
4 +if ( ! defined( 'ABSPATH' ) ) {
5 + exit;
6 +}
4 7
8 +
5 9 use WPDeveloper\BetterDocs\Editors\BlockEditor\Block;
10 +use WPDeveloper\BetterDocs\FrontEnd\WooProductFAQ as WooFAQRenderer;
6 11
7 12 class FAQ extends Block {
8 13 protected $editor_scripts = [
9 14 'betterdocs-faq',
@@ -41,8 +46,9 @@
41 46 'faqSectionText' => 'Frequently Asked Questions',
42 47 'faqSectionTitleTag' => 'h2',
43 48 'faqGroupTitleTag' => 'h3',
44 49 'faqSectionTitleColor' => null,
50 + // JSON string of `[{value:int, label:string}]`; a bare id array (`[5]`) is also accepted since 4.9.0.
45 51 'includeFaqGroup' => '',
46 52 'excludeFaqGroup' => '',
47 53 'faqGroupTitleColor' => null,
48 54 'faqGroupTitleHoverColor' => null,
@@ -52,13 +58,18 @@
52 58 'faqGroupTitleTypography' => null,
53 59 'faqPerPage' => 9,
54 60
55 61 'showButtonIcon' => true,
56 - 'buttonColor' => '#528ffe'
62 + 'buttonColor' => '#528ffe',
63 + 'faqTaxonomy' => 'betterdocs_faq_category'
57 64 ];
58 65 }
59 66
60 67 public function render( $attributes, $content ) {
68 + if ( ( $this->attributes['faqTaxonomy'] ?? '' ) === 'product_assignments' ) {
69 + $this->render_product_assignments();
70 + return;
71 + }
61 72
62 73 add_filter( 'betterdocs_header_layout_sequence', [ $this, 'header_sequence' ], 10, 4 );
63 74 $this->views( 'layouts/faq' );
64 75 remove_filter( 'betterdocs_header_layout_sequence', [ $this, 'header_sequence' ], 10 );
@@ -63,16 +74,54 @@
63 74 $this->views( 'layouts/faq' );
64 75 remove_filter( 'betterdocs_header_layout_sequence', [ $this, 'header_sequence' ], 10 );
65 76 }
66 77
78 + private function render_product_assignments() {
79 + if ( ! class_exists( 'WooCommerce' ) || ! is_product() ) {
80 + return;
81 + }
82 +
83 + // In FSE templates, top-level blocks render outside the loop, so
84 + // get_the_ID() can be 0 — fall back to the queried product.
85 + $product_id = get_the_ID();
86 + if ( ! $product_id ) {
87 + $product_id = get_queried_object_id();
88 + }
89 + if ( ! $product_id ) {
90 + return;
91 + }
92 +
93 + /** @var WooFAQRenderer $renderer */
94 + $renderer = betterdocs()->container->get( WooFAQRenderer::class );
95 +
96 + // This block is the explicit placement; don't also inject into the tab/summary.
97 + $renderer->suppress_auto_placement();
98 + $renderer->render_for_product( $product_id, $this->attributes['faqLayout'] ?? null );
99 + }
100 +
101 + /**
102 + * Flatten a group-selection attribute into a comma separated list of term ids.
103 + *
104 + * The attribute is a JSON string. The editor writes
105 + * `[{"value":5,"label":"Install"}]`; REST clients and other programmatic
106 + * writers commonly write the bare form `[5]`. Both are accepted — before
107 + * 4.9.0 the bare form flattened to an empty string, which
108 + * `Query::faq_terms_query_args()` reads as "no filter", so every group
109 + * rendered and the block warned on every view.
110 + *
111 + * @since 4.9.0 Bare ids are accepted alongside `{ value, label }` objects;
112 + * ids are cast with `absint()` and de-duplicated.
113 + *
114 + * @param mixed $json JSON encoded list of term ids or `{ value, label }` objects.
115 + *
116 + * @return string Comma separated term ids, or '' when there is nothing to filter by.
117 + */
67 118 public function get_groups_ids( $json ) {
68 - $data = json_decode( $json, true );
69 - $ids = '';
70 - if ( $data !== null ) {
71 - $ids = implode( ',', array_column( $data, 'value' ) );
72 - }
119 + $data = is_string( $json ) ? json_decode( $json, true ) : $json;
120 + $ids = array_filter( self::normalize_id_list( $data ), 'is_numeric' );
121 + $ids = array_unique( array_map( 'absint', $ids ) );
73 122
74 - return $ids;
123 + return implode( ',', $ids );
75 124 }
76 125
77 126 public function view_params() {
78 127 $attributes = &$this->attributes;
@@ -80,10 +129,11 @@
80 129 // Sanitize the layout attribute to prevent path traversal (LFI).
81 130 $attributes['faqLayout'] = sanitize_file_name( $attributes['faqLayout'] );
82 131 $exclude = $this->get_groups_ids( $attributes['excludeFaqGroup'] );
83 132 $include = $this->get_groups_ids( $attributes['includeFaqGroup'] );
133 + $taxonomy = sanitize_key( $attributes['faqTaxonomy'] ?? 'betterdocs_faq_category' );
84 134
85 - $terms_query = betterdocs()->query->faq_terms_query_args( $include, $exclude );
135 + $terms_query = betterdocs()->query->faq_terms_query_args( $include, $exclude, [], $taxonomy );
86 136
87 137 return wp_parse_args(
88 138 [
89 139 'enable' => true,
@@ -110,9 +160,12 @@
110 160 'faq_group_title_typography' => $attributes['faqGroupTitleTypography'],
111 161 'faq_per_page' => $attributes['faqPerPage'],
112 162 'show_button_icon' => $attributes['showButtonIcon'],
113 163 'button_icon_position' => $attributes['faqLayout'] == 'layout-1' || $attributes['faqLayout'] == 'layout-3' || $attributes['faqLayout'] == 'layout-4' ? 'after' : 'before',
114 - 'button_color' => $attributes['buttonColor']
164 + 'button_color' => $attributes['buttonColor'],
165 + 'faq_taxonomy' => $taxonomy,
166 + // Per-block order; empty falls back to the global FAQ order.
167 + 'order' => isset( $attributes['faqOrder'] ) ? $attributes['faqOrder'] : ''
115 168 ]
116 169
117 170 ]
118 171 );