HEX
Server: Apache
System: Linux webd003.cluster128.gra.hosting.ovh.net 6.18.42-ovh-vps-grsec-zfs+ #1 SMP PREEMPT_DYNAMIC Wed Aug 5 15:59:48 CEST 2026 x86_64
User: slyfwmm (169339)
PHP: 8.1.34
Disabled: _dyuweyrj4,_dyuweyrj4r,dl
Upload Files
File: /home/slyfwmm/adspray/wp-content/plugins/lazy-blocks/classes/class-blocks.php
<?php
/**
 * LazyBlocks blocks.
 *
 * @package lazyblocks
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}


/**
 * LazyBlocks_Blocks class. Class to work with LazyBlocks CPT.
 */
class LazyBlocks_Blocks {
	/**
	 * Handlebars engine.
	 *
	 * @var null|object
	 */
	private $handlebars = null;

	/**
	 * Rules to sanitize SVG
	 *
	 * @var array
	 */
	private $kses_svg = array(
		'svg'   => array(
			'class'           => true,
			'aria-hidden'     => true,
			'aria-labelledby' => true,
			'role'            => true,
			'xmlns'           => true,
			'width'           => true,
			'height'          => true,
			'viewbox'         => true,   // <= Must be lower case!
		),
		'g'     => array( 'fill' => true ),
		'title' => array( 'title' => true ),
		'path'  => array(
			'd'    => true,
			'fill' => true,
		),
		'rect'  => array(
			'fill'      => true,
			'opacity'   => true,
			'width'     => true,
			'height'    => true,
			'rx'        => true,
			'transform' => true,
		),
	);

	/**
	 * LazyBlocks_Blocks constructor.
	 */
	public function __construct() {
		add_action( 'init', array( $this, 'prepare_handlebars' ) );

		add_action( 'init', array( $this, 'register_post_type' ) );

		add_action( 'init', array( $this, 'remove_custom_fields_support' ), 150 );

		// It is important to have priority higher than 10 to prevent possible conflicts.
		// https://github.com/nk-crew/lazy-blocks/issues/247 .
		add_filter( 'allowed_block_types_all', array( $this, 'allowed_block_types_all' ), 100, 2 );

		// custom post roles.
		add_action( 'admin_init', array( $this, 'add_role_caps' ) );

		// additional elements in blocks list table.
		add_filter( 'disable_months_dropdown', array( $this, 'disable_months_dropdown' ), 10, 2 );
		add_filter( 'post_row_actions', array( $this, 'post_row_actions' ), 10, 2 );
		add_filter( 'bulk_actions-edit-lazyblocks', array( $this, 'bulk_actions_edit' ) );
		add_filter( 'handle_bulk_actions-edit-lazyblocks', array( $this, 'handle_bulk_actions_edit' ), 10, 3 );
		add_filter( 'manage_lazyblocks_posts_columns', array( $this, 'manage_posts_columns' ) );
		add_filter( 'manage_lazyblocks_posts_custom_column', array( $this, 'manage_posts_custom_column' ), 10, 2 );

		// sanitize block configs.
		add_filter( 'lzb/get_blocks', array( $this, 'sanitize_block_configs' ), 100 );

		// add gutenberg blocks assets.
		if ( function_exists( 'register_block_type' ) ) {
			// add custom block categories.
			add_filter( 'block_categories_all', array( $this, 'block_categories_all' ), 100 );

			// It is important to have priority higher than 10 to allow users register
			// custom blocks using standard `init` hook.
			add_action( 'init', array( $this, 'register_block' ), 20 );
			add_action( 'init', array( $this, 'register_block_render' ), 20 );
		}
	}

	/**
	 * Handlebars php.
	 */
	public function prepare_handlebars() {
		require_once lazyblocks()->plugin_path() . 'vendors/Handlebars/Autoloader.php';

		Handlebars\Autoloader::register();

		$this->handlebars = new Handlebars\Handlebars();

		// phpcs:ignore
		// truncate
		// {{truncate 'string' 2 'true'}}.
		$this->handlebars->registerHelper(
			'truncate',
			function( $str, $len, $ellipsis = 'true' ) {
				if ( $str && $len && mb_strlen( $str, 'UTF-8' ) > $len ) {
					$new_str = mb_substr( $str, 0, $len + 1, 'UTF-8' );
					$count   = mb_strlen( $new_str, 'UTF-8' );

					while ( $count > 0 ) {
						$ch      = mb_substr( $new_str, -1, null, 'UTF-8' );
						$new_str = mb_substr( $new_str, 0, -1, 'UTF-8' );

						$count--;

						if ( ' ' === $ch ) {
							break;
						}
					}

					if ( '' === $new_str ) {
						$new_str = mb_substr( $str, 0, $len, 'UTF-8' );
					}

					return new \Handlebars\SafeString( $new_str . ( 'true' === $ellipsis ? '...' : '' ) );
				}
				return $str;
			}
		);

		// compare.
		// {{#compare 1 '===' 2}} Show if true {{/compare}}
		// slightly changed https://gist.github.com/doginthehat/1890659.
		$this->handlebars->registerHelper(
			'compare',
			function( $lvalue, $operator, $rvalue = null, $options = null ) {
				if ( null === $rvalue ) {
					return $options['inverse']();
				}

				if ( null === $options ) {
					$options  = $rvalue;
					$rvalue   = $operator;
					$operator = '===';
				}

				$result = false;

				switch ( $operator ) {
					case '==':
						// phpcs:ignore
						$result = $lvalue == $rvalue;
						break;
					case '===':
						$result = $lvalue === $rvalue;
						break;
					case '!=':
						// phpcs:ignore
						$result = $lvalue != $rvalue;
						break;
					case '!==':
						$result = $lvalue !== $rvalue;
						break;
					case '<':
						$result = $lvalue < $rvalue;
						break;
					case '>':
						$result = $lvalue > $rvalue;
						break;
					case '<=':
						$result = $lvalue <= $rvalue;
						break;
					case '>=':
						$result = $lvalue >= $rvalue;
						break;
					case '&&':
						$result = $lvalue && $rvalue;
						break;
					case '||':
						$result = $lvalue || $rvalue;
						break;
					case 'typeof':
						$result = gettype( $lvalue ) === $rvalue;
						break;
				}

				if ( $result ) {
					return $options['fn']();
				}

				return $options['inverse']();
			}
		);

		// math.
		// {{math 1 '+' 2}}
		// https://stackoverflow.com/questions/33059203/error-missing-helper-in-handlebars-js/46317662#46317662.
		$this->handlebars->registerHelper(
			'math',
			function( $lvalue, $operator, $rvalue ) {
				$result = '';

				switch ( $operator ) {
					case '+':
						$result = $lvalue + $rvalue;
						break;
					case '-':
						$result = $lvalue - $rvalue;
						break;
					case '*':
						$result = $lvalue * $rvalue;
						break;
					case '/':
						$result = $lvalue / $rvalue;
						break;
					case '%':
						$result = $lvalue % $rvalue;
						break;
				}

				return $result;
			}
		);

		// phpcs:ignore
		// do_shortcode.
		// {{{do_shortcode 'my_shortcode' this}}}.
		$this->handlebars->registerHelper(
			'do_shortcode',
			function( $shortcode_name, $attributes ) {
				$result = '[' . $shortcode_name;

				// prepare attributes.
				if ( isset( $attributes ) && ! empty( $attributes ) ) {
					foreach ( $attributes as $name => $val ) {
						if (
						'content' === $name
						|| 'lazyblock_code_frontend_html' === $name
						|| 'lazyblock_code_backend_html' === $name
						|| 'data' === $name
						|| 'hash' === $name
						) {
							continue;
						}

						if ( is_array( $val ) ) {
							$val = wp_json_encode( $val );
						}

						if (
						! is_numeric( $val )
						&& ! is_string( $val )
						&& ! is_bool( $val )
						) {
							continue;
						}

						if ( is_bool( $val ) ) {
							$val = $val ? '1' : '0';
						}

						$result .= ' ' . esc_attr( $name ) . '="' . esc_attr( $val ) . '"';
					}

					// content.
					if ( isset( $attributes['content'] ) ) {
						$result .= ']' . $attributes['content'] . '[/' . $shortcode_name;
					}
				}

				$result .= ']';

				return do_shortcode( $result );
			}
		);

		// phpcs:ignore
		// date_i18n.
		// {{date_i18n 'F j, Y H:i' '2018-09-16 15:35'}}.
		$this->handlebars->registerHelper(
			'date_i18n',
			function( $format, $time ) {
				return date_i18n( $format, strtotime( $time ) );
			}
		);

		// phpcs:ignore
		// var_dump.
		// {{var_dump 'test'}}.
		$this->handlebars->registerHelper(
			'var_dump',
			function( $val ) {
				ob_start();
				// phpcs:ignore
				var_dump( $val );
				return ob_get_clean();
			}
		);

		// custom action for extending default helpers by 3rd-party.
		do_action( 'lzb/handlebars/object', $this->handlebars );
	}

