PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 3.8.1
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v3.8.1
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.8.1, at includes/Modules/StyleHandler.php

373 lines 13.1 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 $deps = apply_filters( 'betterdocs_generated_css_frontend_deps', [] );
156
157 // generatepress elements
158 if ( in_array( 'gp-premium/gp-premium.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
159 $gp_elements = get_posts( [ 'post_type' => 'gp_elements' ] );
160 if ( is_array( $gp_elements ) && ! empty( $gp_elements ) ) {
161 foreach ( $gp_elements as $element ) {
162 if ( file_exists( $this->style_dir . $this->prefix . '-' . $element->ID . '.min.css' ) ) {
163 wp_enqueue_style( 'betterdocs-block-style-' . $element->ID, $this->style_url . $this->prefix . '-' . $element->ID . '.min.css', $deps, substr( md5( microtime( true ) ), 0, 10 ) );
164 }
165 }
166 }
167 }
168
169 if ( ! empty( $post ) && ! empty( $post->ID ) ) {
170 //Page/Post Style Enqueue
171 if ( file_exists( $this->style_dir . $this->prefix . '-' . $post->ID . '.min.css' ) ) {
172 wp_enqueue_style( 'betterdocs-block-style-' . $post->ID, $this->style_url . $this->prefix . '-' . $post->ID . '.min.css', $deps, substr( md5( microtime( true ) ), 0, 10 ) );
173 }
174
175 // Reusable block Style Enqueues
176 $reusableIds = get_post_meta( $post->ID, '_betterdocs_reusable_block_ids', true );
177 $reusableIds = ! empty( $reusableIds ) ? $reusableIds : [];
178 $templateReusableIds = get_option( '_betterdocs_reusable_block_ids', [] );
179 $reusableIds = array_unique( array_merge( $reusableIds, $templateReusableIds ) );
180 if ( ! empty( $reusableIds ) ) {
181 foreach ( $reusableIds as $reusableId ) {
182 if ( file_exists( $this->style_dir . 'reusable-blocks/betterdocs-reusable-' . $reusableId . '.min.css' ) ) {
183 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 ) );
184 }
185 }
186 }
187 }
188
189 //Widget Style Enqueue
190 if ( file_exists( $this->style_dir . $this->prefix . '-widget.min.css' ) ) {
191 wp_enqueue_style( 'betterdocs-widget-style', $this->style_url . $this->prefix . '-widget.min.css', $deps, substr( md5( microtime( true ) ), 0, 10 ) );
192 }
193
194 //FSE Style Enqueue
195 if ( function_exists( 'wp_is_block_theme' ) && wp_is_block_theme() && file_exists( $this->style_dir . $this->prefix . '-edit-site.min.css' ) && betterdocs()->helper->is_templates() ) {
196 wp_enqueue_style( 'betterdocs-fullsite-style', $this->style_url . $this->prefix . '-edit-site.min.css', $deps, substr( md5( microtime( true ) ), 0, 10 ) );
197 }
198
199 /**
200 * Hooks assets for enqueue in frontend
201 *
202 * @param $path string
203 * @param $url string
204 *
205 * @since 3.0.0
206 */
207 do_action( 'betterdocs_frontend_assets', $this->style_dir, $this->style_url );
208 }
209
210 /**
211 * Get post content when page is saved
212 */
213 public function on_save_post( $post_id, $post, $update ) {
214 $post_type = get_post_type( $post_id );
215
216 //If This page is draft, return
217 if ( isset( $post->post_status ) && 'auto-draft' == $post->post_status ) {
218 return;
219 }
220
221 // Autosave, do nothing
222 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
223 return;
224 }
225
226 // Return if it's a post revision
227 if ( false !== wp_is_post_revision( $post_id ) ) {
228 return;
229 }
230
231 $parsed_content = $this->get_parsed_content( $post_id, $post, $post_type );
232
233 if ( is_array( $parsed_content ) && ! empty( $parsed_content ) ) {
234 $this->write_css_from_content( $post, $post_id, $parsed_content );
235 }
236 }
237
238 private function get_parsed_content( $post_id, $post, $post_type ) {
239 if ( $post_type === 'wp_template_part' || $post_type === 'wp_template' ) {
240 $post = get_post( $post_id );
241 }
242
243 $parsed_content = parse_blocks( $post->post_content );
244
245 if ( empty( $parsed_content ) ) {
246 delete_post_meta( $post_id, '_betterdocs_reusable_block_ids' );
247 }
248
249 return $parsed_content;
250 }
251
252 /**
253 * Get post content when page is load in frontend
254 */
255 public function generate_post_content() {
256 $post_id = get_the_ID();
257 if ( $post_id ) {
258 $post_type = get_post_type( $post_id );
259 $post = get_post( $post_id );
260 //If This page is draft, return
261 if ( isset( $post->post_status ) && 'auto-draft' == $post->post_status ) {
262 return;
263 }
264
265 // Return if it's a post revision
266 if ( false !== wp_is_post_revision( $post_id ) ) {
267 return null;
268 }
269
270 $parsed_content = $this->get_parsed_content( $post_id, $post, $post_type );
271
272 if ( is_array( $parsed_content ) && ! empty( $parsed_content ) ) {
273 $this->write_css_from_content( $post, $post_id, $parsed_content );
274 }
275 }
276 }
277
278 /**
279 * Ajax callback to write css in upload directory
280 * @retun void
281 * @since 1.0.2
282 */
283 private function write_block_css( $block_styles, $post ) {
284 //Write CSS for FSE
285 if ( isset( $post->post_type ) && ( $post->post_type === "wp_template_part" || $post->post_type === "wp_template" && ! empty( $block_styles ) ) ) {
286 $this->single_file_css_generator( $block_styles, $this->style_dir, $this->prefix . '-edit-site.min.css' );
287 } // Write CSS for Page/Posts
288 else {
289 if ( ! empty( $css = CSSParser::build_css( $block_styles ) ) ) {
290 if ( ! file_exists( $this->style_dir ) ) {
291 mkdir( $this->style_dir );
292 }
293 file_put_contents( $this->style_dir . $this->prefix . '-' . abs( $post->ID ) . '.min.css', $css );
294 }
295 }
296 }
297
298 /**
299 * Write css for Reusable block
300 * @retun void
301 * @since 3.4.0
302 */
303 private function write_reusable_block_css( $block_styles, $id ) {
304 if ( isset( $block_styles ) && is_array( $block_styles ) ) {
305 if ( ! empty( $css = CSSParser::build_css( $block_styles ) ) ) {
306 $upload_dir = $this->style_dir . 'reusable-blocks/';
307 if ( ! file_exists( $upload_dir ) ) {
308 mkdir( $upload_dir, 0777, true );
309 }
310 file_put_contents( $upload_dir . '/' . 'betterdocs-reusable-' . abs( $id ) . '.min.css', $css );
311 }
312 }
313 }
314
315 /**
316 * Single file css generator
317 * @retun void
318 * @since 3.5.3
319 */
320 private function single_file_css_generator( $block_styles, $upload_dir, $filename ) {
321 $editSiteCssPath = $upload_dir . $filename;
322 if ( file_exists( $editSiteCssPath ) ) {
323 $existingCss = file_get_contents( $editSiteCssPath );
324 $pattern = "~\/\*(.*?)\*\/~";
325 preg_match_all( $pattern, $existingCss, $result, PREG_PATTERN_ORDER );
326 $allComments = $result[0];
327 $seperatedIds = [];
328 foreach ( $allComments as $comment ) {
329 $id = preg_replace( '/[^A-Za-z0-9\-]|Ends|Starts/', '', $comment );
330
331 if ( strpos( $comment, "Starts" ) ) {
332 $seperatedIds[ $id ]['start'] = $comment;
333 } else if ( strpos( $comment, "Ends" ) ) {
334 $seperatedIds[ $id ]['end'] = $comment;
335 }
336 }
337
338 $seperateStyles = [];
339 foreach ( $seperatedIds as $key => $ids ) {
340 $seperateStyles[][ $key ] = isset( $block_styles[ $key ] ) ? $block_styles[ $key ] : [];
341 }
342
343 self::$_block_styles = array_merge( self::$_block_styles, $block_styles );
344
345 if ( ! empty( $css = CSSParser::build_css( self::$_block_styles ) ) ) {
346 if ( ! file_exists( $upload_dir ) ) {
347 mkdir( $upload_dir );
348 }
349
350 file_put_contents( $editSiteCssPath, $css );
351 }
352 } else {
353 if ( ! empty( $css = CSSParser::build_css( $block_styles ) ) ) {
354 if ( ! file_exists( $this->style_dir ) ) {
355 mkdir( $this->style_dir );
356 }
357
358 file_put_contents( $editSiteCssPath, $css );
359 }
360 }
361 }
362
363 /**
364 * Get post id by post_name for template
365 */
366 public static function betterdocs_get_post_content_by_post_name( $post_name ) {
367 global $wpdb;
368 $sql = $wpdb->prepare( "SELECT ID FROM {$wpdb->prefix}posts WHERE post_name = %s", $post_name );
369
370 return $wpdb->get_results( $sql, ARRAY_A );
371 }
372 }
373