'has_no_permission' => array(
'title' => esc_html__( 'Unauthorized Action', 'et_builder' ),
'desc' => esc_html__( 'You do not have permission to edit the module, row or section in this split test.', 'et_builder' ),
'select_ab_testing_subject' => $ab_settings['select_subject'],
'select_ab_testing_goal' => $ab_settings['select_goal'],
'configure_ab_testing_alternative' => $ab_settings['configure_alternative'],
'select_ab_testing_winner_first' => $ab_settings['select_winner_first'],
'select_ab_testing_subject_first' => $ab_settings['select_subject_first'],
'select_ab_testing_goal_first' => $ab_settings['select_goal_first'],
'cannot_select_subject_parent_as_goal' => $ab_settings['cannot_select_subject_parent_as_goal'],
'cannot_select_global_children_as_subject' => $ab_settings['cannot_select_global_children_as_subject'],
'cannot_select_global_children_as_goal' => $ab_settings['cannot_select_global_children_as_goal'],
'cannot_save_app_layout_has_ab_testing' => $ab_settings['cannot_save_app_layout_has_ab_testing'],
'cannot_save_section_layout_has_ab_testing' => $ab_settings['cannot_save_section_layout_has_ab_testing'],
'cannot_save_row_layout_has_ab_testing' => $ab_settings['cannot_save_row_layout_has_ab_testing'],
'cannot_save_row_inner_layout_has_ab_testing' => $ab_settings['cannot_save_row_inner_layout_has_ab_testing'],
'cannot_save_module_layout_has_ab_testing' => $ab_settings['cannot_save_module_layout_has_ab_testing'],
'cannot_load_layout_has_ab_testing' => $ab_settings['cannot_load_layout_has_ab_testing'],
'cannot_clear_layout_has_ab_testing' => $ab_settings['cannot_clear_layout_has_ab_testing'],
// Cannot Import / Export Layout (Portability).
'cannot_import_export_layout_has_ab_testing' => $ab_settings['cannot_import_export_layout_has_ab_testing'],
// Moving Goal / Subject.
'cannot_move_module_goal_out_from_subject' => $ab_settings['cannot_move_module_goal_out_from_subject'],
'cannot_move_row_goal_out_from_subject' => $ab_settings['cannot_move_row_goal_out_from_subject'],
'cannot_move_goal_into_subject' => $ab_settings['cannot_move_goal_into_subject'],
'cannot_move_subject_into_goal' => $ab_settings['cannot_move_subject_into_goal'],
'cannot_clone_section_has_goal' => $ab_settings['cannot_clone_section_has_goal'],
'cannot_clone_row_has_goal' => $ab_settings['cannot_clone_row_has_goal'],
'cannot_remove_section_has_goal' => $ab_settings['cannot_remove_section_has_goal'],
'cannot_remove_row_has_goal' => $ab_settings['cannot_remove_row_has_goal'],
// Removing + Has Unremovable Subjects.
'cannot_remove_section_has_unremovable_subject' => $ab_settings['cannot_remove_section_has_unremovable_subject'],
'cannot_remove_row_has_unremovable_subject' => $ab_settings['cannot_remove_row_has_unremovable_subject'],
// View stats summary table heading.
'view_stats_thead_titles' => $ab_settings['view_stats_thead_titles'],
wp_localize_script( 'et_pb_admin_js', 'et_pb_ab_js_options', apply_filters( 'et_pb_ab_js_options', $pb_ab_js_options ) );
$pb_help_options = array(
'shortcuts' => et_builder_get_shortcuts( 'bb' ),
wp_localize_script( 'et_pb_admin_js', 'et_pb_help_options', apply_filters( 'et_pb_help_options', $pb_help_options ) );
et_core_load_main_fonts();
wp_enqueue_style( 'et_pb_admin_css', ET_BUILDER_URI . '/styles/style.css', array(), ET_BUILDER_VERSION );
wp_enqueue_style( 'et_pb_admin_date_css', ET_BUILDER_URI . '/styles/jquery-ui-1.12.1.custom.css', array(), ET_BUILDER_VERSION );
wp_add_inline_style( 'et_pb_admin_css', et_pb_ab_get_subject_rank_colors_style() );
* Set et-editor-available-post-* cookie
function et_pb_set_editor_available_cookie() {
$post_id = isset( $_GET['post'] ) ? absint( $_GET['post'] ) : false; // phpcs:ignore WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF.
$headers_sent = headers_sent();
if ( et_builder_should_load_framework() && is_admin() && ! $headers_sent && ! empty( $post_id ) ) {
setcookie( 'et-editor-available-post-' . $post_id . '-bb', 'bb', time() + ( MINUTE_IN_SECONDS * 30 ), SITECOOKIEPATH, false, is_ssl() );
add_action( 'admin_init', 'et_pb_set_editor_available_cookie' );
* @return array History meta.
function et_pb_history_localization() {
'did' => esc_html__( 'Did', 'et_builder' ),
'added' => esc_html__( 'Added', 'et_builder' ),
'edited' => esc_html__( 'Edited', 'et_builder' ),
'removed' => esc_html__( 'Removed', 'et_builder' ),
'moved' => esc_html__( 'Moved', 'et_builder' ),
'expanded' => esc_html__( 'Expanded', 'et_builder' ),
'collapsed' => esc_html__( 'Collapsed', 'et_builder' ),
'locked' => esc_html__( 'Locked', 'et_builder' ),
'unlocked' => esc_html__( 'Unlocked', 'et_builder' ),
'cloned' => esc_html__( 'Cloned', 'et_builder' ),
'cleared' => esc_html__( 'Cleared', 'et_builder' ),
'enabled' => esc_html__( 'Enabled', 'et_builder' ),
'disabled' => esc_html__( 'Disabled', 'et_builder' ),
'copied' => esc_html__( 'Copied', 'et_builder' ),
'reset' => esc_html__( 'Reset', 'et_builder' ),
'cut' => esc_html__( 'Cut', 'et_builder' ),
'pasted' => esc_html__( 'Pasted', 'et_builder' ),
'pasted_styles' => esc_html__( 'Pasted Styles', 'et_builder' ),
'renamed' => esc_html__( 'Renamed', 'et_builder' ),
'loaded' => esc_html__( 'Loaded', 'et_builder' ),
'turnon' => esc_html__( 'Turned On', 'et_builder' ),
'turnoff' => esc_html__( 'Turned Off', 'et_builder' ),
'globalon' => esc_html__( 'Made Global', 'et_builder' ),
'globaloff' => esc_html__( 'Disabled Global', 'et_builder' ),
'configured' => esc_html__( 'Configured', 'et_builder' ),
'find_replace' => esc_html__( 'Find & Replace', 'et_builder' ),
'extend_styles' => esc_html__( 'Extend Styles', 'et_builder' ),
'imported' => esc_html__( 'Imported From Layout', 'et_builder' ),
'presetCreated' => esc_html__( 'Preset Created For', 'et_builder' ),
'presetNameChanged' => esc_html__( 'Preset Name Changed For', 'et_builder' ),
'presetDeleted' => esc_html__( 'Preset Deleted For', 'et_builder' ),
'presetAssignedAsDefault' => esc_html__( 'Preset Assigned As Default For', 'et_builder' ),
'section' => esc_html__( 'Section', 'et_builder' ),
'saved_section' => esc_html__( 'Saved Section', 'et_builder' ),
'fullwidth_section' => esc_html__( 'Fullwidth Section', 'et_builder' ),
'specialty_section' => esc_html__( 'Specialty Section', 'et_builder' ),
'column' => esc_html__( 'Column', 'et_builder' ),
'row' => esc_html__( 'Row', 'et_builder' ),
'saved_row' => esc_html__( 'Saved Row', 'et_builder' ),
'module' => esc_html__( 'Module', 'et_builder' ),
'saved_module' => esc_html__( 'Saved Module', 'et_builder' ),
'page' => esc_html__( 'Page', 'et_builder' ),
'layout' => et_builder_i18n( 'Layout' ),
'abtesting' => esc_html__( 'Split Testing', 'et_builder' ),
'settings' => esc_html__( 'Settings', 'et_builder' ),
'phone' => esc_html__( 'on Phone', 'et_builder' ),
'tablet' => esc_html__( 'on Tablet', 'et_builder' ),
'desktop' => esc_html__( 'on Desktop', 'et_builder' ),
* Page Settings Metabox code is included in builder.js which won't be loaded unless BB is.
* In such cases (eg BFB or GB are enabled) we provide the mbox js logic in a separate file.
function et_pb_metabox_scripts() {
// Only act if `builder.js` isn't enqueued.
if ( ! wp_script_is( 'et_pb_admin_js' ) ) {
wp_enqueue_script( 'et_page_settings_metabox_js', ET_BUILDER_URI . '/scripts/page-settings-metabox.js', array( 'jquery' ), ET_BUILDER_PRODUCT_VERSION, true );
'is_third_party_post_type' => et_builder_is_post_type_custom( $typenow ) ? 'yes' : 'no',
wp_localize_script( 'et_page_settings_metabox_js', 'et_pb_options', $pb_options );
* Prevents the Builder mbox from being hidden.
* @param string[] $hidden all hidden metaboxes.
function et_pb_hidden_meta_boxes( $hidden ) {
$found = array_search( 'et_pb_layout', $hidden, true );
if ( false !== $found ) {
unset( $hidden[ $found ] );
* Add "The Divi Builder" BB metabox.
* @param string $post_type post type.
* @param WP_Post $post post object.
function et_pb_add_custom_box( $post_type, $post ) {
add_action( 'admin_enqueue_scripts', 'et_pb_metabox_scripts', 99 );
// Do not add BB metabox if GB is active on this page.
if ( et_core_is_gutenberg_enabled() ) {
// Do not add BB metabox if builder is not activate on this page.
if ( et_builder_bfb_enabled() && ! et_pb_is_pagebuilder_used( $post->ID ) ) {
$post_types = et_builder_get_builder_post_types();
$add = in_array( $post_type, $post_types, true );
if ( ! $add && ! empty( $post ) && et_builder_enabled_for_post( $post->ID ) ) {
add_meta_box( ET_BUILDER_LAYOUT_POST_TYPE, esc_html__( 'The Divi Builder', 'et_builder' ), 'et_pb_pagebuilder_meta_box', $post_type, 'normal', 'high' );
if ( ! function_exists( 'et_pb_get_the_author_posts_link' ) ) :
* Return a post author link markup.
function et_pb_get_the_author_posts_link() {
global $authordata, $post;
if ( empty( $authordata ) && isset( $post->post_author ) ) {
$authordata = get_userdata( $post->post_author ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- If $authordata is not set then set it.
// If $authordata is empty, don't continue.
if ( empty( $authordata ) ) {
'<a href="%1$s" title="%2$s" rel="author">%3$s</a>',
esc_url( get_author_posts_url( $authordata->ID, $authordata->user_nicename ) ),
// translators: post author name.
esc_attr( sprintf( __( 'Posts by %s', 'et_builder' ), get_the_author() ) ),
return apply_filters( 'the_author_posts_link', $link );
if ( ! function_exists( 'et_pb_get_comments_popup_link' ) ) :
* @param bool|string $zero text to display when 0 comments.
* @param bool|string $one text to display when 1 comment.
* @param bool|string $more text to display for more than 1 comments.
function et_pb_get_comments_popup_link( $zero = false, $one = false, $more = false ) {
$number = get_comments_number( $id );
if ( 0 === $number && ! comments_open() && ! pings_open() ) {
// translators: more comments text.
$output = str_replace( '%', number_format_i18n( $number ), ( false === $more ) ? __( '% Comments', 'et_builder' ) : $more );
} elseif ( 0 === $number ) {
$output = ( false === $zero ) ? __( 'No Comments', 'et_builder' ) : $zero;
$output = ( false === $one ) ? __( '1 Comment', 'et_builder' ) : $one;
do_action( 'et_builder_before_comments_number' );
$link = '<span class="comments-number"><a href="' . esc_url( get_permalink() . '#respond' ) . '">' . apply_filters( 'comments_number', esc_html( $output ), esc_html( $number ) ) . '</a></span>';
do_action( 'et_builder_after_comments_number' );
if ( ! function_exists( 'et_pb_postinfo_meta' ) ) :
* @param string[] $postinfo post info e.g date, author, categories.
* @param string $date_format date format.
* @param string $comment_zero text to display for 0 comments.
* @param string $comment_one text to display for 1 comments.
* @param string $comment_more text to display for more comments.
function et_pb_postinfo_meta( $postinfo, $date_format, $comment_zero, $comment_one, $comment_more ) {
$postinfo_meta = array();
if ( in_array( 'author', $postinfo, true ) ) {
$postinfo_meta[] = ' ' . esc_html__( 'by', 'et_builder' ) . ' <span class="author vcard">' . et_pb_get_the_author_posts_link() . '</span>';
if ( in_array( 'date', $postinfo, true ) ) {
$postinfo_meta[] = '<span class="published">' . esc_html( get_the_time( wp_unslash( $date_format ) ) ) . '</span>';
if ( in_array( 'categories', $postinfo, true ) ) {
$categories_list = get_the_category_list( ', ' );
// do not output anything if no categories retrieved.
if ( '' !== $categories_list ) {
$postinfo_meta[] = $categories_list;
if ( in_array( 'comments', $postinfo, true ) ) {
$postinfo_meta[] = et_pb_get_comments_popup_link( $comment_zero, $comment_one, $comment_more );
return implode( ' | ', array_filter( $postinfo_meta ) );
if ( ! function_exists( 'et_pb_fix_shortcodes' ) ) {
* Fix shortcodes? @todo Add function doc.
* @param string $content post content.
* @param bool $is_raw_content whether content is row.
* @return string|string[]|null
function et_pb_fix_shortcodes( $content, $is_raw_content = false ) {
// Turn back the "data-et-target-link" attribute as "target" attribte
// that has been made before saving the content in "et_fb_process_to_shortcode" function.
if ( false !== strpos( $content, 'data-et-target-link=' ) ) {
$content = str_replace( ' data-et-target-link=', ' target=', $content );
$content = et_builder_replace_code_content_entities( $content );
$content = ET_Builder_Element::convert_smart_quotes_and_amp( $content );
$slugs = ET_Builder_Element::get_module_slugs_by_post_type();
// The current patterns take care to replace only the shortcodes that extends `ET_Builder_Element` class
// In order to avoid cases like this: `[3:45]<br>`
// The pattern looks like this `(\[\/?(et_pb_section|et_pb_column|et_pb_row)[^\]]*\])`.
$shortcode_pattern = sprintf( '(\[\/?(%s)[^\]]*\])', implode( '|', $slugs ) );
$opening_pattern = '(<br\s*\/?>|<p>|\n)+';
$closing_pattern = '(<br\s*\/?>|<\/p>|\n)+';
$space_pattern = '[\s*|\n]*';
// Replace `]</p>`, `]<br>` `]\n` with `]`
// Make sure to remove any closing `</p>` tags or line breaks or new lines after shortcode tag.
$pattern_1 = sprintf( '/%1$s%2$s%3$s/', $shortcode_pattern, $space_pattern, $closing_pattern );
// Replace `<p>[`, `<br>[` `\n[` with `[`
// Make sure to remove any opening `<p>` tags or line breaks or new lines before shortcode tag.
$pattern_2 = sprintf( '/%1$s%2$s%3$s/', $opening_pattern, $space_pattern, $shortcode_pattern );
$content = preg_replace( $pattern_1, '$1', $content );
$content = preg_replace( $pattern_2, '$2', $content );
if ( ! function_exists( 'et_pb_load_global_module' ) ) {
* Return gloval module content.
* @param integer $global_id layout id.
* @param string $row_type row type.
* @param string $prev_bg Previous background color.
* @param string $next_bg next background color.
* @return string|string[]|null
function et_pb_load_global_module( $global_id, $row_type = '', $prev_bg = '', $next_bg = '' ) {
if ( '' !== $global_id ) {
'post_type' => ET_BUILDER_LAYOUT_POST_TYPE,
if ( ! empty( $query->post ) ) {
// Call the_post() to properly configure post data. Make sure to call the_post() and
// wp_reset_postdata() only if the posts result exist to avoid unexpected issues.
$global_shortcode = $query->post->post_content;
if ( '' !== $row_type && 'et_pb_row_inner' === $row_type ) {
$global_shortcode = str_replace( 'et_pb_row', 'et_pb_row_inner', $global_shortcode );
$global_shortcode = str_replace( 'et_pb_column', 'et_pb_column_inner', $global_shortcode );
// Set provided prev_background_color.
if ( ! empty( $prev_bg ) ) {
$global_shortcode = preg_replace( '/prev_background_color="(.*?)"/', 'prev_background_color="' . $prev_bg . '"', $global_shortcode, 1 );
// Set provided next_background_color.
if ( ! empty( $next_bg ) ) {
$global_shortcode = preg_replace( '/next_background_color="(.*?)"/', 'next_background_color="' . $next_bg . '"', $global_shortcode, 1 );
return $global_shortcode;
if ( ! function_exists( 'et_pb_extract_shortcode_content' ) ) {
* Return the shortcode content.
* @param string $content content.
* @param string $shortcode_name shortcode name.
* @return bool|false|string
function et_pb_extract_shortcode_content( $content, $shortcode_name ) {
$start = strpos( $content, ']' ) + 1;
$end = strrpos( $content, '[/' . $shortcode_name );
$content = substr( $content, $start, $end - $start );
if ( ! function_exists( 'et_pb_remove_shortcode_content' ) ) {
* Remove the content part of the shortcode.
* @param string $content content.
* @param string $shortcode_name shortcode name.
* @return string|string[]
function et_pb_remove_shortcode_content( $content, $shortcode_name ) {
$shortcode_content = et_pb_extract_shortcode_content( $content, $shortcode_name );
if ( $shortcode_content ) {
// Anchor to the ][ brackets around the content so content that appears in
// attributes does not get removed as well.
return str_replace( ']' . $shortcode_content . '[', '][', $content );
if ( ! function_exists( 'et_pb_get_global_module_content' ) ) {
* Return global module content.
* @param string $content content.
* @param string $shortcode_name shortcode slug.
* @param bool $for_inner_row whether we getting module content for inner row.
* @return bool|false|string|string[]|null
function et_pb_get_global_module_content( $content, $shortcode_name, $for_inner_row = false ) {
// Do not apply autop to code modules.
if ( in_array( $shortcode_name, array( 'et_pb_code', 'et_pb_fullwidth_code' ), true ) ) {
return et_pb_extract_shortcode_content( $content, $shortcode_name );
$original_code_modules = array();
$shortcode_content = et_pb_extract_shortcode_content( $content, $shortcode_name );
// Getting content for Global row when it's turned to inner row in specialty section
// Need to make sure it wrapped in et_pb_column_inner, not et_pb_column.
if ( $for_inner_row && false === strpos( $shortcode_content, '[et_pb_column_inner' ) ) {
$shortcode_content = str_replace( 'et_pb_column', 'et_pb_column_inner', $shortcode_content );
// Get all the code and fullwidth code modules from content.
preg_match_all( '/(\[et_pb(_fullwidth_code|_code).+?\[\/et_pb(_fullwidth_code|_code)\])/s', $shortcode_content, $original_code_modules );
$global_content = et_pb_fix_shortcodes( wpautop( $shortcode_content ) );
// Replace content modified by wpautop for code and fullwidth code modules with original content.
if ( ! empty( $original_code_modules ) ) {
global $et_pb_global_code_replacements;
$et_pb_global_code_replacements = $original_code_modules[0];
$global_content = preg_replace_callback( '/(\[et_pb(_fullwidth_code|_code).+?\[\/et_pb(_fullwidth_code|_code)\])/s', 'et_builder_get_global_code_replacement', $global_content );
if ( ! function_exists( 'et_builder_get_global_code_replacement' ) ) {
* Retrieve the global code original instance to replace the modified in global code shortcode.