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 / views / templates / parts / ai-actions.php

ai-actions.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.9.4, at views/templates/parts/ai-actions.php

169 lines 6.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- view template receives variables via extract(); prefixing is impractical.
3
4 /**
5 * AI Actions split button.
6 *
7 * Shared by all four surfaces — the classic single-doc headers, the
8 * [betterdocs_ai_actions] shortcode, the Gutenberg block and the Elementor
9 * widget. Core\AIActions::render() is the only supported way to include it.
10 *
11 * Built on the WAI-ARIA Disclosure pattern rather than Menu Button: every entry
12 * except "Copy page" is a navigation link, and role="menuitem" would override
13 * the implicit link role — screen readers would announce "menu item" instead of
14 * "link", and the entries would disappear from the links list.
15 *
16 * @var bool $enable
17 * @var array $actions Resolved actions from AIActions::resolve().
18 * @var string $md_url
19 * @var string $copy_url
20 * @var string $page_url
21 * @var int $doc_id
22 * @var string $uid
23 * @var string $button_label Already resolved by AIActions::render() — the global
24 * setting, or a per-instance override from the block,
25 * widget or shortcode. May be blank.
26 * @var string $widget_type
27 * @var string $blockId
28 */
29
30 use WPDeveloper\BetterDocs\Core\AIActions;
31
32 if ( ! defined( 'ABSPATH' ) ) {
33 exit;
34 }
35
36 if ( empty( $enable ) || empty( $actions ) ) {
37 return;
38 }
39
40 $primary = null;
41 foreach ( $actions as $action ) {
42 if ( ! empty( $action['primary'] ) ) {
43 $primary = $action;
44 break;
45 }
46 }
47
48 $classes = 'betterdocs-ai-actions';
49 if ( ! $primary ) {
50 $classes .= ' betterdocs-ai-actions-no-primary';
51 }
52 if ( ! empty( $widget_type ) && 'blocks' === $widget_type && ! empty( $blockId ) ) {
53 $classes .= ' ' . $blockId;
54 }
55
56 $menu_id = $uid . '-menu';
57 $toggle_id = $uid . '-toggle';
58 $svg_kses = AIActions::svg_kses();
59
60 // Blank is a legitimate saved value — the setting is a plain text field and a
61 // per-instance control can be cleared — so fall back to the primary action's own
62 // label rather than shipping an unlabelled button.
63 $button_label = isset( $button_label ) ? (string) $button_label : '';
64 if ( '' === trim( $button_label ) ) {
65 $button_label = $primary ? $primary['label'] : __( 'AI Actions', 'betterdocs' );
66 }
67 ?>
68 <div class="<?php echo esc_attr( $classes ); ?>"
69 data-bd-doc-id="<?php echo esc_attr( $doc_id ); ?>"
70 data-bd-md-url="<?php echo esc_url( $md_url ); ?>"
71 data-bd-copy-url="<?php echo esc_url( $copy_url ); ?>"
72 data-bd-page-url="<?php echo esc_url( $page_url ); ?>">
73
74 <?php if ( $primary ) : ?>
75 <button type="button" class="betterdocs-ai-actions-primary"
76 data-bd-action="<?php echo esc_attr( $primary['id'] ); ?>"
77 data-bd-handler="<?php echo esc_attr( $primary['handler'] ); ?>"
78 data-bd-source="<?php echo esc_attr( $primary['source'] ); ?>">
79 <span class="betterdocs-ai-actions-icon betterdocs-ai-actions-icon-idle" aria-hidden="true">
80 <?php echo wp_kses( AIActions::icon( $primary['icon'] ), $svg_kses ); ?>
81 </span>
82 <?php
83 // The copied / failed icons ship with the button and are swapped in by
84 // CSS off the wrapper class. That keeps the state change a one-class flip
85 // in public/betterdocs.js and keeps every icon in one place —
86 // AIActions::icon() — rather than half of them in JavaScript.
87 ?>
88 <span class="betterdocs-ai-actions-icon betterdocs-ai-actions-icon-done" aria-hidden="true">
89 <?php echo wp_kses( AIActions::icon( 'check' ), $svg_kses ); ?>
90 </span>
91 <span class="betterdocs-ai-actions-icon betterdocs-ai-actions-icon-fail" aria-hidden="true">
92 <?php echo wp_kses( AIActions::icon( 'alert' ), $svg_kses ); ?>
93 </span>
94 <span class="betterdocs-ai-actions-label"><?php echo esc_html( $button_label ); ?></span>
95 </button>
96 <?php endif; ?>
97
98 <button type="button" class="betterdocs-ai-actions-toggle"
99 id="<?php echo esc_attr( $toggle_id ); ?>"
100 aria-expanded="false"
101 aria-controls="<?php echo esc_attr( $menu_id ); ?>"
102 aria-label="<?php esc_attr_e( 'AI actions', 'betterdocs' ); ?>">
103 <span class="betterdocs-ai-actions-caret" aria-hidden="true">
104 <?php echo wp_kses( AIActions::icon( 'caret' ), $svg_kses ); ?>
105 </span>
106 </button>
107
108 <ul class="betterdocs-ai-actions-menu"
109 id="<?php echo esc_attr( $menu_id ); ?>"
110 aria-labelledby="<?php echo esc_attr( $toggle_id ); ?>" hidden>
111 <?php
112 $last_group = null;
113 foreach ( $actions as $action ) :
114 $group = isset( $action['group'] ) ? (string) $action['group'] : '';
115
116 // A rule between the actions that act on this page and the ones that
117 // hand it to an assistant. Driven off the group rather than a hard-coded
118 // index so a third-party registry entry lands on the right side of it,
119 // and never emitted first or for a single group.
120 if ( null !== $last_group && $group !== $last_group ) :
121 ?>
122 <li class="betterdocs-ai-actions-separator" role="presentation"></li>
123 <?php
124 endif;
125 $last_group = $group;
126 ?>
127 <li class="betterdocs-ai-actions-row">
128 <?php
129 $icon = wp_kses( AIActions::icon( $action['icon'] ), $svg_kses );
130 $is_copy = 'copy' === $action['type'];
131 $blank = ! $is_copy && '_blank' === $action['target'];
132 ?>
133 <?php if ( $is_copy ) : ?>
134 <button type="button" class="betterdocs-ai-actions-item"
135 data-bd-action="<?php echo esc_attr( $action['id'] ); ?>"
136 data-bd-handler="<?php echo esc_attr( $action['handler'] ); ?>"
137 data-bd-source="<?php echo esc_attr( $action['source'] ); ?>">
138 <?php else : ?>
139 <a class="betterdocs-ai-actions-item"
140 href="<?php echo esc_url( $action['href'] ); ?>"
141 data-bd-action="<?php echo esc_attr( $action['id'] ); ?>"
142 <?php echo $blank ? 'target="_blank" rel="noopener noreferrer"' : ''; ?>>
143 <?php endif; ?>
144
145 <span class="betterdocs-ai-actions-icon" aria-hidden="true"><?php echo $icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- wp_kses'd above. ?></span>
146 <span class="betterdocs-ai-actions-text">
147 <span class="betterdocs-ai-actions-title"><?php echo esc_html( $action['label'] ); ?></span>
148 <?php if ( '' !== $action['description'] ) : ?>
149 <span class="betterdocs-ai-actions-desc"><?php echo esc_html( $action['description'] ); ?></span>
150 <?php endif; ?>
151 </span>
152 <?php if ( $blank ) : ?>
153 <?php
154 // No visible arrow: each row already says where it goes, and the
155 // glyph was a third column fighting the icon tile for the eye.
156 // The hint stays for screen readers, which get no other warning
157 // that the link leaves the page.
158 ?>
159 <span class="betterdocs-sr-only"><?php esc_html_e( '(opens in a new tab)', 'betterdocs' ); ?></span>
160 <?php endif; ?>
161
162 <?php echo $is_copy ? '</button>' : '</a>'; ?>
163 </li>
164 <?php endforeach; ?>
165 </ul>
166
167 <span class="betterdocs-ai-actions-status betterdocs-sr-only" role="status" aria-live="polite"></span>
168 </div>
169