: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
$render_method = $et_fb_processing_shortcode_object ? 'render_as_builder_data' : 'render';
$output = $this->{$render_method}( $attrs, $content, $render_slug, $parent_address, $global_parent, $global_parent_type, $parent_type );
$this->is_rendering = false;
// Wrap 3rd party module rendered output with proper module wrapper
// @TODO implement module wrapper on official module
if ( 'on' === $this->vb_support && 'render' === $render_method && ! $this->_is_official_module ) {
$output = $this->_render_module_wrapper( $output, $render_slug );
* Filters every builder modules shortcode output.
* @param string $module_slug
$output = apply_filters( 'et_module_shortcode_output', $output, $render_slug, $this );
* Filters builder module shortcode output. The dynamic portion of the filter name, `$render_slug`,
* refers to the slug of the module for which the shortcode output was generated.
* @param string $module_slug
$output = apply_filters( "{$render_slug}_shortcode_output", $output, $render_slug );
$this->_bump_render_count();
if ( ! $post_interference ) {
ET_Post_Stack::restore();
if ( $hide_subject_module ) {
if ( $hide_subject_module_cached ) {
$previous_subjects_cache = get_post_meta( $post_id, 'et_pb_subjects_cache', true );
if ( empty( $previous_subjects_cache ) ) {
$previous_subjects_cache = array();
if ( empty( $this->template_name ) ) {
$previous_subjects_cache[ $this->props['ab_subject_id'] ] = $output;
$previous_subjects_cache[ $this->props['ab_subject_id'] ] = $this->output();
// update the subjects cache in post meta to use it later
update_post_meta( $post_id, 'et_pb_subjects_cache', $previous_subjects_cache );
// generate the placeholder to output on front-end instead of actual content
$subject_placeholder = sprintf(
'<div class="et_pb_subject_placeholder et_pb_subject_placeholder_id_%1$s_%2$s" style="display: none;"></div>',
esc_attr( $this->props['ab_subject_id'] )
return $subject_placeholder;
// Do not use `template_name` while processing object for VB
if ( $et_fb_processing_shortcode_object || empty( $this->template_name ) ) {
* Add "et_animated" class using filter. Obsolete method and only applied to old 3rd party modules without `modules_classname()` method
* @param string $module_slug
function add_et_animated_class( $output, $module_slug ) {
if ( ! is_string( $output ) || in_array( $module_slug, ET_Builder_Element::$uses_module_classname ) ) {
remove_filter( "{$module_slug}_shortcode_output", array( $this, 'add_et_animated_class' ), 10 );
return preg_replace( "/class=\"(.*?{$module_slug}_\d+.*?)\"/", 'class="$1 et_animated"', $output, 1 );
* Delete attribute values that are equal to the global default value (if one exists).
protected function _maybe_remove_global_default_values_from_props() {
$fields = $this->fields_unprocessed;
$must_print_fields = array( 'text_orientation' );
* Filters Must Print attributes array.
* Must Print attributes - attributes which defaults should always be printed on Front End
* @param array $must_print_fields Array of attribute names.
$must_print_fields = apply_filters( $this->slug . '_must_print_attributes', $must_print_fields );
$slug = isset( $this->global_settings_slug ) ? $this->global_settings_slug : $this->slug;
$module_slug = self::$global_presets_manager->maybe_convert_module_type( $this->slug, $this->props );
$module_preset_settings = self::$global_presets_manager->get_module_presets_settings( $module_slug, $this->props );
foreach ( $fields as $field_key => $field_settings ) {
$global_setting_name = "$slug-$field_key";
$global_setting_value = ET_Global_Settings::get_value( $global_setting_name );
if ( ! $global_setting_value || in_array( $field_key, $must_print_fields ) ) {
$attr_value = self::$_->array_get( $this->props, $field_key, '' );
if ( $attr_value && $attr_value === $global_setting_value && ! array_key_exists( $field_key, $module_preset_settings ) ) {
$this->props[ $field_key ] = '';
// intended to be overridden as needed
function maybe_inherit_values() {}
* Like {@see self::render()}, but sources the output from a template file. The template name
* should be set in {@see self::$template_name}.
* Note: this functionality is not currently supported by the Visual Builder. Pages containing
* modules that use this method to render their output cannot be edited using the Visual Builder
* at this time. However, full support will be added in the coming months.
* @since 3.1 Renamed from `shortcode_output()` to `output()`
if ( empty( $this->template_name ) ) {
if ( method_exists( $this, 'shortcode_output' ) ) {
// Backwards compatibility
return $this->__call( 'shortcode_output', array() );
$this->props['content'] = $this->content;
require( locate_template( $this->template_name . '.php' ) );
* Generates HTML data attributes from an array of props.
* @since 3.1 Rename from `shortcode_atts_to_data_atts()` to `props_to_html_data_attrs()`
public function props_to_html_data_attrs( $props = array() ) {
foreach ( $props as $attr ) {
$output[] = 'data-' . esc_attr( $attr ) . '="' . esc_attr( $this->props[ $attr ] ) . '"';
return implode( ' ', $output );
* This method is called before {@self::_render()} for rows, columns, and modules. It can
* be overridden by elements that need to perform any tasks before rendering begins.
* @since 3.1 Renamed from `pre_shortcode_content()` to `before_render()`.
public function before_render() {
if ( method_exists( $this, 'pre_shortcode_content' ) ) {
// Backwards compatibility
$this->__call( 'pre_shortcode_content', array() );
* Generates the module's HTML output based on {@see self::$props}. This method should be
* overridden in module classes.
* @since 3.1 Renamed from `shortcode_callback()` to `render()`.
* @param array $attrs List of unprocessed attributes
* @param string $content Content being processed
* @param string $render_slug Slug of module that is used for rendering output
* @return string The module's HTML output.
public function render( $attrs, $content = null, $render_slug ) {
if ( method_exists( $this, 'shortcode_callback' ) ) {
// Backwards compatibility
return $this->__call( 'shortcode_callback', array( $attrs, $content, $render_slug ) );
* Replace the et_pb_row with et_pb_row_inner and et_pb_column with et_pb_column_inner.
* Used as a callback function in {@self::et_pb_maybe_fix_specialty_columns} when fixing content of Specialty Sections
* @return string Shortcode string.
public function et_pb_fix_specialty_columns( $rows ) {
$sanitized_shortcode = str_replace( array( 'et_pb_row ', 'et_pb_row]' ), array( 'et_pb_row_inner ', 'et_pb_row_inner]' ), $rows[0] );
$sanitized_shortcode = str_replace( array( 'et_pb_column ', 'et_pb_column]' ), array( 'et_pb_column_inner ', 'et_pb_column_inner]' ), $rows[0] );
return $sanitized_shortcode;
* Run regex against the Specialty Section content to find and fix invalid inner shortcodes
* @return string Shortcode string.
public function et_pb_maybe_fix_specialty_columns( $section_content ) {
return preg_replace_callback('/(\[et_pb_(row |row_inner) .*?\].*\[\/et_pb_(row |row_inner)\])/mis', array( $this, 'et_pb_fix_specialty_columns' ), $section_content );
* Generates data used to render the module in the builder.
* See {@see self::render()} for parameter info.
* @since 3.1 Renamed from `_shortcode_passthru_callback()` to `render_as_builder_data()`
* @return array|string An array when called during AJAX request, an empty string otherwise.
public function render_as_builder_data( $atts, $content = null, $render_slug, $parent_address = '', $global_parent = '', $global_parent_type = '', $parent_type = '' ) {
// this is called during pageload, but we want to ignore that round, as this data will be built and returned on separate ajax request instead
et_core_nonce_verified_previously();
if ( ! ( isset( $_POST['action'] ) || apply_filters( 'et_builder_module_force_render', false ) ) ) {
$fields = $this->process_fields( $this->fields_unprocessed );
$function_name_processed = et_fb_prepare_tag( $render_slug );
$unsynced_global_attributes = array();
$use_updated_global_sync_method = false;
$global_module_id = isset( $atts['global_module'] ) ? $atts['global_module'] : false;
$is_specialty_placeholder = isset( $atts['template_type'] ) && 'section' === $atts['template_type'] && isset( $atts['specialty'] ) && 'on' === $atts['specialty'] && ( ! $content || '' === trim( $content ) );
$is_global_template = false;
$real_parent_type = $parent_type;
if ( $render_slug && $render_slug !== $this->slug ) {
if ( $rendering_module = self::get_module( $render_slug, $this->get_post_type() ) ) {
$fields = array_merge( $fields, $this->process_fields( $rendering_module->fields_unprocessed ) );
$output_render_slug = $render_slug;
// When rendering specialty columns we should make sure correct tags are used for inner content
// Global Rows inside may break it in some cases, so handle it.
if ( 'et_pb_specialty_column' === $parent_type && 'et_pb_row' === $render_slug ) {
$output_render_slug = 'et_pb_row_inner';
$function_name_processed = 'et_pb_row_inner';
if ( 'et_pb_row_inner' === $parent_type && 'et_pb_column' === $render_slug ) {
$output_render_slug = 'et_pb_column_inner';
$function_name_processed = 'et_pb_column_inner';
$post_id = isset( $post->ID ) ? $post->ID : intval( self::$_->array_get( $_POST, 'et_post_id' ) );
$post_type = isset( $post->post_type ) ? $post->post_type : sanitize_text_field( self::$_->array_get( $_POST, 'et_post_type' ) );
$layout_type = isset( $post_type, $post_id ) && 'et_pb_layout' === $post_type ? et_fb_get_layout_type( $post_id ) : '';
if ( 'module' === $layout_type ) {
// Add support of new selective sync feature for library modules in VB
$template_scope = wp_get_object_terms( $post_id, 'scope' );
$is_global_template = ! empty( $template_scope[0] ) && 'global' === $template_scope[0]->slug;
if ( $is_global_template ) {
$global_module_id = $post_id;
//override module attributes for global module
if ( ! empty( $global_module_id ) ) {
if ( ! in_array( $render_slug, array( 'et_pb_section', 'et_pb_row', 'et_pb_row_inner', 'et_pb_column', 'et_pb_column_inner' ) ) ) {
$processing_global_module = $global_module_id;
$unsynced_global_attributes = get_post_meta( $processing_global_module, '_et_pb_excluded_global_options' );
$use_updated_global_sync_method = ! empty( $unsynced_global_attributes );
$global_module_data = et_pb_load_global_module( $global_module_id, $function_name_processed );
if ( '' !== $global_module_data ) {
$unsynced_options = ! empty( $unsynced_global_attributes[0] ) ? json_decode( $unsynced_global_attributes[0], true ) : array() ;
$content_synced = $use_updated_global_sync_method && ! in_array( 'et_pb_content_field', $unsynced_options );
$is_module_fully_global = $use_updated_global_sync_method && empty( $unsynced_options );
$unsynced_legacy_options = array();
// support legacy selective sync system
if ( ! $use_updated_global_sync_method ) {
$content_synced = ! isset( $atts['saved_tabs'] ) || false !== strpos( $atts['saved_tabs'], 'general' ) || 'all' === $atts['saved_tabs'];
$is_module_fully_global = ! isset( $atts['saved_tabs'] ) || 'all' === $atts['saved_tabs'];
if ( $content_synced && ! $is_global_template ) {
$global_content = et_pb_get_global_module_content( $global_module_data, $function_name_processed );
// When saving global rows from specialty sections, they get saved as et_pb_row instead of et_pb_row_inner.
// Handle this special case when parsing to avoid empty global row content.
if ( empty( $global_content ) && 'et_pb_row_inner' === $function_name_processed ) {
$global_content = et_pb_get_global_module_content( $global_module_data, 'et_pb_row', true );
// remove the shortcode content to avoid conflicts of parent attributes with similar attrs from child modules
if ( false !== $global_content ) {
$global_content_processed = str_replace( $global_content, '', $global_module_data );
$global_content_processed = $global_module_data;
// Ensuring that all possible attributes exist to avoid remaining child attributes being used by global parents' attributes
// Do that only in case the module is fully global
if ( $is_module_fully_global ) {
$global_atts = shortcode_parse_atts( et_pb_remove_shortcode_content( $global_content_processed, $this->slug ) );
$global_atts = shortcode_parse_atts( $global_content_processed );
// Run et_pb_module_shortcode_attributes filter to apply migration system on attributes of global module
$global_atts = apply_filters( 'et_pb_module_shortcode_attributes', $global_atts, $atts, $this->slug, $this->generate_element_address( $render_slug ), $content );
// Parse dynamic content in global attributes.
$enabled_dynamic_attributes = $this->_get_enabled_dynamic_attributes( $global_atts );
$global_atts = $this->_encode_legacy_dynamic_content( $global_atts, $enabled_dynamic_attributes );
$global_atts = $this->process_dynamic_attrs( $global_atts );
// Parse dynamic content in global content.
if ( false !== $global_content ) {
$global_content = $this->_encode_legacy_dynamic_content_value(
$enabled_dynamic_attributes
$global_content = $this->_resolve_value(
$this->_get_enabled_dynamic_attributes( $global_atts ),
foreach( $this->props as $single_attr => $value ) {
if ( isset( $global_atts[$single_attr] ) && ! in_array( $single_attr, $unsynced_options ) ) {
// replace %22 with double quotes in options to make sure it's rendered correctly
if ( ! $is_global_template ) {
$this->props[ $single_attr ] = is_string( $global_atts[ $single_attr ] ) && ! array_intersect( array( "et_pb_{$single_attr}", $single_attr ), $this->dbl_quote_exception_options ) ? str_replace( '%22', '"', $global_atts[ $single_attr ] ) : $global_atts[ $single_attr ];
} else if ( ! $use_updated_global_sync_method ) {
// prepare array of unsynced options to migrate the legacy modules to new system
$unsynced_legacy_options[] = $single_attr;
$unsynced_global_attributes[0] = $unsynced_options;
// migrate unsynced options to the new selective sync method
if ( ! $use_updated_global_sync_method ) {
$unsynced_global_attributes[0] = $unsynced_legacy_options;
// check the content and add it into list if needed.
if ( ! $content_synced ) {
$unsynced_global_attributes[0][] = 'et_pb_content_field';
$unsynced_global_attributes[0] = $unsynced_options;
// remove global_module attr if it doesn't exist in DB
$this->props['global_module'] = '';
$module_slug = self::$global_presets_manager->maybe_convert_module_type( $this->slug, $this->props );
$module_preset_settings = self::$global_presets_manager->get_module_presets_settings( $module_slug, $this->props );
foreach( $this->props as $shortcode_attr_key => $shortcode_attr_value ) {
$value = $shortcode_attr_value;
// don't set the default, unless, lol, the value is literally 'default'
if ( 'default' !== $value ) {
$has_preset_value = isset( $module_preset_settings[ $shortcode_attr_key ] );
if ( $has_preset_value && isset( $atts[ $shortcode_attr_key ] ) ) {
$is_equal_to_preset_value = $atts[ $shortcode_attr_key ] === $module_preset_settings[ $shortcode_attr_key ];
$value = $is_equal_to_preset_value ? '' : $atts[ $shortcode_attr_key ];
// handle 'preset' type of attributes
if ( isset( $fields[ $shortcode_attr_key ]['default'] ) && is_array( $fields[ $shortcode_attr_key ]['default'] ) ) {
$field = $fields[ $shortcode_attr_key ];
$preset_attribute_name = $field['default'][0];
if ( 'filter' === $preset_attribute_name ) {
if ( apply_filters( $field['default'][1], $shortcode_attr_key ) === $value ) {
$preset_default_value = et_()->array_get( $fields[ $preset_attribute_name ], 'default', 'none' );
$preset_attribute_value = et_()->array_get( $this->props, $preset_attribute_name, $preset_default_value );
if ( ! empty( $preset_attribute_value ) ) {
$value_from_preset = et_()->array_get( $fields[ $shortcode_attr_key ]['default'][1], $preset_attribute_value, '' );
if ( $value == $value_from_preset ) {
$is_equal_to_default = isset( $fields[ $shortcode_attr_key ]['default'] ) && $value === $fields[ $shortcode_attr_key ]['default'];
$is_equal_to_default_on_front = isset( $fields[ $shortcode_attr_key ]['default_on_front'] ) && $value === $fields[ $shortcode_attr_key ]['default_on_front'];
if ( $is_equal_to_default || $is_equal_to_default_on_front ) {
if ( $shortcode_attr_key !== '_module_preset' ) {
// generic override, disabled=off is an unspoken default
if ( $shortcode_attr_key === 'disabled' && $shortcode_attr_value === 'off' ) {
// this override is necessary becuase et_pb_column and et_pb_column_inner type default is 4_4 and will get stomped
// above since its default, but we need it explicitly set anyways, so we force set it
if ( in_array( $render_slug, array( 'et_pb_column', 'et_pb_column_inner' ) ) && $shortcode_attr_key === 'type' ) {
$value = $shortcode_attr_value;
$is_include_attr = false;
&& $shortcode_attr_key !== et_pb_hover_options()->get_field_base_name( $shortcode_attr_key )
&& et_pb_hover_options()->is_enabled( et_pb_hover_options()->get_field_base_name( $shortcode_attr_key ), $atts ) ) {
&& $shortcode_attr_key !== et_pb_responsive_options()->get_field_base_name( $shortcode_attr_key )
&& et_pb_responsive_options()->is_enabled( et_pb_responsive_options()->get_field_base_name( $shortcode_attr_key ), $atts ) ) {
if ( $is_include_attr ) {
$attrs[$shortcode_attr_key] = is_string($value) ? html_entity_decode($value) : $value;