: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
$manager->forced_inline = $forced_inline;
$manager->write_file_location = 'footer';
if ( $forced_in_footer || $forced_inline ) {
// Restore legacy behavior--output inline styles in the footer.
$manager->set_output_location( 'footer' );
* Passes the module design styles for the current page to the advanced styles manager.
* {@see 'wp_footer' (19) Must run before the style manager's footer callback}
public static function set_advanced_styles() {
if ( et_core_is_builder_used_on_current_request() ) {
$styles .= et_pb_get_page_custom_css();
$styles .= self::get_style() . self::get_style( true );
// Pass styles to page resource which will handle their output
self::$advanced_styles_manager->set_data( $styles, 40 );
* Filters the unified page resource data. The data is an array of arrays of strings keyed by
* priority. The builder's styles are set with a priority of 40. Here we want to make sure
* only the builder's styles are output in the footer on first-page load so we aren't
* duplicating the customizer and custom css styles which are already in the <head>.
* {@see 'et_core_page_resource_get_data'}
public static function filter_page_resource_data( $data, $context, $resource ) {
global $wp_current_filter;
if ( 'inline' !== $context || ! in_array( 'wp_footer', $wp_current_filter ) ) {
if ( false === strpos( $resource->slug, 'unified' ) ) {
if ( 'footer' !== $resource->location ) {
// This is the first load of a page that doesn't currently have a unified static css file.
// The theme customizer and custom css have already been inlined in the <head> using the
// unified resource's ID. It's invalid HTML to have duplicated IDs on the page so we'll
// fix that here since it only applies to this page load anyway.
$resource->slug = $resource->slug . '-2';
return isset( $data[40] ) ? array( 40 => $data[40] ) : array();
* Get the slugs for all current builder modules.
* @param string $post_type Get module slugs for this post type. If falsy, all slugs are returned.
public static function get_module_slugs_by_post_type( $post_type = 'post' ) {
if ( ! isset( self::$_module_slugs_by_post_type[ $post_type ] ) ) {
// We get all modules when post type is not enabled so that posts that have
// had their post type support disabled still load all necessary modules.
return array_keys( self::get_modules() );
return self::$_module_slugs_by_post_type[ $post_type ];
return self::$_module_slugs_by_post_type;
* Get whether the module has Visual Builder support or not
function has_vb_support() {
return 'off' !== $this->vb_support;
* @since 3.23 Add margin padding fields object.
function set_factory_objects() {
$this->text_shadow = ET_Builder_Module_Fields_Factory::get( 'TextShadow' );
$this->margin_padding = ET_Builder_Module_Fields_Factory::get( 'MarginPadding' );
protected function _set_fields_unprocessed( $fields ) {
$unprocessed = &self::$_fields_unprocessed;
foreach ( $fields as $field => $definition ) {
if ( true === $definition ) {
// Have to use md5 now because needed by modules cache.
$key = md5( serialize( $definition ) );
if ( ! isset( $unprocessed[ $key ] ) ) {
$unprocessed[ $key ] = $definition;
$this->fields_unprocessed[ $field ] = $unprocessed[ $key ];
* Populates {@see self::$fields_unprocessed}.
public function set_fields() {
$fields_unprocessed = $this->get_complete_fields();
// Add _builder_version field to all modules
$fields_unprocessed['_builder_version'] = array( 'type' => 'skip' );
// Add _dynamic_attributes field to all modules.
$fields_unprocessed['_dynamic_attributes'] = array( 'type' => 'skip' );
// Add support for the style presets
$fields_unprocessed['_module_preset'] = array( 'type' => 'skip' );
if ( function_exists( 'et_builder_definition_sort' ) ) {
et_builder_definition_sort( $fields_unprocessed );
if ( $this->_is_official_module ) {
$this->_set_fields_unprocessed( $fields_unprocessed );
// 3rd-Party module backwards compatability starts here
foreach ( $fields_unprocessed as $field => $info ) {
if ( isset( $info['depends_to'] ) ) {
$fields_unprocessed[ $field ]['depends_on'] = $info['depends_to'];
if ( isset( $info['depends_default'] ) && $info['depends_default'] && ! isset( $info['depends_show_if'] ) ) {
$fields_unprocessed[ $field ]['depends_show_if'] = 'on';
$message = "You're Doing It Wrong! Setting definition for {$field} includes deprecated parameter: 'depends_default'. Use 'show_if' instead.";
// Process renderer of string type only.
if ( isset( $info['renderer'] ) && is_string( $info['renderer'] ) ) {
$original_renderer = $info['renderer'];
$updated_field_type = $info['renderer'];
// convert renderer into type
switch ( $info['renderer'] ) {
case 'et_builder_include_categories_option' :
case 'et_builder_include_categories_shop_option' :
$updated_field_type = 'categories';
case 'et_builder_get_widget_areas' :
$updated_field_type = 'select_sidebar';
case 'et_pb_get_font_icon_list' :
case 'et_pb_get_font_down_icon_list' :
$updated_field_type = 'select_icon';
case 'et_builder_get_gallery_settings' :
$updated_field_type = 'upload_gallery';
case 'et_builder_generate_center_map_setting' :
$updated_field_type = 'center_map';
$fields_unprocessed[ $field ]['type'] = $updated_field_type;
if ( 'et_pb_get_font_down_icon_list' === $info['renderer'] ) {
$fields_unprocessed[ $field ]['renderer_options'] = array( 'icons_list' => 'icon_down', );
// Output developer warning if renderer was converted to type
if ( $original_renderer !== $updated_field_type ) {
$message = "You're Doing It Wrong! Module setting definition for {$field} has a deprecated value: ";
$message .= "'{$original_renderer}' for parameter 'renderer'. Use '{$updated_field_type}' instead.";
// Normalize `affects` field names if needed.
if ( isset( $info['affects'] ) ) {
$affects_original = $fields_unprocessed[ $field ]['affects'];
$fields_unprocessed[ $field ]['affects'] = array();
// BB supports comma separated list of affected fields, convert it to array of fields if this is the case.
// Some plugins use combination of various lists, handle all of them
foreach( $affects_original as $affect_item ) {
if ( strpos( $affect_item, ',' ) !== false ) {
$fields_unprocessed[ $field ]['affects'] = array_merge( $fields_unprocessed[ $field ]['affects'], explode( ',', str_replace( ' ', '', $affect_item ) ) );
$fields_unprocessed[ $field ]['affects'][] = $affect_item;
array_walk( $fields_unprocessed[ $field ]['affects'], array( $this, 'normalize_affect_fields' ) );
if ( 'content_new' === $field ) {
$fields_unprocessed['content'] = $fields_unprocessed['content_new'];
unset( $fields_unprocessed['content_new'] );
$message = "You're Doing It Wrong! Setting definition for {$field} includes deprecated parameter: 'content_new'. Use 'content' instead.";
// convert old color pickers to the new ones supporting alpha channel
if ( 'color' === self::$_->array_get( $info, 'type' ) ) {
$info['type'] = 'color-alpha';
$fields_unprocessed[ $field ] = $info;
$message = "You're Doing It Wrong! You're using wrong type for the '" . $field . "'. It should be 'color-alpha' instead of 'color'.";
et_debug( $message, 4, false );
// convert input type to text
if ( 'input' === self::$_->array_get( $info, 'type' ) ) {
$fields_unprocessed[ $field ] = $info;
$message = "You're Doing It Wrong! Setting definition for {$field} has a deprecated value: 'input' for parameter: 'type'. Use 'text' instead.";
// Normalize default values
if ( isset( $info['default'] ) ) {
$fields_unprocessed[ $field ]['default'] = $this->_normalize_field_default( $field, $info['default'], $fields_unprocessed[ $field ]['type'] );
// Set default values in field definitions based on the legacy defaults "rules"
if ( isset( $this->fields_defaults ) ) {
foreach ( $this->fields_defaults as $field => $value ) {
if ( ! isset( $fields_unprocessed[ $field ] ) ) {
$condition = is_array( $value ) ? self::$_->array_get( $value, '1' ) : false;
$set_default_on_front = 'only_default_setting' !== $condition;
$default = $this->_normalize_field_default( $field, $value, $fields_unprocessed[ $field ]['type'] );
// Always set default value if exists. Only default_on_front should be conditional
$fields_unprocessed[ $field ]['default'] = $default;
if ( ! $set_default_on_front ) {
$has_default = isset( $fields_unprocessed[ $field ]['default'] );
if ( ! $has_default || $fields_unprocessed[ $field ]['default'] !== $default ) {
$fields_unprocessed[ $field ]['default_on_front'] = $default;
// Legacy Defaults Rule #4 (AKA: longest-running undetected bug in the codebase):
// Fields listed in allowlisted_fields that aren't in fields_defaults lose their definitions
if ( isset( $this->allowlisted_fields ) ) {
$disable_allowlisted_fields = isset( $this->force_unallowlisted_fields ) && $this->force_unallowlisted_fields;
if ( ! $disable_allowlisted_fields && ! is_admin() && ! et_fb_is_enabled() ) {
foreach ( $this->allowlisted_fields as $field ) {
if ( isset( $this->fields_defaults ) && array_key_exists( $field, $this->fields_defaults ) ) {
$fields_unprocessed[ $field ] = array();
$this->_set_fields_unprocessed( $fields_unprocessed );
protected function _normalize_field_default( $field, $default_value, $type = '' ) {
$normalized_value = is_array( $default_value ) ? $default_value[0] : $default_value;
// normalize default value depends on field type
if ( is_numeric( $normalized_value ) ) {
$normalized_value = (bool) $normalized_value ? 'on' : 'off';
$message = "You're Doing It Wrong! You're using wrong value for '{$field}' default value. It should be either 'on' or 'off'.";
et_debug( $message, 4 , false );
if ( is_numeric( $normalized_value ) ) {
$message = "You're Doing It Wrong! You're using wrong value for '{$field}' default value. It should be string value.";
et_debug( $message, 4 , false );
// Make sure provided HEX code is a valid color code
if ( strpos( $normalized_value, '#' ) === 0 && ! in_array( strlen( $normalized_value ), array( 4, 7 ) ) ) {
$message = "You're Doing It Wrong! You're using wrong value for '{$field}' default value. It should be valid hex color code.";
et_debug( $message, 4 , false );
return $normalized_value;
* Normalize `affects` fields name if needed.
* Some 3rd party modules use `#et_pb_<field_name>` format which is wrong and doesn't work in VB, but works in BB.
* Convert it to correct format and output notice for developer
function normalize_affect_fields( &$field_name ) {
if ( strpos( $field_name, '#et_pb_' ) !== false ) {
// Truncate field name from the string wherever it's placed
$new_field_name = substr( $field_name, strpos( $field_name, '#et_pb_' ) + 7 );
$message = "You're Doing It Wrong! You're using wrong name for 'affects' attribute. It should be '" . $new_field_name . "' instead of '" . $field_name . "'";
$field_name = $new_field_name;
et_debug( $message, 4, false );
// content_new renamed to content, so rename it in affected fields list as well
if ( $field_name === 'content_new' ) {
* Finalizes the configuration of {@see self::$fields_unprocessed}.
* Includes filter and fields processing for Visual Builder
protected function _finalize_all_fields() {
$fields_unprocessed = $this->fields_unprocessed;
$fields_before_filter = $fields_unprocessed;
* @param array $fields_unprocessed See {@see self::$fields_unprocessed}.
$fields_unprocessed = apply_filters( "et_pb_all_fields_unprocessed_{$this->slug}", $fields_unprocessed );
$is_saving_modules_cache = et_core_is_saving_builder_modules_cache();
$need_dynamic_assets = et_core_is_fb_enabled() && ! et_fb_dynamic_asset_exists( 'definitions' );
// Check if this is an AJAX request since this is how VB and BB loads the initial module data et_core_is_fb_enabled() always returns `false` here
// Make exception for requests that are regenerating modules cache and
// VB page which has no dynamic definitions asset so it can cache the definitions correctly.
if ( ! wp_doing_ajax() && ! $is_saving_modules_cache && ! $need_dynamic_assets ) {
$this->_set_fields_unprocessed( $fields_unprocessed );
foreach ( array_keys( $fields_unprocessed ) as $field_name ) {
$field_info = $fields_unprocessed[ $field_name ];
$affected_fields = self::$_->array_get( $field_info, 'affects', array() );
foreach ( $affected_fields as $affected_field ) {
if ( ! isset( $fields_unprocessed[ $affected_field ] ) ) {
if ( ! isset( $fields_unprocessed[ $affected_field ]['depends_on'] ) ) {
$fields_unprocessed[ $affected_field ]['depends_on'] = array();
// Avoid value duplication
if ( ! in_array( $field_name, $fields_unprocessed[ $affected_field ]['depends_on'] ) ) {
$fields_unprocessed[ $affected_field ]['depends_on'][] = $field_name;
// Set `depends_show_if = on` if no condition defined for the affected field for backward compatibility with old plugins
if ( ! isset( $fields_unprocessed[ $affected_field ]['depends_show_if'] ) && ! isset( $fields_unprocessed[ $affected_field ]['depends_show_if_not'] ) ) {
// Deprecation notice has already been logged for this.
$fields_unprocessed[ $affected_field ]['depends_show_if'] = 'on';
// Unset renderer to avoid errors in VB because of errors in 3rd party plugins
// BB compat. Still need this data, so leave it for BB
if ( ( self::is_loading_vb_data() || et_fb_is_enabled() ) && isset( $fields_unprocessed[ $field_name ]['renderer'] ) ) {
unset( $fields_unprocessed[ $field_name ]['renderer'] );
if ( isset( $fields_unprocessed[ $field_name ]['use_plugin_main'] ) ) {
$fields_unprocessed[ $field_name ]['use_limited_main'] = $fields_unprocessed[ $field_name ]['use_plugin_main'];
unset( $fields_unprocessed[ $field_name ]['use_plugin_main'] );
$message = "You're Doing It Wrong! Setting definition for {$field_name} includes deprecated parameter: 'use_plugin_main'. Use 'use_limited_main' instead.";
if ( isset( $fields_unprocessed[ $field_name ]['plugin_main'] ) ) {
$fields_unprocessed[ $field_name ]['limited_main'] = $fields_unprocessed[ $field_name ]['plugin_main'];
unset( $fields_unprocessed[ $field_name ]['plugin_main'] );
$message = "You're Doing It Wrong! Setting definition for {$field_name} includes deprecated parameter: 'plugin_main'. Use 'limited_main' instead.";
// determine custom fields added via filter and add specific flag to identify them in VB
$keys_before_filter = array_keys( $fields_before_filter );
$keys_after_filter = array_keys( $fields_unprocessed );
$added_fields = array_diff( $keys_after_filter, $keys_before_filter );
if ( ! empty( $added_fields ) ) {
foreach ( $added_fields as $key ) {
$fields_unprocessed[ $key ]['vb_support'] = false;
$this->_set_fields_unprocessed( $fields_unprocessed );
private function register_post_type( $post_type ) {
$this->post_types[] = $post_type;
self::$parent_modules[ $post_type ] = array();
self::$child_modules[ $post_type ] = array();
* Double quote are saved as "%22" in shortcode attributes.
* Decode them back into "
* @param string[] $enabled_dynamic_attributes
* @param bool $et_fb_processing_shortcode_object
private function _decode_double_quotes( $enabled_dynamic_attributes, $et_fb_processing_shortcode_object ) {
if ( ! isset( $this->props ) ) {
// need to encode HTML entities in Admin Area( for BB ) if Visual Editor disabled for the user.
$need_html_entities_decode = is_admin() && ! user_can_richedit();
$shortcode_attributes = array();
$font_icon_options = array( 'font_icon', 'button_icon', 'button_one_icon', 'button_two_icon', 'hover_icon' );
foreach ( $this->props as $attribute_key => $attribute_value ) {
if ( $et_fb_processing_shortcode_object && in_array( $attribute_key, $enabled_dynamic_attributes, true ) ) {
// Do not decode dynamic content values when preparing them for VB.
$shortcode_attributes[ $attribute_key ] = $attribute_value;
// decode HTML entities and remove trailing and leading quote if needed
$processed_attr_value = $need_html_entities_decode ? trim( htmlspecialchars_decode( $attribute_value, ENT_QUOTES ), '"' ) : $attribute_value;
// the icon shortcodes are fine.
if ( in_array( $attribute_key, $font_icon_options, true ) ) {
$shortcode_attributes[ $attribute_key ] = $processed_attr_value;
// icon attributes must not be str_replaced
// Set empty TinyMCE content '<br /><br />' as empty string.
$field_type = empty( $this->fields_unprocessed[ $attribute_key ]['type'] ) ? '' : $this->fields_unprocessed[ $attribute_key ]['type'];
if ( 'tiny_mce' === $field_type && 'ltbrgtbr' === preg_replace( '/[^a-z]/', '', $processed_attr_value ) ) {
$processed_attr_value = '';
// URLs are weird since they can allow non-ascii characters so we escape those separately.
if ( in_array( $attribute_key, array( 'url', 'button_link', 'button_url' ), true ) ) {
$shortcode_attributes[ $attribute_key ] = esc_url_raw( str_replace(
array( '[', ']' ),
// Manipulate string for font icon attribute with value "%%xx%%" to "##xx##".
$processed_attr_value = preg_replace( '/%%([0-9]+)%%/', '##$1##', $processed_attr_value );