	/**
	 * Register CPT.
	 */
	public function register_post_type() {
		register_post_type(
			'lazyblocks',
			array(
				'labels'            => array(
					'menu_name'     => __( 'Lazy Blocks', 'lazy-blocks' ),
					'name'          => __( 'Blocks', 'lazy-blocks' ),
					'all_items'     => __( 'Blocks', 'lazy-blocks' ),
					'singular_name' => __( 'Block', 'lazy-blocks' ),
				),
				'public'            => false,
				'has_archive'       => false,
				'show_ui'           => true,

				// adding to custom menu manually.
				'show_in_menu'      => true,
				'show_in_admin_bar' => true,
				'show_in_rest'      => true,
				// phpcs:ignore
				'menu_icon'         => 'data:image/svg+xml;base64,' . base64_encode( file_get_contents( lazyblocks()->plugin_path() . 'assets/svg/icon-lazyblocks.svg' ) ),
				'menu_position'     => 80,
				'capabilities'      => array(
					'edit_post'          => 'edit_lazyblock',
					'edit_posts'         => 'edit_lazyblocks',
					'edit_others_posts'  => 'edit_other_lazyblocks',
					'publish_posts'      => 'publish_lazyblocks',
					'read_post'          => 'read_lazyblock',
					'read_private_posts' => 'read_private_lazyblocks',
					'delete_posts'       => 'delete_lazyblocks',
					'delete_post'        => 'delete_lazyblock',
				),
				'rewrite'           => true,
				'supports'          => array(
					'title',
					'editor',
					'revisions',
				),
				'template'          => array(
					array(
						'lzb-constructor/main',
					),
				),
				// we can't use it since blocks didn't inserted in some posts.
				// 'template_lock' => 'all',.
			)
		);
	}

	/**
	 * Remove custom fields support from lazy blocks constructor page.
	 * Some plugins adds support for custom fields to all post types, but we don't need it in our constructor.
	 *
	 * @link https://github.com/nk-crew/lazy-blocks/issues/141
	 */
	public function remove_custom_fields_support() {
		remove_post_type_support( 'lazyblocks', 'custom-fields' );
	}

	/**
	 * Allowed blocks for lazyblocks post type.
	 *
	 * @param array  $allowed_block_types - blocks.
	 * @param object $editor_context - editor context.
	 * @return array
	 */
	public function allowed_block_types_all( $allowed_block_types, $editor_context ) {
		if ( empty( $editor_context->post ) || 'lazyblocks' !== $editor_context->post->post_type ) {
			return $allowed_block_types;
		}
		return array( 'lzb-constructor/main' );
	}

	/**
	 * Add Roles
	 */
	public function add_role_caps() {
		global $wp_roles;

		if ( isset( $wp_roles ) ) {
			$wp_roles->add_cap( 'administrator', 'edit_lazyblock' );
			$wp_roles->add_cap( 'administrator', 'edit_lazyblocks' );
			$wp_roles->add_cap( 'administrator', 'edit_other_lazyblocks' );
			$wp_roles->add_cap( 'administrator', 'publish_lazyblocks' );
			$wp_roles->add_cap( 'administrator', 'read_lazyblock' );
			$wp_roles->add_cap( 'administrator', 'read_private_lazyblocks' );
			$wp_roles->add_cap( 'administrator', 'delete_lazyblocks' );
			$wp_roles->add_cap( 'administrator', 'delete_lazyblock' );

			$wp_roles->add_cap( 'editor', 'read_lazyblock' );
			$wp_roles->add_cap( 'editor', 'read_private_lazyblocks' );

			$wp_roles->add_cap( 'author', 'read_lazyblock' );
			$wp_roles->add_cap( 'author', 'read_private_lazyblocks' );

			$wp_roles->add_cap( 'contributor', 'read_lazyblock' );
			$wp_roles->add_cap( 'contributor', 'read_private_lazyblocks' );
		}
	}

	/**
	 * Disable month dropdown.
	 *
	 * @param array  $return disabled dropdown or no.
	 * @param object $post_type current post type name.
	 *
	 * @return array
	 */
	public function disable_months_dropdown( $return, $post_type ) {
		return 'lazyblocks' === $post_type ? true : $return;
	}

