PluginProbe
TableKit – WordPress Table Builder for Data Tables, WooCommerce Product Tables & Post Tables / 2.0.1
TableKit – WordPress Table Builder for Data Tables, WooCommerce Product Tables & Post Tables v2.0.1
2.3.0 2.2.14 2.2.13 2.2.12 2.2.11 2.2.10 2.2.9 2.2.8 2.2.7 2.2.6 2.2.5 2.2.4 2.2.3 trunk 1.0.0 1.0.1 2.0.0 2.0.1 2.1.0 2.1.1 2.1.2 2.2.0 2.2.1 2.2.2
table-builder-block / includes / Hooks / AssetGenerator.php

AssetGenerator.php in TableKit – WordPress Table Builder for Data Tables, WooCommerce Product Tables & Post Tables 2.0.1, at includes/Hooks/AssetGenerator.php

358 lines 12.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace TableBuilder\Hooks;
3
4 defined('ABSPATH') || exit;
5
6 class AssetGenerator {
7
8 use \TableBuilder\Traits\Singleton;
9
10 /**
11 * Defining css
12 */
13 public $css = '';
14
15 /**
16 * Defining fonts
17 */
18 protected $fonts = array();
19
20 /**
21 * AssetGenerator class constructor.
22 * private for singleton
23 *
24 * @return void
25 * @since 1.0.0
26 */
27 public function __construct() {
28 add_action('save_post', array($this, 'save_post_hook'), 10, 3);
29 add_filter('render_block_data', array($this, 'set_blocks_css'), 10);
30 add_filter('wp_resource_hints', array($this, 'fonts_resource_hints'), 10, 2);
31 add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'), 10);
32 add_action('enqueue_block_assets', array($this, 'block_assets'), 10);
33 }
34
35 /**
36 * Filters an array of blocks and returns only those where the block name contains 'tablebuilder'.
37 *
38 * @param array $blocks An array of blocks. Each block is an associative array that must contain a 'blockName' key. Default is an empty array.
39 * @return array Returns an array of blocks where the block name contains 'tablebuilder'.
40 */
41 public function filter_blocks($blocks = array()) {
42 $filtered_blocks = [];
43
44 foreach ($blocks as $block) {
45 if (isset($block['blockName']) && strpos($block['blockName'], 'tablebuilder') !== false) {
46 $filtered_blocks[] = $block;
47 }
48
49 if (!empty($block['innerBlocks'])) {
50 $filtered_blocks = array_merge($filtered_blocks, $this->filter_blocks($block['innerBlocks']));
51 }
52 }
53
54 return $filtered_blocks;
55 }
56
57 /**
58 * Minify CSS by condensing white spaces and removing comments.
59 *
60 * @param string $css The input CSS.
61 * @return string Minified CSS.
62 */
63 public function minimize_css($css) {
64 if (trim($css) === '') {
65 return $css;
66 }
67
68 return preg_replace(
69 array(
70 '#("(?:[^"\\\]++|\\\.)*+"|\'(?:[^\'\\\\]++|\\\.)*+\')|\/\*(?!\!)(?>.*?\*\/)|^\s*|\s*$#s',
71 '#("(?:[^"\\\]++|\\\.)*+"|\'(?:[^\'\\\\]++|\\\.)*+\'|\/\*(?>.*?\*\/))|\s*+;\s*+(})\s*+|\s*+([*$~^|]?+=|[{};,>~]|\s(?![0-9\.])|!important\b)\s*+|([[(:])\s++|\s++([])])|\s++(:)\s*+(?!(?>[^{}"\']++|"(?:[^"\\\]++|\\\.)*+"|\'(?:[^\'\\\\]++|\\\.)*+\')*+{)|^\s++|\s++\z|(\s)\s+#si',
72 '#(?<=[\s:])(0)(cm|em|ex|in|mm|pc|pt|px|vh|vw|%)#si',
73 '#:(0\s+0|0\s+0\s+0\s+0)(?=[;\}]|\!important)#i',
74 '#(background-position):0(?=[;\}])#si',
75 '#(?<=[\s:,\-])0+\.(\d+)#s',
76 '#(\/\*(?>.*?\*\/))|(?<!content\:)([\'"])([a-z_][a-z0-9\-_]*?)\2(?=[\s\{\}\];,])#si',
77 '#(\/\*(?>.*?\*\/))|(\burl\()([\'"])([^\s]+?)\3(\))#si',
78 '#(?<=[\{;])(border|outline):none(?=[;\}\!])#',
79 '#(\/\*(?>.*?\*\/))|(^|[\{\}])(?:[^\s\{\}]+)\{\}#s',
80 ),
81 array(
82 '$1',
83 '$1$2$3$4$5$6$7',
84 '$1',
85 ':0',
86 '$1:0 0',
87 '.$1',
88 '$1$3',
89 '$1$2$4$5',
90 '$1$2$3',
91 '$1:0',
92 '$1$2',
93 ),
94 $css
95 );
96 }
97
98 /**
99 * Fires once a post has been saved.
100 *
101 * @param int $post_id The ID of the post.
102 * @param WP_Post $post The post object.
103 * @param bool $update Whether this is an existing post being updated.
104 * @return void
105 */
106 public function save_post_hook($post_id, $post, $update) {
107 // Bail out if is draft, revision, or autosave
108 if ('auto-draft' === $post->post_status || wp_is_post_revision($post_id) || defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
109 return;
110 }
111
112 // Skip if this is an autosave or update
113 if (!$update) {
114 return;
115 }
116
117 $post = get_post($post_id);
118 $parse_blocks = $this->filter_blocks(parse_blocks($post->post_content));
119
120 if ($parse_blocks) {
121 $fse = in_array($post->post_type, ['wp_template_part', 'wp_template']);
122 if ($fse) {
123 $this->set_fonts(null, $this->generate_fse_assets(), true);
124 } else {
125 $this->set_fonts($post_id, $parse_blocks);
126 }
127 }
128 }
129
130 /**
131 * Generate assets for templates.
132 *
133 * @return array $filtered_blocks The filtered blocks for FSE templates.
134 */
135 protected function generate_fse_assets() {
136 $args = [
137 'post_type' => ['wp_template_part', 'wp_template'],
138 'posts_per_page' => -1,
139 ];
140
141 $posts = get_posts($args);
142 $merged_blocks = [];
143
144 foreach ($posts as $post) {
145 $merged_blocks = array_merge($merged_blocks, parse_blocks($post->post_content));
146 }
147
148 return $this->filter_blocks($merged_blocks);
149 }
150
151 /**
152 * Set the fonts for a given post or Full Site Editing (FSE) template.
153 *
154 * @param int $post_id The ID of the post or FSE template.
155 * @param array $blocks An array of blocks.
156 * @param bool $fse Whether this is an FSE template.
157 * @return void
158 */
159 protected function set_fonts($post_id, $blocks, $fse = false) {
160 $fonts = [];
161
162 foreach ($blocks as $block) {
163 if (isset($block['attrs'])) {
164 $typographies = array_filter($block['attrs'], function ($key) {
165 return str_contains(strtolower($key), 'typography');
166 }, ARRAY_FILTER_USE_KEY);
167
168 if (!empty($typographies)) {
169 foreach ($typographies as $typography) {
170 $font_weight = !empty($typography['fontWeight']['value']) ? $typography['fontWeight']['value'] : 400;
171 if (!empty($typography['fontFamily']['value'])) {
172 $fonts[$typography['fontFamily']['value']][] = $font_weight;
173 }
174 }
175 }
176 }
177 }
178
179 // Update fonts
180 if (!empty($fonts)) {
181 if ($fse) {
182 update_option('table_builder_fse_fonts', $fonts);
183 } else {
184 update_post_meta($post_id, 'table_builder_posts_fonts', $fonts);
185 }
186 } else {
187 if ($fse) {
188 delete_option('table_builder_fse_fonts');
189 } else {
190 delete_post_meta($post_id, 'table_builder_posts_fonts');
191 }
192 }
193 }
194
195 /**
196 * Combine block assets (CSS and JS) based on the used blocks.
197 *
198 * @param array $parsed_block The parsed block.
199 * @return string Combined CSS content.
200 */
201 protected function combine_blocks_assets($parsed_block = []) {
202 $blocks_css = [];
203
204 if (isset($parsed_block['blockName']) && strpos($parsed_block['blockName'], 'tablebuilder') !== false) {
205 if (isset($parsed_block['attrs']['blocksCSS'])) {
206 foreach ($parsed_block['attrs']['blocksCSS'] as $device => $css) {
207 if (!isset($blocks_css[$device])) {
208 $blocks_css[$device] = '';
209 }
210 $blocks_css[$device] .= $css;
211 }
212 }
213
214 if (isset($parsed_block['attrs'])) {
215 $typographies = array_filter($parsed_block['attrs'], function ($key) {
216 return str_contains(strtolower($key), 'typography');
217 }, ARRAY_FILTER_USE_KEY);
218
219 if (!empty($typographies)) {
220 foreach ($typographies as $typography) {
221 $font_weight = !empty($typography['fontWeight']['value']) ? $typography['fontWeight']['value'] : 400;
222 if (!empty($typography['fontFamily']['value'])) {
223 $this->fonts[$typography['fontFamily']['value']][] = $font_weight;
224 }
225 }
226 }
227 }
228 }
229
230 // Concatenate CSS content into a single string
231 $css_content = '';
232 $is_custom_styles_added = false;
233 $device_list = \TableBuilder\Helpers\Utils::get_device_list();
234
235 if (!empty($blocks_css)) {
236 foreach ($device_list as $device) {
237 foreach ($blocks_css as $key => $block) {
238 if (!empty($block) && trim($block) !== '') {
239 $direction = $device['direction'] ?? 'max';
240 $width = $device['value'] ?? '';
241 $device_key = strtolower($device['slug'] ?? '');
242
243 if ('base' === $device['value'] && 'desktop' === $key) {
244 $css_content .= $block;
245 } elseif (!empty($direction) && !empty($width) && $device_key === $key) {
246 $css_content .= "@media ({$direction}-width: {$width}px) {" . trim($block) . '}';
247 }
248
249 if ('customStyles' === $key && !$is_custom_styles_added) {
250 $is_custom_styles_added = true;
251 $css_content .= $block;
252 }
253 }
254 }
255 }
256 }
257
258 return $css_content;
259 }
260
261 /**
262 * Generate Google Fonts URL.
263 *
264 * @return string|bool Google Fonts URL or false if no fonts.
265 */
266 protected function generate_fonts_url() {
267 if (!empty($this->fonts)) {
268 $font_families = [];
269 $font_url = 'https://fonts.googleapis.com/css2?family=';
270
271 // Remove duplicates and sort the fonts
272 $all_fonts = array_map(function ($arr) {
273 $arr = array_unique($arr);
274 sort($arr);
275 return $arr;
276 }, $this->fonts);
277
278 foreach ($all_fonts as $font => $weights) {
279 $weights = array_map(function ($weight) {
280 $invalid_list = ['normal', 'inherit', 'initial'];
281 return in_array($weight, $invalid_list) ? '400' : $weight;
282 }, $weights);
283 sort($weights);
284 $font_families[] = str_replace(' ', '+', $font) . ':wght@' . implode(';', array_unique($weights));
285 }
286
287 $font_url .= implode('&family=', $font_families);
288 $font_url .= '&display=swap';
289
290 return $font_url;
291 }
292
293 return false;
294 }
295
296 /**
297 * Sets the CSS for the blocks.
298 *
299 * @param array $parsed_block The parsed block data.
300 * @return array The modified parsed block data.
301 */
302 public function set_blocks_css($parsed_block) {
303 $css_content = $this->combine_blocks_assets($parsed_block);
304 if (!empty($css_content)) {
305 $this->css .= $css_content;
306 }
307 return $parsed_block;
308 }
309
310 /**
311 * Add preconnect for Google Fonts.
312 *
313 * @param array $urls URLs to print for resource hints.
314 * @param string $relation_type The relation type the URLs are printed.
315 * @return array
316 */
317 public function fonts_resource_hints($urls, $relation_type) {
318 if (wp_style_is('table-builder-google-fonts', 'queue') && 'preconnect' === $relation_type) {
319 $urls[] = [
320 'href' => 'https://fonts.gstatic.com',
321 'crossorigin',
322 ];
323 }
324
325 return $urls;
326 }
327
328 /**
329 * Enqueues the Google Fonts stylesheet if available.
330 * Enqueues inline styles for the TableBuilder frontend.
331 */
332 public function enqueue_scripts() {
333 global $post;
334
335 if (!wp_is_block_theme() && !empty($post->post_content)) {
336 do_blocks($post->post_content);
337 }
338
339 $fonts_url = $this->generate_fonts_url();
340 if ($fonts_url) {
341 wp_enqueue_style('table-builder-google-fonts', $fonts_url, false, null);
342 }
343
344 if ($this->css) {
345 wp_add_inline_style('table-builder-style', $this->css);
346 }
347 }
348
349 /**
350 * Enqueues block assets (CSS & JS).
351 *
352 * @return void
353 */
354 public function block_assets() {
355 wp_enqueue_style('table-builder-style', get_stylesheet_uri());
356 }
357 }
358