PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.9.4
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.9.4
4.9.4 4.9.3 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 All 202 releases
betterdocs / includes / FrontEnd / FrontEnd.php

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

397 lines 13.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace WPDeveloper\BetterDocs\FrontEnd;
3
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit;
6 }
7
8
9 use WPDeveloper\BetterDocs\Utils\Base;
10 use WPDeveloper\BetterDocs\Utils\Helper;
11 use WPDeveloper\BetterDocs\Core\Settings;
12 use WPDeveloper\BetterDocs\Utils\Enqueue;
13 use WPDeveloper\BetterDocs\Utils\Database;
14 use WPDeveloper\BetterDocs\Dependencies\DI\Container;
15
16 class FrontEnd extends Base {
17 private $container;
18 private $database;
19 /**
20 * Enqueue
21 * @var Enqueue
22 */
23 private $assets;
24 /**
25 * Settings
26 * @var Settings
27 */
28 private $settings;
29 private $widget_attributes = [];
30 private $widget_type = '';
31
32 public function __construct( Container $container, Database $database, Settings $settings ) {
33 $this->container = $container;
34 $this->database = $database;
35 $this->settings = $settings;
36
37 $this->assets = $this->container->get( Enqueue::class );
38
39 add_action( 'init', [ $this, 'init' ] );
40 add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
41
42 /**
43 * Update The 'Edit Site' Url In FSE Mode For Betterdocs Templates Only
44 */
45 if ( Helper::is_plugin_active( 'sitepress-multilingual-cms/sitepress.php' ) && wp_is_block_theme() ) {
46 add_action( 'admin_bar_menu', [$this, 'fse_url_update'], 41, 1 );
47 }
48
49 add_filter( 'betterdocs_layout_filename', [ $this, 'layout_filename' ], 10, 2 );
50
51 add_action( 'betterdocs_docs_before_social', [ $this, 'article_reactions' ] );
52
53 add_action( 'betterdocs_before_render', [ $this, 'before_render' ], 11, 2 );
54 add_action( 'betterdocs_after_render', [ $this, 'after_render' ], 11, 2 );
55
56 //Remove Saliant Theme Script For (Delay Javascript Exection), which causes issue with betterdocs sidebar toggle, issue number (#1234)
57 add_action( 'nectar_hook_before_body_close', [ $this, 'dequeue_saliant_theme_script' ], 99999 );
58
59 //Remove our search selector from from Searchanise plugin for woocommerce, conflicts with betterdocs search (Bug Fix Card -> https://trello.com/c/lXzrtv2f/1313-client-issue-betterdocs-is-conflicting-with-the-searchanise-plugin)
60 add_filter( 'se_load_search_widgets', [ $this, 'exclude_betterdocs_search' ], 10, 1 );
61
62 //Fix Betterdocs Search Issue With HostCluster Theme
63 if ( function_exists( 'hostcluster_search_filter' ) ) {
64 remove_filter( 'pre_get_posts', 'hostcluster_search_filter' );
65 }
66
67 //render authors template
68 add_filter( 'template_include', [$this, 'render_authors_template'], 10, 1 );
69 }
70
71 public function render_authors_template( $template_path ) {
72 $post_type = get_query_var( 'post_type' ) != null ? get_query_var( 'post_type' ) : '';
73 $author_id = get_query_var( 'author' ) != null ? get_query_var( 'author' ) : 0;
74 $author_docs_count = count_user_posts( $author_id, 'docs' );
75
76 // Only override on an actual author archive — otherwise this also fires on
77 // the main docs archive (where $author_id falls back to 0) whenever any doc
78 // has post_author 0, hijacking the docs/MKB archive (and its FSE template)
79 // with the author template.
80 if ( is_author() && $post_type == 'docs' && $author_docs_count > 0 ) {
81 wp_enqueue_style('betterdocs-breadcrumb');
82 $template_path = BETTERDOCS_ABSPATH . 'views/templates/authors/author.php';
83 }
84
85 return $template_path;
86 }
87
88 public function fse_url_update( &$wp_admin_bar ) {
89 $site_edit_node = $wp_admin_bar->get_node( 'site-editor' );
90
91 if ( empty( $site_edit_node ) ) {
92 return;
93 }
94
95 if ( is_post_type_archive( 'docs' ) || is_tax( 'knowledge_base' ) || is_tax( 'doc_category' ) || is_tax( 'doc_tag' ) || is_singular( 'docs' ) ) {
96 $href = isset( $site_edit_node->href ) ? $site_edit_node->href : '';
97 if ( ! empty( $href ) ) {
98 $href = $href . '&lang=' . ICL_LANGUAGE_CODE;
99 $site_edit_node->href = $href;
100 $wp_admin_bar->add_node( $site_edit_node );
101 }
102 }
103 }
104
105 public function exclude_betterdocs_search( $options ) {
106 $options['search_input'] = $options['search_input'] . ':not(.betterdocs-search-field)';
107 return $options;
108 }
109 public function before_render( $widget, $widget_type ) {
110 $this->widget_attributes = isset( $widget->attributes ) ? $widget->attributes : [];
111 $this->widget_type = $widget_type;
112
113 /**
114 * This line of code will run for reactions shortcode, elementor widget and blocks
115 */
116 if ( strpos( $widget->get_name(), 'reactions' ) !== false ) {
117 $this->localize_reactions_data();
118 }
119
120 /**
121 * This line of code will run for Feedback form Shortcode, Elementor widget and blocks
122 */
123 if ( strpos( $widget->get_name(), 'feedback_form' ) ) {
124 $this->localize_feedback_form_data();
125 }
126
127 add_filter( 'betterdocs_nested_terms_args', [ $this, 'terms_args' ], 11, 1 );
128 add_filter( 'betterdocs_nested_docs_args', [ $this, 'docs_args' ], 11, 1 );
129 }
130
131 public function dequeue_saliant_theme_script() {
132 if ( is_singular( 'docs' ) || is_tax( 'doc_category' ) || is_tax( 'doc_tag' ) ) {
133 wp_dequeue_script( 'salient-delay-js' );
134 }
135 }
136
137 public function after_render( $widget, $widget_type ) {
138 remove_filter( 'betterdocs_nested_terms_args', [ $this, 'terms_args' ], 11 );
139 remove_filter( 'betterdocs_nested_docs_args', [ $this, 'docs_args' ], 11 );
140
141 $this->widget_attributes = [];
142 $this->widget_type = '';
143 }
144
145 public function terms_args( $args ) {
146 switch ( $this->widget_type ) {
147 case 'shortcode':
148 if ( isset( $this->widget_attributes['terms_orderby'] ) ) {
149 $args['orderby'] = $this->widget_attributes['terms_orderby'];
150 }
151
152 if ( isset( $this->widget_attributes['terms_order'] ) ) {
153 $args['order'] = $this->widget_attributes['terms_order'];
154 }
155 break;
156 case 'blocks':
157 if ( isset( $this->widget_attributes['orderBy'] ) ) {
158 if ( $this->widget_attributes['orderBy'] == 'doc_category_order' ) {
159 // Use betterdocs_order which handles fallback logic automatically
160 $args['orderby'] = 'betterdocs_order';
161 } else {
162 $args['orderby'] = $this->widget_attributes['orderBy'];
163 }
164 }
165 if ( isset( $this->widget_attributes['order'] ) ) {
166 $args['order'] = $this->widget_attributes['order'];
167 }
168 break;
169 case 'elementor':
170 $args['orderby'] = $this->widget_attributes['orderby'];
171 $args['order'] = $this->widget_attributes['order'];
172 break;
173 }
174
175 return $args;
176 }
177
178 public function docs_args( $args ) {
179 switch ( $this->widget_type ) {
180 case 'shortcode':
181 if ( isset( $this->widget_attributes['orderby'] ) ) {
182 $args['orderby'] = $this->widget_attributes['orderby'];
183 }
184
185 if ( isset( $this->widget_attributes['order'] ) ) {
186 $args['order'] = $this->widget_attributes['order'];
187 }
188 break;
189 case 'blocks':
190 if ( isset( $this->widget_attributes['postsOrderBy'] ) ) {
191 $args['orderby'] = $this->widget_attributes['postsOrderBy'];
192 }
193 if ( isset( $this->widget_attributes['postsOrder'] ) ) {
194 $args['order'] = $this->widget_attributes['postsOrder'];
195 }
196
197 //This is for archive category block only
198 if ( isset( $this->widget_attributes['orderby'] ) ) {
199 $args['orderby'] = $this->widget_attributes['orderby'];
200 }
201 if ( isset( $this->widget_attributes['order'] ) ) {
202 $args['order'] = $this->widget_attributes['order'];
203 }
204 break;
205 case 'elementor':
206 $args['orderby'] = $this->widget_attributes['post_orderby'];
207 $args['order'] = $this->widget_attributes['post_order'];
208 break;
209 }
210
211 return $args;
212 }
213
214 public function article_reactions() {
215 $args = [];
216 $single_layout = $this->database->get_theme_mod( 'betterdocs_single_layout_select', true );
217 $reactions = $this->database->get_theme_mod( 'betterdocs_post_reactions', true );
218
219 // Get the title tag from customizer
220 $text_tag = $this->database->get_theme_mod( 'betterdocs_reactions_title_tag', 'h5' );
221
222 // Collect reaction values and icons
223 $reactions_data = [
224 'happy' => 'betterdocs_post_reactions_happy',
225 'happy_icon' => 'betterdocs_post_reactions_happy_icon',
226 'normal' => 'betterdocs_post_reactions_normal',
227 'normal_icon' => 'betterdocs_post_reactions_normal_icon',
228 'sad' => 'betterdocs_post_reactions_sad',
229 'sad_icon' => 'betterdocs_post_reactions_sad_icon'
230 ];
231
232 foreach ( $reactions_data as $key => $theme_mod ) {
233 $value = betterdocs()->customizer->defaults->get( $theme_mod );
234 if ( $value ) {
235 $args[ $key ] = $value;
236 } else {
237 $args[ $key ] = false;
238 }
239 }
240
241 // Build the attribute string for the shortcode
242 $attr = '';
243 foreach ( $args as $key => $value ) {
244 $attr .= sprintf( ' %s="%s"', esc_attr( $key ), esc_attr( $value ) );
245 }
246
247 // Render the shortcode based on layout and reactions availability
248 if ( ( $single_layout == 'layout-8' || $single_layout == 'layout-10' ) && $reactions ) {
249 echo do_shortcode( '[betterdocs_article_reactions text_tag="' . esc_attr( $text_tag ) . '" layout="layout-2"' . $attr . ']' );
250 } elseif ( $single_layout == 'layout-9' && $reactions ) {
251 echo '';
252 } elseif ( $reactions ) {
253 echo do_shortcode( '[betterdocs_article_reactions text_tag="' . esc_attr( $text_tag ) . '"' . $attr . ']' );
254 }
255 }
256
257
258 public function enqueue_scripts() {
259 if ( is_singular( 'docs' ) ) {
260 wp_enqueue_style( 'betterdocs-single' );
261 wp_enqueue_style( 'betterdocs-article-summary' );
262 wp_enqueue_style( 'betterdocs-ai-actions' );
263 if ( betterdocs()->listen->is_enabled() ) {
264 wp_enqueue_style( 'betterdocs-listen' );
265 }
266 wp_enqueue_style( 'betterdocs-encyclopedia' );
267 wp_enqueue_style( 'betterdocs-glossaries' );
268 wp_enqueue_script( 'clipboard' );
269 wp_enqueue_script( 'betterdocs-glossaries' );
270 }
271
272 // The shortcode / block / widget can place AI Actions and Listen on any page.
273 // Their own dependency lists run during the_content, i.e. after wp_head has
274 // already printed, which would leave the controls unstyled until the footer
275 // stylesheet parsed — so catch those placements here instead.
276 $this->maybe_enqueue_meta_row();
277
278 if ( is_post_type_archive( 'docs' ) ) {
279 wp_enqueue_style( 'betterdocs-category-grid' ); //category grid shortcode is supposed to enqueue this style, but this is called again to fix flicking of UI on Docs Page
280 wp_enqueue_style( 'betterdocs-docs' );
281 }
282
283 if ( is_tax( 'doc_category' ) || is_tax( 'doc_tag' ) ) {
284 wp_enqueue_style( 'betterdocs-doc_category' );
285 }
286
287 if ( is_tax( 'doc_category' ) || is_tax( 'doc_tag' ) || is_singular( 'docs' ) ) {
288 wp_enqueue_style( 'simplebar' );
289 wp_enqueue_script( 'simplebar' );
290 wp_enqueue_script( 'betterdocs-category-grid' );
291 }
292
293 if ( is_post_type_archive( 'docs' ) || is_singular( 'docs' ) || is_tax( 'doc_category' ) || is_tax( 'doc_tag' ) || is_tax( 'knowledge_base' ) ) {
294 wp_enqueue_script( 'betterdocs' );
295 }
296
297 if ( is_tax( 'glossaries' ) ) {
298 wp_enqueue_style( 'betterdocs-encyclopedia' );
299 wp_enqueue_style( 'betterdocs-single' );
300 wp_enqueue_style( 'betterdocs-glossaries' );
301 }
302 }
303
304 /**
305 * Enqueue the meta-row assets in wp_head when the current post embeds one of
306 * the shortcodes or the block, so an off-doc placement is styled on first
307 * paint.
308 *
309 * @since 4.8.0
310 * @since 4.9.2 Listen added, and renamed from maybe_enqueue_ai_actions().
311 */
312 protected function maybe_enqueue_meta_row() {
313 if ( is_singular( 'docs' ) ) {
314 return;
315 }
316
317 $post = get_post();
318 if ( ! $post instanceof \WP_Post ) {
319 return;
320 }
321
322 // `betterdocs/reading-time` is the merged meta-row block: it carries the AI
323 // Actions button and the Listen pill, and the `.betterdocs-doc-meta` wrapper
324 // lives in both stylesheets, so it needs a handle whichever third of it is
325 // switched on. Which is also why the block counts for both features here.
326 $block = has_block( 'betterdocs/reading-time', $post );
327
328 if ( betterdocs()->ai_actions->is_enabled()
329 && ( $block || has_shortcode( $post->post_content, 'betterdocs_ai_actions' ) ) ) {
330 wp_enqueue_style( 'betterdocs-ai-actions' );
331 wp_enqueue_script( 'clipboard' );
332 wp_enqueue_script( 'betterdocs' );
333 }
334
335 if ( betterdocs()->listen->is_enabled()
336 && ( $block || has_shortcode( $post->post_content, 'betterdocs_listen' ) ) ) {
337 wp_enqueue_style( 'betterdocs-listen' );
338 wp_enqueue_script( 'betterdocs' );
339 }
340 }
341
342 public function layout_filename( $filename, $origin_layout ) {
343 $filename = ( $origin_layout === 'layout-2' ) ? 'default' : $filename;
344 return $filename;
345 }
346
347 public function init() {
348 $this->container->get( TemplateLoader::class )->init();
349
350 add_filter( 'betterdocs_articles_args', [ $this, 'article_args' ], 11, 3 );
351 }
352
353 public function article_args( $args, $term_id, $_origin_args ) {
354 if ( null == $term_id || isset( $args['orderby'] ) ) {
355 return $args;
356 }
357
358 $post__in = betterdocs()->query->get_docs_order_by_terms( $term_id );
359
360 if ( ! empty( $post__in ) ) {
361 $args['orderby'] = 'post__in';
362 $args['post__in'] = $post__in;
363 }
364
365 return $args;
366 }
367
368 public function localize_reactions_data() {
369 $this->assets->localize(
370 'betterdocs-reactions',
371 'betterdocsReactionsConfig',
372 [
373 'post_id' => get_the_ID(),
374 'nonce' => wp_create_nonce( 'wp_rest' ),
375 'FEEDBACK' => [
376 'DISPLAY' => true,
377 'TEXT' => esc_html__( 'How did you feel?', 'betterdocs' ),
378 'SUCCESS' => betterdocs()->settings->get( 'reaction_feedback_text', __( 'Thanks for your feedback', 'betterdocs' ) ),
379 'URL' => get_rest_url( null, '/betterdocs/v1/feedback' )
380 ]
381 ]
382 );
383 }
384
385 public function localize_feedback_form_data() {
386 $this->assets->localize(
387 'betterdocs',
388 'betterdocsSubmitFormConfig',
389 [
390 'ajax_url' => admin_url( 'admin-ajax.php' ),
391 'post_id' => get_the_ID(),
392 'nonce' => wp_create_nonce( 'betterdocs_submit_data' )
393 ]
394 );
395 }
396 }
397