	/**
	 * Add featured image in lazyblocks list
	 *
	 * @param array  $actions actions for posts.
	 * @param object $post current post data.
	 *
	 * @return array
	 */
	public function post_row_actions( $actions = array(), $post = null ) {
		if ( ! $post || 'lazyblocks' !== $post->post_type ) {
			return $actions;
		}

		// remove quick edit link.
		if ( isset( $actions['inline hide-if-no-js'] ) ) {
			unset( $actions['inline hide-if-no-js'] );
		}

		// add duplicate and export link.
		$actions = array_merge(
			array_slice( $actions, 0, 1 ),
			array(
				'duplicate' => sprintf(
					'<a href="%1$s" aria-label="%2$s">%3$s</a>',
					add_query_arg(
						array(
							'lazyblocks_duplicate_block' => $post->ID,
							'lazyblocks_duplicate_block_nonce' => wp_create_nonce( 'lzb-duplicate-block-nonce' ),
						)
					),
					sprintf(
						// translators: %1$ - post title.
						esc_html__( 'Duplicate ā€œ%1$sā€', 'lazy-blocks' ),
						get_the_title( $post->ID )
					),
					esc_html__( 'Duplicate', 'lazy-blocks' )
				),
				'export' => sprintf(
					'<a href="%1$s" aria-label="%2$s">%3$s</a>',
					add_query_arg(
						array(
							'lazyblocks_export_block' => $post->ID,
						)
					),
					sprintf(
						// translators: %1$ - post title.
						esc_html__( 'Export ā€œ%1$sā€', 'lazy-blocks' ),
						get_the_title( $post->ID )
					),
					esc_html__( 'Export', 'lazy-blocks' )
				),
			),
			array_slice( $actions, 1 )
		);

		return $actions;
	}

	/**
	 * Bulk actions.
	 *
	 * @param array $actions bulk actions for posts.
	 *
	 * @return array
	 */
	public function bulk_actions_edit( $actions = array() ) {
		unset( $actions['edit'] );

		$actions['export'] = esc_html__( 'Export', 'lazy-blocks' );

		return $actions;
	}

	/**
	 * Prepare to bulk export blocks.
	 *
	 * @param string $redirect redirect url after export.
	 * @param string $action action name.
	 * @param array  $post_ids post ids to export.
	 *
	 * @return string
	 */
	public function handle_bulk_actions_edit( $redirect, $action, $post_ids ) {
		if ( 'export' !== $action ) {
			return $redirect;
		}

		lazyblocks()->tools()->export_json( $post_ids, 'blocks' );

		return $redirect;
	}

	/**
	 * Add featured image in lazyblocks list
	 *
	 * @param array $columns columns of the table.
	 *
	 * @return array
	 */
	public function manage_posts_columns( $columns = array() ) {
		$columns = array(
			'cb'                          => $columns['cb'],
			'lazyblocks_post_icon'        => esc_html__( 'Icon', 'lazy-blocks' ),
			'title'                       => $columns['title'],
			'lazyblocks_post_category'    => esc_html__( 'Category', 'lazy-blocks' ),
			'lazyblocks_post_description' => esc_html__( 'Description', 'lazy-blocks' ),
		);

		return $columns;
	}

	/**
	 * Add thumb to the column
	 *
	 * @param bool $column_name column name.
	 */
	public function manage_posts_custom_column( $column_name = false ) {
		global $post;

		if ( 'lazyblocks_post_icon' === $column_name ) {
			$icon      = $this->get_meta_value_by_id( 'lazyblocks_icon' );
			$admin_url = get_edit_post_link( $post->ID );

			echo '<a class="lzb-admin-block-icon" href="' . esc_url( $admin_url ) . '">';

			if ( $icon && strpos( $icon, 'dashicons' ) === 0 ) {
				echo '<span class="dashicons ' . esc_attr( $icon ) . '"></span>';
			} elseif ( $icon ) {
				echo wp_kses( $icon, $this->kses_svg );
			}

			echo '</a>';
		}

		if ( 'lazyblocks_post_category' === $column_name ) {
			$category = $this->get_meta_value_by_id( 'lazyblocks_category' );
			if ( $category ) {
				$gutenberg_categories = array();
				if ( function_exists( 'get_block_categories' ) ) {
					$gutenberg_categories = get_block_categories( $post );
				} elseif ( function_exists( 'gutenberg_get_block_categories' ) ) {
					$gutenberg_categories = gutenberg_get_block_categories( $post );
				}

				foreach ( $gutenberg_categories as $cat ) {
					if ( $cat['slug'] === $category ) {
						$category = $cat['title'];
						break;
					}
				}

				echo esc_html( $category );
			}
		}

		if ( 'lazyblocks_post_description' === $column_name ) {
			$description = $this->get_meta_value_by_id( 'lazyblocks_description' );
			echo wp_kses_post( $description );
		}
	}

	/**
	 * Default block data.
	 *
	 * @var array
	 */
	private $block_defaults = array(
		'lazyblocks_controls'                        => array(),

		'lazyblocks_slug'                            => '',
		'lazyblocks_icon'                            => '',
		'lazyblocks_description'                     => '',
		'lazyblocks_keywords'                        => '',
		'lazyblocks_category'                        => 'text',

		'lazyblocks_code_show_preview'               => 'always',
		'lazyblocks_code_single_output'              => 'false',
		'lazyblocks_code_output_method'              => 'html',

		'lazyblocks_code_editor_html'                => '',
		'lazyblocks_code_editor_callback'            => '',
		'lazyblocks_code_editor_css'                 => '',
		'lazyblocks_code_frontend_html'              => '',
		'lazyblocks_code_frontend_callback'          => '',
		'lazyblocks_code_frontend_css'               => '',

		'lazyblocks_styles'                          => array(),

		'lazyblocks_supports_multiple'               => 'true',
		'lazyblocks_supports_classname'              => 'true',
		'lazyblocks_supports_anchor'                 => 'false',
		'lazyblocks_supports_html'                   => 'false',
		'lazyblocks_supports_inserter'               => 'true',
		'lazyblocks_supports_reusable'               => 'true',
		'lazyblocks_supports_lock'                   => 'true',
		'lazyblocks_supports_align'                  => array( 'wide', 'full' ),

		// Ghost Kit Extensions.
		'lazyblocks_supports_ghostkit_spacings'      => 'false',
		'lazyblocks_supports_ghostkit_display'       => 'false',
		'lazyblocks_supports_ghostkit_scroll_reveal' => 'false',
		'lazyblocks_supports_ghostkit_frame'         => 'false',
		'lazyblocks_supports_ghostkit_custom_css'    => 'false',

		'lazyblocks_condition_post_types'            => '',
	);

	/**
	 * Return default constructor block data.
	 *
	 * @return array
	 */
	public function get_block_defaults() {
		return apply_filters( 'lzb/block_defaults', $this->block_defaults );
	}

	/**
	 * Get metabox value by name.
	 *
	 * @param string $name - meta name.
	 * @param mixed  $result - result of stored attribute.
	 *
	 * @return mixed
	 */
	private function get_meta_value( $name, $result ) {
		$defaults = $this->get_block_defaults();
		$default  = null;

		if ( isset( $defaults[ $name ] ) ) {
			$default = $defaults[ $name ];
		}

		if ( '' === $result && null !== $default ) {
			$result = $default;
		}

		if ( 'true' === $result ) {
			$result = true;
		} elseif ( 'false' === $result ) {
			$result = false;
		}

		return $result;
	}

