PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 3.6.3
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v3.6.3
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 3.5.2 All 199 releases
betterdocs / includes / Modules / StyleHandler.php

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

374 lines 13.0 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\Modules;
4
5 use WPDeveloper\BetterDocs\Utils\CSSParser;
6
7 final class StyleHandler {
8 private static $instance;
9
10 private $prefix = 'betterdocs-style';
11 private $style_dir;
12 private $style_url;
13 /**
14 * Holds block styles array
15 *
16 * @var array
17 */
18 public static $_block_styles = [];
19
20 public static function init() {
21 if ( null === self::$instance ) {
22 self::$instance = new self;
23 }
24
25 return self::$instance;
26 }
27
28 public function __construct() {
29 $upload_dir = wp_upload_dir();
30
31 $this->style_dir = $upload_dir['basedir'] . '/' . $this->prefix . DIRECTORY_SEPARATOR;
32 $this->style_url = set_url_scheme( $upload_dir['baseurl'] ) . '/' . $this->prefix . '/';
33
34 add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_frontend_assets' ], 99 );
35 add_action( 'save_post', [ $this, 'on_save_post' ], 10, 3 );
36 add_action( 'wp', [ $this, 'generate_post_content' ] );
37 add_action( 'rest_after_save_widget', [ $this, 'after_save_widget' ], 10, 4 );
38
39 // FSE assets generation
40 add_action( 'init', function () {
41 if ( function_exists( 'wp_is_block_theme' ) && wp_is_block_theme() ) {
42 add_filter( "404_template", [ $this, 'fse_assets_generation' ], 99, 3 );
43 add_filter( "archive_template", [ $this, 'fse_assets_generation' ], 99, 3 );
44 add_filter( "category_template", [ $this, 'fse_assets_generation' ], 99, 3 );
45 add_filter( "frontpage_template", [ $this, 'fse_assets_generation' ], 99, 3 );
46 add_filter( "home_template", [ $this, 'fse_assets_generation' ], 99, 3 );
47 add_filter( "index_template", [ $this, 'fse_assets_generation' ], 99, 3 );
48 add_filter( "page_template", [ $this, 'fse_assets_generation' ], 99, 3 );
49 add_filter( "search_template", [ $this, 'fse_assets_generation' ], 99, 3 );
50 add_filter( "single_template", [ $this, 'fse_assets_generation' ], 99, 3 );
51 add_filter( "singular_template", [ $this, 'fse_assets_generation' ], 99, 3 );
52 add_filter( "tag_template", [ $this, 'fse_assets_generation' ], 99, 3 );
53 add_filter( "taxonomy_template", [ $this, 'fse_assets_generation' ], 99, 3 );
54 }
55 }, 999 );
56 }
57
58 /**
59 * Generate FSE Assets
60 */
61 public function fse_assets_generation( $template, $type, $templates ) {
62 $block_template = resolve_block_template( $type, $templates, $template );
63 if ( ! empty( $block_template ) ) {
64 $parsed_content = parse_blocks( $block_template->content );
65 if ( is_array( $parsed_content ) && ! empty( $parsed_content ) ) {
66 foreach ( $parsed_content as $content ) {
67 if ( ( 'core/template-part' == $content['blockName'] ) || ( 'core/template' == $content['blockName'] ) ) {
68 $post_ids = isset( $content['attrs']['slug'] ) ? self::betterdocs_get_post_content_by_post_name( $content['attrs']['slug'] ) : [];
69
70 if ( ! empty( $post_ids ) ) {
71 foreach ( $post_ids as $id ) {
72 $post_id = (int) $id['ID'];
73 $post = get_post( $post_id );
74 $parsed_content = parse_blocks( $post->post_content );
75 $this->write_css_from_content( $post, $post_id, $parsed_content );
76 }
77 }
78 } else {
79 $post_ids = self::betterdocs_get_post_content_by_post_name( $block_template->slug );
80 if ( ! empty( $post_ids ) ) {
81 foreach ( $post_ids as $id ) {
82 $post_id = (int) $id['ID'];
83 $post = get_post( $post_id );
84 $parsed_content = parse_blocks( $post->post_content );
85 $this->write_css_from_content( $post, $post_id, $parsed_content );
86 }
87 }
88 }
89 }
90 }
91 }
92
93 return $template;
94 }
95
96 /**
97 * Write CSS
98 */
99 public function write_css_from_content( $post, $post_id, $parsed_content ) {
100 $betterdocs_blocks = [];
101 $recursive_response = CSSParser::betterdocs_block_style_recursive( $parsed_content, $betterdocs_blocks );
102 $reusable_Blocks = ! empty( $recursive_response['reusableBlocks'] ) ? $recursive_response['reusableBlocks'] : [];
103 // remove empty reusable blocks
104 $reusable_Blocks = array_filter( $reusable_Blocks, function ( $v ) {
105 return ! empty( $v );
106 } );
107 unset( $recursive_response["reusableBlocks"] );
108 $style = CSSParser::blocks_to_style_array( $recursive_response );
109 $reusableIds = $reusable_Blocks ? array_keys( $reusable_Blocks ) : [];
110 if ( ! empty( $reusableIds ) ) {
111 update_option( '_betterdocs_reusable_block_ids', $reusableIds );
112 }
113 update_post_meta( $post_id, '_betterdocs_reusable_block_ids', $reusableIds );
114 $this->write_block_css( $style, $post ); //Write CSS file for this page
115
116 if ( ! empty( $reusable_Blocks ) ) {
117 foreach ( $reusable_Blocks as $blockId => $block ) {
118 $style = CSSParser::blocks_to_style_array( $block );
119 $this->write_reusable_block_css( $style, $blockId );
120 }
121 }
122 }
123
124 /**
125 * Save Widget CSS when Widget is saved
126 * @return void
127 * @since 3.5.3
128 */
129 public function after_save_widget( $id, $sidebar_id, $request, $creating ) {
130 $parsed_content = isset( $request['instance']['raw']['content'] ) ? parse_blocks( $request['instance']['raw']['content'] ) : [];
131 if ( is_array( $parsed_content ) && ! empty( $parsed_content ) ) {
132 $betterdocs_blocks = [];
133 $recursive_response = CSSParser::betterdocs_block_style_recursive( $parsed_content, $betterdocs_blocks );
134 unset( $recursive_response["reusableBlocks"] );
135 $style = CSSParser::blocks_to_style_array( $recursive_response );
136 //Write CSS file for Widget
137 $this->single_file_css_generator( $style, $this->style_dir, $this->prefix . '-widget.min.css' );
138 }
139 }
140
141 /**
142 * Load Dependencies
143 */
144 private function load_style_handler_dependencies() {
145 require_once plugin_dir_path( __FILE__ ) . 'includes/class-parse-css.php';
146 }
147
148 /**
149 * Enqueue frontend css for post if have one
150 * @return void
151 * @since 1.0.2
152 */
153 public function enqueue_frontend_assets() {
154 global $post;
155
156 $deps = apply_filters( 'betterdocs_generated_css_frontend_deps', [] );
157
158 // generatepress elements
159 if ( in_array( 'gp-premium/gp-premium.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
160 $gp_elements = get_posts( [ 'post_type' => 'gp_elements' ] );
161 if ( is_array( $gp_elements ) && ! empty( $gp_elements ) ) {
162 foreach ( $gp_elements as $element ) {
163 if ( file_exists( $this->style_dir . $this->prefix . '-' . $element->ID . '.min.css' ) ) {
164 wp_enqueue_style( 'betterdocs-block-style-' . $element->ID, $this->style_url . $this->prefix . '-' . $element->ID . '.min.css', $deps, substr( md5( microtime( true ) ), 0, 10 ) );
165 }
166 }
167 }
168 }
169
170 if ( ! empty( $post ) && ! empty( $post->ID ) ) {
171 //Page/Post Style Enqueue
172 if ( file_exists( $this->style_dir . $this->prefix . '-' . $post->ID . '.min.css' ) ) {
173 wp_enqueue_style( 'betterdocs-block-style-' . $post->ID, $this->style_url . $this->prefix . '-' . $post->ID . '.min.css', $deps, substr( md5( microtime( true ) ), 0, 10 ) );
174 }
175
176 // Reusable block Style Enqueues
177 $reusableIds = get_post_meta( $post->ID, '_betterdocs_reusable_block_ids', true );
178 $reusableIds = ! empty( $reusableIds ) ? $reusableIds : [];
179 $templateReusableIds = get_option( '_betterdocs_reusable_block_ids', [] );
180 $reusableIds = array_unique( array_merge( $reusableIds, $templateReusableIds ) );
181 if ( ! empty( $reusableIds ) ) {
182 foreach ( $reusableIds as $reusableId ) {
183 if ( file_exists( $this->style_dir . 'reusable-blocks/betterdocs-reusable-' . $reusableId . '.min.css' ) ) {
184 wp_enqueue_style( 'betterdocs-reusable-block-style-' . $reusableId, $this->style_url . 'reusable-blocks/betterdocs-reusable-' . $reusableId . '.min.css', $deps, substr( md5( microtime( true ) ), 0, 10 ) );
185 }
186 }
187 }
188 }
189
190 //Widget Style Enqueue
191 if ( file_exists( $this->style_dir . $this->prefix . '-widget.min.css' ) ) {
192 wp_enqueue_style( 'betterdocs-widget-style', $this->style_url . $this->prefix . '-widget.min.css', $deps, substr( md5( microtime( true ) ), 0, 10 ) );
193 }
194
195 //FSE Style Enqueue
196 if ( function_exists( 'wp_is_block_theme' ) && wp_is_block_theme() && file_exists( $this->style_dir . $this->prefix . '-edit-site.min.css' ) ) {
197 wp_enqueue_style( 'betterdocs-fullsite-style', $this->style_url . $this->prefix . '-edit-site.min.css', $deps, substr( md5( microtime( true ) ), 0, 10 ) );
198 }
199
200 /**
201 * Hooks assets for enqueue in frontend
202 *
203 * @param $path string
204 * @param $url string
205 *
206 * @since 3.0.0
207 */
208 do_action( 'betterdocs_frontend_assets', $this->style_dir, $this->style_url );
209 }
210
211 /**
212 * Get post content when page is saved
213 */
214 public function on_save_post( $post_id, $post, $update ) {
215 $post_type = get_post_type( $post_id );
216
217 //If This page is draft, return
218 if ( isset( $post->post_status ) && 'auto-draft' == $post->post_status ) {
219 return;
220 }
221
222 // Autosave, do nothing
223 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
224 return;
225 }
226
227 // Return if it's a post revision
228 if ( false !== wp_is_post_revision( $post_id ) ) {
229 return;
230 }
231
232 $parsed_content = $this->get_parsed_content( $post_id, $post, $post_type );
233
234 if ( is_array( $parsed_content ) && ! empty( $parsed_content ) ) {
235 $this->write_css_from_content( $post, $post_id, $parsed_content );
236 }
237 }
238
239 private function get_parsed_content( $post_id, $post, $post_type ) {
240 if ( $post_type === 'wp_template_part' || $post_type === 'wp_template' ) {
241 $post = get_post( $post_id );
242 }
243
244 $parsed_content = parse_blocks( $post->post_content );
245
246 if ( empty( $parsed_content ) ) {
247 delete_post_meta( $post_id, '_betterdocs_reusable_block_ids' );
248 }
249
250 return $parsed_content;
251 }
252
253 /**
254 * Get post content when page is load in frontend
255 */
256 public function generate_post_content() {
257 $post_id = get_the_ID();
258 if ( $post_id ) {
259 $post_type = get_post_type( $post_id );
260 $post = get_post( $post_id );
261 //If This page is draft, return
262 if ( isset( $post->post_status ) && 'auto-draft' == $post->post_status ) {
263 return;
264 }
265
266 // Return if it's a post revision
267 if ( false !== wp_is_post_revision( $post_id ) ) {
268 return null;
269 }
270
271 $parsed_content = $this->get_parsed_content( $post_id, $post, $post_type );
272
273 if ( is_array( $parsed_content ) && ! empty( $parsed_content ) ) {
274 $this->write_css_from_content( $post, $post_id, $parsed_content );
275 }
276 }
277 }
278
279 /**
280 * Ajax callback to write css in upload directory
281 * @retun void
282 * @since 1.0.2
283 */
284 private function write_block_css( $block_styles, $post ) {
285 //Write CSS for FSE
286 if ( isset( $post->post_type ) && ( $post->post_type === "wp_template_part" || $post->post_type === "wp_template" && ! empty( $block_styles ) ) ) {
287 $this->single_file_css_generator( $block_styles, $this->style_dir, $this->prefix . '-edit-site.min.css' );
288 } // Write CSS for Page/Posts
289 else {
290 if ( ! empty( $css = CSSParser::build_css( $block_styles ) ) ) {
291 if ( ! file_exists( $this->style_dir ) ) {
292 mkdir( $this->style_dir );
293 }
294 file_put_contents( $this->style_dir . $this->prefix . '-' . abs( $post->ID ) . '.min.css', $css );
295 }
296 }
297 }
298
299 /**
300 * Write css for Reusable block
301 * @retun void
302 * @since 3.4.0
303 */
304 private function write_reusable_block_css( $block_styles, $id ) {
305 if ( isset( $block_styles ) && is_array( $block_styles ) ) {
306 if ( ! empty( $css = CSSParser::build_css( $block_styles ) ) ) {
307 $upload_dir = $this->style_dir . 'reusable-blocks/';
308 if ( ! file_exists( $upload_dir ) ) {
309 mkdir( $upload_dir, 0777, true );
310 }
311 file_put_contents( $upload_dir . '/' . 'betterdocs-reusable-' . abs( $id ) . '.min.css', $css );
312 }
313 }
314 }
315
316 /**
317 * Single file css generator
318 * @retun void
319 * @since 3.5.3
320 */
321 private function single_file_css_generator( $block_styles, $upload_dir, $filename ) {
322 $editSiteCssPath = $upload_dir . $filename;
323 if ( file_exists( $editSiteCssPath ) ) {
324 $existingCss = file_get_contents( $editSiteCssPath );
325 $pattern = "~\/\*(.*?)\*\/~";
326 preg_match_all( $pattern, $existingCss, $result, PREG_PATTERN_ORDER );
327 $allComments = $result[0];
328 $seperatedIds = [];
329 foreach ( $allComments as $comment ) {
330 $id = preg_replace( '/[^A-Za-z0-9\-]|Ends|Starts/', '', $comment );
331
332 if ( strpos( $comment, "Starts" ) ) {
333 $seperatedIds[ $id ]['start'] = $comment;
334 } else if ( strpos( $comment, "Ends" ) ) {
335 $seperatedIds[ $id ]['end'] = $comment;
336 }
337 }
338
339 $seperateStyles = [];
340 foreach ( $seperatedIds as $key => $ids ) {
341 $seperateStyles[][ $key ] = isset( $block_styles[ $key ] ) ? $block_styles[ $key ] : [];
342 }
343
344 self::$_block_styles = array_merge( self::$_block_styles, $block_styles );
345
346 if ( ! empty( $css = CSSParser::build_css( self::$_block_styles ) ) ) {
347 if ( ! file_exists( $upload_dir ) ) {
348 mkdir( $upload_dir );
349 }
350
351 file_put_contents( $editSiteCssPath, $css );
352 }
353 } else {
354 if ( ! empty( $css = CSSParser::build_css( $block_styles ) ) ) {
355 if ( ! file_exists( $this->style_dir ) ) {
356 mkdir( $this->style_dir );
357 }
358
359 file_put_contents( $editSiteCssPath, $css );
360 }
361 }
362 }
363
364 /**
365 * Get post id by post_name for template
366 */
367 public static function betterdocs_get_post_content_by_post_name( $post_name ) {
368 global $wpdb;
369 $sql = $wpdb->prepare( "SELECT ID FROM {$wpdb->prefix}posts WHERE post_name = %s", $post_name );
370
371 return $wpdb->get_results( $sql, ARRAY_A );
372 }
373 }
374