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 / Editors / BlockEditor.php

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

293 lines 8.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WPDeveloper\BetterDocs\Editors;
4
5 if ( ! defined( 'ABSPATH' ) ) {
6 exit; // Exit if accessed directly
7 }
8
9 use WPDeveloper\BetterDocs\Core\AIActions;
10 use WPDeveloper\BetterDocs\Utils\Helper;
11 use WPDeveloper\BetterDocs\Editors\BlockEditor\FontLoader;
12 use WPDeveloper\BetterDocs\Editors\BlockEditor\StyleHandler;
13 use WPDeveloper\BetterDocs\Editors\BlockEditor\TemplatesController;
14 use WPDeveloper\BetterDocs\Editors\BlockEditor\Patterns\BasePattern;
15
16 class BlockEditor extends BaseEditor {
17
18 public function init() {
19 betterdocs()->container->get( TemplatesController::class );
20
21 add_action( 'admin_init', [ $this, 'enqueue' ] );
22 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] );
23
24 $_blocks_category_hook = version_compare( get_bloginfo( 'version' ), '5.8', '>=' ) ? 'block_categories_all' : 'block_categories';
25 add_filter( $_blocks_category_hook, [ $this, 'register_category' ], 9, 1 );
26
27 StyleHandler::get_instance();
28 FontLoader::get_instance();
29
30 $this->register_blocks();
31
32 add_action( 'init', [ $this, 'pattern_category' ] );
33
34 $this->pattern_initialization();
35 }
36
37 /**
38 * Pattren category registration.
39 * @return void
40 */
41 public function pattern_category() {
42 register_block_pattern_category(
43 'batterdocs',
44 [ 'label' => __( 'BetterDocs', 'betterdocs' ) ]
45 );
46 }
47
48 /**
49 * Get all the Pattrens initialized.
50 * @return void
51 */
52 public function pattern_initialization() {
53 $_api_classes = scandir( __DIR__ . DIRECTORY_SEPARATOR . 'BlockEditor' . DIRECTORY_SEPARATOR . 'Patterns' );
54
55 if ( ! empty( $_api_classes ) && is_array( $_api_classes ) ) {
56 foreach ( $_api_classes as $class ) {
57 if ( $class == '.' || $class == '..' || $class == 'BasePattern.php' || strpos( $class, '.' ) === 0 ) {
58 continue;
59 }
60
61 $classname = basename( $class, '.php' );
62 $classname = '\\' . __NAMESPACE__ . "\\BlockEditor\\Patterns\\$classname";
63 $patterns_class = betterdocs()->container->get( $classname );
64
65 if ( $patterns_class instanceof BasePattern ) {
66 $patterns_class->pattern_category();
67 $patterns_class->register();
68 }
69 }
70 }
71 }
72
73 public function admin_init() {
74 $blocks = $this->get_blocks();
75
76 if ( empty( $blocks ) || ! is_array( $blocks ) ) {
77 return;
78 }
79
80 foreach ( $blocks as $block_name => $block ) {
81 if ( isset( $block['object'] ) ) {
82 $block_object = betterdocs()->container->get( $block['object'] );
83
84 if ( ! $block_object->can_enable() ) {
85 continue;
86 }
87
88 $block_object->register_scripts();
89 }
90 }
91 }
92
93 /**
94 * Only for Admin Add/Edit Pages
95 */
96 public function enqueue( $hook ) {
97 $editor = 'core/edit-post';
98 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only screen detection.
99 $page = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : '';
100 if ( $hook === 'site-editor.php' || ( $hook === 'themes.php' && $page === 'gutenberg-edit-site' ) ) {
101 $editor = 'core/edit-site';
102 }
103
104 $this->assets->register( 'betterdocs-blocks-editor-controls', 'blocks/controls.css' );
105 $this->assets->register( 'betterdocs-blocks-editor', 'blocks/style-editor.css', [ 'betterdocs-blocks-editor-controls' ] );
106 $this->assets->register( 'betterdocs-blocks-editor', 'blocks/editor.js', [ 'betterdocs-blocks-style-handler' ] );
107 $this->assets->enqueue( 'betterdocs-blocks-actions', 'blocks/actions.js', [] );
108 $this->assets->localize(
109 'betterdocs-blocks-editor',
110 'betterDocsBlocksHelper',
111 array_merge([
112 'is_pro_active' => betterdocs()->is_pro_active(),
113 'is_woocommerce_active' => class_exists( 'WooCommerce' ),
114 'resturl' => get_rest_url(),
115 'editorType' => $editor,
116 'betterdocs_glossaries' => Helper::get_glossaries(),
117 'docsIcon' => isset( betterdocs()->settings->get( 'docs_list_icon' )['url'] ) ? betterdocs()->settings->get( 'docs_list_icon' )['url'] : '',
118 'settings' => $this->block_setting_defaults(),
119 'aiActions' => $this->ai_actions_catalog()
120 ], betterdocs()->settings->chatbot_active_localize())
121 );
122
123 if ( $hook == 'post-new.php' || $hook == 'post.php' || $hook == 'site-editor.php' ) {
124 $this->assets->enqueue( 'fontpicker-default-theme', 'vendor/css/fonticonpicker.base-theme.react.css' );
125 $this->assets->enqueue( 'fontpicker-material-theme', 'vendor/css/fonticonpicker.material-theme.react.css' );
126 }
127 }
128
129 /**
130 * Settings-panel values a block uses as the default for its own controls.
131 *
132 * A block control that has never been touched should show — and render — what
133 * the Settings panel says, so switching a feature on there is enough and the
134 * per-block control is only for overriding one instance. The editor cannot read
135 * `betterdocs_settings` (the REST route behind it needs `edit_docs_settings`,
136 * which an author does not have), so the values are handed over at enqueue time.
137 *
138 * Nested deliberately: wp_localize_script casts every *top-level* scalar to a
139 * string, so a top-level `false` would arrive in JS as `""` and a `true` as
140 * `"1"`. Inside a sub-array the values are JSON-encoded and keep their types,
141 * which is what a boolean block attribute needs.
142 *
143 * Keys mirror the Settings keys exactly, so the JS side needs no translation
144 * table and a mismatch is obvious on sight.
145 *
146 * @return array
147 */
148 protected function block_setting_defaults() {
149 $settings = betterdocs()->settings;
150
151 $booleans = [
152 'enable_estimated_reading_time',
153 'enable_ai_actions',
154 'enable_listen',
155 'listen_show_speed',
156 'ai_actions_copy_page',
157 'ai_actions_view_markdown',
158 'ai_actions_chatgpt',
159 'ai_actions_claude',
160 'ai_actions_gemini',
161 'ai_actions_perplexity',
162 'ai_actions_grok'
163 ];
164
165 $strings = [
166 'estimated_reading_time_title',
167 'estimated_reading_time_text',
168 'singular_estimated_reading_time_text',
169 'ai_actions_button_label',
170 'ai_actions_prompt_template',
171 'listen_button_label'
172 ];
173
174 // Kept apart from the strings above so it arrives as a number: the JS side
175 // divides by it, and "180" would work by coercion while an empty string
176 // would quietly become 0.
177 $numbers = [
178 'listen_words_per_minute'
179 ];
180
181 $defaults = [];
182
183 foreach ( $booleans as $key ) {
184 $defaults[ $key ] = (bool) $settings->get( $key );
185 }
186
187 foreach ( $strings as $key ) {
188 $defaults[ $key ] = (string) $settings->get( $key );
189 }
190
191 foreach ( $numbers as $key ) {
192 $defaults[ $key ] = (int) $settings->get( $key );
193 }
194
195 return $defaults;
196 }
197
198 /**
199 * Everything the editor needs to draw the AI Actions dropdown.
200 *
201 * The reading-time block's preview shows the real menu — same order, labels,
202 * descriptions and icons as the frontend — so an author can see what the action
203 * toggles do without saving and previewing. There is no post to resolve against
204 * in the editor, so no hrefs are sent: the preview is deliberately inert.
205 *
206 * `caret` rides along because the disclosure arrow is not a registry entry but
207 * still has to match the button the frontend renders.
208 *
209 * @return array
210 */
211 protected function ai_actions_catalog() {
212 if ( ! isset( betterdocs()->ai_actions ) ) {
213 return [
214 'items' => [],
215 'caret' => ''
216 ];
217 }
218
219 return [
220 'items' => betterdocs()->ai_actions->catalog(),
221 'caret' => AIActions::icon( 'caret' )
222 ];
223 }
224
225 /**
226 * Add a block category
227 *
228 * @param $block_categories array
229 *
230 * @return array
231 */
232 public function register_category( array $block_categories ): array {
233 $categories_slugs = wp_list_pluck( $block_categories, 'slug' );
234
235 return in_array( 'betterdocs', $categories_slugs, true ) ? $block_categories : array_merge(
236 [
237 [
238 'slug' => 'betterdocs',
239 'title' => __( 'Betterdocs', 'betterdocs' )
240 ]
241 ],
242 $block_categories
243 );
244 }
245
246 /**
247 * Get Blocks
248 *
249 * @since 2.5.0
250 * @return array<array>
251 */
252 public function get_blocks() {
253 $config_array = require_once BETTERDOCS_ABSPATH . 'includes/blocks.php';
254 return apply_filters( 'betterdocs_blocks_config', $config_array );
255 }
256
257 public function register_blocks( $enqueue = false ) {
258 $blocks = $this->get_blocks();
259
260 if ( empty( $blocks ) || ! is_array( $blocks ) ) {
261 return;
262 }
263
264 foreach ( $blocks as $block_name => $block ) {
265 if ( isset( $block['object'] ) ) {
266 $block_object = betterdocs()->container->get( $block['object'] );
267
268 if ( ! $block_object->can_enable() ) {
269 continue;
270 }
271
272 if ( method_exists( $block_object, 'load_dependencies' ) ) {
273 $block_object->load_dependencies();
274 }
275
276 if ( $enqueue && method_exists( $block_object, 'enqueue' ) ) {
277 $block_object->enqueue( $this->assets );
278 continue;
279 }
280
281 if ( method_exists( $block_object, 'inner_blocks' ) ) {
282 $_inner_blocks = $block_object->inner_blocks();
283 foreach ( $_inner_blocks as $block_name => $block ) {
284 $block->register( $this->assets );
285 }
286 }
287
288 $block_object->register( $this->assets );
289 }
290 }
291 }
292 }
293