	/**
	 * Get metabox value by name and post id.
	 *
	 * @param string      $name - meta name.
	 * @param int|boolean $id - post id.
	 * @return mixed
	 */
	private function get_meta_value_by_id( $name, $id = false ) {
		if ( ! $id ) {
			global $post;
			$id = $post->ID;
		}

		$result = get_post_meta( $id, $name, true );

		return $this->get_meta_value( $name, $result );
	}

	/**
	 * Get metabox value by name and block data.
	 *
	 * @param string $name - meta name.
	 * @param array  $block_data - supplied block data.
	 *
	 * @return mixed
	 */
	private function get_meta_value_by_block( $name, $block_data ) {
		$result = $block_data[ $name ] ?? null;

		return $this->get_meta_value( $name, $result );
	}

	/**
	 * Sanitize block slug name.
	 * Keep only alpha and numbers.
	 * Make it lowercase.
	 *
	 * @param string $slug - slug name.
	 *
	 * @return string
	 */
	public function sanitize_slug( $slug ) {
		return strtolower( preg_replace( '/[^a-zA-Z0-9\-]+/', '', $slug ) );
	}

	/**
	 * Returns true if the current user is allowed to save unfiltered HTML.
	 *
	 * @return bool
	 */
	public function is_allowed_unfiltered_html() {
		$allow_unfiltered_html = current_user_can( 'unfiltered_html' );

		return apply_filters( 'lzb/allow_unfiltered_html', $allow_unfiltered_html );
	}

	/**
	 * Save Format metabox
	 *
	 * @param int   $post_id The post ID.
	 * @param array $data Metaboxes data for save.
	 */
	public function save_meta_boxes( $post_id, $data ) {
		$defaults = $this->get_block_defaults();

		foreach ( $defaults as $meta => $default ) {
			$new_meta_value = '';

			if ( isset( $data[ $meta ] ) ) {
				// convert boolean to string.
				if ( is_bool( $data[ $meta ] ) ) {
					$data[ $meta ] = $data[ $meta ] ? 'true' : 'false';
				}

				// icon and editors.
				if (
					'lazyblocks_icon' === $meta ||
					'lazyblocks_code_editor_html' === $meta ||
					'lazyblocks_code_editor_css' === $meta ||
					'lazyblocks_code_frontend_html' === $meta ||
					'lazyblocks_code_frontend_css' === $meta
				) {
					$new_meta_value = wp_slash( $data[ $meta ] );
				} else {
					$new_meta_value = wp_slash( $data[ $meta ] );

					// Filter $_POST data for users without the 'unfiltered_html' capability.
					if ( ! $this->is_allowed_unfiltered_html() ) {
						$new_meta_value = wp_kses_post_deep( $new_meta_value );
					}
				}
			}

			// keep only alpha and numbers in slug.
			if ( 'lazyblocks_slug' === $meta ) {
				$new_meta_value = $this->sanitize_slug( $new_meta_value );

				// generate slug from title.
				if ( ! $new_meta_value ) {
					$new_meta_value = get_the_title();
					$new_meta_value = $this->sanitize_slug( $new_meta_value );
				}

				// if no slug available.
				if ( ! $new_meta_value ) {
					$new_meta_value = 'no-slug';
				}
			}

			/* Get the meta value of the custom field key. */
			$meta_value = get_post_meta( $post_id, $meta, true );

			$meta_value_to_check     = $meta_value;
			$new_meta_value_to_check = $new_meta_value;

			if ( is_array( $meta_value_to_check ) ) {
				$meta_value_to_check = wp_json_encode( $meta_value_to_check );
			}
			if ( is_array( $new_meta_value_to_check ) ) {
				$new_meta_value_to_check = wp_json_encode( $new_meta_value_to_check );
			}

			/* If a new meta value was added and there was no previous value, add it. */
			if ( $new_meta_value_to_check && '' === $meta_value_to_check ) {
				add_post_meta( $post_id, $meta, $new_meta_value, true );

				/* If the new meta value does not match the old value, update it. */
			} elseif ( $new_meta_value_to_check && $new_meta_value_to_check !== $meta_value_to_check ) {
				update_post_meta( $post_id, $meta, $new_meta_value );

				/* If there is no new meta value but an old value exists, delete it. */
			} elseif ( '' === $new_meta_value_to_check && $meta_value_to_check ) {
				delete_post_meta( $post_id, $meta, $meta_value );
			}
		}
	}

	/**
	 * Get metabox data
	 *
	 * @param int $post_id The post ID.
	 *
	 * @return array|null
	 */
	public function get_meta_boxes( $post_id ) {
		$defaults    = $this->get_block_defaults();
		$result_meta = array();

		foreach ( $defaults as $meta => $default ) {
			$result_meta[ $meta ] = $this->get_meta_value_by_id( $meta, $post_id );
		}

		return $result_meta;
	}

	/**
	 * Blocks list.
	 *
	 * @var array|null
	 */
	private $blocks = null;

	/**
	 * Blocks list added by user using add_blocks method.
	 *
	 * @var null
	 */
	private $user_blocks = null;

	/**
	 * Add block.
	 *
	 * @param array $data - block data.
	 */
	public function add_block( $data ) {
		if ( null === $this->user_blocks ) {
			$this->user_blocks = array();
		}

		$this->user_blocks[] = apply_filters( 'lzb/add_user_block', $data );
	}

	/**
	 * Remove block.
	 *
	 * @param string $block_slug - block slug.
	 */
	public function remove_block( $block_slug ) {
		if ( is_array( $this->user_blocks ) ) {
			foreach ( $this->user_blocks as $k => $val ) {
				if ( isset( $val['slug'] ) && $val['slug'] === $block_slug ) {
					unset( $this->user_blocks[ $k ] );
				}
			}
		}
	}

	/**
	 * Prepare block controls by adding defaults.
	 *
	 * @param array $controls - block controls.
	 * @param array $all_controls - all registered controls.
	 *
	 * @return array
	 */
	public function prepare_block_controls( $controls, $all_controls ) {
		$result = array();

		foreach ( (array) $controls as $k => $control ) {
			if ( isset( $control['type'] ) && isset( $all_controls[ $control['type'] ] ) && isset( $all_controls[ $control['type'] ]['attributes'] ) ) {
				$result[ $k ] = array_merge(
					$all_controls[ $control['type'] ]['attributes'],
					$control
				);
			}
		}

		return $result;
	}

	/**
	 * Convert block format.
	 *
	 * @param array $block_data - block data.
	 */
	public function marshal_block_data( $block_data ) {
		$all_controls = lazyblocks()->controls()->get_controls();
		return $this->marshal_block_data_with_controls( null, null, $block_data, $all_controls );
	}

