File: /var/www/html/wp-content/plugins/jet-engine/includes/components/blocks-views/render.php
<?php
/**
* Elementor views manager
*/
// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
die;
}
if ( ! class_exists( 'Jet_Engine_Blocks_Views_Render' ) ) {
/**
* Define Jet_Engine_Blocks_Views_Render class
*/
class Jet_Engine_Blocks_Views_Render {
private $contents = array();
private $enqueued_css = array();
private $printed_css = array();
private $current_listing = null;
/**
* Counter of wp containers in listing item.
* @var int
*/
private $counter = 0;
/**
* Current listing css generated by wp.
* @var string
*/
private $wp_listing_css = null;
/**
* Printed listing css generated by wp.
* @var array
*/
private $printed_wp_css = array();
/**
* Selectors mappings
* @var array
*/
private $selectors_map = array();
public function __construct() {
add_action( 'enqueue_block_assets', array( jet_engine()->frontend, 'frontend_styles' ) );
add_action( 'wp_footer', array( $this, 'print_css' ) );
add_action( 'jet-engine/listing/grid/after', array( $this, 'print_preview_css' ) );
add_action( 'jet-engine/blocks-views/print-template-styles', array( $this, 'print_template_css' ) );
add_filter( 'jet-engine/listing/content/blocks', array( $this, 'get_listing_content_cb' ), 10, 2 );
if ( $this->wp_is_supports_style_engine() ) {
add_filter( 'render_block', array( $this, 'modify_listing_content' ), 20 );
}
}
/**
* Print preview CSS for listing by render instance
*
* @param object $render Render instance
* @return void
*/
public function print_preview_css( $render ) {
$this->print_listing_css( $render->listing_id );
}
/**
* Print listing CSS for given listing ID
*
* @param int $listing_id Listing/Component ID to print CSS for
* @return void
*/
public function print_listing_css( $listing_id ) {
if ( ! empty( $this->enqueued_css[ $listing_id ] ) && ! in_array( $listing_id, $this->printed_css ) ) {
// Sanitized by $this->enqueue_listing_css()
echo $this->enqueued_css[ $listing_id ]; // phpcs:ignore
$this->printed_css[] = $listing_id;
}
}
/**
* Print all enqueued CSS
*
* @return void
*/
public function print_css() {
foreach ( $this->enqueued_css as $post_id => $css ) {
if ( ! empty( $css ) && ! in_array( $post_id, $this->printed_css ) ) {
// Sanitized by $this->enqueue_listing_css()
echo $css; // phpcs:ignore
$this->printed_css[] = $post_id;
}
}
}
/**
* Main callback to print Block listing item
*
* @param string $content Initial content to replace.
* @param int $listing_id Listing ID to get content for.
* @return string
*/
public function get_listing_content_cb( $content, $listing_id ) {
return $this->get_listing_content( $listing_id );
}
/**
* Returns listing content for given listing ID
*
* @param int $listing_id Listing ID to get content for.
* @return string
*/
public function get_listing_content( $listing_id ) {
$print_css = false;
if ( jet_engine()->listings->components->is_component( $listing_id ) ) {
$print_css = true;
}
$this->enqueue_listing_css( $listing_id, $print_css );
$content = $this->get_raw_content( $listing_id );
$content = do_shortcode( $this->parse_content( $content, $listing_id ) );
$content = $this->add_link_to_content( $content, $listing_id );
return apply_filters( 'jet-engine/blocks-views/render/listing-content', $content, $listing_id );
}
public function add_link_to_content( $content, $listing_id ) {
$settings = get_post_meta( $listing_id, '_elementor_page_settings', true );
if ( empty( $settings ) || empty( $settings['listing_link'] ) ) {
return $content;
}
$dynamic_settings = array(
'listing_link_aria_label',
);
foreach ( $dynamic_settings as $dynamic_setting ) {
if ( empty( $settings[ $dynamic_setting ] ) ) {
continue;
}
$settings[ $dynamic_setting ] = jet_engine()->listings->macros->do_macros( $settings[ $dynamic_setting ] );
$settings[ $dynamic_setting ] = do_shortcode( $settings[ $dynamic_setting ] );
}
return jet_engine()->frontend->add_listing_link_to_content( $content, $settings );
}
public function fix_context( $context ) {
$object = jet_engine()->listings->data->get_current_object();
if ( $object && 'WP_Post' === get_class( $object ) ) {
$context['postId'] = $object->ID;
$context['postType'] = $object->post_type;
}
return $context;
}
/**
* Returns current listing ID
*
* @return [type] [description]
*/
public function get_current_listing_id() {
return $this->current_listing;
}
/**
* Parse listing item content
*
* @param [type] $content [description]
* @return [type] [description]
*/
public function parse_content( $content, $listing_id ) {
add_filter( 'render_block_context', array( $this, 'fix_context' ) );
// Removed `wp_render_layout_support_flag` filter and added modified filter.
if ( ! $this->wp_is_supports_style_engine() ) {
remove_filter( 'render_block', 'wp_render_layout_support_flag' );
add_filter( 'render_block', array( $this, 'wp_render_layout_support_flag' ), 10, 2 );
}
$initial_listing = $this->current_listing;
$initial_counter = $this->counter;
$this->current_listing = $listing_id;
$parsed = do_blocks( $content );
$this->current_listing = $initial_listing;
$this->counter = $initial_counter;
remove_filter( 'render_block_context', array( $this, 'fix_context' ) );
// Restore `wp_render_layout_support_flag` filter.
if ( ! $this->wp_is_supports_style_engine() && null === $this->current_listing ) {
add_filter( 'render_block', 'wp_render_layout_support_flag', 10, 2 );
remove_filter( 'render_block', array( $this, 'wp_render_layout_support_flag' ) );
}
// Enqueue wp listing css.
$inline_wp_css = $this->enqueue_wp_listing_css( $listing_id );
if ( ! empty( $inline_wp_css ) ) {
$parsed = $inline_wp_css . $parsed;
}
return $parsed;
}
public function print_template_css( $template_id ) {
$this->enqueue_listing_css( $template_id, true );
}
/**
* Enqueue listing CSS for given listing ID.
* Directly prints CSS if `$print` is true or add to the queue if false
*
* @param int $listing_id Listing ID to enqueue CSS for.
* @param bool $print Whether to print CSS immediately or not.
*
* @return void
*/
public function enqueue_listing_css( $listing_id, $print = false ) {
$listing_id = absint( $listing_id );
if ( isset( $this->enqueued_css[ $listing_id ] ) ) {
return;
}
$css = get_post_meta( $listing_id, '_jet_engine_listing_css', true );
$result = '';
$style = '';
if ( class_exists( '\JET_SM\Gutenberg\Style_Manager' ) ) {
$style = \JET_SM\Gutenberg\Style_Manager::get_instance()->get_blocks_style( $listing_id );
}
$css .= $style;
if ( $css ) {
$css = str_replace( 'selector', '.jet-listing-grid--' . $listing_id, $css );
$css = Jet_Engine_Sanitizer::sanitize_inline_css( $css ); // phpcs:ignore
$result = '<style class="listing-css-' . $listing_id . '">' . $css . '</style>';
}
if ( $print ) {
// Sanitized above
echo $result; // phpcs:ignore
$this->printed_css[] = $listing_id;
} else {
$this->enqueued_css[ $listing_id ] = $result;
}
if ( class_exists( '\JET_SM\Gutenberg\Style_Manager' ) ) {
\JET_SM\Gutenberg\Style_Manager::get_instance()->render_blocks_fonts( $listing_id );
}
}
/**
* Returns raw listing content
*
* @param int $listing_id Listing ID to get content for.
* @return string
*/
public function get_raw_content( $listing_id ) {
if ( ! isset( $this->contents[ $listing_id ] ) ) {
$post = get_post( $listing_id );
$this->contents[ $listing_id ] = $post->post_content;
}
return $this->contents[ $listing_id ];
}
/**
* `wp_render_layout_support_flag` is rewritten
* to prevent conflict of not uniq css classes (`wp-container-`) on ajax.
* @see: https://github.com/Crocoblock/issues-tracker/issues/700
*
* @param $block_content
* @param $block
*
* @return string|string[]|null
*/
public function wp_render_layout_support_flag( $block_content, $block ) {
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
$support_layout = block_has_support( $block_type, array( '__experimentalLayout' ), false );
if ( ! $support_layout ) {
return $block_content;
}
$block_gap = wp_get_global_settings( array( 'spacing', 'blockGap' ) );
$global_layout_settings = wp_get_global_settings( array( 'layout' ) );
$has_block_gap_support = isset( $block_gap ) ? null !== $block_gap : false;
$default_block_layout = _wp_array_get( $block_type->supports, array( '__experimentalLayout', 'default' ), array() );
$used_layout = isset( $block['attrs']['layout'] ) ? $block['attrs']['layout'] : $default_block_layout;
if ( isset( $used_layout['inherit'] ) && $used_layout['inherit'] ) {
if ( ! $global_layout_settings ) {
return $block_content;
}
}
$class_names = array();
$layout_definitions = _wp_array_get( $global_layout_settings, array( 'definitions' ), array() );
$block_classname = wp_get_block_default_classname( $block['blockName'] );
// Editing $container_class var
// $container_class = wp_unique_id( 'wp-container-' );
$container_class = sprintf( 'wp-container-%s-%s', $this->current_listing, ++$this->counter );
$layout_classname = '';
// Set the correct layout type for blocks using legacy content width.
if ( isset( $used_layout['inherit'] ) && $used_layout['inherit'] || isset( $used_layout['contentSize'] ) && $used_layout['contentSize'] ) {
$used_layout['type'] = 'constrained';
}
if (
wp_get_global_settings( array( 'useRootPaddingAwareAlignments' ) ) &&
isset( $used_layout['type'] ) &&
'constrained' === $used_layout['type']
) {
$class_names[] = 'has-global-padding';
}
// The following section was added to reintroduce a small set of layout classnames that were
// removed in the 5.9 release (https://github.com/WordPress/gutenberg/issues/38719). It is
// not intended to provide an extended set of classes to match all block layout attributes
// here.
if ( ! empty( $block['attrs']['layout']['orientation'] ) ) {
$class_names[] = 'is-' . sanitize_title( $block['attrs']['layout']['orientation'] );
}
if ( ! empty( $block['attrs']['layout']['justifyContent'] ) ) {
$class_names[] = 'is-content-justification-' . sanitize_title( $block['attrs']['layout']['justifyContent'] );
}
if ( ! empty( $block['attrs']['layout']['flexWrap'] ) && 'nowrap' === $block['attrs']['layout']['flexWrap'] ) {
$class_names[] = 'is-nowrap';
}
// Get classname for layout type.
if ( isset( $used_layout['type'] ) ) {
$layout_classname = _wp_array_get( $layout_definitions, array( $used_layout['type'], 'className' ), '' );
} else {
$layout_classname = _wp_array_get( $layout_definitions, array( 'default', 'className' ), '' );
}
if ( $layout_classname && is_string( $layout_classname ) ) {
$class_names[] = sanitize_title( $layout_classname );
}
/*
* Only generate Layout styles if the theme has not opted-out.
* Attribute-based Layout classnames are output in all cases.
*/
if ( ! current_theme_supports( 'disable-layout-styles' ) ) {
$gap_value = _wp_array_get( $block, array( 'attrs', 'style', 'spacing', 'blockGap' ) );
/*
* Skip if gap value contains unsupported characters.
* Regex for CSS value borrowed from `safecss_filter_attr`, and used here
* to only match against the value, not the CSS attribute.
*/
if ( is_array( $gap_value ) ) {
foreach ( $gap_value as $key => $value ) {
$gap_value[ $key ] = $value && preg_match( '%[\\\(&=}]|/\*%', $value ) ? null : $value;
}
} else {
$gap_value = $gap_value && preg_match( '%[\\\(&=}]|/\*%', $gap_value ) ? null : $gap_value;
}
$fallback_gap_value = _wp_array_get( $block_type->supports, array( 'spacing', 'blockGap', '__experimentalDefault' ), '0.5em' );
$block_spacing = _wp_array_get( $block, array( 'attrs', 'style', 'spacing' ), null );
/*
* If a block's block.json skips serialization for spacing or spacing.blockGap,
* don't apply the user-defined value to the styles.
*/
$should_skip_gap_serialization = wp_should_skip_block_supports_serialization( $block_type, 'spacing', 'blockGap' );
$style = wp_get_layout_style(
".$block_classname.$container_class",
$used_layout,
$has_block_gap_support,
$gap_value,
$should_skip_gap_serialization,
$fallback_gap_value,
$block_spacing
);
// Only add container class and enqueue block support styles if unique styles were generated.
if ( ! empty( $style ) ) {
$class_names[] = $container_class;
// Style stored to `wp_listing_css` prop.
$this->wp_listing_css .= $style;
}
}
/*
* This assumes the hook only applies to blocks with a single wrapper.
* A limitation of this hook is that nested inner blocks wrappers are not yet supported.
*/
$content = preg_replace(
'/' . preg_quote( 'class="', '/' ) . '/',
'class="' . esc_attr( implode( ' ', $class_names ) ) . ' ',
$block_content,
1
);
// wp_enqueue_block_support_styles( $style );
return $content;
}
public function enqueue_wp_listing_css( $listing_id ) {
$inline_css = null;
if ( ! empty( $this->wp_listing_css ) && ! in_array( $listing_id, $this->printed_wp_css ) ) {
if ( wp_doing_ajax() ) {
$inline_css = sprintf( '<style>%s</style>', $this->wp_listing_css );
} else {
wp_enqueue_block_support_styles( $this->wp_listing_css );
}
$this->printed_wp_css[] = $listing_id;
}
$this->wp_listing_css = null;
return $inline_css;
}
public function wp_is_supports_style_engine() {
return version_compare( $GLOBALS['wp_version'], '6.1', '>=' );
}
/**
* Replace the `wp-container-{$id}` and `wp-container-content-{$id}` classes
* to `wp-container-{$listing_id}-{$counter}` and wp-container-content-{$listing_id}-{$counter}`
* in content and styles to prevent css conflict on ajax.
*
* @param string $block_content
* @return string|null
*/
public function modify_listing_content( $block_content ) {
if ( ! $this->current_listing ) {
return $block_content;
}
$this->selectors_map = array();
$block_content = preg_replace_callback( '/wp-container(?:|-content)-\d++(?!-)/', function( $matches ) {
$selector = $matches[0];
if ( isset( $this->selectors_map[ $selector ] ) ) {
return $this->selectors_map[ $selector ];
}
$prefix = ( false !== strpos( $selector, '-content' ) ) ? 'wp-container-content' : 'wp-container';
$new_selector = sprintf( '%s-%s-%s', $prefix, $this->current_listing, ++ $this->counter );
$this->selectors_map[ $selector ] = $new_selector;
return $new_selector;
}, $block_content );
if ( ! empty( $this->selectors_map ) ) {
$store = WP_Style_Engine::get_store( 'block-supports' );
$css_rules = $store->get_all_rules();
if ( empty( $css_rules ) ) {
return $block_content;
}
$check_keys = array_map( function( $old_selector ) {
return $old_selector . '(?![\d-])';
}, array_keys( $this->selectors_map ) );
$check_regex = '/' . join( '|', $check_keys ) . '/';
foreach ( $css_rules as $selector => $css_rule ) {
if ( ! preg_match( $check_regex, $selector ) && ! empty( $css_rule ) ) {
continue;
}
$store->remove_rule( $selector );
$new_selector = str_replace( array_keys( $this->selectors_map ), array_values( $this->selectors_map ), $selector );
if ( isset( $css_rules[ $new_selector ] ) ) {
continue;
}
$store->add_rule( $new_selector )->add_declarations( $css_rule->get_declarations()->get_declarations() );
if ( wp_doing_ajax() ) {
$css_rule->set_selector( $new_selector );
$this->wp_listing_css .= $css_rule->get_css();
}
}
}
return $block_content;
}
}
}