: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
* Compatibility for Gutenberg
* @since 3.18 Added support for WP 5.0
class ET_Builder_Block_Editor_Integration {
protected $gb_gallery_regexp = '/<ul class="wp-block-gallery[^"]*?">.*?<\/ul>/mis';
public function __construct() {
protected function _can_edit_post( $post ) {
if ( function_exists( 'gutenberg_can_edit_post' ) ) {
return gutenberg_can_edit_post( $post );
// In case WordPress is lower than version 5.0
if ( ! function_exists( 'use_block_editor_for_post' ) ) {
return use_block_editor_for_post( $post );
protected function _can_edit_post_type( $type ) {
if ( function_exists( 'gutenberg_can_edit_post_type' ) ) {
return gutenberg_can_edit_post_type( $type );
// In case WordPress is lower than version 5.0
if ( ! function_exists( 'use_block_editor_for_post_type' ) ) {
return use_block_editor_for_post_type( $type );
protected function _is_block_editor_page() {
if ( function_exists( 'is_gutenberg_page' ) ) {
return is_gutenberg_page();
// In case WordPress is lower than version 5.0
if ( ! function_exists( 'use_block_editor_for_post' ) ) {
return use_block_editor_for_post( get_the_ID() );
* Filter on map_meta_cap.
* @param array $caps Capabilities.
* @param string $cap Capability to check.
* @param string $user_id User ID.
* @param array $args Additional args.
public function map_meta_cap( $caps, $cap, $user_id, $args ) {
// This only needs to run once,
remove_filter( 'map_meta_cap', array( $this, 'map_meta_cap' ), 10 );
// GB checks for 'edit_post' so do nothing in all other cases
// Ignore the case where Divi wasn't used to edit the post
! et_pb_is_pagebuilder_used( $args[0] )
// We need to add `do_not_allow` for superadmins
$caps = array( 'do_not_allow' );
* Get user capabilities that is relevant to block editor integration
public function get_current_user_capabilities() {
* Make relevant capabilities filterable should the need to check for more caps arises
* @param array user capabilities
$relevant_capabilities = apply_filters( 'et_block_editor_relevant_capabilities', array(
foreach ( $relevant_capabilities as $cap_name ) {
$capabilities[ $cap_name ] = et_pb_is_allowed( $cap_name );
* Filter used to disable GB for certain post types.
* @param string $post_type
public function gutenberg_can_edit_post_type( $can_edit, $post_type ) {
// The tricky part here is that GB doesn't pass the post ID to this filter but only its type
// but we need the ID to determine whether the post has been edited with Divi.
// Since GB uses `current_user_can( 'edit_post', $post->ID )` right after call this filter,
// We hook into `map_meta_cap` (which gets passed the ID) and do our checks there
add_filter( 'map_meta_cap', array( $this, 'map_meta_cap' ), 10, 4 );
* Enqueue our GB compatibility bundle.
public function enqueue_block_editor_assets() {
// Load script dependencies that is used by builder on top window. These dependencies
// happen to be the exact same scripts required by BFB top window's scripts
et_bfb_enqueue_scripts_dependencies();
et_builder_enqueue_open_sans();
// Enqueue integration & blocks scripts
et_fb_enqueue_bundle( 'et-builder-gutenberg', 'gutenberg.js', array(
'et_bfb_admin_date_addon_js',
// Enqueue top window style
ET_BUILDER_URI . '/frontend-builder/assets/css/fb-top-window.css',
// Enqueue integration & blocks styles
et_fb_enqueue_bundle( 'et-builder-gutenberg', 'gutenberg.css', array(
// this enqueue bundle.css
et_builder_enqueue_assets_main();
$post_type = get_post_type();
$enabled_for_post_type = et_builder_enabled_for_post_type( $post_type );
$updates_options = get_site_option( 'et_automatic_updates_options', array() );
'et_username' => et_()->array_get( $updates_options, 'username', '' ),
'et_api_key' => et_()->array_get( $updates_options, 'api_key', '' ),
'status' => get_site_option( 'et_account_status', 'not_active' ),
// Set helpers needed by our own Gutenberg bundle.
wp_localize_script( 'et-builder-gutenberg', 'et_builder_gutenberg', array(
'postType' => $post_type,
'is3rdPartyPostType' => et_builder_is_post_type_custom( $post_type ) ? 'yes' : 'no',
'vbUrl' => et_fb_get_vb_url(),
'builderUsed' => et_pb_is_pagebuilder_used( $post_id ),
'scriptDebug' => defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG,
'canToggle' => et_pb_is_allowed( 'divi_builder_control' ) && $enabled_for_post_type,
'isEnabled' => $enabled_for_post_type,
'title' => esc_html__( 'Divi Builder', 'et_builder' ),
'description' => esc_html__( 'The Divi Builder is activated on this page. To edit your page using the builder, click the Edit With The Divi Builder button.', 'et_builder' ),
'new' => esc_html__( 'Build Your Layout Using Divi', 'et_builder' ),
'old' => esc_html__( 'This Layout Is Built With Divi', 'et_builder' ),
'new' => esc_html__( 'Use Divi Builder', 'et_builder' ),
'old' => esc_html__( 'Edit With The Divi Builder', 'et_builder' ),
'default' => esc_html__( 'Use Default Editor', 'et_builder' ),
// Loaded into ETBlockUserStore
'capabilities' => $this->get_current_user_capabilities(),
// Loaded into ETBlockLibraryStore
'etAccount' => $et_account,
// Loaded into ETBlockSettingsStore
'emptyLayout' => '[et_pb_section admin_label="section"][et_pb_row admin_label="row"][/et_pb_row][/et_pb_section]',
'et_builder_library_get_layouts_data' => wp_create_nonce( 'et_builder_library_get_layouts_data' ),
'et_builder_library_update_account' => wp_create_nonce( 'et_builder_library_update_account' ),
'et_block_layout_preview' => wp_create_nonce( 'et_block_layout_preview' ),
'et_rest_get_layout_content' => wp_create_nonce( 'et_rest_get_layout_content' ),
'et_rest_process_builder_edit_data' => wp_create_nonce( 'et_rest_process_builder_edit_data' ),
'adminAjax' => admin_url( 'admin-ajax.php' ),
'diviLibrary' => ET_BUILDER_DIVI_LIBRARY_URL,
* Make DOM selectors list filterable so third party can modified it if needed
* @param array list of selectors
'selectors' => apply_filters( 'et_gb_selectors', array(
'pageLayoutSelect' => '#et_pb_page_layout',
* Make Content Widhts settings filterable so third party can modified it if needed
* @param array content width configurations
'contentWidths' => apply_filters( 'et_gb_content_widths', array(
// Intentionally set null for default and undefined if no saved content width found
// unless `et_gb_content_widths` is being filtered to handle Divi Builder Plugin
// situation which might not have deifined content width
'current' => get_post_meta( $post_id, '_et_gb_content_width', true),
'min' => 320, // Min content width (small smartphone width)
'max' => 2880, // Max content width (15" laptop * 2)
// Set translated strings for the scripts
wp_set_script_translations( 'et-builder-gutenberg', 'et_builder', ET_BUILDER_DIR . 'languages' );
public function add_new_button() {
if ( ! $this->_can_edit_post_type( $typenow ) ) {
$edit .= 'post' !== $typenow ? "?post_type=$typenow" : '';
// Create a nonce to auto activate VB on a new Auto Draft
$url = add_query_arg( 'et_fb_new_vb_nonce', wp_create_nonce( 'et_fb_new_vb_nonce' ), admin_url( $edit ) );
$button = sprintf( '<a href="%s">%s</a>', esc_url( $url ), 'Divi' );
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', function() {
var menu = document.querySelector('#split-page-title-action .dropdown');
menu.insertAdjacentHTML('afterbegin', '<?php echo et_core_esc_previously( $button ); ?>');
* This filter allows VB to be directly activated for Auto Drafts.
* @param object $post Auto Draft post.
public function auto_draft( $post ) {
if ( ! wp_verify_nonce( $_GET['et_fb_new_vb_nonce'], 'et_fb_new_vb_nonce' ) ) {
'post_status' => 'draft',
// Add VB activation nonce
'et_fb_activation_nonce',
wp_create_nonce( 'et_fb_activation_nonce_' . $post->ID ),
et_fb_prepare_ssl_link( get_permalink( $post ) )
// Set post meta to `off` or else `et_builder_set_content_activation` won't work...
update_post_meta( $post->ID, '_et_pb_use_builder', 'off' );
* Add 'Edit With Divi Editor' links
* @param array $actions Currently defined actions for the row.
* @param object $post Current post object.
public function add_edit_link( $actions, $post ) {
// Maybe change this with et_fb_current_user_can_save or equivalent
if ( ! $this->_can_edit_post( $post ) || ! et_builder_enabled_for_post_type( $post->post_type )) {
if ( (int) get_option( 'page_for_posts' ) === $post->ID ) {
// Post is assigned as the blog page so it does not have editable content.
$is_divi_library = 'et_pb_layout' === get_post_type( $post_id );
$edit_url = $is_divi_library ? get_edit_post_link( $post_id, 'raw' ) : get_permalink( $post_id );
if ( et_pb_is_pagebuilder_used( $post_id ) ) {
$edit_url = et_fb_get_vb_url( $edit_url );
if ( ! et_pb_is_allowed( 'divi_builder_control' ) ) {
// Do not add Divi activation link when user lacks `Toggle Divi Builder` capability.
$edit_url = add_query_arg(
'et_fb_activation_nonce' => wp_create_nonce( 'et_fb_activation_nonce_' . $post_id ),
'<a href="%s" aria-label="%s">%s</a>',
__( 'Edit “%s” in Divi', 'et_builder' ),
_draft_or_post_title( $post->ID )
esc_html__( 'Edit With Divi', 'et_builder' )
$actions = array_merge( $actions, $edit_action );
// I'm leaving this here in case we wanna change item position.
// $edit_offset = array_search( 'edit', array_keys( $actions ), true );
// $actions = array_merge(
// array_slice( $actions, 0, $edit_offset + 1 ),
// array_slice( $actions, $edit_offset + 1 )
* Add filters needed to show our extra row action.
public function add_edit_link_filters() {
// For hierarchical post types.
add_filter( 'page_row_actions', array( $this, 'add_edit_link' ), 10, 2 );
// For non-hierarchical post types.
add_filter( 'post_row_actions', array( $this, 'add_edit_link' ), 10, 2 );
* Add 'Divi' to post states when builder is enabled for it.
* @param array $post_states Existing post states.
* @param object $post Current post object.
public function display_post_states( $post_states, $post ) {
// Make sure that $post_states is an array. Third party plugin might modify $post_states and makes it null
// which create various issue (i.e. Piklist + Having a page configured as a static page)
if ( ! is_array( $post_states ) ) {
if ( et_pb_is_pagebuilder_used( $post->ID ) ) {
// Remove Gutenberg if existing
$key = array_search( 'Gutenberg', $post_states );
unset( $post_states[ $key ] );
// GB devs didn't allow this to be translated so why should we ?
* Ensures that Divi enabled CPTs support 'custom-fields'.
public function ensure_post_type_supports() {
$post_types = et_builder_get_builder_post_types();
foreach ( $post_types as $post_type ) {
if ( ! post_type_supports( $post_type, 'custom-fields' ) ) {
add_post_type_support( $post_type, 'custom-fields' );
* Alter update_post_metadata return value from during a REST API update
* when meta value isn't changed.
* @param mixed $result Previous result.
* @param int $object_id Post ID.
* @param string $meta_key Meta key.
* @param mixed $meta_value Meta value.
public function update_post_metadata( $result, $object_id, $meta_key, $meta_value ) {
if ( ! in_array( $meta_key, array( '_et_pb_use_builder', '_et_pb_old_content' ) ) ) {
// Only act if it's one of our metas
if ( $meta_value === get_metadata( 'post', $object_id, $meta_key, true ) ) {
// Return true instead of false so silly WP REST API call won't die on us....
* Remove empty Divi GB placeholder when processing shortcode.
* @param string $post_content Raw post content (shortcode).
public function et_fb_load_raw_post_content( $post_content ) {
// Replace empty placeholder with no content so page creation will
// still work in this case.
return '<!-- wp:divi/placeholder /-->' === $post_content ? '' : $post_content;
* Convert a single GB gallery to shortcode.
* @param string $content Post content
public function gb_gallery_to_shortcode( $gallery ) {
$gallery = is_array( $gallery ) ? $gallery[0] : $gallery;