	/**
	 * Convert block format.
	 *
	 * @param int   $id - registered block id.
	 * @param int   $post_title - registered block post title.
	 * @param array $block_data - block data.
	 * @param array $all_controls - control data.
	 */
	public function marshal_block_data_with_controls( $id = null, $post_title = null, $block_data = null, $all_controls = null ) {
		$get_meta_value = function( $name ) use ( $id, $block_data ) {
			// Get post meta data.
			if ( $id ) {
				return $this->get_meta_value_by_id( $name, $id );

				// Get provided block data.
			} elseif ( $block_data ) {
				return $this->get_meta_value_by_block( $name, $block_data );

				// Get defaults.
			} else {
				return $this->get_meta_value( $name, '' );
			}
		};

		$icon = $get_meta_value( 'lazyblocks_icon' );

		// add default icon.
		if ( ! $icon ) {
			// phpcs:ignore
			$icon = file_get_contents( lazyblocks()->plugin_path() . 'assets/svg/icon-lazyblocks.svg' );
			$icon = str_replace( 'fill="white"', 'fill="currentColor"', $icon );
		}

		if ( $icon && strpos( $icon, 'dashicons' ) === 0 ) {
			$icon = esc_attr( str_replace( 'dashicons-', 'dashicons dashicons-', $icon ) );
		} elseif ( $icon ) {
			$icon = wp_kses( $icon, $this->kses_svg );
		}

		$keywords = esc_attr( $get_meta_value( 'lazyblocks_keywords' ) );
		if ( $keywords ) {
			$keywords = explode( ',', $keywords );
		} else {
			$keywords = array();
		}

		// prepare default control data.
		$controls = $this->prepare_block_controls( $get_meta_value( 'lazyblocks_controls' ), $all_controls );

		$align          = (array) $get_meta_value( 'lazyblocks_supports_align' );
		$align_none_key = array_search( 'none', $align, true );

		if ( false !== $align_none_key ) {
			unset( $align[ $align_none_key ] );
		}

		$styles = (array) $get_meta_value( 'lazyblocks_styles' );

		// Prepare supports.
		$supports = array(
			'customClassName' => $get_meta_value( 'lazyblocks_supports_classname' ),
			'anchor'          => $get_meta_value( 'lazyblocks_supports_anchor' ),
			'html'            => $get_meta_value( 'lazyblocks_supports_html' ),
			'multiple'        => $get_meta_value( 'lazyblocks_supports_multiple' ),
			'inserter'        => $get_meta_value( 'lazyblocks_supports_inserter' ),
			'reusable'        => $get_meta_value( 'lazyblocks_supports_reusable' ),
			'lock'            => $get_meta_value( 'lazyblocks_supports_lock' ),
			'align'           => $align,
			'ghostkit'        => array(
				'effects'    => $get_meta_value( 'lazyblocks_supports_ghostkit_effects' ) || $get_meta_value( 'lazyblocks_supports_ghostkit_scroll_reveal' ) || false,
				'position'   => $get_meta_value( 'lazyblocks_supports_ghostkit_position' ) || false,
				'spacings'   => $get_meta_value( 'lazyblocks_supports_ghostkit_spacings' ) || false,
				'frame'      => $get_meta_value( 'lazyblocks_supports_ghostkit_frame' ) || false,
				'transform'  => $get_meta_value( 'lazyblocks_supports_ghostkit_transform' ) || false,
				'customCSS'  => $get_meta_value( 'lazyblocks_supports_ghostkit_custom_css' ) || false,
				'display'    => $get_meta_value( 'lazyblocks_supports_ghostkit_display' ) || false,
				'attributes' => $get_meta_value( 'lazyblocks_supports_ghostkit_attributes' ) || false,
			),
		);

		return apply_filters(
			'lzb/block_data',
			array(
				'id'             => $id,
				'title'          => $post_title,
				'icon'           => $icon,
				'keywords'       => $keywords,
				'slug'           => 'lazyblock/' . esc_html( $get_meta_value( 'lazyblocks_slug' ) ),
				'description'    => $get_meta_value( 'lazyblocks_description' ),
				'category'       => $this->sanitize_slug( esc_html( $get_meta_value( 'lazyblocks_category' ) ) ),
				'category_label' => esc_html( $get_meta_value( 'lazyblocks_category' ) ),
				'supports'       => $supports,
				'controls'       => $controls,
				'code'           => array(
					'output_method'     => $get_meta_value( 'lazyblocks_code_output_method' ),
					'editor_html'       => $get_meta_value( 'lazyblocks_code_editor_html' ),
					'editor_callback'   => '',
					'editor_css'        => $get_meta_value( 'lazyblocks_code_editor_css' ),
					'frontend_html'     => $get_meta_value( 'lazyblocks_code_frontend_html' ),
					'frontend_callback' => '',
					'frontend_css'      => $get_meta_value( 'lazyblocks_code_frontend_css' ),
					'show_preview'      => $get_meta_value( 'lazyblocks_code_show_preview' ),
					'single_output'     => $get_meta_value( 'lazyblocks_code_single_output' ),
				),
				'styles'         => $styles,
				'condition'      => $get_meta_value( 'lazyblocks_condition_post_types' ) ? $get_meta_value( 'lazyblocks_condition_post_types' ) : array(),
				'edit_url'       => get_edit_post_link( $id ),
			),
			$get_meta_value
		);
	}

	/**
	 * Get all blocks array.
	 *
	 * @param bool $db_only - get blocks from database only.
	 * @param bool $no_cache - get blocks without cache.
	 * @param bool $keep_duplicates - get blocks with same slugs.
	 *
	 * @return array|null
	 */
	public function get_blocks( $db_only = false, $no_cache = false, $keep_duplicates = false ) {
		// fetch blocks.
		if ( null === $this->blocks || $no_cache ) {
			$this->blocks = array();

			// get all lazyblocks post types.
			// Don't use WP_Query on the admin side https://core.trac.wordpress.org/ticket/18408 .
			$all_blocks = get_posts(
				array(
					'post_type'      => 'lazyblocks',
					// phpcs:ignore
					'posts_per_page' => -1,
					'showposts'      => -1,
					'paged'          => -1,
				)
			);

			$all_controls = lazyblocks()->controls()->get_controls();

			foreach ( $all_blocks as $block ) {
				$this->blocks[] = $this->marshal_block_data_with_controls( $block->ID, $block->post_title, null, $all_controls );
			}
		}

		$result = $this->blocks;

		if ( ! $db_only && $this->user_blocks ) {
			$all_user_blocks = $this->user_blocks;
			$all_controls    = lazyblocks()->controls()->get_controls();

			foreach ( $all_user_blocks as $block ) {
				$user_block = array_merge( $this->marshal_block_data_with_controls(), $block );

				$user_block['controls'] = $this->prepare_block_controls( $user_block['controls'], $all_controls );

				$result[] = $user_block;
			}
		}

		// unique only.
		if ( ! $keep_duplicates ) {
			$unique_result = array();
			$slug_array    = array();

			foreach ( $result as $item ) {
				if ( ! in_array( $item['slug'], $slug_array, true ) ) {
					$slug_array[]    = $item['slug'];
					$unique_result[] = $item;
				}
			}

			return apply_filters( 'lzb/get_blocks', $unique_result );
		}

		return apply_filters( 'lzb/get_blocks', $result );
	}

