PluginProbe
aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder / 2.11.1
aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder v2.11.1
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 / block-base-abstract.php

block-base-abstract.php in aBlocks – Gutenberg Blocks, User Dashboard Builder, Popup Builder, Form Builder & Animation Builder 2.11.1, at includes/classes/block-base-abstract.php

371 lines 13.4 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 if ( ! defined( 'ABSPATH' ) ) {
5 exit;
6 }
7
8 use \ABlocks\Helper;
9 use \ABlocks\Assets;
10
11 abstract class BlockBaseAbstract {
12
13 protected $namespace = 'ablocks';
14
15 protected $assets_url = ABLOCKS_ASSETS_URL;
16
17 protected $assets_path = ABLOCKS_ASSETS_PATH;
18
19 protected $blocks_dir_path = ABLOCKS_BLOCKS_DIR_PATH;
20
21 protected $parent_block_name = '';
22
23 protected $is_skip_inner_block = false;
24
25 protected $block_name = '';
26
27 protected $style_depends = [];
28 protected $script_depends = [];
29
30 protected $animation_settings = [];
31
32 public function __construct( $keep_silent = false ) {
33 if ( $this->is_enabled_block() && ! $keep_silent ) {
34 add_action( 'init', array( $this, 'register_block' ), 20 );
35 add_action( 'ablocks/before_render_' . $this->block_name . '_block_content', array( $this, 'enqueue_block_specific_static_assets' ) );
36 }
37 }
38
39 public function is_enabled_block() {
40 global $ablocks_blocks;
41 $block_name = ! empty( $this->parent_block_name ) ? $this->parent_block_name : $this->block_name;
42 if ( isset( $ablocks_blocks->{$block_name} ) ) {
43 return (bool) $ablocks_blocks->{$block_name};
44 }
45 return false;
46 }
47
48 public function register_block() {
49 $block_path = $this->assets_path . 'build/blocks/' . $this->block_name . '/block.json';
50 $args = [
51 'render_callback' => array( $this, 'render_callback' ),
52 ];
53 if ( isset( $metadata['usesContext'] ) ) {
54 $args['usesContext'] = $metadata['usesContext'];
55 }
56 if ( isset( $metadata['providesContext'] ) ) {
57 $args['providesContext'] = $metadata['providesContext'];
58 }
59 if ( $this->is_skip_inner_block ) {
60 $args['skip_inner_blocks'] = $this->is_skip_inner_block;
61 }
62 register_block_type( $block_path, $args );
63 }
64
65 public function get_attributes() {
66 $block_attributes = include $this->blocks_dir_path . $this->block_name . '/attributes.php';
67 return apply_filters( 'ablocks/register_block_attributes', $block_attributes, $this->block_name, $this->parent_block_name );
68 }
69
70 private function get_block_class( $css_class ) {
71 $classes = array();
72 if ( $css_class ) {
73 if ( ! is_array( $css_class ) ) {
74 $css_class = preg_split( '#\s+#', $css_class );
75 }
76 $classes = array_map( 'esc_attr', $css_class );
77 } else {
78 // Ensure that we always coerce class to being an array.
79 $css_class = array();
80 }
81
82 return 'class="' . esc_attr( implode( ' ', $classes ) ) . '"';
83 }
84 private function get_block_data_settings_attributes( $settings ) {
85 if ( ! is_array( $settings ) || empty( $settings['animationType'] ) || $settings['animationType'] !== 'none' ) {
86 return '';
87 }
88
89 // Sanitize each setting in the array
90 $sanitized_settings = array_map( 'esc_attr', $settings );
91 // Encode sanitized settings to JSON and escape it for safe output
92 return sprintf( 'data-settings="%s"', esc_attr( wp_json_encode( $sanitized_settings ) ) );
93 }
94 private function get_dynamic_block_wrap( $attributes, $content, $block_instance ) {
95 $block_id = ( isset( $attributes['block_id'] ) ? $attributes['block_id'] : '' );
96 $animation = ( isset( $attributes['_animation'] ) ? $attributes['_animation'] : [] );
97 $block_class_args = array( 'ablocks-block', 'ablocks-block-' . $block_id, 'ablocks-block--' . $this->block_name );
98 $has_transform = (bool) ( isset( $attributes['_transform']['isApplied'] ) ? $attributes['_transform']['isApplied'] : false );
99
100 if ( count( $animation ) && (
101 ( ! empty( $animation['animationType'] ) && $animation['animationType'] !== 'none' ) ||
102 ( ! empty( $animation['animationTypeTablet'] ) && $animation['animationTypeTablet'] !== 'none' ) ||
103 ( ! empty( $animation['animationTypeMobile'] ) && $animation['animationTypeMobile'] !== 'none' )
104 )
105 ) {
106 array_push( $block_class_args, 'ablocks-invisible' );
107 }
108
109 if ( $has_transform ) {
110 array_push( $block_class_args, 'ablocks-has-block-container' );
111 }
112
113 ob_start();
114 ?>
115 <div <?php echo $this->get_block_class( $block_class_args ) . $this->get_block_data_settings_attributes( $animation ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
116 <?php
117 if ( $has_transform ) :
118 ?>
119 <div class="ablocks-block-container">
120 <?php
121 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
122 echo $this->render_block_content( $attributes, $content, $block_instance );
123 ?>
124 </div>
125 <?php
126 else :
127 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
128 echo $this->render_block_content( $attributes, $content, $block_instance );
129 endif;
130 ?>
131 </div>
132 <?php
133 return ob_get_clean();
134 }
135
136
137 public function render_block_content( $attributes, $content, $block_instance ) {
138 return $content;
139 }
140
141 public function render_callback( $attributes, $content, $block_instance ) {
142 $block_name = $block_instance->name;
143 do_action( 'ablocks/before_render_' . explode( '/', $block_name )[1] . '_block_content', $block_name );
144 do_action( 'ablocks/render_callback', $block_name, $attributes );
145
146 // Animation CSS is no longer a global dependency (see get_style_depends).
147 // In the fallback path (asset-generation off) enqueue it only when this
148 // block actually animates, so pages without animation never load it.
149 $this->maybe_enqueue_animate_style( $attributes );
150
151 // Dynamic block
152 if ( ! $content || $this->is_skip_inner_block ) {
153 // When called from the editor's ServerSideRender (REST API), RenderContainer (JS) already
154 // provides the outer ablocks-block-{blockId} wrapper via useBlockProps. Including it here
155 // too causes the Advanced-settings CSS selector to match two elements → double border/padding.
156 if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
157 ob_start();
158 echo $this->render_block_content( $attributes, $content, $block_instance );
159 $content = ob_get_clean();
160 } else {
161 $content = $this->get_dynamic_block_wrap( $attributes, $content, $block_instance );
162 }
163 }
164
165 // Static Block but control render from php
166 $content = apply_filters( 'ablocks/get_render_block_content', $content, $attributes, $block_instance );
167 if ( ! $content ) {
168 return $content;
169 }
170
171 // Pure Static block
172 if ( apply_filters( 'ablocks/is_allow_block_inline_assets', ( ! is_admin() || defined( 'DOING_AJAX' ) && DOING_AJAX ) && ! Helper::is_enabled_assets_generation() ) ) {
173
174 $build_css = '<style>' . $this->build_css( $attributes ) . '</style>';
175
176 return $build_css . $content;
177 }
178
179 return $content;
180 }
181
182 /**
183 * file_exists + filemtime for a per-block asset, memoized per request so
184 * repeated instances of the same block don't re-stat the same file.
185 * Returns the mtime (cache-bust version) or false when the file is missing.
186 */
187 private static $asset_version_cache = [];
188 private function cached_asset_version( $path ) {
189 if ( ! array_key_exists( $path, self::$asset_version_cache ) ) {
190 self::$asset_version_cache[ $path ] = file_exists( $path ) ? filemtime( $path ) : false;
191 }
192 return self::$asset_version_cache[ $path ];
193 }
194
195 /**
196 * Enqueue the animate.css library only when a block actually uses an
197 * animation, and only in the fallback (per-block) asset path — with
198 * asset-generation on, the combined generator bakes it in on demand instead.
199 */
200 private function maybe_enqueue_animate_style( $attributes ) {
201 if ( is_admin() || Helper::is_enabled_assets_generation() ) {
202 return;
203 }
204 $animation_type = isset( $attributes['_animation']['animationType'] ) ? $attributes['_animation']['animationType'] : '';
205 if ( ! empty( $animation_type ) && 'none' !== $animation_type ) {
206 wp_enqueue_style( 'ablocks-animate-style' );
207 }
208 }
209
210 private function enqueue_static_assets( $block_name ) {
211 // Library
212 if ( count( $this->get_style_depends() ) ) {
213 foreach ( $this->get_style_depends() as $style_handler ) {
214 wp_enqueue_style( $style_handler );
215 }
216 }
217
218 // Library
219 if ( count( $this->get_script_depends() ) ) {
220 foreach ( $this->get_script_depends() as $script_handler ) {
221 wp_enqueue_script( $script_handler );
222 }
223 }
224
225 // block static css
226 $style_version = $this->cached_asset_version( $this->assets_path . 'build/blocks/' . $block_name . '/style.css' );
227 if ( false !== $style_version ) {
228 wp_enqueue_style( 'ablocks-' . $block_name . '-block-static-style', $this->assets_url . 'build/blocks/' . $block_name . '/style.css', array(), $style_version, 'all' );
229 }
230
231 $script_loading_strategy = \ABlocks\Helper::get_script_loading_strategy();
232 $args = [ 'strategy' => $script_loading_strategy ];
233
234 if ( false !== $this->cached_asset_version( $this->assets_path . 'build/blocks/' . $block_name . '/view.js' ) && ! wp_script_is( 'ablocks-' . $block_name . '-block-static-script', 'enqueued' ) ) {
235 $dependencies = include $this->assets_path . 'build/blocks/' . $block_name . '/view.asset.php';
236 // Depend on the shared data-only handle so ABlocksGlobal is printed
237 // before this script runs, without each block carrying its own copy.
238 $deps = array_values( array_unique( array_merge( array( 'ablocks-globals' ), $dependencies['dependencies'] ) ) );
239 wp_enqueue_script(
240 'ablocks-' . $block_name . '-block-static-script',
241 $this->assets_url . 'build/blocks/' . $block_name . '/view.js',
242 $deps,
243 $dependencies['version'],
244 $args
245 );
246 Assets::localize_globals_once();
247 }
248
249 }
250
251 public function enqueue_block_static_assets() {
252 if ( ! is_admin() && ! Helper::is_enabled_assets_generation() ) {
253 $this->enqueue_static_assets( $this->block_name );
254 }//end if
255 }
256 public function enqueue_block_specific_static_assets( $block_name ) {
257 if ( ! is_admin() && ! Helper::is_enabled_assets_generation() ) {
258 $this->enqueue_static_assets( explode( '/', $block_name )[1] );
259 }//end if
260 }
261
262 public function get_static_css() {
263 if ( file_exists( $this->assets_path . 'build/blocks/' . $this->block_name . '/style.css' ) ) {
264 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
265 $has_static_css = file_get_contents( $this->assets_path . 'build/blocks/' . $this->block_name . '/style.css' );
266 if ( $has_static_css ) {
267 // A block's style.css doubles as its editorStyle, so it can carry
268 // editor-chrome rules (.block-editor-*, .editor-styles-wrapper …)
269 // that never match on the front end. Strip them from the frontend
270 // static CSS (this getter feeds the combined/inline page CSS; the
271 // editor loads style.css directly via block.json).
272 return self::strip_editor_only_css( $has_static_css );
273 }
274 }
275 return '';
276 }
277
278 /**
279 * Remove rules whose selector is entirely editor-chrome (every
280 * comma-separated part references an editor-only class), leaving mixed and
281 * front-end rules untouched. Brace-aware so @media blocks stay intact.
282 */
283 private static $editor_css_cache = [];
284 public static function strip_editor_only_css( $css ) {
285 $key = md5( $css );
286 if ( isset( self::$editor_css_cache[ $key ] ) ) {
287 return self::$editor_css_cache[ $key ];
288 }
289 $markers = [ '.block-editor-', '.editor-styles-wrapper', '.block-list-appender', '.components-base-control', '.block-editor-block-variation-picker', '.block-editor-button-block-appender' ];
290
291 $out = '';
292 $len = strlen( $css );
293 $i = 0;
294 $buf = '';
295 while ( $i < $len ) {
296 $ch = $css[ $i ];
297 $buf .= $ch;
298 if ( '{' === $ch ) {
299 $prelude = trim( substr( $buf, 0, -1 ) );
300 $depth = 1;
301 $i++;
302 while ( $i < $len && $depth > 0 ) {
303 $c = $css[ $i ];
304 $buf .= $c;
305 if ( '{' === $c ) {
306 $depth++;
307 } elseif ( '}' === $c ) {
308 $depth--;
309 }
310 $i++;
311 }
312 $drop = false;
313 if ( '' !== $prelude && '@' !== $prelude[0] ) {
314 $parts = array_map( 'trim', explode( ',', $prelude ) );
315 $drop = true;
316 foreach ( $parts as $part ) {
317 // Drop :not(...) negations first — a selector like
318 // ":not(.block-editor-block-list__block) .foo" is a FRONT-END
319 // rule (applies everywhere except the editor), NOT editor
320 // chrome, so the marker inside :not() must not count.
321 $positive = preg_replace( '/:not\([^)]*\)/', '', $part );
322 $is_editor = false;
323 foreach ( $markers as $m ) {
324 if ( false !== strpos( $positive, $m ) ) {
325 $is_editor = true;
326 break;
327 }
328 }
329 if ( ! $is_editor ) {
330 $drop = false; // a front-end part exists — keep the rule
331 break;
332 }
333 }
334 }
335 if ( ! $drop ) {
336 $out .= $buf;
337 }
338 $buf = '';
339 continue;
340 }
341 $i++;
342 }
343 $out .= $buf;
344
345 self::$editor_css_cache[ $key ] = $out;
346 return $out;
347 }
348 public function get_static_js() {
349 if ( file_exists( $this->assets_path . 'build/blocks/' . $this->block_name . '/view.js' ) ) {
350 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
351 $has_static_js = file_get_contents( $this->assets_path . 'build/blocks/' . $this->block_name . '/view.js' );
352 if ( $has_static_js ) {
353 return $has_static_js;
354 }
355 }
356 return '';
357 }
358 public function get_style_depends() {
359 // NOTE: 'ablocks-animate-style' is intentionally NOT included here. Animation
360 // CSS is heavy and most blocks don't animate, so it's added on demand only:
361 // the combined generator adds it when a block's _animation attribute is set
362 // (AssetsGenerator::recursive_block_parser), and the fallback render path
363 // enqueues it per-block via maybe_enqueue_animate_style().
364 return apply_filters( 'ablocks/block_style_depends', array_merge( $this->style_depends, array( 'ablocks-frontend-google-fonts', 'ablocks-common-style' ) ) );
365 }
366 public function get_script_depends() {
367 return apply_filters( 'ablocks/block_script_depends', array_merge( $this->script_depends, array( 'ablocks-common-script' ) ) );
368 }
369 abstract public function build_css( $attributes);
370 }
371