PluginProbe
aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder / 2.13.0
aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder v2.13.0
2.13.0 2.13.1 2.12.0 2.11.1 2.11.0 2.10.0 2.9.0 2.7.4 2.7.5 2.7.6 2.7.7 2.8.0 2.8.1 2.9.1 trunk 1.0 1.0-beta1 1.0-beta2 1.0-beta3 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.2 All 80 releases
ablocks / includes / classes / assets-generator.php

assets-generator.php in aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder 2.13.0, at includes/classes/assets-generator.php

280 lines 10.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace ABlocks\Classes;
3
4 use ABlocks\Classes\FileUpload;
5 use ABlocks\Classes\RegisterScripts;
6
7 class AssetsGenerator {
8 public static function write_frontend_css_in_uploads_folder( $file_name, $parse_blocks_content ) {
9 if ( empty( $file_name ) ) {
10 return;
11 }
12
13 $style_depends = [];
14 $scripts_depends = [];
15 $aBlocks = [];
16 $seen_refs = [];
17 $global_classes = [];
18
19 self::recursive_block_parser( $parse_blocks_content, $aBlocks, $style_depends, $scripts_depends, $seen_refs, $global_classes );
20
21 // Dedupe once, after the whole block tree has been walked.
22 $style_depends = array_unique( $style_depends );
23 $scripts_depends = array_unique( $scripts_depends );
24
25 $register_styles = RegisterScripts::get_register_styles();
26 $register_scripts = RegisterScripts::get_register_scripts();
27 $library_css = '';
28 $static_css = '';
29 $dynamic_css = '';
30 $static_js = '';
31 $library_js = '';
32
33 foreach ( $aBlocks as $aBlock ) {
34 if ( isset( $aBlock['static_js'] ) ) {
35 $static_js .= $aBlock['static_js'] . "\n";
36 }
37 if ( isset( $aBlock['dynamic_style'] ) ) {
38 $dynamic_css .= $aBlock['dynamic_style'] . "\n";
39 }
40
41 if ( isset( $aBlock['static_style'] ) ) {
42 $static_css .= self::minify_css( $aBlock['static_style'] ) . "\n";
43 }
44 }
45
46 // css
47 foreach ( $style_depends as $style_depend ) {
48 if ( isset( $register_styles[ $style_depend ]['path'] ) ) {
49 $library_css .= self::read_library_file( $register_styles[ $style_depend ]['path'] ) . "\n";
50 }
51 }
52
53 // js
54 foreach ( $scripts_depends as $script_depend ) {
55 if ( isset( $register_scripts[ $script_depend ]['path'] ) ) {
56 $library_js .= self::read_library_file( $register_scripts[ $script_depend ]['path'] ) . "\n";
57 }
58 }
59
60 // Merge per-instance rules that share an identical body into a single
61 // grouped selector (see CssDedupe). Always applied during generation —
62 // it produces byte-for-byte equivalent styling, just smaller. Filterable
63 // as a code-level escape hatch (no UI setting).
64 if ( (bool) apply_filters( 'ablocks/perf/dedupe_css', true ) ) {
65 $dynamic_css = CssDedupe::process( $dynamic_css );
66 }
67
68 // Global classes, narrowed to the ones this page uses. They sit between
69 // the blocks' static CSS and their per-instance rules so a block's own
70 // styles still win over a class it carries.
71 $global_css = GlobalClasses::compiled_css_for(
72 array_values( array_unique( $global_classes ) )
73 );
74
75 $FileUpload = new FileUpload();
76 $destination_folder = $FileUpload->get_upload_dir();
77 self::copy_build_image_folder_to_uploads( $destination_folder );
78 $FileUpload->create_file( $file_name . '.min.css', $library_css . $static_css . $global_css . $dynamic_css );
79 $FileUpload->create_file( $file_name . '.min.js', $library_js . $static_js );
80
81 return $aBlocks;
82 }
83
84 public static function recursive_block_parser( $parse_content, &$aBlocks, &$style_depends, &$scripts_depends, &$seen_refs = [], &$global_classes = [] ) {
85 if ( count( $parse_content ) > 0 ) {
86 foreach ( $parse_content as $item ) {
87 if ( ! empty( $item['blockName'] ) ) {
88 // Handle reusable blocks or patterns using "ref"
89 if ( $item['blockName'] === 'core/block' && ! empty( $item['attrs']['ref'] ) ) {
90 $ref_post_id = (int) $item['attrs']['ref'];
91 // Guard against circular / repeated reusable-block references.
92 if ( in_array( $ref_post_id, $seen_refs, true ) ) {
93 continue;
94 }
95 $seen_refs[] = $ref_post_id;
96 $ref_post = get_post( $ref_post_id ); // Get the reusable block or pattern
97 if ( $ref_post ) {
98 $ref_content = parse_blocks( $ref_post->post_content ); // Parse the reusable block's content
99 self::recursive_block_parser( $ref_content, $aBlocks, $style_depends, $scripts_depends, $seen_refs, $global_classes ); // Recursively parse the referenced block/pattern
100 }
101 } elseif ( strpos( $item['blockName'], 'ablocks' ) !== false ) {
102 // Which global classes this page actually needs. Collected
103 // from the walk rather than from a stored per-post index:
104 // the walk is happening anyway, and an index would be one
105 // more thing to keep in sync with the content.
106 if ( ! empty( $item['attrs']['globalClasses'] ) && is_array( $item['attrs']['globalClasses'] ) ) {
107 foreach ( $item['attrs']['globalClasses'] as $global_class_id ) {
108 $global_classes[] = (string) $global_class_id;
109 }
110 }
111
112 $block_name_class = str_replace( ' ', '', ucwords( str_replace( '-', ' ', explode( '/', $item['blockName'] )[1] ) ) );
113
114 $dynamic_class = '\\ABlocks\\Blocks\\' . $block_name_class . '\\Block';
115 if ( ! class_exists( $dynamic_class ) ) {
116 $dynamic_class = '\\ABlocksPro\\Blocks\\' . $block_name_class . '\\Block';
117 }
118
119 if ( class_exists( $dynamic_class ) ) {
120 $instance = new $dynamic_class( true );
121 $attributes = wp_parse_args( $item['attrs'], array_map( function( $attr ) {
122 return isset( $attr['default'] ) ? $attr['default'] : '';
123 }, $instance->get_attributes() ));
124
125 // Capture static CSS/JS
126 if ( ! isset( $aBlocks[ $item['blockName'] ] ) ) {
127 $aBlocks[ $item['blockName'] ] = [
128 'static_style' => $instance->get_static_css(),
129 'static_js' => $instance->get_static_js(),
130 ];
131 }
132 // 'ablocks-animate-style',
133 // if animation is used then added ablocks-animate-style dependency
134 if ( isset( $attributes['_animation']['animationType'] ) && ! empty( $attributes['_animation']['animationType'] ) && $attributes['_animation']['animationType'] !== 'none' ) {
135 $style_depends[] = 'ablocks-animate-style';
136 }
137 // Capture library scripts. Just accumulate here; the caller
138 // dedups once after the full tree is walked (avoids O(n^2)
139 // array_unique on every block).
140 $style_depends = array_merge( $style_depends, $instance->get_style_depends() );
141 $scripts_depends = array_merge( $scripts_depends, $instance->get_script_depends() );
142
143 // Capture dynamic CSS.
144 //
145 // A block can be reached twice in one walk: on an FSE theme
146 // `pre_render_block` collects EVERY block — parents and their
147 // children alike — and this parser also recurses into
148 // `innerBlocks`, so a nested block is seen once as a child and
149 // once as a collected top-level entry. Compiling it a second
150 // time is not idempotent for blocks whose CSS is emitted
151 // through a per-request dedupe (the atomic blocks' hashed
152 // `ablocks-s-*` rules): the repeat build returns an empty
153 // string because the rule was already emitted, and writing
154 // that over the first result stripped every nested atomic
155 // block's styles from the page. Keep the first result.
156 if ( isset( $item['attrs']['ref'] ) || isset( $item['attrs']['block_id'] ) ) {
157 $block_id_or_ref = ! empty( $item['attrs']['block_id'] ) ? $item['attrs']['block_id'] : 'core_pattern_ref_' . $item['attrs']['ref'];
158 if ( ! isset( $aBlocks[ $block_id_or_ref ]['dynamic_style'] ) ) {
159 $aBlocks[ $block_id_or_ref ] = [
160 'block_name' => $item['blockName'],
161 'dynamic_style' => $instance->build_css( $attributes ),
162 ];
163 }
164 }
165 }//end if
166 }//end if
167
168 // Check for inner blocks and recursively process them
169 if ( is_array( $item['innerBlocks'] ) && count( $item['innerBlocks'] ) ) {
170 self::recursive_block_parser( $item['innerBlocks'], $aBlocks, $style_depends, $scripts_depends, $seen_refs, $global_classes );
171 }
172 }//end if
173 }//end foreach
174 }//end if
175 }
176
177 /**
178 * Read a static library asset file, cached per request so the same library
179 * file isn't re-read from disk when multiple pages are generated in one
180 * request (e.g. regenerate-all).
181 */
182 private static $library_file_cache = [];
183 private static function read_library_file( $path ) {
184 if ( ! isset( self::$library_file_cache[ $path ] ) ) {
185 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
186 self::$library_file_cache[ $path ] = (string) file_get_contents( $path );
187 }
188 return self::$library_file_cache[ $path ];
189 }
190
191 public static function minify_css( $css_string ) {
192 // Remove comments (/* ... */) using regex
193 $css_string = preg_replace( '/\/\*[\s\S]*?\*\//', '', $css_string );
194
195 // Remove double spaces, tabs, and newlines
196 $css_string = preg_replace( '/\s+/', ' ', $css_string );
197
198 // Remove spaces around colons, semicolons, curly braces, and commas
199 $css_string = preg_replace( '/\s?([:,;{}])\s?/', '$1', $css_string );
200
201 return $css_string;
202 }
203
204 public static function copy_build_image_folder_to_uploads( $destination_folder ) {
205 global $wp_filesystem;
206 if ( empty( $wp_filesystem ) ) {
207 require_once ABSPATH . '/wp-admin/includes/file.php';
208 WP_Filesystem();
209 }
210
211 $source_folders = [
212 ABLOCKS_ASSETS_PATH . 'build/images/', // First folder
213 ABLOCKS_ASSETS_PATH . 'library/leaflet/', // Add more folders as needed
214 // Add more source folders here
215 ];
216
217 if ( ! file_exists( $destination_folder ) ) {
218 wp_mkdir_p( $destination_folder );
219 }
220
221 // Loop through each source folder and copy its contents
222 foreach ( $source_folders as $source_folder ) {
223 if ( file_exists( $source_folder ) ) {
224 self::recursive_copy( $source_folder, $destination_folder );
225 }
226 }
227 }
228
229 public static function recursive_copy( $source, $destination ) {
230 global $wp_filesystem;
231 if ( empty( $wp_filesystem ) ) {
232 require_once ABSPATH . '/wp-admin/includes/file.php';
233 WP_Filesystem();
234 }
235
236 $valid_extensions = [ 'jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp' ];
237
238 // Ensure WP_Filesystem is initialized
239 if ( ! is_object( $GLOBALS['wp_filesystem'] ) ) {
240 request_filesystem_credentials( '', '', true );
241 }
242
243 // Initialize the WP_Filesystem object
244 $wp_file_system = $GLOBALS['wp_filesystem'];
245
246 // Open the source directory
247 $dir = opendir( $source );
248
249 // Create the destination directory using WP_Filesystem
250 if ( ! $wp_file_system->is_dir( $destination ) ) {
251 $wp_file_system->mkdir( $destination );
252 }
253
254 // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
255 while ( false !== ( $file = readdir( $dir ) ) ) {
256 if ( ( $file !== '.' ) && ( $file !== '..' ) ) {
257 $source_file_path = $source . '/' . $file;
258 $destination_file_path = $destination . '/' . $file;
259
260 // If it's a directory, recursively copy its contents
261 if ( is_dir( $source_file_path ) ) {
262 self::recursive_copy( $source_file_path, $destination_file_path );
263 } else {
264 // Get file extension
265 $file_extension = pathinfo( $file, PATHINFO_EXTENSION );
266
267 // Check if the file has a valid image extension
268 if ( in_array( strtolower( $file_extension ), $valid_extensions, true ) ) {
269 // Copy only image files
270 $wp_file_system->copy( $source_file_path, $destination_file_path );
271 }
272 }
273 }
274 }
275
276 // Close the directory handle
277 closedir( $dir );
278 }
279 }
280