	/**
	 * Get specific block data by name.
	 *
	 * @param string $name - block name.
	 * @param bool   $db_only - get blocks from database only.
	 *
	 * @return array|null
	 */
	public function get_block( $name, $db_only = false ) {
		$blocks = $this->get_blocks( $db_only );

		foreach ( $blocks as $block ) {
			if ( $name === $block['slug'] ) {
				return $block;
			}
		}

		return null;
	}

	/**
	 * Get all custom blocks categories array.
	 *
	 * @param bool $db_only - get blocks from database only.
	 *
	 * @return array|null
	 */
	public function get_blocks_categories( $db_only = false ) {
		$blocks             = $this->get_blocks( $db_only );
		$default_categories = array(
			'text',
			'media',
			'design',
			'widgets',
			'embed',
			'reusable',
		);

		$custom_categories = array();

		foreach ( $blocks as $block ) {
			if (
				! isset( $default_categories[ $block['category'] ] ) &&
				! isset( $custom_categories[ $block['category'] ] ) &&
				! in_array( $block['category'], $default_categories, true ) &&
				isset( $block['category_label'] )
			) {
				$custom_categories[ $block['category'] ] = $block['category_label'];
			}
		}

		return $custom_categories;
	}

	/**
	 * Sanitize block configs.
	 *
	 * @param array $blocks - block list.
	 *
	 * @return array
	 */
	public function sanitize_block_configs( $blocks ) {
		if ( empty( $blocks ) ) {
			return $blocks;
		}

		$sanitize_block_data = apply_filters(
			'lzb/sanitize_block_data',
			array(
				'title',
				'description',
				'category',
				'category_label',
			)
		);

		$sanitize_control_data = apply_filters(
			'lzb/sanitize_block_control_data',
			array(
				'label',
				'help',
				'rows_label',
				'rows_add_button_label',
				'placeholder',
			)
		);

		foreach ( $blocks as &$block ) {
			foreach ( $sanitize_block_data as $name ) {
				if ( ! empty( $block[ $name ] ) ) {
					$block[ $name ] = wp_kses_post( $block[ $name ] );
				}
			}

			if ( ! empty( $block['controls'] ) ) {
				foreach ( $block['controls'] as &$control_data ) {
					foreach ( $sanitize_control_data as $name ) {
						if ( ! empty( $control_data[ $name ] ) ) {
							$control_data[ $name ] = wp_kses_post( $control_data[ $name ] );
						}
					}
				}
			}
		}

		return $blocks;
	}

	/**
	 * Register custom categories for blocks
	 *
	 * @param array $categories - available categories.
	 * @return array
	 */
	public function block_categories_all( $categories ) {
		// lazyblocks core category.
		$categories[] = array(
			'slug'  => 'lazyblocks',
			'title' => esc_html__( 'Lazy Blocks', 'lazy-blocks' ),
		);

		$new_categories = $this->get_blocks_categories();
		if ( ! empty( $new_categories ) ) {
			foreach ( $new_categories as $slug => $category ) {
				// no duplicates.
				$allow = true;

				foreach ( $categories as $existing_cat ) {
					if ( isset( $existing_cat['slug'] ) && $slug === $existing_cat['slug'] ) {
						$allow = false;
					}
				}

				if ( $allow ) {
					$categories[] = array(
						'slug'  => $slug,
						'title' => $category,
					);
				}
			}
		}

		return $categories;
	}

	/**
	 * Add Gutenberg block assets.
	 */
	public function register_block() {
		$blocks = $this->get_blocks();

		LazyBlocks_Assets::register_style( 'lazyblocks-editor', 'build/editor' );
		wp_style_add_data( 'lazyblocks-editor', 'rtl', 'replace' );

		// enqueue block js.
		LazyBlocks_Assets::register_script( 'lazyblocks-editor', 'build/editor' );

		// additional data for block js.
		wp_localize_script(
			'lazyblocks-editor',
			'lazyblocksGutenberg',
			array(
				'blocks'             => $blocks,
				'controls'           => lazyblocks()->controls()->get_controls(),
				'icons'              => lazyblocks()->icons()->get_all(),
				'allowed_mime_types' => get_allowed_mime_types(),
			)
		);
	}

	/**
	 * Prepare attributes.
	 *
	 * @param array          $controls - controls list.
	 * @param string|boolean $child_of - childOf control name.
	 * @param array          $block - block data.
	 *
	 * @return array.
	 */
	public function prepare_block_attributes( $controls, $child_of = '', $block = null ) {
		$all_controls = lazyblocks()->controls()->get_controls();
		$attributes   = array();

		foreach ( $controls as $k => $control ) {
			if (
				isset( $control['child_of'] ) &&
				$control['child_of'] === $child_of &&
				$control['name']
			) {
				// Prevent registering Meta controls, as we don't save the meta inside the block.
				// Meta controls prepared inside `prepare_block_meta_attributes` method.
				if ( isset( $control['save_in_meta'] ) && 'true' === $control['save_in_meta'] ) {
					continue;
				}

				$attribute_data = array(
					'type'    => 'string',
					'default' => isset( $control['default'] ) ? $control['default'] : null,
				);

				// get attribute type from control data.
				if ( isset( $control['type'] ) && isset( $all_controls[ $control['type'] ] ) ) {
					$attribute_data['type'] = $all_controls[ $control['type'] ]['type'];

					// We also check for an empty string, as it is the default value when no default is provided.
					// If this empty string is converted to a float, the control will default to `0`.
					if ( 'number' === $attribute_data['type'] && null !== $attribute_data['default'] && '' !== $attribute_data['default'] ) {
						$attribute_data['default'] = (float) $attribute_data['default'];
					}
				}

				$attributes[ $control['name'] ] = apply_filters( 'lzb/prepare_block_attribute', $attribute_data, $control, $controls, $k, $block );
			}
		}

		// reserved attributes.
		$attributes['lazyblock']        = array(
			'type'    => 'object',
			'default' => array(
				'slug' => $block['slug'],
			),
		);
		$attributes['className']        = array(
			'type'    => 'string',
			'default' => '',
		);
		$attributes['align']            = array(
			'type'    => 'string',
			'default' => '',
		);
		$attributes['anchor']           = array(
			'type'    => 'string',
			'default' => '',
		);
		$attributes['blockId']          = array(
			'type'    => 'string',
			'default' => '',
		);
		$attributes['blockUniqueClass'] = array(
			'type'    => 'string',
			'default' => '',
		);

		// Ghost Kit.
		$attributes['ghostkitSpacings'] = array(
			'type'    => 'object',
			'default' => '',
		);
		$attributes['ghostkitSR']       = array(
			'type'    => 'string',
			'default' => '',
		);

		return $attributes;
	}

