: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* Is Gutenberg active and enabled for the current post
* WP 5.0 WARNING - don't use before global post has been set
* @deprecated See {@see et_core_is_gutenberg_enabled()}
* @since 3.19.2 Renamed and moved to core.
* @return bool True - if the plugin is active and enabled.
if ( ! function_exists( 'et_is_gutenberg_enabled' ) ) :
function et_is_gutenberg_enabled() {
return et_core_is_gutenberg_enabled();
* Modify comment count for preview screen. Somehow WordPress' get_comments_number() doesn't get correct $post_id
* param and doesn't have proper fallback to global $post if $post_id variable isn't found. This causes incorrect
* comment count in preview screen
* @see get_comments_number()
* @see get_comments_number_text()
function et_pb_preview_comment_count( $count, $post_id ) {
if ( is_et_pb_preview() ) {
$count = isset( $post->comment_count ) ? $post->comment_count : $count;
add_filter( 'get_comments_number', 'et_pb_preview_comment_count', 10, 2 );
* List of shortcodes that triggers error if being used in admin
* @return array shortcode tag
function et_pb_admin_excluded_shortcodes() {
// Triggers issue if Sensei and YOAST SEO are activated
if ( et_is_yoast_seo_plugin_active() && function_exists( 'Sensei' ) ) {
$shortcodes[] = 'usercourses';
// WPL real estate prints unwanted on-page JS that caused an issue on BB
if ( class_exists( 'wpl_extensions' ) ) {
// [submit_job_form] shortcode prints wp_editor this creating problems post edit page render
if ( et_is_wp_job_manager_plugin_active() ) {
$shortcodes[] = 'submit_job_form';
// [shop_messages] shortcode causes a fatal error when rendered too soon
if ( et_is_woocommerce_plugin_active() ) {
$shortcodes[] = 'shop_messages';
return apply_filters( 'et_pb_admin_excluded_shortcodes', $shortcodes );
* Get GMT offset string that can be used for parsing date into correct timestamp
function et_pb_get_gmt_offset_string() {
$gmt_offset = get_option( 'gmt_offset' );
$gmt_divider = '-' === substr( $gmt_offset, 0, 1 ) ? '-' : '+';
$gmt_offset_hour = str_pad( abs( intval( $gmt_offset ) ), 2, "0", STR_PAD_LEFT );
$gmt_offset_minute = str_pad( ( ( abs( $gmt_offset ) * 100 ) % 100 ) * ( 60 / 100 ), 2, "0", STR_PAD_LEFT );
$gmt_offset_string = "GMT{$gmt_divider}{$gmt_offset_hour}{$gmt_offset_minute}";
return $gmt_offset_string;
* Get post's category label and permalink to be used on frontend
* @param WP_Term[] $default
* @return array categories
function et_pb_get_post_categories( $post_id, $default = array() ) {
$categories = get_the_category( $post_id );
$post_categories = array();
if ( $default && ! $categories ) {
$categories = array_values( $default );
foreach ( array_keys( $categories ) as $key ) {
_make_cat_compat( $categories[ $key ] );
// Filter out any falsy values that may appear due from $default.
$categories = array_filter( $categories );
if ( ! empty( $categories ) ) {
foreach ( $categories as $category ) {
$post_categories[ $category->cat_ID ] = array(
'id' => $category->cat_ID,
'label' => $category->cat_name,
'permalink' => get_category_link( $category->cat_ID ),
* Add "Use Visual Builder" link to WP-Admin bar
function et_fb_add_admin_bar_link() {
$is_not_builder_enabled_single = ! is_singular() || ! et_builder_fb_enabled_for_post( get_the_ID() );
$is_not_in_wc_shop = ! et_builder_used_in_wc_shop();
$not_allowed_fb_access = ! et_pb_is_allowed( 'use_visual_builder' );
if ( $not_allowed_fb_access || ( $is_not_builder_enabled_single && $is_not_in_wc_shop ) ) {
global $wp_admin_bar, $wp_the_query;
// WooCommerce Shop Page replaces main query, thus it has to be normalized
if ( et_builder_used_in_wc_shop() && method_exists( $wp_the_query, 'get_queried_object' ) && isset( $wp_the_query->get_queried_object()->ID ) ) {
$post_id = $wp_the_query->get_queried_object()->ID;
$is_divi_library = 'et_pb_layout' === get_post_type( $post_id );
$page_url = $is_divi_library ? get_edit_post_link( $post_id ) : get_permalink( $post_id );
// Don't add the link, if Frontend Builder has been loaded already
if ( et_fb_is_enabled() ) {
$wp_admin_bar->add_menu( array(
'id' => 'et-disable-visual-builder',
'title' => esc_html__( 'Exit Visual Builder', 'et_builder' ),
'href' => esc_url( $page_url ),
$current_object = $wp_the_query->get_queried_object();
if ( ! current_user_can( 'edit_post', $current_object->ID ) || ! et_pb_is_allowed( 'divi_builder_control' ) ) {
$use_visual_builder_url = et_pb_is_pagebuilder_used( $post_id ) ?
et_fb_get_builder_url( $page_url ) :
'et_fb_activation_nonce' => wp_create_nonce( 'et_fb_activation_nonce_' . $post_id ),
$wp_admin_bar->add_menu( array(
'id' => 'et-use-visual-builder',
'title' => esc_html__( 'Enable Visual Builder', 'et_builder' ),
'href' => esc_url( $use_visual_builder_url ),
add_action( 'admin_bar_menu', 'et_fb_add_admin_bar_link', 999 );
* Retrieve and process saved Layouts.
* It different than the function which retrieves saved Sections, Rows and Modules from library because layouts require different processing
function et_fb_get_saved_layouts() {
if ( ! wp_verify_nonce( $_POST['et_fb_retrieve_library_modules_nonce'], 'et_fb_retrieve_library_modules_nonce' ) ){
if ( ! current_user_can( 'edit_posts' ) ) {
// Reduce number of results per page if we're hosted on wpengine to avoid 500 error due to memory allocation.
// This is caused by one of their custom mu-plugins doing additional stuff but we have no control over there.
$page_size = function_exists( 'is_wpe' ) || function_exists( 'is_wpe_snapshot' ) ? 25 : 50;
$post_type = ! empty( $_POST['et_post_type'] ) ? sanitize_text_field( $_POST['et_post_type'] ) : 'post';
$layouts_type = ! empty( $_POST['et_load_layouts_type'] ) ? sanitize_text_field( $_POST['et_load_layouts_type'] ) : 'all';
$start_from = ! empty( $_POST['et_templates_start_page'] ) ? sanitize_text_field( $_POST['et_templates_start_page'] ) : 0;
$post_type = apply_filters( 'et_pb_show_all_layouts_built_for_post_type', $post_type, $layouts_type );
$all_layouts_data = et_pb_retrieve_templates( 'layout', '', 'false', '0', $post_type, $layouts_type, array( $start_from, $page_size ) );
$all_layouts_data_processed = $all_layouts_data;
if ( 0 !== $start_from && empty( $all_layouts_data ) ) {
$all_layouts_data_processed = array();
if ( empty( $all_layouts_data ) ) {
$all_layouts_data_processed = array( 'error' => esc_html__( 'You have not saved any items to your Divi Library yet. Once an item has been saved to your library, it will appear here for easy use.', 'et_builder' ) );
foreach( $all_layouts_data as $index => $data ) {
$all_layouts_data_processed[ $index ]['shortcode'] = et_fb_process_shortcode( $data['shortcode'] );
$next_page = $start_from + $page_size;
$json_templates = json_encode( array( 'templates_data' => $all_layouts_data_processed, 'next_page' => $next_page ) );
die( et_core_intentionally_unescaped( $json_templates, 'html' ) );
add_action( 'wp_ajax_et_fb_get_saved_layouts', 'et_fb_get_saved_layouts' );
function et_fb_process_imported_content() {
if ( ! isset( $_POST['et_fb_process_imported_data_nonce'] ) || ! wp_verify_nonce( $_POST['et_fb_process_imported_data_nonce'], 'et_fb_process_imported_data_nonce' ) ) {
if ( ! current_user_can( 'edit_posts' ) ) {
$processed_shortcode = et_fb_process_shortcode( stripslashes( $_POST['et_raw_shortcode'] ) );
die( json_encode( $processed_shortcode ) );
add_action( 'wp_ajax_et_fb_process_imported_content', 'et_fb_process_imported_content' );
function et_fb_maybe_get_bfb_initial_content( $content, $post_id ) {
if ( isset( $_GET['from_post'] ) && 'empty' !== $_GET['from_post'] ) {
$copy_content_from = get_post( $_GET['from_post'] );
$existing_content = $copy_content_from->post_content;
if ( '' !== $existing_content && has_shortcode( $existing_content, 'et_pb_section' ) ) {
return $existing_content;
// process the content only for BFB
if ( ! et_builder_bfb_enabled() ) {
// If content already has a section, it means builder is active and activation has to be
// skipped to avoid nested and unwanted builder structure.
if ( has_shortcode( $content, 'et_pb_section' ) ) {
$saved_old_content = get_post_meta( $post_id, '_et_pb_old_content', true );
$save_old_content = false;
$post = get_post( $post_id );
$save_old_content = update_post_meta( $post_id, '_et_pb_old_content', $content );
* Filters the flag that sets default Content during Builder activation.
* @used-by et_builder_wc_init()
if ( apply_filters( 'et_builder_skip_content_activation', false, $post ) ) {
if ( true !== $save_old_content && $saved_old_content !== $content && '' !== $content ) {
$text_module = '' !== $content ? '[et_pb_text admin_label="Text"]'. $content .'[/et_pb_text]' : '';
'[et_pb_section admin_label="section"]
[et_pb_row admin_label="row"]
[et_pb_column type="4_4"]'. $text_module .'[/et_pb_column]
// Called via async AJAX call after the builder rendered. It will regenerate both helper/definitions files.
// If their content changed, the builder will trigger a page reload to use the updated cached files.
function et_fb_update_builder_assets() {
if ( ! isset( $_POST['et_fb_helper_nonce'] ) || ! wp_verify_nonce( $_POST['et_fb_helper_nonce'], 'et_fb_update_helper_assets_nonce' ) ) {
$post_id = ! empty( $_POST['et_post_id'] ) ? sanitize_text_field( $_POST['et_post_id'] ) : '';
if ( ! current_user_can( 'edit_post', $post_id ) ) {
// Set current post as global $post
$post = get_post( $post_id ); // phpcs:ignore WordPress.Variables.GlobalVariables.OverrideProhibited
$post_type = ! empty( $_POST['et_post_type'] ) ? sanitize_text_field( $_POST['et_post_type'] ) : 'post';
// Update helpers cached js file
$helpers = et_fb_get_dynamic_asset( 'helpers', $post_type, true );
// Update definitions cached js file
$definitions = et_fb_get_dynamic_asset( 'definitions', $post_type, true );
// When either definitions or helpers needs an update, also clear modules cache.
if ( $definitions['updated'] || $helpers['updated'] ) {
$modules_cache = ET_Builder_Element::get_cache_filename( $post_type );
if ( file_exists( $modules_cache ) ) {
@unlink( $modules_cache );
die( json_encode( array( 'helpers' => $helpers, 'definitions' => $definitions ) ) );
add_action( 'wp_ajax_et_fb_update_builder_assets', 'et_fb_update_builder_assets' );
// Returns builder module defintions.
function et_fb_get_builder_definitions( $post_type ) {
// force render builder data when retrieving builder definition to ensure definitions retrieved via ajax call
// equal to definitions retrieved on wp_footer when no dynamic asset cache found
add_filter( 'et_builder_module_force_render', '__return_true' );
$fields_data['custom_css'] = ET_Builder_Element::get_custom_css_fields( $post_type );
$fields_data['advanced_fields'] = ET_Builder_Element::get_advanced_fields( $post_type );
$fields_data['general_fields'] = ET_Builder_Element::get_general_fields( $post_type );
$fields_data['childModuleTitles'] = ET_Builder_Element::get_child_module_titles( $post_type );
$fields_data['optionsToggles'] = ET_Builder_Element::get_toggles( $post_type );
$fields_data['customTabs'] = ET_Builder_Element::get_tabs( $post_type );
$fields_data['customTabsFields'] = ET_Builder_Element::get_settings_modal_tabs_fields( $post_type );
$fields_data['customLayoutsTabs'] = ET_Builder_Library::builder_library_modal_custom_tabs( $post_type );
$fields_data['moduleItemsConfig'] = ET_Builder_Element::get_module_items_configs( $post_type );
$fields_data['moduleTransitions'] = ET_Builder_Element::get_modules_transitions( $post_type );
$fields_data['contact_form_input_defaults'] = et_fb_process_shortcode( sprintf(
'[et_pb_contact_field field_title="%1$s" field_type="input" field_id="Name" required_mark="on" fullwidth_field="off" /][et_pb_contact_field field_title="%2$s" field_type="email" field_id="Email" required_mark="on" fullwidth_field="off" /][et_pb_contact_field field_title="%3$s" field_type="text" field_id="Message" required_mark="on" fullwidth_field="on" /]',
esc_attr__( 'Name', 'et_builder' ),
esc_attr__( 'Email Address', 'et_builder' ),
esc_attr__( 'Message', 'et_builder' )
// Remove duplicates from field definitions
$unique_fields = array();
foreach ( array( 'custom_css', 'general_fields', 'advanced_fields' ) as $source ) {
$definitions = &$fields_data[ $source ];
$module_names = array_keys( $definitions );
foreach ( $module_names as $module_name ) {
$module = &$definitions[ $module_name ];
$setting_names = array_keys( $module );
foreach ( $setting_names as $setting_name ) {
$setting = &$module[ $setting_name ];
if ( 'advanced_defaults' === $setting_name ) {
// advanced_defaults are just duplicated data, we can rebuilt them later.
$key = json_encode( $setting );
if ( ! isset( $map[ $key ] ) ) {
// Found a duplicate here
$unique_fields[] = $setting;
$map[ $key ] = $unique_count++;
// Remove force builder data render
remove_filter( 'et_builder_module_force_render', '__return_true' );
// Include the unique fields in the AJAX payload
$fields_data['unique_fields'] = $unique_fields;
// Returns builder shortcode object
function et_fb_get_builder_shortcode_object( $post_type, $post_id, $layout_type ) {
// We need to store the current post when this function is executed in a wp-admin page
// to prevent post based modules included in the shortcode from altering the loop.
add_filter( 'et_builder_module_force_render', '__return_true' );
$post_data = get_post( $post_id );
$post_data_post_modified = date( 'U', strtotime( $post_data->post_modified ) );
$post_content = $post_data->post_content;
// if autosave exists here, return it with the real content, autosave.js and getServerSavedPostData() will look for it
$current_user_id = get_current_user_id();
// Store one autosave per author. If there is already an autosave, overwrite it.
$autosave = wp_get_post_autosave( $post_id, $current_user_id );
if ( !empty( $autosave ) ) {
$autosave_post_modified = date( 'U', strtotime( $autosave->post_modified ) );
if ( $autosave_post_modified > $post_data_post_modified ) {
$fields_data['autosave_shortcode_object'] = et_fb_process_shortcode( $autosave->post_content );
$fields_data['has_newer_autosave'] = true;
$fields_data['has_newer_autosave'] = false;
// Delete the autosave, becuase we will present the option to use the autosave to the user, and they will use it or not
// we need to delete the db copy now
wp_delete_post_revision( $autosave->ID );
switch ( $layout_type ) {
$use_fullwidth_section = false !== strpos( $post_content, '[et_pb_fullwidth_' ) ? true : false;
// Remove module placeholders
$post_content = false !== strpos( $post_content, 'et_pb_fullwidth_module_placeholder' ) || false !== strpos( $post_content, 'et_pb_module_placeholder' ) ? '' : $post_content;
if ( ! $use_fullwidth_section ) {
$post_content = sprintf( '[et_pb_row][et_pb_column type="4_4"]%1$s[/et_pb_column][/et_pb_row]', $post_content );
'[et_pb_section%2$s]%1$s[/et_pb_section]',
$use_fullwidth_section ? ' fullwidth="on"' : ''
$post_content = '[et_pb_section]' . $post_content . '[/et_pb_section]';
$post_content = et_fb_maybe_get_bfb_initial_content( $post_content, $post_id );
* Filters the raw post content when the Builder is loaded.
* @param string $post_content The raw/unprocessed post content.
* @param int $post_id Post ID.
$post_content = apply_filters( 'et_fb_load_raw_post_content', $post_content, $post_id );
$fields_data['shortcode_object'] = et_fb_process_shortcode( $post_content );
remove_filter( 'et_builder_module_force_render', '__return_true' );
function et_fb_retrieve_builder_data() {
if ( ! isset( $_POST['et_fb_helper_nonce'] ) || ! wp_verify_nonce( $_POST['et_fb_helper_nonce'], 'et_fb_load_helper_assets_nonce' ) ) {
$post_id = ! empty( $_POST['et_post_id'] ) ? sanitize_text_field( $_POST['et_post_id'] ) : '';
if ( ! current_user_can( 'edit_posts' ) || ! current_user_can( 'edit_post', $post_id ) ) {
$post_type = ! empty( $_POST['et_post_type'] ) ? sanitize_text_field( $_POST['et_post_type'] ) : 'post';
$layout_type = ! empty( $_POST['et_layout_type'] ) ? sanitize_text_field( $_POST['et_layout_type'] ) : '';
$fields_data = array_merge(
et_fb_get_builder_definitions( $post_type ),