* Core builder functions.
if ( ! defined( 'ET_BUILDER_PRODUCT_VERSION' ) ) {
// Note, this will be updated automatically during grunt release task.
define( 'ET_BUILDER_PRODUCT_VERSION', '4.8.1' );
if ( ! defined( 'ET_BUILDER_VERSION' ) ) {
define( 'ET_BUILDER_VERSION', 0.7 );
if ( ! defined( 'ET_BUILDER_FORCE_CACHE_PURGE' ) ) {
define( 'ET_BUILDER_FORCE_CACHE_PURGE', false );
if ( ! defined( 'ET_BUILDER_DIR_RESOLVED_PATH' ) ) {
define( 'ET_BUILDER_DIR_RESOLVED_PATH', dirname( __FILE__ ) );
// When set to true, the builder will use the new loading method.
if ( ! defined( 'ET_BUILDER_CACHE_ASSETS' ) ) {
define( 'ET_BUILDER_CACHE_ASSETS', ! isset( $_REQUEST['nocache'] ) ); // phpcs:ignore WordPress.Security.NonceVerification -- CSRF ok.
if ( ! defined( 'ET_BUILDER_CACHE_MODULES' ) ) {
define( 'ET_BUILDER_CACHE_MODULES', apply_filters( 'et_builder_cache_modules', true ) );
if ( ! defined( 'ET_BUILDER_JSON_ENCODE_OPTIONS' ) ) {
define( 'ET_BUILDER_JSON_ENCODE_OPTIONS', 0 );
if ( ! defined( 'ET_BUILDER_KEEP_OLDEST_CACHED_ASSETS' ) ) {
define( 'ET_BUILDER_KEEP_OLDEST_CACHED_ASSETS', false );
if ( ! defined( 'ET_BUILDER_PURGE_OLD_CACHED_ASSETS' ) ) {
define( 'ET_BUILDER_PURGE_OLD_CACHED_ASSETS', true );
if ( defined( 'ET_BUILDER_DEFINITION_SORT' ) && ET_BUILDER_DEFINITION_SORT ) {
* You don't want to know and this isn't the function you're looking for.
* Still reading ? Aight, this is only used to debug definitions.
* @param array $definitions Definitions.
function et_builder_definition_sort( &$definitions ) {
if ( ! is_array( $definitions ) ) {
$fields = array_keys( $definitions );
foreach ( $fields as $field ) {
$definition =& $definitions[ $field ];
if ( is_array( $definition ) ) {
foreach ( $order as $key ) {
if ( isset( $definition[ $key ] ) ) {
$value = $definition[ $key ];
unset( $definition[ $key ] );
$definition[ $key ] = $value;
et_builder_definition_sort( $definition );
$et_fonts_queue = array();
* Exclude predefined layouts from import.
* @param array $posts The imported posts.
function et_remove_predefined_layouts_from_import( $posts ) {
$processed_posts = $posts;
if ( isset( $posts ) && is_array( $posts ) ) {
$processed_posts = array();
foreach ( $posts as $post ) {
if ( isset( $post['postmeta'] ) && is_array( $post['postmeta'] ) ) {
foreach ( $post['postmeta'] as $meta ) {
if ( '_et_pb_predefined_layout' === $meta['key'] && 'on' === $meta['value'] ) {
$processed_posts[] = $post;
add_filter( 'wp_import_posts', 'et_remove_predefined_layouts_from_import', 5 );
* Output all module fields JSON dump.
// phpcs:disable Squiz.PHP.CommentedOutCode -- This is debug function.
// function et_dev_output_all_fields() {
// die( json_encode( ET_Builder_Element::get_all_fields('page') ) );
// add_action('wp', 'et_dev_output_all_fields', 100);.
* Set the layout_type taxonomy to "layout" for layouts imported from old version of Divi.
* @param array $posts Imported posts.
function et_update_old_layouts_taxonomy( $posts ) {
$processed_posts = $posts;
if ( isset( $posts ) && is_array( $posts ) ) {
$processed_posts = array();
foreach ( $posts as $post ) {
$update_built_for_post_type = false;
if ( 'et_pb_layout' === $post['post_type'] ) {
if ( ! isset( $post['terms'] ) ) {
$post['terms'][] = array(
'domain' => 'layout_type',
$post['terms'][] = array(
$update_built_for_post_type = true;
// check whether _et_pb_built_for_post_type custom field exists.
if ( ! empty( $post['postmeta'] ) ) {
foreach ( $post['postmeta'] as $index => $value ) {
if ( '_et_pb_built_for_post_type' === $value['key'] ) {
$update_built_for_post_type = false;
// set _et_pb_built_for_post_type value to 'page' if not exists.
if ( $update_built_for_post_type ) {
$post['postmeta'][] = array(
'key' => '_et_pb_built_for_post_type',
$processed_posts[] = $post;
add_filter( 'wp_import_posts', 'et_update_old_layouts_taxonomy', 10 );
if ( ! function_exists( 'et_pb_add_layout_filters' ) ) :
* Add custom filters for posts in the Divi Library.
function et_pb_add_layout_filters() {
// phpcs:disable WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF.
if ( isset( $_GET['post_type'] ) && 'et_pb_layout' === $_GET['post_type'] ) {
$layout_categories = get_terms( 'layout_category' );
$filter_category = array();
$filter_category[''] = esc_html__( 'All Categories', 'et_builder' );
if ( is_array( $layout_categories ) && ! empty( $layout_categories ) ) {
foreach ( $layout_categories as $category ) {
$filter_category[ $category->slug ] = $category->name;
$layout_packs = get_terms( 'layout_pack' );
$filter_pack[''] = esc_html_x( 'All Packs', 'Layout Packs', 'et_builder' );
if ( is_array( $layout_packs ) ) {
foreach ( $layout_packs as $pack ) {
$filter_pack[ $pack->slug ] = $pack->name;
$filter_layout_type = array(
'' => esc_html__( 'All Types', 'et_builder' ),
'module' => esc_html__( 'Modules', 'et_builder' ),
'row' => esc_html__( 'Rows', 'et_builder' ),
'section' => esc_html__( 'Sections', 'et_builder' ),
'layout' => esc_html__( 'Layouts', 'et_builder' ),
'' => esc_html__( 'All Scopes', 'et_builder' ),
'global' => esc_html__( 'Global', 'et_builder' ),
'not_global' => esc_html__( 'Not Global', 'et_builder' ),
<select name="layout_type">
$selected = isset( $_GET['layout_type'] ) ? sanitize_text_field( $_GET['layout_type'] ) : '';
foreach ( $filter_layout_type as $value => $label ) {
'<option value="%1$s"%2$s>%3$s</option>',
selected( $value, $selected ),
$selected = isset( $_GET['scope'] ) ? sanitize_text_field( $_GET['scope'] ) : '';
foreach ( $filter_scope as $value => $label ) {
'<option value="%1$s"%2$s>%3$s</option>',
selected( $value, $selected ),
<select name="layout_category">
$selected = isset( $_GET['layout_category'] ) ? sanitize_text_field( $_GET['layout_category'] ) : '';
foreach ( $filter_category as $value => $label ) {
'<option value="%1$s"%2$s>%3$s</option>',
selected( $value, $selected ),
<select name="layout_pack">
$selected = isset( $_GET['layout_pack'] ) ? sanitize_text_field( $_GET['layout_pack'] ) : '';
foreach ( $filter_pack as $value => $label ) {
'<option value="%1$s"%2$s>%3$s</option>',
selected( $value, $selected ),
add_action( 'restrict_manage_posts', 'et_pb_add_layout_filters' );
if ( ! function_exists( 'et_pb_load_export_section' ) ) :
* Add "Export Divi Layouts" button to the Divi Library page.
function et_pb_load_export_section() {
$current_screen = get_current_screen();
if ( 'edit-et_pb_layout' === $current_screen->id ) {
// display wp error screen if library is disabled for current user.
if ( ! et_pb_is_allowed( 'divi_library' ) || ! et_pb_is_allowed( 'add_library' ) || ! et_pb_is_allowed( 'save_library' ) ) {
wp_die( esc_html__( "you don't have sufficient permissions to access this page", 'et_builder' ) );
add_action( 'all_admin_notices', 'et_pb_export_layouts_interface' );
add_action( 'load-edit.php', 'et_pb_load_export_section' );
if ( ! function_exists( 'et_pb_edit_library_categories' ) ) :
* Enqueue script on Library Categories editing screen.
function et_pb_edit_library_categories() {
$current_screen = get_current_screen();
if ( 'edit-layout_category' === $current_screen->id || 'edit-layout_pack' === $current_screen->id ) {
// display wp error screen if library is disabled for current user.
if ( ! et_pb_is_allowed( 'divi_library' ) || ! et_pb_is_allowed( 'add_library' ) || ! et_pb_is_allowed( 'save_library' ) ) {
wp_die( esc_html__( "you don't have sufficient permissions to access this page", 'et_builder' ) );
wp_enqueue_script( 'builder-library-category', ET_BUILDER_URI . '/scripts/library_category.js', array( 'jquery' ), ET_BUILDER_VERSION, true );
add_action( 'load-edit-tags.php', 'et_pb_edit_library_categories' );
* Check whether the library editor page should be displayed or not.
function et_pb_check_library_permissions() {
$current_screen = get_current_screen();
if ( 'et_pb_layout' === $current_screen->id && ( ! et_pb_is_allowed( 'divi_library' ) || ! et_pb_is_allowed( 'save_library' ) ) ) {
// display wp error screen if library is disabled for current user.
wp_die( esc_html__( "you don't have sufficient permissions to access this page", 'et_builder' ) );
add_action( 'load-post.php', 'et_pb_check_library_permissions' );
if ( ! function_exists( 'exclude_premade_layouts_library' ) ) :
* Exclude premade layouts from the list of all templates in the library.
* @param WP_Query $query Query.
function exclude_premade_layouts_library( $query ) {
// phpcs:disable WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF.
$current_post_type = get_query_var( 'post_type' );
if ( is_admin() && 'edit.php' === $pagenow && $current_post_type && 'et_pb_layout' === $current_post_type ) {
'key' => '_et_pb_predefined_layout',
'compare' => 'NOT EXISTS',
$used_built_for_post_types = ET_Builder_Library::built_for_post_types( 'all' );
if ( isset( $_GET['built_for'] ) && count( $used_built_for_post_types ) > 1 ) {
$built_for_post_type = sanitize_text_field( $_GET['built_for'] );
// get array of all standard post types if built_for is one of them.
$built_for_post_type_processed = in_array( $built_for_post_type, ET_Builder_Library::built_for_post_types(), true ) ? ET_Builder_Library::built_for_post_types() : $built_for_post_type;
if ( in_array( $built_for_post_type, $used_built_for_post_types, true ) ) {
'key' => '_et_pb_built_for_post_type',
'value' => $built_for_post_type_processed,
$query->set( 'meta_query', $meta_query );
add_action( 'pre_get_posts', 'exclude_premade_layouts_library' );
if ( ! function_exists( 'exclude_premade_layouts_library_count' ) ) :
* Post count for "mine" in post table relies to fixed value set by WP_Posts_List_Table->user_posts_count
* Thus, exclude_premade_layouts_library() action doesn't automatically exclude premade layout and
* it has to be late filtered via this exclude_premade_layouts_library_count().
* @see WP_Posts_List_Table->user_posts_count to see how mine post value is retrieved.
* @param array $views All views in post list table.
function exclude_premade_layouts_library_count( $views ) {
if ( isset( $views['mine'] ) ) {
$current_user_id = get_current_user_id();
if ( isset( $_GET['author'] ) && ( $_GET['author'] === $current_user_id ) ) { // phpcs:ignore WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF.
// Reuse current $wp_query global.
$mine_posts_count = $wp_query->found_posts;
// Use WP_Query instead of plain MySQL SELECT because the custom field filtering uses
// GROUP BY which needs FOUND_ROWS() and this has been automatically handled by WP_Query.
'post_type' => 'et_pb_layout',
'author' => $current_user_id,
'key' => '_et_pb_predefined_layout',
'compare' => 'NOT EXISTS',
$mine_posts_count = $query->found_posts;
'post_type' => 'et_pb_layout',
'author' => $current_user_id,
$views['mine'] = sprintf(
'<a href="%1$s" class="%2$s">%3$s <span class="count">(%4$s)</span></a>',
esc_html__( 'Mine', 'et_builder' ),
esc_html( intval( $mine_posts_count ) )
add_filter( 'views_edit-et_pb_layout', 'exclude_premade_layouts_library_count' );
if ( ! function_exists( 'et_pb_get_standard_post_types' ) ) :
* Returns the standard '_et_pb_built_for_post_type' post types.
* @deprecated {@see ET_Builder_Post_Type_Layout::get_built_for_post_types()}
function et_pb_get_standard_post_types() {
return ET_Builder_Library::built_for_post_types();
if ( ! function_exists( 'et_pb_get_used_built_for_post_types' ) ) :
* Returns all current '_et_pb_built_for_post_type' post types.
* @deprecated {@see ET_Builder_Post_Type_Layout::get_built_for_post_types()}
function et_pb_get_used_built_for_post_types() {
return ET_Builder_Library::built_for_post_types( 'all' );
if ( ! function_exists( 'et_pb_get_font_icon_symbols' ) ) :
* Return fon icon symbols.
function et_pb_get_font_icon_symbols() {
$symbols = array( '&#x21;', '&#x22;', '&#x23;', '&#x24;', '&#x25;', '&#x26;', '&#x27;', '&#x28;', '&#x29;', '&#x2a;', '&#x2b;', '&#x2c;', '&#x2d;', '&#x2e;', '&#x2f;', '&#x30;', '&#x31;', '&#x32;', '&#x33;', '&#x34;', '&#x35;', '&#x36;', '&#x37;', '&#x38;', '&#x39;', '&#x3a;', '&#x3b;', '&#x3c;', '&#x3d;', '&#x3e;', '&#x3f;', '&#x40;', '&#x41;', '&#x42;', '&#x43;', '&#x44;', '&#x45;', '&#x46;', '&#x47;', '&#x48;', '&#x49;', '&#x4a;', '&#x4b;', '&#x4c;', '&#x4d;', '&#x4e;', '&#x4f;', '&#x50;', '&#x51;', '&#x52;', '&#x53;', '&#x54;', '&#x55;', '&#x56;', '&#x57;', '&#x58;', '&#x59;', '&#x5a;', '&#x5b;', '&#x5c;', '&#x5d;', '&#x5e;', '&#x5f;', '&#x60;', '&#x61;', '&#x62;', '&#x63;', '&#x64;', '&#x65;', '&#x66;', '&#x67;', '&#x68;', '&#x69;', '&#x6a;', '&#x6b;', '&#x6c;', '&#x6d;', '&#x6e;', '&#x6f;', '&#x70;', '&#x71;', '&#x72;', '&#x73;', '&#x74;', '&#x75;', '&#x76;', '&#x77;', '&#x78;', '&#x79;', '&#x7a;', '&#x7b;', '&#x7c;', '&#x7d;', '&#x7e;', '&#xe000;', '&#xe001;', '&#xe002;', '&#xe003;', '&#xe004;', '&#xe005;', '&#xe006;', '&#xe007;', '&#xe009;', '&#xe00a;', '&#xe00b;', '&#xe00c;', '&#xe00d;', '&#xe00e;', '&#xe00f;', '&#xe010;', '&#xe011;', '&#xe012;', '&#xe013;', '&#xe014;', '&#xe015;', '&#xe016;', '&#xe017;', '&#xe018;', '&#xe019;', '&#xe01a;', '&#xe01b;', '&#xe01c;', '&#xe01d;', '&#xe01e;', '&#xe01f;', '&#xe020;', '&#xe021;', '&#xe022;', '&#xe023;', '&#xe024;', '&#xe025;', '&#xe026;', '&#xe027;', '&#xe028;', '&#xe029;', '&#xe02a;', '&#xe02b;', '&#xe02c;', '&#xe02d;', '&#xe02e;', '&#xe02f;', '&#xe030;', '&#xe103;', '&#xe0ee;', '&#xe0ef;', '&#xe0e8;', '&#xe0ea;', '&#xe101;', '&#xe107;', '&#xe108;', '&#xe102;', '&#xe106;', '&#xe0eb;', '&#xe010;', '&#xe105;', '&#xe0ed;', '&#xe100;', '&#xe104;', '&#xe0e9;', '&#xe109;', '&#xe0ec;', '&#xe0fe;', '&#xe0f6;', '&#xe0fb;', '&#xe0e2;', '&#xe0e3;', '&#xe0f5;', '&#xe0e1;', '&#xe0ff;', '&#xe031;', '&#xe032;', '&#xe033;', '&#xe034;', '&#xe035;', '&#xe036;', '&#xe037;', '&#xe038;', '&#xe039;', '&#xe03a;', '&#xe03b;', '&#xe03c;', '&#xe03d;', '&#xe03e;', '&#xe03f;', '&#xe040;', '&#xe041;', '&#xe042;', '&#xe043;', '&#xe044;', '&#xe045;', '&#xe046;', '&#xe047;', '&#xe048;', '&#xe049;', '&#xe04a;', '&#xe04b;', '&#xe04c;', '&#xe04d;', '&#xe04e;', '&#xe04f;', '&#xe050;', '&#xe051;', '&#xe052;', '&#xe053;', '&#xe054;', '&#xe055;', '&#xe056;', '&#xe057;', '&#xe058;', '&#xe059;', '&#xe05a;', '&#xe05b;', '&#xe05c;', '&#xe05d;', '&#xe05e;', '&#xe05f;', '&#xe060;', '&#xe061;', '&#xe062;', '&#xe063;', '&#xe064;', '&#xe065;', '&#xe066;', '&#xe067;', '&#xe068;', '&#xe069;', '&#xe06a;', '&#xe06b;', '&#xe06c;', '&#xe06d;', '&#xe06e;', '&#xe06f;', '&#xe070;', '&#xe071;', '&#xe072;', '&#xe073;', '&#xe074;', '&#xe075;', '&#xe076;', '&#xe077;', '&#xe078;', '&#xe079;', '&#xe07a;', '&#xe07b;', '&#xe07c;', '&#xe07d;', '&#xe07e;', '&#xe07f;', '&#xe080;', '&#xe081;', '&#xe082;', '&#xe083;', '&#xe084;', '&#xe085;', '&#xe086;', '&#xe087;', '&#xe088;', '&#xe089;', '&#xe08a;', '&#xe08b;', '&#xe08c;', '&#xe08d;', '&#xe08e;', '&#xe08f;', '&#xe090;', '&#xe091;', '&#xe092;', '&#xe0f8;', '&#xe0fa;', '&#xe0e7;', '&#xe0fd;', '&#xe0e4;', '&#xe0e5;', '&#xe0f7;', '&#xe0e0;', '&#xe0fc;', '&#xe0f9;', '&#xe0dd;', '&#xe0f1;', '&#xe0dc;', '&#xe0f3;', '&#xe0d8;', '&#xe0db;', '&#xe0f0;', '&#xe0df;', '&#xe0f2;', '&#xe0f4;', '&#xe0d9;', '&#xe0da;', '&#xe0de;', '&#xe0e6;', '&#xe093;', '&#xe094;', '&#xe095;', '&#xe096;', '&#xe097;', '&#xe098;', '&#xe099;', '&#xe09a;', '&#xe09b;', '&#xe09c;', '&#xe09d;', '&#xe09e;', '&#xe09f;', '&#xe0a0;', '&#xe0a1;', '&#xe0a2;', '&#xe0a3;', '&#xe0a4;', '&#xe0a5;', '&#xe0a6;', '&#xe0a7;', '&#xe0a8;', '&#xe0a9;', '&#xe0aa;', '&#xe0ab;', '&#xe0ac;', '&#xe0ad;', '&#xe0ae;', '&#xe0af;', '&#xe0b0;', '&#xe0b1;', '&#xe0b2;', '&#xe0b3;', '&#xe0b4;', '&#xe0b5;', '&#xe0b6;', '&#xe0b7;', '&#xe0b8;', '&#xe0b9;', '&#xe0ba;', '&#xe0bb;', '&#xe0bc;', '&#xe0bd;', '&#xe0be;', '&#xe0bf;', '&#xe0c0;', '&#xe0c1;', '&#xe0c2;', '&#xe0c3;', '&#xe0c4;', '&#xe0c5;', '&#xe0c6;', '&#xe0c7;', '&#xe0c8;', '&#xe0c9;', '&#xe0ca;', '&#xe0cb;', '&#xe0cc;', '&#xe0cd;', '&#xe0ce;', '&#xe0cf;', '&#xe0d0;', '&#xe0d1;', '&#xe0d2;', '&#xe0d3;', '&#xe0d4;', '&#xe0d5;', '&#xe0d6;', '&#xe0d7;', '&#xe600;', '&#xe601;', '&#xe602;', '&#xe603;', '&#xe604;', '&#xe605;', '&#xe606;', '&#xe607;', '&#xe608;', '&#xe609;', '&#xe60a;', '&#xe60b;', '&#xe60c;', '&#xe60d;', '&#xe60e;', '&#xe60f;', '&#xe610;', '&#xe611;', '&#xe612;', '&#xe008;' );
$symbols = apply_filters( 'et_pb_font_icon_symbols', $symbols );
if ( ! function_exists( 'et_pb_get_font_icon_list' ) ) :