	/**
	 * Prepare meta attributes.
	 *
	 * @param array          $controls - controls list.
	 * @param string|boolean $child_of - childOf control name.
	 * @param array          $block - block data.
	 *
	 * @return array.
	 */
	public function prepare_block_meta_attributes( $controls, $child_of = '', $block = null ) {
		$all_controls = lazyblocks()->controls()->get_controls();
		$attributes   = array();

		foreach ( $controls as $k => $control ) {
			if (
				isset( $control['child_of'] ) &&
				$control['child_of'] === $child_of &&
				$control['name'] &&
				isset( $control['save_in_meta'] ) &&
				'true' === $control['save_in_meta']
			) {
				$attribute_data = array(
					'source'  => 'meta',
					'type'    => 'string',
					'meta'    => isset( $control['save_in_meta_name'] ) && $control['save_in_meta_name'] ? $control['save_in_meta_name'] : $control['name'],
					'default' => isset( $control['default'] ) ? $control['default'] : null,
				);

				// get attribute type from control data.
				if ( isset( $control['type'] ) && isset( $all_controls[ $control['type'] ] ) ) {
					$attribute_data['type'] = $all_controls[ $control['type'] ]['type'];

					if ( 'number' === $attribute_data['type'] && null !== $attribute_data['default'] ) {
						$attribute_data['default'] = (float) $attribute_data['default'];
					}
				}

				$attributes[ $control['name'] ] = apply_filters( 'lzb/prepare_block_attribute', $attribute_data, $control, $controls, $k, $block );
			}
		}

		return $attributes;
	}

	/**
	 * Eval custom user code and return as string.
	 *
	 * @param string $code - user code string.
	 * @param array  $attributes - block attributes.
	 *
	 * @return string
	 */
	// phpcs:disable
	public function php_eval( $code, $attributes ) {
		ob_start();

		eval( '?>' . $code );

		return ob_get_clean();
	}
	// phpcs:enable

	/**
	 * Register block attributes, meta data, and custom frontend render callback if exists.
	 */
	public function register_block_render() {
		$blocks = $this->get_blocks();

		foreach ( $blocks as $block ) {
			// Check if slug valid.
			$name_after_slug = explode( '/', $block['slug'] );
			$name_after_slug = isset( $name_after_slug[1] ) ? $name_after_slug[1] : '';

			if ( ! $block['slug'] || ! $name_after_slug ) {
				continue;
			}

			$attributes = $this->prepare_block_attributes( $block['controls'], '', $block );

			$data = array(
				'api_version'     => 3,
				'attributes'      => $attributes,
				'supports'        => $block['supports'],
				'render_callback' => function( $render_attributes, $render_content = null ) {
					// Usually this context is used to properly preload content in the Pro plugin.
					$render_context = is_admin() ? 'editor' : 'frontend';

					// We should run our function in this way because Gutenberg
					// has a 3rd parameter in the `render_callback` which conflicts with ours.
					return $this->render_callback( $render_attributes, $render_content, $render_context );
				},
				'example'         => array(),
				'styles'          => $block['styles'],
				'editor_script'   => 'lazyblocks-editor',
				'editor_style'    => 'lazyblocks-editor',
			);

			// Register block type.
			register_block_type( $block['slug'], $data );

			// Register meta.
			$meta_attributes = $this->prepare_block_meta_attributes( $block['controls'], '', $block );
			foreach ( $meta_attributes as $attribute ) {
				if ( isset( $attribute['meta'] ) && $attribute['meta'] ) {
					register_meta(
						'post',
						$attribute['meta'],
						array(
							'show_in_rest' => true,
							'single'       => true,
							'type'         => $attribute['type'],
							'default'      => $attribute['default'],
						)
					);
				}
			}
		}
	}

