: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
et_builder_google_fonts_sync();
$google_fonts_cache = get_option( 'et_google_fonts_cache', array() );
if ( ! empty( $google_fonts_cache ) ) {
// Use cache if it's not empty
return apply_filters( 'et_builder_google_fonts', $google_fonts_cache );
// use hardcoded google fonts as fallback if no cache exists
return apply_filters( 'et_builder_google_fonts', et_core_get_saved_google_fonts() );
* Use correct conditional tag for compute callback. Compute callback can use actual conditional tag
* on page load. Compute callback relies on passed conditional tag params for update due to the
* @param string conditional tag name
* @param array all conditional tags params
* @return bool conditional tag value
function et_fb_conditional_tag( $name, $conditional_tags ) {
if ( defined( 'DOING_AJAX' ) && isset( $conditional_tags[ $name ] ) ) {
return $conditional_tags[ $name ] === 'true' ? true : false;
return is_callable( $name ) ? $name() : false;
* Retrieves the content of saved modules and process the shortcode into array.
function et_fb_get_saved_templates() {
if ( ! wp_verify_nonce( $_POST['et_fb_retrieve_library_modules_nonce'], 'et_fb_retrieve_library_modules_nonce' ) ){
if ( ! current_user_can( 'edit_posts' ) ) {
$layout_type = ! empty( $_POST['et_layout_type'] ) ? sanitize_text_field( $_POST['et_layout_type'] ) : 'layout';
$module_width = ! empty( $_POST['et_module_width'] ) && 'module' === $layout_type ? sanitize_text_field( $_POST['et_module_width'] ) : '';
$is_global = ! empty( $_POST['et_is_global'] ) ? sanitize_text_field( $_POST['et_is_global'] ) : 'all';
$specialty_query = ! empty( $_POST['et_specialty_columns'] ) && 'row' === $layout_type ? sanitize_text_field( $_POST['et_specialty_columns'] ) : '0';
$post_type = ! empty( $_POST['et_post_type'] ) ? sanitize_text_field( $_POST['et_post_type'] ) : 'post';
$start_from = ! empty( $_POST['et_templates_start_page'] ) ? sanitize_text_field( $_POST['et_templates_start_page'] ) : 0;
if ( et_theme_builder_is_layout_post_type( $post_type ) ) {
// Treat TB layouts as normal posts when fetching layouts from the library.
if ( 'all' === $is_global ) {
$templates_data_regular = et_pb_retrieve_templates( $layout_type, $module_width, 'not_global', $specialty_query, $post_type, '', array( $start_from, 25 ) );
$templates_data_global = et_pb_retrieve_templates( $layout_type, $module_width, 'global', $specialty_query, $post_type, '', array( $start_from, 25 ) );
$templates_data = array_merge( $templates_data_regular, $templates_data_global );
$templates_data = et_pb_retrieve_templates( $layout_type, $module_width, $is_global, $specialty_query, $post_type, array( $start_from, 50 ) );
$templates_data_processed = $templates_data;
if ( 0 !== $start_from && empty( $templates_data ) ) {
$templates_data_processed = array();
if ( empty( $templates_data ) ) {
$templates_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( $templates_data as $index => $data ) {
$templates_data_processed[ $index ]['shortcode'] = et_fb_process_shortcode( $data['shortcode'] );
if ( 'global' === $templates_data_processed[ $index ]['is_global'] && 'module' === $templates_data_processed[ $index ]['layout_type'] && is_array( $templates_data_processed[ $index ]['shortcode'] ) ) {
$templates_data_processed[ $index ]['shortcode'][0]['unsyncedGlobalSettings'] = $templates_data_processed[ $index ]['unsynced_options'];
if ( empty( $templates_data_processed[ $index ]['unsynced_options'] ) && isset( $templates_data_processed[ $index ]['shortcode'][0]['attrs']['saved_tabs'] ) && 'all' !== $templates_data_processed[ $index ]['shortcode'][0]['attrs']['saved_tabs'] ) {
$templates_data_processed[ $index ]['shortcode'][0]['unsyncedGlobalSettings'] = et_pb_get_unsynced_legacy_options( $post_type, $templates_data_processed[ $index ]['shortcode'][0] );
$next_page = 'all' === $is_global ? $start_from + 25 : $start_from + 50;
$json_templates = wp_json_encode( array( 'templates_data' => $templates_data_processed, 'next_page' => $next_page ) );
die( et_core_esc_previously( $json_templates ) );
add_action( 'wp_ajax_et_fb_get_saved_templates', 'et_fb_get_saved_templates' );
* Retrieves posts list that builder enabled
function et_fb_get_posts_list() {
et_core_security_check( 'edit_posts', 'et_fb_get_posts_list' );
$post_types = et_get_registered_post_type_options();
$post_type = isset( $_POST['post_type'] ) ? $_POST['post_type'] : false;
if ( empty( $post_type ) || ! isset( $post_types[ $post_type ] ) ) {
$query = new ET_Core_Post_Query( $post_type );
$posts = $query->run( array(
'post_status' => array( 'draft', 'publish', 'pending' ),
$_utils = ET_Core_Data_Utils::instance();
$posts = $_utils->array_sort_by( is_array( $posts ) ? $posts : array( $posts ), 'post_title' );
foreach ( $posts as $post ) {
// Check if page builder is activated.
if ( ! et_pb_is_pagebuilder_used( $post->ID ) ) {
// Only include posts that the user is allowed to edit
if ( ! current_user_can( 'edit_post', $post->ID ) ) {
// Skip for post has no title
if ( empty( $post->post_title ) ) {
$posts_list[ $post->ID ] = array(
'title' => $post->post_title,
'vb' => et_fb_get_vb_url( get_permalink( $post->ID ) ),
'bfb' => add_query_arg( array( 'post' => $post->ID, 'action' => 'edit', 'classic-editor' => '1' ), admin_url( 'post.php' ) ),
wp_send_json_success( $posts_list );
add_action( 'wp_ajax_et_fb_get_posts_list', 'et_fb_get_posts_list' );
function et_pb_get_supported_font_formats() {
return apply_filters( 'et_pb_supported_font_formats', array( 'ttf', 'otf' ) );
function et_pb_process_custom_font() {
et_core_security_check( 'upload_files', 'et_fb_upload_font_nonce' );
// action "add" or "remove"
$action = ! empty( $_POST['et_pb_font_action'] ) ? sanitize_text_field( $_POST['et_pb_font_action'] ) : 'save';
if ( 'add' === $action ) {
$supported_font_files = et_pb_get_supported_font_formats();
$custom_font_name = ! empty( $_POST['et_pb_font_name'] ) ? sanitize_text_field( $_POST['et_pb_font_name'] ) : '';
$custom_font_settings = ! empty( $_POST['et_pb_font_settings'] ) ? sanitize_text_field( $_POST['et_pb_font_settings'] ) : '';
$custom_font_settings_processed = '' === $custom_font_settings ? array() : json_decode( str_replace( '\\', '', $custom_font_settings ), true );
foreach ( $supported_font_files as $format ) {
if ( isset( $_FILES['et_pb_font_file_' . $format ] ) ) {
$fonts_array[ $format ] = $_FILES['et_pb_font_file_' . $format ];
die( wp_json_encode( et_pb_add_font( $fonts_array, $custom_font_name, $custom_font_settings_processed ) ) );
} elseif ( 'remove' === $action ) {
$font_slug = ! empty( $_POST['et_pb_font_name'] ) ? sanitize_text_field( $_POST['et_pb_font_name'] ) : '';
die( wp_json_encode( et_pb_remove_font( $font_slug ) ) );
add_action( 'wp_ajax_et_pb_process_custom_font', 'et_pb_process_custom_font' );
* Drag and Droploader :: Process Media
if ( ! function_exists( 'et_builder_droploader_process') ):
function et_builder_droploader_process() {
et_core_security_check( 'upload_files', 'et_builder_droploader_process_nonce' );
$post_id = ! empty( $_POST['post_id'] ) ? (int) $_POST['post_id'] : '';
if ( ! current_user_can( 'edit_post', $post_id ) ) {
et_core_security_check( 'edit_posts', 'et_builder_droploader_process_nonce' );
require_once( ABSPATH . 'wp-admin/includes/image.php' );
require_once( ABSPATH . 'wp-admin/includes/file.php' );
require_once( ABSPATH . 'wp-admin/includes/media.php' );
$attachment_id = media_handle_upload( 'file', (int) $_POST['post_id'] );
if ( is_wp_error( $attachment_id ) ) {
wp_send_json_error( $attachment_id->get_error_message() );
wp_send_json_success( $attachment_id );
add_action( 'wp_ajax_et_builder_droploader_process', 'et_builder_droploader_process' );
* Add allowed mime types and file extensions for font files.
function et_pb_filter_upload_mimes_custom_fonts() {
'otf' => 'application/x-font-opentype',
'ttf' => 'application/x-font-ttf',
'woff' => 'application/font-woff',
'woff2' => 'application/font-woff2',
'eot' => 'application/vnd.ms-fontobject',
function et_pb_add_font( $font_files, $font_name, $font_settings ) {
if ( ! isset( $font_files ) || empty( $font_files ) ) {
return array( 'error' => esc_html__( 'No Font File Provided', 'et_builder' ) );
// remove all special characters from the font name
$font_name = preg_replace( '/[^A-Za-z0-9\s\_-]/', '', $font_name );
if ( '' === $font_name ) {
return array( 'error' => esc_html__( 'Font Name Cannot be Empty and Cannot Contain Special Characters', 'et_builder' ) );
$google_fonts = et_builder_get_google_fonts();
$all_custom_fonts = get_option( 'et_uploaded_fonts', array() );
// Don't allow to add fonts with the names which already used by User Fonts or Google Fonts.
if ( isset( $all_custom_fonts[ $font_name ] ) || isset( $google_fonts[ $font_name ] ) ) {
return array( 'error' => esc_html__( 'Font With This Name Already Exists. Please Use a Different Name', 'et_builder' ) );
// set the upload Directory for builder font files
add_filter( 'upload_dir', 'et_pb_set_fonts_upload_dir' );
// Set the upload_mimes filter before uploading font file.
add_filter( 'upload_mimes', 'et_pb_filter_upload_mimes_custom_fonts' );
$uploaded_files_error = '';
foreach ( $font_files as $ext => $font_file) {
// Try to upload font file.
$upload = wp_handle_upload( $font_file, array(
'mimes' => et_pb_filter_upload_mimes_custom_fonts(),
// try with different MIME types if uploading .otf file and error occurs
if ( 'otf' === $ext && ! empty( $upload['error'] ) ) {
foreach ( array( 'application/x-font-ttf', 'application/vnd.ms-opentype' ) as $mime_type ) {
if ( ! empty( $upload['error'] ) ) {
$upload = wp_handle_upload( $font_file, array(
if ( ! empty( $upload['error'] ) ) {
$uploaded_files_error = $upload['error'];
$uploaded_files['font_file'][$ext] = esc_url( $upload['file'] );
$uploaded_files['font_url'][$ext] = esc_url( $upload['url'] );
// Reset the upload Directory after uploading font file
remove_filter( 'upload_dir', 'et_pb_set_fonts_upload_dir' );
// Reset the upload_mimes filter after uploading font file.
remove_filter( 'upload_mimes', 'et_pb_filter_upload_mimes_custom_fonts' );
// return error if no files were uploaded
if ( empty( $uploaded_files['font_file'] ) && '' !== $uploaded_files_error ) {
return array( 'error' => $uploaded_files_error );
//organize uploaded files
$all_custom_fonts[ $font_name ] = array(
'font_file' => $uploaded_files['font_file'],
'font_url' => $uploaded_files['font_url'],
if ( ! empty( $font_settings ) ) {
$all_custom_fonts[ $font_name ]['styles'] = ! isset( $font_settings['font_weights'] ) || 'all' === $font_settings['font_weights'] ? '100,200,300,400,500,600,700,800,900' : $font_settings['font_weights'];
$all_custom_fonts[ $font_name ]['type'] = isset( $font_settings['generic_family'] ) ? $font_settings['generic_family'] : 'serif';
update_option( 'et_uploaded_fonts', $all_custom_fonts );
// Need to update cached assets because custom fonts are included in static helpers.
et_fb_delete_builder_assets();
return array( 'error' => array(), 'success' => true, 'uploaded_font' => $font_name, 'updated_fonts' => $all_custom_fonts );
function et_pb_remove_font( $font_name ) {
if ( '' === $font_name ) {
return array( 'error' => esc_html__( 'Font Name Cannot be Empty', 'et_builder' ) );
$all_custom_fonts = get_option( 'et_uploaded_fonts', array() );
if ( ! isset( $all_custom_fonts[ $font_name ] ) ) {
return array( 'error' => esc_html__( 'Font Does not Exist', 'et_builder' ) );
// remove all uploaded font files if array
if ( is_array( $all_custom_fonts[ $font_name ]['font_file'] ) ) {
foreach ( $all_custom_fonts[ $font_name ]['font_file'] as $ext => $font_file ) {
et_pb_safe_unlink_font_file( $font_file );
$font_file = $all_custom_fonts[ $font_name ]['font_file'];
et_pb_safe_unlink_font_file( $font_file );
unset( $all_custom_fonts[ $font_name ] );
update_option( 'et_uploaded_fonts', $all_custom_fonts );
// Need to update cached assets because custom fonts are included in static helpers.
et_fb_delete_builder_assets();
return array( 'error' => array(), 'success' => true, 'updated_fonts' => $all_custom_fonts );
function et_pb_safe_unlink_font_file( $font_file ) {
$data_utils = ET_Core_Data_Utils::instance();
// get the extensions from our list of allowed font ext/mimes.
$valid_font_exts = array_keys( et_pb_filter_upload_mimes_custom_fonts() );
// set the upload Directory for builder font files, so we can retrieve the proper font upload dir info.
add_filter( 'upload_dir', 'et_pb_set_fonts_upload_dir' );
$wp_upload_dir_array = wp_get_upload_dir();
// get the absolute path to the et fonts upload dir.
$et_fonts_dir = $wp_upload_dir_array['path'];
// reset the upload Directory after getting the upload dir.
remove_filter( 'upload_dir', 'et_pb_set_fonts_upload_dir' );
// expand all symbolic links and resolve references to /./, /../ and extra / characters in the input path and return the canonicalized absolute pathname.
$file_realpath = realpath( $font_file );
// get information about the path.
$file_pathinfo = pathinfo( $font_file );
// Build the full file path based on the parsed pathinfo pieces.
$file_pathinfo_filename = $file_pathinfo['dirname'] . '/' . $file_pathinfo['basename'];
// make sure the realpath matches the parsed pathinfo file path, so there is no funny business.
if ( $data_utils->normalize_path( $file_realpath ) !== $data_utils->normalize_path( $file_pathinfo_filename ) ) {
// make sure the font file to be deleted is an actual font file extension (not an arbitrarty PHP file somehow for example).
if ( ! in_array( $file_pathinfo['extension'], $valid_font_exts ) ) {
// the proper upload dir for fonts.
$proper_font_file_path = $et_fonts_dir . '/' . $file_pathinfo['basename'];
// make sure the file is located in the proper fonts upload dir.
if ( $data_utils->normalize_path( $file_realpath ) !== $data_utils->normalize_path( $proper_font_file_path ) ) {
// now that all checks have passed, the file can be safely deleted.
return unlink( $file_realpath );
function et_pb_set_fonts_upload_dir( $directory ) {
$directory['path'] = $directory['basedir'] . '/et-fonts';
$directory['url'] = $directory['baseurl'] . '/et-fonts';
$directory['subdir'] = '/et-fonts';
function et_pb_get_unsynced_legacy_options( $post_type, $shortcode_data ) {
if ( ! isset( $shortcode_data['attrs']['saved_tabs'] ) && 'all' === $shortcode_data['attrs']['saved_tabs'] ) {
$general_fields = ET_Builder_Element::get_general_fields( $post_type, 'all', $shortcode_data['type'] );
$advanced_fields = ET_Builder_Element::get_advanced_fields( $post_type, 'all', $shortcode_data['type'] );
$css_fields = ET_Builder_Element::get_custom_css_fields( $post_type, 'all', $shortcode_data['type'] );
$saved_fields = array_keys( $shortcode_data['attrs'] );
// content fields should never be included into unsynced options. We use different key for the content options.
$saved_fields[] = 'content';
$saved_fields[] = 'raw_content';
$all_fields = array_merge( array_keys( $general_fields ), array_keys( $advanced_fields ), array_keys( $css_fields ) );
// compare all options with saved options to get array of unsynced ones.
$unsynced_options = array_diff( $all_fields, $saved_fields );
if ( false === strpos( $shortcode_data['attrs']['saved_tabs'], 'general' ) ) {
$unsynced_options[] = 'et_pb_content_field';
return $unsynced_options;
// prepare the ssl link for FB
function et_fb_prepare_ssl_link( $link ) {
// replace http:// with https:// if FORCE_SSL_ADMIN option enabled
if ( defined( 'FORCE_SSL_ADMIN' ) && FORCE_SSL_ADMIN ) {
return str_replace( 'http://', 'https://', $link );
* @param string $url Post url.
* @param string $builder 'vb' or 'bfb'.
if ( ! function_exists( 'et_fb_get_builder_url' ) ) :
function et_fb_get_builder_url( $url = false, $builder = 'vb', $is_new_page = false, $custom_page_id = false ) {
'et_bfb' => 'bfb' === $builder ? '1' : false,
if ('bfb' === $builder && $is_new_page) {
$duplicate_options = get_user_meta( get_current_user_id(), 'pll_duplicate_content', true );
$duplicate_content = ! empty( $duplicate_options ) && ! empty( $duplicate_options[ $post->post_type ] );
$duplicate_fallback = (int) $custom_page_id === (int) get_option( 'page_for_posts' ) ? (int) $custom_page_id : 'empty';
$from_post_id = isset( $_GET['from_post'] ) ? (int) sanitize_text_field( $_GET['from_post'] ) : false;
$args['from_post'] = $duplicate_content && $from_post_id ? $from_post_id : $duplicate_fallback;
$args['is_new_page'] = '1';
$args['custom_page_id'] = $custom_page_id;
// Additional info need to be appended via query strings if current request is used to get
// BFB URL and the given page's custom post type has its publicly_queryable setting is set
// to false. These additional information is be used to deterimined whether the BFB page
// request needs to modify its global $query and rewrite_rule configuration so correct BFB
// page can be rendered for valid user
if ( 'bfb' === $builder && ! $url ) {
$post_type = get_post_type();
// 'page' and 'et_pb_layout' are not queryable so post type needs to be checked against
// third party post types first to avoid false positive for these default post types
$is_third_party_post_type = in_array( $post_type, et_builder_get_third_party_post_types() );
$is_unqueryable = $is_third_party_post_type && in_array(
get_post_types( array( 'publicly_queryable' => false ) )
// These post id & post type query strings should only be added if current post type