	/**
	 * Render block custom frontend HTML.
	 *
	 * @param array  $attributes - The block attributes.
	 * @param string $content - The block content.
	 * @param string $context - block context [frontend, editor].
	 * @param array  $block - Data containing the block's specifications (mostly used for live block preview in the block's constructor).
	 *
	 * @return string Returns the post content with latest posts added.
	 */
	public function render_callback( $attributes, $content = null, $context = 'frontend', $block = null ) {
		if ( ! $block && ( ! isset( $attributes['lazyblock'] ) || ! isset( $attributes['lazyblock']['slug'] ) ) ) {
			return null;
		}
		if ( ! $block ) {
			$block = $this->get_block( $attributes['lazyblock']['slug'] );
		}

		$context = 'editor' === $context ? 'editor' : 'frontend';
		$result  = null;

		if ( isset( $block['controls'] ) && ! empty( $block['controls'] ) ) {
			foreach ( $block['controls'] as $control ) {
				if ( ! isset( $control['child_of'] ) || ! $control['child_of'] ) {
					$control_val = $attributes[ $control['name'] ] ?? null;

					// apply filters for control values.
					$control_val = lazyblocks()->controls()->filter_control_value( $control_val, $control, $block, $context );

					if ( null !== $control_val ) {
						$attributes[ $control['name'] ] = $control_val;
					}
				}
			}
		}

		// phpcs:disable

		// apply filter for block attributes.
		$attributes = apply_filters( 'lzb/block_render/attributes', $attributes, $content, $block, $context );
		$attributes = apply_filters( $block['slug'] . '/' . $context . '_attributes', $attributes, $content, $block );
		$attributes = apply_filters( $block['slug'] . '/attributes', $attributes, $content, $block, $context );

		// apply filter for custom output callback.
		$result = apply_filters( 'lzb/block_render/callback', $result, $attributes, $context );
		$result = apply_filters( $block['slug'] . '/' . $context . '_callback', $result, $attributes );
		$result = apply_filters( $block['slug'] . '/callback', $result, $attributes, $context );

		// phpcs:enable

		// Custom render name.
		$custom_render_name = $context . '_html';
		if ( isset( $block['code']['output_method'] ) && isset( $block['code']['single_output'] ) && $block['code']['single_output'] ) {
			$custom_render_name = 'frontend_html';
		}

		// Custom output.
		if ( ! $result && isset( $block['code'] ) ) {
			$code = $block['code'];

			// Theme template file.
			if ( isset( $code['output_method'] ) && 'template' === $code['output_method'] ) {
				ob_start();
				$template_slug        = str_replace( '/', '-', $block ? $block['slug'] : $attributes['lazyblock']['slug'] );
				$template_path_editor = '/blocks/' . $template_slug . '/editor.php';
				$template_path        = '/blocks/' . $template_slug . '/block.php';
				$template_args        = array(
					'attributes' => $attributes,
					'block'      => $block,
					'context'    => $context,
				);

				// Editor template.
				if ( 'editor' === $context && $this->template_exists( $template_path_editor, $template_args ) ) {
					$this->include_template( $template_path_editor, $template_args );

					// Frontend template.
				} elseif ( $this->template_exists( $template_path, $template_args ) ) {
					$this->include_template( $template_path, $template_args );

					// Template not found.
				} else {
					$this->include_template( lazyblocks()->plugin_path . 'templates/template-not-found.php', $template_args );
				}

				$result = ob_get_clean();
				// Callback function.
			} elseif ( isset( $code[ $context . '_callback' ] ) && ! empty( $code[ $context . '_callback' ] ) && is_callable( $code[ $context . '_callback' ] ) ) {
				ob_start();
				call_user_func( $code[ $context . '_callback' ], $attributes );
				$result = ob_get_clean();

				// Custom code.
				// Previously we checked for empty, but we should output even an empty string
				// to prevent error message in the editor block preview.
			} elseif ( isset( $code[ $custom_render_name ] ) ) {
				// PHP output.
				if ( isset( $code['output_method'] ) && 'php' === $code['output_method'] ) {
					$result = $this->php_eval( $code[ $custom_render_name ], $attributes );

					// Handlebars.
				} else {
					$result = $this->handlebars->render( $code[ $custom_render_name ], $attributes );
				}
			}
		}

		// Replace the <InnerBlocks /> with the block content.
		if ( 'frontend' === $context ) {
			// Add inner-blocks wrapper with class lazyblock-inner-blocks.
			$allow_inner_blocks_wrapper = apply_filters( 'lzb/block_render/allow_inner_blocks_wrapper', true, $attributes );
			// phpcs:ignore
			$allow_inner_blocks_wrapper = apply_filters( $block['slug'] . '/allow_inner_blocks_wrapper', $allow_inner_blocks_wrapper, $attributes );

			if ( $allow_inner_blocks_wrapper ) {
				// Check for a class/className attribute provided in the template to become the InnerBlocks wrapper class.
				$matches = array();

				if ( preg_match( '/<InnerBlocks(?:[^<]+?)(?:class|className)=(?:["\']\W+\s*(?:\w+)\()?["\']([^\'"]+)[\'"]/', $result, $matches ) ) {
					$class = isset( $matches[1] ) ? $matches[1] : 'lazyblock-inner-blocks';
				} else {
					$class = 'lazyblock-inner-blocks';
				}

				$content = '<div class="' . $class . '">' . $content . '</div>';
			}

			// Escape "$" to prevent it's replacement with preg_replace.
			$content = str_replace( '$', '\$', $content );

			$result = preg_replace( '/<InnerBlocks([\S\s]*?)\/>/', $content, $result );
		}

		// add wrapper.
		$allow_wrapper = apply_filters( 'lzb/block_render/allow_wrapper', $result && 'frontend' === $context, $attributes, $context );
		// phpcs:ignore
		$allow_wrapper = apply_filters( $block['slug'] . '/' . $context . '_allow_wrapper', $allow_wrapper, $attributes );
		// phpcs:ignore
		$allow_wrapper = apply_filters( $block['slug'] . '/allow_wrapper', $allow_wrapper, $attributes, $context );

		if ( $allow_wrapper ) {
			$array_atts = array(
				'class' => '',
			);

			if ( $attributes['blockUniqueClass'] ) {
				$array_atts['class'] .= $attributes['blockUniqueClass'];
			}

			if ( $attributes['align'] ) {
				$array_atts['class'] .= ' align' . $attributes['align'];
			}

			// The anchor rendering was removed in v3.4.0 because of Gutenberg added support for automatic anchor render.
			// Then they are removed this option and we reverted this anchor render back
			//
			// @link https://github.com/WordPress/gutenberg/pull/51288.
			if ( isset( $attributes['anchor'] ) && $attributes['anchor'] ) {
				$array_atts['id'] = esc_attr( $attributes['anchor'] );
			}

			$html_atts = get_block_wrapper_attributes( $array_atts );

			$result = '<div ' . $html_atts . '>' . $result . '</div>';
		}

		// add filter for block output.
		$result = apply_filters( 'lzb/block_render/output', $result, $attributes, $context );
		// phpcs:ignore
		$result = apply_filters( $block['slug'] . '/' . $context . '_output', $result, $attributes );
		// phpcs:ignore
		$result = apply_filters( $block['slug'] . '/output', $result, $attributes, $context );

		return $result;
	}

	/**
	 * Check if template exists.
	 *
	 * @param string $template_name file name.
	 * @param array  $args args for template.
	 */
	public function template_exists( $template_name, $args = array() ) {
		if ( ! empty( $args ) && is_array( $args ) ) {
			// phpcs:ignore
			extract( $args );
		}

		// template in theme folder.
		$template = locate_template( array( $template_name ) );

		// Allow 3rd party plugin filter template file from their plugin.
		$template = apply_filters( 'lzb/block_render/template_exists', $template, $template_name, $args['attributes'], $args['block'], $args['context'] );
		// phpcs:ignore
		$template = apply_filters( $args['block']['slug'] . '/' . $args['context'] . '_template_exists', $template, $template_name, $args['attributes'], $args['block'] );
		// phpcs:ignore
		$template = apply_filters( $args['block']['slug'] . '/template_exists', $template, $template_name, $args['attributes'], $args['block'], $args['context'] );

		// DEPRECATED.
		$template = apply_filters( 'lzb/template_exists', $template, $template_name, $args );

		return file_exists( $template );
	}

	/**
	 * Include template
	 *
	 * @param string $template_name file name.
	 * @param array  $args args for template.
	 */
	public function include_template( $template_name, $args = array() ) {
		if ( ! empty( $args ) && is_array( $args ) ) {
			// phpcs:ignore
			extract( $args );
		}

		// template in theme folder.
		$template = locate_template( array( $template_name ) );

		// Allow 3rd party plugin filter template file from their plugin.
		$template = apply_filters( 'lzb/block_render/include_template', $template, $args['attributes'], $args['block'], $args['context'] );
		// phpcs:ignore
		$template = apply_filters( $args['block']['slug'] . '/' . $args['context'] . '_include_template', $template, $args['attributes'], $args['block'] );
		// phpcs:ignore
		$template = apply_filters( $args['block']['slug'] . '/include_template', $template, $args['attributes'], $args['block'], $args['context'] );

		// DEPRECATED.
		$template = apply_filters( 'lzb/include_template', $template, $template_name, $args );

		if ( file_exists( $template ) ) {
			include $template;
		}
	}
}