';
}
endif;
if ( ! function_exists( 'et_builder_get_cache_notification_modal' ) ) :
/**
* Return Builder Cache Warning modal template.
*/
function et_builder_get_cache_notification_modal() {
$cache_plugin_message = '';
$cache_plugin = et_pb_detect_cache_plugins();
if ( false !== $cache_plugin ) {
$cache_plugin_message = sprintf(
// translators: %1$s: cache plugin name.
esc_html__( 'You are using the %1$s plugin. We recommend clearing the plugin cache after updating your theme.', 'et_builder' ),
esc_html( $cache_plugin['name'] )
);
$cache_plugin_message = '' . $cache_plugin_message . '
';
if ( ! empty( $cache_plugin['page'] ) ) {
$cache_plugin_message .= sprintf(
'%2$s ',
esc_url( admin_url( $cache_plugin['page'] ) ),
esc_html__( 'Clear Plugin Cache', 'et_builder' )
);
}
}
$browser_cache_message = '' . esc_html__( 'Builder files may also be cached in your browser. Please clear your browser cache.', 'et_builder' ) . '
';
$browser_cache_message .= sprintf(
'%1$s ',
esc_html__( 'Clear Browser Cache', 'et_builder' )
);
$output = sprintf(
'',
esc_html__( 'Builder Cache Warning', 'et_builder' ),
esc_html__( 'The Divi Builder has been updated, however your browser is loading an old cached version of the builder. Loading old files can cause the builder to malfunction.', 'et_builder' ),
esc_html__( 'Reload The Builder', 'et_builder' ),
et_core_esc_previously( $cache_plugin_message ),
et_core_esc_previously( $browser_cache_message ),
esc_html__( 'If you have cleared your plugin cache and browser cache, but still get this warning, then your files may be cached at the DNS or Server level. Contact your host or CDN for assistance.', 'et_builder' )
);
return $output;
}
endif;
if ( ! function_exists( 'et_builder_get_failure_notification_modal' ) ) :
/**
* Return Divi Builder Timeout failure notice modal.
*/
function et_builder_get_failure_notification_modal() {
$warnings = et_builder_get_warnings();
if ( false === $warnings ) {
return '';
}
$messages = '';
$i = 1;
foreach ( $warnings as $warning ) {
$messages .= sprintf(
'%1$s. %2$s
',
esc_html( $i ),
et_core_esc_previously( $warning )
);
$i++;
}
$output = sprintf(
'',
esc_html__( 'Divi Builder Timeout', 'et_builder' ),
et_core_esc_previously( $messages ),
esc_html__( 'Reload The Builder', 'et_builder' ),
esc_html__( 'Oops, it looks like the Divi Builder failed to load. Performing the following actions may help solve the problem.', 'et_builder' )
);
return $output;
}
endif;
if ( ! function_exists( 'et_builder_get_no_builder_notification_modal' ) ) :
/**
* Incompatible Post Type modal template.
*/
function et_builder_get_no_builder_notification_modal() {
$output = sprintf(
'',
esc_html__( 'Incompatible Post Type', 'et_builder' ),
esc_html__( 'This post does not show the standard WordPress content area. Unfortunately, that means the Divi Builder cannot be used on this post.', 'et_builder' )
);
return $output;
}
endif;
if ( ! function_exists( 'et_builder_get_no_browser_notification_modal' ) ) :
/**
* Browser Is Not Supported modal template.
*/
function et_builder_get_no_browser_notification_modal() {
$output = sprintf(
'',
esc_html__( 'Your Browser Is Not Supported', 'et_builder' ),
esc_html__( 'The Divi Builder does not support the browser you are using. Your browser is no longer being developed, so it is time to switch to something new! The Divi Builder works best in the most recent versions of Chrome, Firefox, Safari, Opera and Edge.', 'et_builder' )
);
return $output;
}
endif;
if ( ! function_exists( 'et_builder_get_exit_notification_modal' ) ) :
/**
* Have Unsaved Changes modal template.
*/
function et_builder_get_exit_notification_modal() {
$output = sprintf(
'',
esc_html__( 'You Have Unsaved Changes', 'et_builder' ),
et_get_safe_localization( __( 'Your page contains changes that have not been saved. If you close the builder without saving, these changes will be lost. If you would like to leave the builder and save all changes, please select Save & Exit . If you would like to discard all recent changes, choose Discard & Exit .', 'et_builder' ) ),
esc_html__( 'Discard & Exit', 'et_builder' ),
esc_html__( 'Save & Exit', 'et_builder' )
);
return $output;
}
endif;
if ( ! function_exists( 'et_builder_get_browser_autosave_notification_modal' ) ) :
/**
* Return Browser Backup Exists modal template.
*/
function et_builder_get_browser_autosave_notification_modal() {
$output = sprintf(
'',
esc_html__( 'A Browser Backup Exists', 'et_builder' ),
et_get_safe_localization( __( 'A browser backup exists for this post that is newer than the version you are currently viewing. This backup was captured during your previous editing session, but you never saved it. Would you like to restore this backup and continue editing where you left off?', 'et_builder' ) ),
esc_html__( "Don't Restore", 'et_builder' ), // left button.
esc_html__( 'Restore', 'et_builder' ) // right button.
);
return $output;
}
endif;
if ( ! function_exists( 'et_builder_get_server_autosave_notification_modal' ) ) :
/**
* Return Autosave Exists modal template.
*/
function et_builder_get_server_autosave_notification_modal() {
$output = sprintf(
'',
esc_html__( 'An Autosave Exists', 'et_builder' ),
et_get_safe_localization( __( 'A recent autosave exists for this post that is newer than the version you are currently viewing. This autosave was captured during your previous editing session, but you never saved it. Would you like to restore this autosave and continue editing where you left off?', 'et_builder' ) ),
esc_html__( "Don't Restore", 'et_builder' ), // left button.
esc_html__( 'Restore', 'et_builder' ) // right button.
);
return $output;
}
endif;
if ( ! function_exists( 'et_builder_get_unsaved_notification_texts' ) ) :
/**
* Return Save Has Failed notification template.
*/
function et_builder_get_unsaved_notification_texts() {
$text = sprintf(
'%1$s
%2$s
%3$s
',
et_get_safe_localization( __( 'An error has occurred while saving your page. Various problems can cause a save to fail, such as a lack of server resources, firewall blockages, plugin conflicts or server misconfiguration. You can try saving again by clicking Try Again, or you can download a backup of your unsaved page by clicking Download Backup. Backups can be restored using the portability system while next editing your page.', 'et_builder' ) ),
et_get_safe_localization( __( 'Contacting your host and asking them to increase the following PHP variables may help: memory_limit, max_execution_time, upload_max_filesize, post_max_size, max_input_time, max_input_vars. In addition, auditing your firewall error log (such as ModSecurity) may reveal false positives that are preventing saves from completing.', 'et_builder' ) ),
et_get_safe_localization( __( 'Lastly, it is recommended that you temporarily disable all WordPress plugins and browser extensions and try to save again to determine if something is causing a conflict.', 'et_builder' ) )
);
return array(
'header' => esc_html__( 'Your Save Has Failed', 'et_builder' ),
'text' => $text,
'buttons' => array(
'secondary' => sprintf( '%1$s ', esc_html__( 'Try Again', 'et_builder' ) ),
'primary' => sprintf( '%1$s ', esc_html__( 'Download Backup', 'et_builder' ) ),
),
'classes' => 'et-builder-unsaved-modal',
);
}
endif;
if ( ! function_exists( 'et_builder_get_global_presets_save_failure_texts' ) ) :
/**
* Return Global Presets Save Failed notification modal template.
*/
function et_builder_get_global_presets_save_failure_texts() {
$text = sprintf(
'%1$s
',
et_get_safe_localization( __( 'An error has occurred while saving the Global Presets settings. Various problems can cause a save to fail, such as a lack of server resources, firewall blockages or plugin conflicts or server misconfiguration. You can try saving again by clicking Try Again, or you can download a backup of your unsaved defaults by clicking Download Backup. A backup can be helpful when contacting our Support Team.', 'et_builder' ) )
);
return array(
'header' => esc_html__( 'Save of Global Presets Has Failed', 'et_builder' ),
'text' => $text,
'buttons' => array(
'secondary' => sprintf( '%1$s ', esc_html__( 'Try Again', 'et_builder' ) ),
'primary' => sprintf( '%1$s ', esc_html__( 'Download Backup', 'et_builder' ) ),
),
'classes' => 'et-builder-global-presets-save-failure-modal',
);
}
endif;
if ( ! function_exists( 'et_builder_get_global_presets_save_forbidden_texts' ) ) :
/**
* Return Global presets save failure text.
*/
function et_builder_get_global_presets_save_forbidden_texts() {
$text = sprintf(
'%1$s
',
et_get_safe_localization( __( 'You do not have sufficient permissions to edit Divi Presets.', 'et_builder' ) )
);
return array(
'header' => esc_html__( 'Save of Global Presets Has Failed', 'et_builder' ),
'text' => $text,
'buttons' => array(
'primary' => sprintf( '%1$s ', esc_html__( 'Ok', 'et_builder' ) ),
),
'classes' => 'et-builder-global-presets-save-forbidden-modal',
);
}
endif;
if ( ! function_exists( 'et_builder_get_global_presets_load_failure_texts' ) ) :
/**
* Return Global Presets Load Failed notification modal template.
*/
function et_builder_get_global_presets_load_failure_texts() {
$text = sprintf(
'%1$s
',
et_get_safe_localization( __( 'An error has occurred while loading the Global History States. Various problems can cause a save to fail, such as a lack of server resources, firewall blockages or plugin conflicts or server misconfiguration. You can try loading again by clicking Try Again.', 'et_builder' ) )
);
return array(
'header' => esc_html__( 'Load of Global Presets Has Failed', 'et_builder' ),
'text' => $text,
'buttons' => array(
'primary' => sprintf( '%1$s ', esc_html__( 'Try Again', 'et_builder' ) ),
),
'classes' => 'et-builder-global-presets-load-failure-modal',
);
}
endif;
if ( ! function_exists( 'et_builder_page_creation_modal' ) ) :
/**
* Return Page Creation Card modal template.
*/
function et_builder_page_creation_modal() {
return '
<%= option.titleText %>
<%= option.descriptionText %>
<%= option.buttonText %>
';
}
endif;
if ( ! function_exists( 'et_builder_get_disabled_link_modal' ) ) :
/**
* Return Link Disabled notification modal template.
*/
function et_builder_disabled_link_modal() {
$output = sprintf(
'',
esc_html__( 'Link Disabled', 'et_builder' ),
esc_html__( 'During preview, link to different page is disabled', 'et_builder' ),
esc_html__( 'Close', 'et_builder' )
);
return $output;
}
endif;
if ( ! function_exists( 'et_builder_get_warnings' ) ) :
/**
* Return
*/
function et_builder_get_warnings() {
if ( ! current_user_can( 'manage_options' ) ) {
return false;
}
$warnings = array();
// WP_DEBUG check.
if ( defined( 'WP_DEBUG' ) && true === WP_DEBUG ) {
$warnings[] = sprintf(
'%1$s. %2$s ',
esc_html__( 'You have WP_DEBUG enabled. Please disable this setting in wp-config.php', 'et_builder' ),
esc_html__( 'Disable Debug Mode', 'et_builder' )
);
}
// Plugins check.
$third_party_plugins_active = false;
$excluded_plugins = array(
'wordpress-importer/wordpress-importer.php',
'divi-builder/divi-builder.php',
'elegant-themes-updater/elegant-themes-updater.php',
'et-security-patcher/et-security-patcher.php',
);
$active_plugins = get_option( 'active_plugins' );
if ( is_array( $active_plugins ) && ! empty( $active_plugins ) ) {
foreach ( $active_plugins as $plugin ) {
if ( in_array( $plugin, $excluded_plugins, true ) ) {
continue;
}
$third_party_plugins_active = true;
break;
}
}
if ( $third_party_plugins_active ) {
$warnings[] = sprintf(
'%1$s %2$s ',
esc_html__( 'You are using third party plugins. Try disabling each plugin to see if one is causing a conflict.', 'et_builder' ),
esc_html__( 'Manage Your Plugins', 'et_builder' ),
esc_url( admin_url( 'plugins.php' ) )
);
}
// WordPress update check.
require_once ABSPATH . 'wp-admin/includes/update.php';
$updates = get_core_updates();
if ( isset( $updates[0]->response ) && 'latest' !== $updates[0]->response ) {
$warnings[] = sprintf(
'%1$s %2$s ',
esc_html__( 'You are using an outdated version of WordPress. Please upgrade.', 'et_builder' ),
esc_html__( 'Upgrade WordPress', 'et_builder' ),
esc_url( admin_url( 'update-core.php' ) )
);
}
global $et_current_memory_limit; // Memory check.
if ( ! empty( $et_current_memory_limit ) && intval( $et_current_memory_limit ) < 128 ) {
$class = ' et_builder_increase_memory';
$warnings[] = sprintf(
'%1$s. %2$s ',
esc_html__( 'Please increase your PHP Memory Limit. You can return the value to default via the Divi Theme Options in the future', 'et_builder' ),
esc_html__( 'Increase Your Memory Limit Now', 'et_builder' ),
esc_attr( $class )
);
}
// Version check.
$et_update_themes = get_site_transient( 'et_update_themes' );
if ( is_object( $et_update_themes ) && isset( $et_update_themes->response ) ) {
$theme_info = wp_get_theme();
if ( is_child_theme() ) {
$theme_info = wp_get_theme( $theme_info->parent_theme );
}
$name = $theme_info->get( 'Name' );
$version = $theme_info->get( 'Version' );
if ( isset( $et_update_themes->response[ $name ] ) && isset( $et_update_themes->response[ $name ]['new_version'] ) && version_compare( $version, $et_update_themes->response[ $name ]['new_version'], '<' ) ) {
$warnings[] = sprintf(
'%1$s %2$s ',
sprintf(
// translators: %1$s theme version.
esc_html__( 'You are using an outdated version of the theme. The latest version is %1$s', 'et_builder' ),
esc_html( $et_update_themes->response[ $name ]['new_version'] )
),
esc_html__( 'Upgrade', 'et_builder' ),
esc_url( admin_url( 'themes.php' ) )
);
}
}
if ( empty( $warnings ) ) {
return false;
}
return $warnings;
}
endif;
if ( ! function_exists( 'et_increase_memory_limit' ) ) :
/**
* Increase the memory limit.
*/
function et_increase_memory_limit() {
if ( ! is_admin() ) {
return false;
}
if ( ! current_user_can( 'edit_posts' ) ) {
return false;
}
// proceed only if current memory limit < 256.
if ( et_core_get_memory_limit() >= 256 ) {
return true;
}
$result = wp_raise_memory_limit();
return ! empty( $result );
}
endif;
if ( ! function_exists( 'et_maybe_increase_memory_limit' ) ) :
/**
* Try to increase the php memory limit.
*/
function et_maybe_increase_memory_limit() {
global $pagenow;
if ( ! is_admin() ) {
return;
}
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
return;
}
if ( ! current_user_can( 'edit_posts' ) ) {
return;
}
if ( empty( $pagenow ) ) {
return;
}
// increase memory limit on Edit Post page only.
if ( ! in_array( $pagenow, array( 'post.php', 'post-new.php' ), true ) ) {
return;
}
/**
* Check if a user clicked "Increase Memory Limit" button
* in the "Failure Notification" modal window.
*/
if ( ! et_should_memory_limit_increase() ) {
return;
}
et_increase_memory_limit();
}
endif;
add_action( 'admin_init', 'et_maybe_increase_memory_limit' );
if ( ! function_exists( 'et_should_memory_limit_increase' ) ) :
/**
* Force php memory limit increase.
*/
function et_should_memory_limit_increase() {
$memory_limit = et_get_option( 'set_memory_limit' );
if ( '1' === $memory_limit ) {
return true;
}
return false;
}
endif;
if ( ! function_exists( 'et_reset_memory_limit_increase_setting' ) ) :
/**
* Return Memory Limit Increase template.
*/
function et_reset_memory_limit_increase_setting() {
wp_enqueue_script( 'et-builder-reset-memory-limit-increase', ET_BUILDER_URI . '/scripts/reset_memory_limit_increase_setting.js', array( 'jquery' ), ET_BUILDER_VERSION, true );
wp_localize_script(
'et-builder-reset-memory-limit-increase',
'et_reset_memory_limit_increase',
array(
'et_builder_reset_memory_limit_nonce' => wp_create_nonce( 'et_builder_reset_memory_limit_nonce' ),
)
);
printf(
'%1$s ',
esc_html__( 'Disable Memory Limit Increase' )
);
}
endif;
if ( ! function_exists( 'et_pb_detect_cache_plugins' ) ) :
/**
* Detect the activated cache plugins and return the link to plugin options and return its page link or false.
*
* @return string or bool
*/
function et_pb_detect_cache_plugins() {
// Cache Plugins.
if ( function_exists( 'edd_w3edge_w3tc_activate_license' ) ) {
return array(
'name' => 'W3 Total Cache',
'page' => 'admin.php?page=w3tc_pgcache',
);
}
if ( function_exists( 'wpsupercache_activate' ) ) {
return array(
'name' => 'WP Super Cache',
'page' => 'options-general.php?page=wpsupercache',
);
}
if ( class_exists( 'HyperCache' ) ) {
return array(
'name' => 'Hyper Cache',
'page' => 'options-general.php?page=hyper-cache%2Foptions.php',
);
}
if ( class_exists( '\zencache\plugin' ) ) {
return array(
'name' => 'ZenCache',
'page' => 'admin.php?page=zencache',
);
}
if ( class_exists( 'WpFastestCache' ) ) {
return array(
'name' => 'WP Fastest Cache',
'page' => 'admin.php?page=WpFastestCacheOptions',
);
}
if ( '1' === get_option( 'wordfenceActivated' ) ) {
// Wordfence removed their support of Falcon cache in v6.2.8, so we'll
// just check against their `cacheType` setting (if it exists).
if ( class_exists( 'wfConfig' ) && 'falcon' === wfConfig::get( 'cacheType' ) ) {
return array(
'name' => 'Wordfence',
'page' => 'admin.php?page=WordfenceSitePerf',
);
}
}
if ( function_exists( 'cachify_autoload' ) ) {
return array(
'name' => 'Cachify',
'page' => 'options-general.php?page=cachify',
);
}
if ( class_exists( 'FlexiCache' ) ) {
return array(
'name' => 'FlexiCache',
'page' => 'options-general.php?page=flexicache',
);
}
if ( function_exists( 'rocket_init' ) ) {
return array(
'name' => 'WP Rocket',
'page' => 'options-general.php?page=wprocket',
);
}
if ( function_exists( 'cloudflare_init' ) ) {
return array(
'name' => 'CloudFlare',
'page' => 'options-general.php?page=cloudflare',
);
}
if ( class_exists( 'Hummingbird\\WP_Hummingbird' ) ) {
return array(
'name' => 'Hummingbird',
'page' => 'admin.php?page=wphb',
);
}
if ( class_exists( 'comet_cache' ) ) {
return array(
'name' => 'Comet Cache',
'page' => 'admin.php?page=comet_cache',
);
}
if ( class_exists( 'Cache_Enabler' ) ) {
return array(
'name' => 'Cache Enabler',
'page' => 'options-general.php?page=cache-enabler',
);
}
// Hosting Provider Caching.
if ( class_exists( 'batcache' ) ) {
// Doesn't have clear cache button on WP Admin area.
return array(
'name' => 'Pressable Cache',
'page' => '',
);
}
if ( class_exists( 'WpeCommon' ) ) {
return array(
'name' => 'WP Engine Cache',
'page' => 'admin.php?page=wpengine-common',
);
}
if ( class_exists( 'Endurance_Page_Cache' ) ) {
// The purge cache button exists on MU plugins page.
return array(
'name' => 'Endurance Page Cache',
'page' => 'plugins.php?plugin_status=mustuse',
);
}
if ( function_exists( 'pantheon_wp_clear_edge_all' ) ) {
// Doesn't have clear cache button on WP Admin area.
return array(
'name' => 'Pantheon Advanced Page Cache',
'page' => '',
);
}
if ( function_exists( 'sg_cachepress_purge_cache' ) ) {
return array(
'name' => 'SG Optimizer',
'page' => 'admin.php?page=sg-cachepress',
);
}
if ( class_exists( 'Breeze_Admin' ) ) {
return array(
'name' => 'Breeze',
'page' => 'options-general.php?page=breeze',
);
}
if ( class_exists( '\Kinsta\Cache' ) ) {
return array(
'name' => 'Kinsta Cache',
'page' => 'admin.php?page=kinsta-tools',
);
}
if ( class_exists( '\WPaaS\Cache' ) ) {
return array(
'name' => 'GoDaddy Cache',
'page' => '',
);
}
// Complimentary Performance Plugins.
if ( class_exists( 'autoptimizeCache' ) ) {
return array(
'name' => 'Autoptimize',
'page' => 'options-general.php?page=autoptimize',
);
}
if ( class_exists( 'WP_Optimize' ) ) {
return array(
'name' => 'WP-Optimize',
'page' => 'admin.php?page=wpo_settings',
);
}
return false;
}
endif;
/**
* Clear templates cache and delete cached definitions to force regenerate templates.
*/
function et_pb_force_regenerate_templates() {
// add option to indicate that templates cache should be updated in case of term added/removed/updated.
et_update_option( 'et_pb_clear_templates_cache', true );
// Delete cached definitions / helpers.
et_fb_delete_builder_assets();
}
add_action( 'created_term', 'et_pb_force_regenerate_templates' );
add_action( 'edited_term', 'et_pb_force_regenerate_templates' );
add_action( 'delete_term', 'et_pb_force_regenerate_templates' );
// @Todo we should remove this hook after BB is retired
// purge BB microtemplates cache after Theme Customizer changes.
add_action( 'customize_save_after', 'et_pb_force_regenerate_templates' );
/**
* Return current ab module id.
*
* @param integer $test_id Test id.
* @param bool $subject_index Subject index.
*
* @return int|mixed
*/
function et_pb_ab_get_current_ab_module_id( $test_id, $subject_index = false ) {
$all_subjects_raw = get_post_meta( $test_id, '_et_pb_ab_subjects', true );
$all_subjects = false !== $all_subjects_raw ? explode( ',', $all_subjects_raw ) : array();
if ( false === $subject_index ) {
$saved_next_subject = get_post_meta( $test_id, '_et_pb_ab_next_subject', true );
$current_subject_index = false !== $saved_next_subject ? (int) $saved_next_subject : 0;
} else {
$current_subject_index = $subject_index;
}
if ( empty( $all_subjects ) ) {
return 0;
}
if ( ! isset( $all_subjects[ $current_subject_index ] ) ) {
return $all_subjects[0];
}
return $all_subjects[ $current_subject_index ];
}
/**
* Increment current subject index value on post meta.
*
* @param int $test_id test id.
*/
function et_pb_ab_increment_current_ab_module_id( $test_id ) {
global $wpdb;
// Get subjects and current subject index.
$all_subjects_raw = get_post_meta( $test_id, '_et_pb_ab_subjects', true );
$all_subjects = false !== $all_subjects_raw ? explode( ',', $all_subjects_raw ) : array();
$saved_next_subject = get_post_meta( $test_id, '_et_pb_ab_next_subject', true );
$current_subject_index = false !== $saved_next_subject ? (int) $saved_next_subject : 0;
if ( empty( $all_subjects ) ) {
return;
}
// increment the index of next subject, set to 0 if it's a last subject in the list.
$next_subject_index = ( count( $all_subjects ) - 1 ) < ( $current_subject_index + 1 ) ? 0 : $current_subject_index + 1;
update_post_meta( $test_id, '_et_pb_ab_next_subject', $next_subject_index );
}
/**
* Add the record into AB Testing log table.
*
* @param array $stats_data_array State record data.
*
* @return void
*/
function et_pb_add_stats_record( $stats_data_array ) {
global $wpdb;
$table_name = $wpdb->prefix . 'et_divi_ab_testing_stats';
$record_date = current_time( 'mysql' );
// sanitize and set vars.
$test_id = intval( $stats_data_array['test_id'] );
$subject_id = intval( $stats_data_array['subject_id'] );
$record_type = sanitize_text_field( $stats_data_array['record_type'] );
$record_date = sanitize_text_field( $record_date );
// Check visitor cookie and do not proceed if event already logged for current visitor.
if ( et_pb_ab_get_visitor_cookie( $test_id, $record_date ) ) {
return;
}
$wpdb->insert(
$table_name,
array(
'record_date' => $record_date,
'test_id' => $test_id,
'subject_id' => $subject_id,
'event' => $record_type,
),
array(
'%s', // record_date.
'%d', // test_id.
'%d', // subject_id.
'%s', // event.
)
);
}
/**
* Set AB Testing formatted cookie.
*
* @since 4.4.3 Set cookie path, so the cookie will be available on overall site.
*
* @param int $post_id post ID.
* @param string $record_type record type.
* @param mixed $value cookie value.
*
* @return bool|mixed
*/
function et_pb_ab_set_visitor_cookie( $post_id, $record_type, $value = true ) {
$unique_test_id = get_post_meta( $post_id, '_et_pb_ab_testing_id', true );
$cookie_name = sanitize_text_field( "et_pb_ab_{$record_type}_{$post_id}{$unique_test_id}" );
return setcookie( $cookie_name, $value, 0, SITECOOKIEPATH );
}
/**
* Get AB Testing formatted cookie.
*
* @param int $post_id post ID.
* @param string $record_type record type.
*
* @return bool|mixed
*/
function et_pb_ab_get_visitor_cookie( $post_id, $record_type ) {
$unique_test_id = get_post_meta( $post_id, '_et_pb_ab_testing_id', true );
$cookie_name = "et_pb_ab_{$record_type}_{$post_id}{$unique_test_id}";
return isset( $_COOKIE[ $cookie_name ] ) ? sanitize_text_field( $_COOKIE[ $cookie_name ] ) : false;
}
/**
* Get subjects of particular post / AB Testing.
*
* @param int $post_id post id.
* @param string $type array|string type of output.
* @param mixed $prefix string|bool prefix that should be prepended.
* @param bool $is_cron_task Whether subjects is autosave/draft or not.
*
* @return array
*/
function et_pb_ab_get_subjects( $post_id, $type = 'array', $prefix = false, $is_cron_task = false ) {
$subjects_data = get_post_meta( $post_id, '_et_pb_ab_subjects', true );
$fb_enabled = et_fb_is_enabled();
// Get autosave/draft subjects if post hasn't been published.
if ( ! $is_cron_task && ! $subjects_data && $fb_enabled && 'publish' !== get_post_status() ) {
$subjects_data = get_post_meta( $post_id, '_et_pb_ab_subjects_draft', true );
}
// If user wants string.
if ( 'string' === $type ) {
return $subjects_data;
}
// Convert into array.
$subjects = explode( ',', $subjects_data );
if ( ! empty( $subjects ) && $prefix ) {
$prefixed_subjects = array();
// Loop subject, add prefix.
foreach ( $subjects as $subject ) {
$prefixed_subjects[] = $prefix . (string) $subject;
}
return $prefixed_subjects;
}
return $subjects;
}
/**
* Unhashed hashed subject id.
*
* @param int $post_id post ID.
* @param string $hashed_subject_id hashed subject id.
*
* @return string subject ID
*/
function et_pb_ab_unhashed_subject_id( $post_id, $hashed_subject_id ) {
if ( ! $post_id || ! $hashed_subject_id ) {
return false;
}
$ab_subjects = et_pb_ab_get_subjects( $post_id );
$ab_hash_key = defined( 'NONCE_SALT' ) ? NONCE_SALT : 'default-divi-hash-key';
$subject_id = false;
// Compare subjects against hashed subject id found on cookie to verify whether cookie value is valid or not.
foreach ( $ab_subjects as $ab_subject ) {
// Valid subject_id is found.
if ( hash_hmac( 'md5', $ab_subject, $ab_hash_key ) === $hashed_subject_id ) {
$subject_id = $ab_subject;
// no need to continue.
break;
}
}
// If no valid subject found, get the first one.
if ( ! $subject_id && isset( $ab_subjects[0] ) ) {
$subject_id = $ab_subjects[0];
}
return $subject_id;
}
/**
* AJAX Callback :: AB Testing :: Get subject id.
*/
function et_pb_ab_get_subject_id() {
if ( ! isset( $_POST['et_frontend_nonce'] ) || ! wp_verify_nonce( $_POST['et_frontend_nonce'], 'et_frontend_nonce' ) ) { // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput.InputNotSanitized -- wp_verify_nonce() function does sanitation.
die( -1 );
}
$test_id = isset( $_POST['et_pb_ab_test_id'] ) ? intval( $_POST['et_pb_ab_test_id'] ) : 0;
$hashed_subject_id = et_pb_ab_get_visitor_cookie( $test_id, 'view_page' );
$current_ab_module_id = et_pb_ab_unhashed_subject_id( $test_id, $hashed_subject_id );
// retrieve the cached subjects HTML.
$subjects_cache = get_post_meta( $test_id, 'et_pb_subjects_cache', true );
$result = array(
'id' => $current_ab_module_id,
'content' => isset( $subjects_cache[ $current_ab_module_id ] ) ? $subjects_cache[ $current_ab_module_id ] : '',
);
die( wp_json_encode( $result ) );
}
add_action( 'wp_ajax_et_pb_ab_get_subject_id', 'et_pb_ab_get_subject_id' );
add_action( 'wp_ajax_nopriv_et_pb_ab_get_subject_id', 'et_pb_ab_get_subject_id' );
/**
* Register Builder Portability.
*
* @since 2.7.0
*/
function et_pb_register_builder_portabilities() {
global $shortname;
// Don't overwrite global.
$_shortname = empty( $shortname ) ? 'divi' : $shortname;
// get all the roles that can edit theme options.
$applicability_roles = et_core_get_roles_by_capabilities( [ 'edit_theme_options' ] );
// Make sure the Portability is loaded.
et_core_load_component( 'portability' );
if ( current_user_can( 'edit_theme_options' ) ) {
// phpcs:disable WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF.
// Register the Roles Editor portability.
$pb_roles = array(
'title' => esc_html__( 'Import & Export Roles', 'et_builder' ),
'name' => esc_html__( 'Divi Role Editor Settings', 'et_builder' ),
'type' => 'options',
'target' => 'et_pb_role_settings',
'view' => ( isset( $_GET['page'] ) && "et_{$_shortname}_role_editor" === $_GET['page'] ),
'applicability' => $applicability_roles,
);
et_core_portability_register( 'et_pb_roles', $pb_roles );
// phpcs:enable
}
if ( current_user_can( 'edit_posts' ) ) {
// Register the Builder individual layouts portability.
$args = array(
'title' => esc_html__( 'Import & Export Layouts', 'et_builder' ),
'name' => esc_html__( 'Divi Builder Layout', 'et_builder' ),
'type' => 'post',
'view' => ( function_exists( 'et_builder_should_load_framework' ) && et_builder_should_load_framework() ),
);
et_core_portability_register( 'et_builder', $args );
// phpcs:disable WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF.
// Register the Builder Layouts Post Type portability.
$layouts = array(
'title' => esc_html__( 'Import & Export Layouts', 'et_builder' ),
'name' => esc_html__( 'Divi Builder Layouts', 'et_builder' ),
'type' => 'post_type',
'target' => ET_BUILDER_LAYOUT_POST_TYPE,
'view' => ( isset( $_GET['post_type'] ) && ET_BUILDER_LAYOUT_POST_TYPE === $_GET['post_type'] ),
);
et_core_portability_register( 'et_builder_layouts', $layouts );
// phpcs:enable
}
}
add_action( 'admin_init', 'et_pb_register_builder_portabilities' );
/**
* Modify the portability export WP query.
*
* @since To define
*
* @param WP_Query $query portability query.
*
* @return string New query.
*/
function et_pb_modify_portability_export_wp_query( $query ) {
// Exclude predefined layout from export.
return array_merge(
$query,
array(
'meta_query' => array(
'relation' => 'OR',
array(
'key' => '_et_pb_predefined_layout',
'compare' => 'NOT EXISTS',
),
array(
'key' => '_et_pb_predefined_layout',
'value' => 'on',
'compare' => 'NOT LIKE',
),
),
)
);
}
add_filter( 'et_core_portability_export_wp_query_et_builder_layouts', 'et_pb_modify_portability_export_wp_query' );
/**
* Check whether current page is pagebuilder preview page.
*
* @return bool
*/
function is_et_pb_preview() {
global $wp_query;
// phpcs:ignore WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF.
return ( 'true' === $wp_query->get( 'et_pb_preview' ) && isset( $_GET['et_pb_preview_nonce'] ) );
}
if ( ! function_exists( 'et_pb_is_pagebuilder_used' ) ) :
/**
* Determine whether page builder is used or not on the post/page.
*
* @param integer $page_id The post id to be checked.
*
* @return bool
*/
function et_pb_is_pagebuilder_used( $page_id = 0 ) {
if ( 0 === $page_id ) {
$page_id = et_core_page_resource_get_the_ID();
}
return (
'on' === get_post_meta( $page_id, '_et_pb_use_builder', true ) ||
// Divi layout post type always use the builder.
'et_pb_layout' === get_post_type( $page_id ) ||
// Extra Category post type always use the builder.
'layout' === get_post_type( $page_id )
);
}
endif;
if ( ! function_exists( 'et_fb_is_enabled' ) ) :
/**
* Determine fb enabled status of a post / page.
*
* @internal NOTE: Don't use this from outside builder code! {@see et_core_is_fb_enabled()}.
*
* @param bool|integer $post_id The post ID to determine fb enabled status of a post / page.
*
* @return bool
*/
function et_fb_is_enabled( $post_id = false ) {
// Cache results since the function could end up being called thousands of times.
static $cache = array();
if ( ! $post_id ) {
global $post;
$post_id = isset( $post->ID ) ? $post->ID : false;
}
$check = apply_filters( 'et_fb_is_enabled', null, $post_id );
if ( null !== $check ) {
return $check;
}
// phpcs:ignore WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF.
if ( empty( $_GET['et_fb'] ) ) {
return false;
}
if ( isset( $cache[ $post_id ] ) ) {
return $cache[ $post_id ];
}
$cache[ $post_id ] = false;
if ( is_admin() ) {
return false;
}
if ( is_customize_preview() ) {
return false;
}
if ( ! current_user_can( 'edit_posts' ) ) {
return false;
}
if ( ! et_pb_is_pagebuilder_used( $post_id ) && ! et_fb_is_theme_builder_used_on_page() ) {
return false;
}
if ( is_singular() && ! current_user_can( 'edit_post', $post_id ) ) {
return false;
}
if ( ! is_singular() && ! et_pb_is_allowed( 'theme_builder' ) ) {
return false;
}
if ( ! et_pb_is_allowed( 'use_visual_builder' ) ) {
return false;
}
$cache[ $post_id ] = true;
return true;
}
endif;
if ( ! function_exists( 'et_fb_is_enabled_on_any_template' ) ) :
/**
* Determine fb enabled status of a post / page or any theme builder layout used in the page.
*
* @internal NOTE: Don't use this from outside builder code! {@see et_core_is_fb_enabled()}.
*
* @return bool
*/
function et_fb_is_enabled_on_any_template() {
$theme_builder_layouts = et_theme_builder_get_template_layouts();
// Unset main template from Theme Builder layouts to avoid PHP Notices.
if ( isset( $theme_builder_layouts['et_template'] ) ) {
unset( $theme_builder_layouts['et_template'] );
}
// Check if Builder is enabled on any Theme Builder Layout used.
foreach ( $theme_builder_layouts as $key => $theme_builder_layout ) {
if ( $theme_builder_layout['enabled'] && $theme_builder_layout['override'] ) {
if ( et_fb_is_enabled( $theme_builder_layout['id'] ) ) {
return true;
}
}
}
return false;
}
endif;
if ( ! function_exists( 'et_fb_is_theme_builder_used_on_page' ) ) :
/**
* Check if Theme Builder is Used on the page.
*
* @return bool
*/
function et_fb_is_theme_builder_used_on_page() {
$theme_builder_layouts = et_theme_builder_get_template_layouts();
// Unset main template from Theme Builder layouts to avoid PHP Notices.
if ( isset( $theme_builder_layouts['et_template'] ) ) {
unset( $theme_builder_layouts['et_template'] );
}
// If any template is used and enabled return true.
foreach ( $theme_builder_layouts as $theme_builder_layout ) {
if ( $theme_builder_layout['enabled'] && $theme_builder_layout['override'] ) {
return true;
}
}
return false;
}
endif;
if ( ! function_exists( 'et_fb_is_builder_ajax' ) ) :
/**
* Returns whether current request is a builder AJAX call.
*
* @return bool
*/
function et_fb_is_builder_ajax() {
// phpcs:disable WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF.
if ( ! wp_doing_ajax() || empty( $_REQUEST['action'] ) ) {
return false;
}
return in_array(
$_REQUEST['action'],
array(
'et_fb_update_builder_assets',
'et_fb_retrieve_builder_data',
),
true
);
// phpcs:enable
}
endif;
if ( ! function_exists( 'et_fb_is_computed_callback_ajax' ) ) :
/**
* Returns whether current request is computed callback AJAX call
*
* @return bool
*/
function et_fb_is_computed_callback_ajax() {
// phpcs:disable WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF.
if ( ! wp_doing_ajax() || empty( $_REQUEST['action'] ) ) {
return false;
}
return 'et_pb_process_computed_property' === $_REQUEST['action'];
// phpcs:enable
}
endif;
if ( ! function_exists( 'et_fb_is_before_after_components_callback_ajax' ) ) :
/**
* Returns whether current request is before & after components callback AJAX call.
*
* @since 4.14.5
*
* @return bool
*/
function et_fb_is_before_after_components_callback_ajax() {
// phpcs:disable WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF.
$action = ! empty( $_REQUEST['action'] ) ? sanitize_text_field( $_REQUEST['action'] ) : '';
return wp_doing_ajax() && 'et_fb_fetch_before_after_components' === $action;
// phpcs:enable
}
endif;
if ( ! function_exists( 'et_fb_is_resolve_post_content_callback_ajax' ) ) :
/**
* Returns whether current request is resolve post content callback AJAX call
*
* @return bool
*/
function et_fb_is_resolve_post_content_callback_ajax() {
// phpcs:disable WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF.
if ( ! wp_doing_ajax() || empty( $_REQUEST['action'] ) ) {
return false;
}
return 'et_builder_resolve_post_content' === $_REQUEST['action'];
// phpcs:enable
}
endif;
if ( ! function_exists( 'et_fb_auto_activate_builder' ) ) :
/**
* FB :: enable page with no BB activated to directly use visual builder by clicking use visual builder link on WP admin bar.
*/
function et_fb_auto_activate_builder() {
$post_id = get_the_ID();
if (
! is_admin() &&
$post_id &&
current_user_can( 'edit_post', $post_id ) &&
isset( $_GET['et_fb_activation_nonce'] ) &&
wp_verify_nonce( sanitize_text_field( $_GET['et_fb_activation_nonce'] ), 'et_fb_activation_nonce_' . get_the_ID() )
) {
$set_content = et_builder_set_content_activation( $post_id );
$post_url = get_permalink( $post_id );
$redirect_url = $set_content ? et_fb_get_vb_url( $post_url ) : $post_url;
wp_safe_redirect( $redirect_url );
exit();
}
}
endif;
add_action( 'template_redirect', 'et_fb_auto_activate_builder' );
/**
* Enable the VB for a post.
*
* @since 4.0
*
* @param integer $post_id The post id for which to enable VB.
* @param bool $show_page_creation Whether to show page creation card.
*
* @return bool Success.
*/
function et_builder_enable_for_post( $post_id, $show_page_creation = true ) {
$_post = get_post( $post_id );
if ( ! $post_id || ! $_post || ! is_object( $_post ) ) {
return false;
}
$activate_builder = update_post_meta( $post_id, '_et_pb_use_builder', 'on' );
if ( false === $activate_builder ) {
return false;
}
update_post_meta( $post_id, '_et_pb_show_page_creation', $show_page_creation ? 'on' : 'off' );
return true;
}
/**
* Set builder content at the time of enabling visual builder.
*
* @param bool|integer $post_id The post id on which visual builder get enable.
*
* @return bool
*/
function et_builder_set_content_activation( $post_id = false ) {
$_post = get_post( $post_id );
if ( ! $post_id || ! $_post || ! is_object( $_post ) ) {
return false;
}
$activate_builder = et_builder_enable_for_post( $post_id );
if ( false === $activate_builder ) {
return false;
}
// If content already has a section (not as divi/layout block attribute), it means builder is
// active and activation has to be skipped to avoid nested and unwanted builder structure.
if ( ! has_block( 'divi/layout', $post_id ) && has_shortcode( $_post->post_content, 'et_pb_section' ) ) {
return true;
}
// `update_post_meta()`'s saved value is run through `stripslashes()` which makes encoded
// shortcode on layout block's `layoutContent` attributes looses its slashes when being saved.
// To fix this, If saved content has layout block, add one more slash using `wp_slash()`.
// NOTE:`$new_old_content` parameter is meant to be used as `update_post_meta()` parameter only
// {@see https://codex.wordpress.org/Function_Reference/update_post_meta#Character_Escaping}
$content_has_layout_block = has_block( 'divi/layout', $_post->post_content );
$new_old_content = $_post->post_content;
// Save old content.
$saved_old_content = get_post_meta( $post_id, '_et_pb_old_content', true );
$save_old_content = update_post_meta( $post_id, '_et_pb_old_content', $new_old_content );
/**
* Filters the flag that sets default Content during Builder activation.
*
* @since 3.29
*
* @param bool $is_skip_content_activation TRUE skips the content activation.
* @param WP_Post $_post The Post.
*
* @used-by et_builder_wc_init()
*/
if ( apply_filters( 'et_builder_skip_content_activation', false, $_post ) ) {
return true;
}
if ( false === $save_old_content && $saved_old_content !== $_post->post_content && '' !== $_post->post_content ) {
return false;
}
$text_module = '' !== $_post->post_content ? '[et_pb_text admin_label="Text"]' . $_post->post_content . '[/et_pb_text]' : '';
if ( has_block( 'divi/layout', $post_id ) ) {
$updated_content = et_builder_convert_block_to_shortcode( $_post->post_content );
} else {
// Re-format content.
$updated_content = '[et_pb_section admin_label="section"]
[et_pb_row admin_label="row"]
[et_pb_column type="4_4"]' . $text_module . '[/et_pb_column]
[/et_pb_row]
[/et_pb_section]';
}
// Update post_content.
$_post->post_content = $updated_content;
// Update post.
$update_post = wp_update_post( $_post );
if ( 0 < $update_post ) {
setup_postdata( $_post );
}
return 0 < $update_post;
}
if ( ! function_exists( 'et_builder_get_font_family' ) ) :
/**
* Load font family.
*
* @param string $font_name Font name slug.
* @param bool $use_important Whether to use !important in font-family css property.
*
* @return string
*/
function et_builder_get_font_family( $font_name, $use_important = false ) {
$is_global_font = in_array( $font_name, array( '--et_global_heading_font', '--et_global_body_font' ), true );
$font_name = $is_global_font ? '--et_global_heading_font' === $font_name ? et_get_option( 'heading_font', '' ) : et_get_option( 'body_font', '' ) : $font_name;
$user_fonts = et_builder_get_custom_fonts();
$fonts = isset( $user_fonts[ $font_name ] ) ? $user_fonts : et_builder_get_fonts();
$removed_fonts_mapping = et_builder_old_fonts_mapping();
$font_style = '';
$font_weight = '';
$font_name_ms = isset( $fonts[ $font_name ] ) && isset( $fonts[ $font_name ]['add_ms_version'] ) ? "'{$font_name} MS', " : '';
if ( isset( $removed_fonts_mapping[ $font_name ] ) && isset( $removed_fonts_mapping[ $font_name ]['parent_font'] ) ) {
$font_style = $removed_fonts_mapping[ $font_name ]['styles'];
$font_name = $removed_fonts_mapping[ $font_name ]['parent_font'];
}
if ( '' !== $font_style ) {
$is_global_font_weigth = in_array( $font_style, array( '--et_global_heading_font_weight', '--et_global_body_font_weight' ), true );
$font_weight_value = $is_global_font_weigth ? '--et_global_heading_font_weight' === $font_style ? et_get_option( 'heading_font_weight', '' ) : et_get_option( 'body_font_weight', '' ) : $font_style;
$font_weight = sprintf( ' font-weight: %1$s;', esc_html( $font_style ) );
}
$style = sprintf(
'font-family: \'%1$s\', %5$s%2$s%3$s;%4$s',
esc_html( $font_name ),
isset( $fonts[ $font_name ] ) ? et_builder_get_websafe_font_stack( $fonts[ $font_name ]['type'] ) : 'sans-serif',
( $use_important ? ' !important' : '' ),
$font_weight,
$font_name_ms
);
return $style;
}
endif;
if ( ! function_exists( 'et_builder_get_fonts' ) ) :
/**
* Return websafe and google font list.
*
* @param array $settings {
* Font settings.
* @type string $prepend_standard_fonts Whether to prepend or append websafe fonts in returned list.
* }
*
* @return array
*/
function et_builder_get_fonts( $settings = array() ) {
// Only return websafe fonts if google fonts disabled.
if ( ! et_core_use_google_fonts() ) {
return et_builder_get_websafe_fonts();
}
$defaults = array(
'prepend_standard_fonts' => true,
);
$settings = wp_parse_args( $settings, $defaults );
$fonts = $settings['prepend_standard_fonts']
? array_merge( et_builder_get_websafe_fonts(), et_builder_get_google_fonts() )
: array_merge( et_builder_get_google_fonts(), et_builder_get_websafe_fonts() );
ksort( $fonts );
return $fonts;
}
endif;
if ( ! function_exists( 'et_builder_get_websafe_font_stack' ) ) :
/**
* Return websafe font stack.
*
* @param string $type the font stack type.
*
* @return string
*/
function et_builder_get_websafe_font_stack( $type = 'sans-serif' ) {
$font_stack = $type;
switch ( $type ) {
case 'sans-serif':
$font_stack = 'Helvetica, Arial, Lucida, sans-serif';
break;
case 'serif':
$font_stack = 'Georgia, "Times New Roman", serif';
break;
case 'cursive':
$font_stack = 'cursive';
break;
}
return $font_stack;
}
endif;
if ( ! function_exists( 'et_builder_get_websafe_fonts' ) ) :
/**
* Return websafe fonts list.
*/
function et_builder_get_websafe_fonts() {
return et_core_get_websafe_fonts();
}
endif;
if ( ! function_exists( 'et_builder_get_font_weight_list' ) ) :
/**
* Return font weight list.
*/
function et_builder_get_font_weight_list() {
$default_font_weights_list = array(
'100' => esc_html__( 'Thin', 'et_builder' ),
'200' => esc_html__( 'Ultra Light', 'et_builder' ),
'300' => et_builder_i18n( 'Light' ),
'400' => esc_html__( 'Regular', 'et_builder' ),
'500' => esc_html__( 'Medium', 'et_builder' ),
'600' => esc_html__( 'Semi Bold', 'et_builder' ),
'700' => esc_html__( 'Bold', 'et_builder' ),
'800' => esc_html__( 'Ultra Bold', 'et_builder' ),
'900' => esc_html__( 'Heavy', 'et_builder' ),
);
return apply_filters( 'et_builder_all_font_weights', $default_font_weights_list );
}
endif;
/**
* Retrieve list of uploaded user fonts stored in `et_uploaded_fonts` option.
*
* @since 3.0
*
* @return array fonts list
*/
if ( ! function_exists( 'et_builder_get_custom_fonts' ) ) :
/**
* Return user uploaded custom fonts.
*
* @return array
*/
function et_builder_get_custom_fonts() {
$all_custom_fonts = get_option( 'et_uploaded_fonts', array() );
// Convert any falsey value to empty array to avoid PHP errors.
if ( ! is_array( $all_custom_fonts ) ) {
$all_custom_fonts = array();
}
return (array) apply_filters( 'et_builder_custom_fonts', $all_custom_fonts );
}
endif;
/**
* Return old(removed) fonts mapping.
*
* @return array
*/
function et_builder_old_fonts_mapping() {
return array(
'Raleway Light' => array(
'parent_font' => 'Raleway',
'styles' => '300',
),
'Roboto Light' => array(
'parent_font' => 'Roboto',
'styles' => '100',
),
'Source Sans Pro Light' => array(
'parent_font' => 'Source Sans Pro',
'styles' => '300',
),
'Lato Light' => array(
'parent_font' => 'Lato',
'styles' => '300',
),
'Open Sans Light' => array(
'parent_font' => 'Open Sans',
'styles' => '300',
),
);
}
if ( ! function_exists( 'et_builder_google_fonts_sync' ) ) :
/**
* Sync Google Fonts. Clear font cache every 24 hours.
*/
function et_builder_google_fonts_sync() {
$google_api_key = et_pb_get_google_api_key();
// Bail early if 'fonts_cache_status' transient is not expired.
if ( false !== get_transient( 'fonts_cache_status' ) ) {
return;
}
// Bail early if Google API Key is empty or Google Fonts is disabled.
if ( '' === $google_api_key || ! et_core_use_google_fonts() ) {
return;
}
// Set 'fonts_cache_status' transient to true, marking the font cache update attempt to avoid making the request more than once a day in case of an error.
set_transient( 'fonts_cache_status', true, 24 * HOUR_IN_SECONDS );
$google_fonts_api_url = sprintf( 'https://www.googleapis.com/webfonts/v1/webfonts?key=%1$s', $google_api_key );
$google_fonts_response = wp_remote_get( esc_url_raw( $google_fonts_api_url ) );
// Check if the response is an array and we have a valid 200 response, otherwise log an error.
if ( is_array( $google_fonts_response ) && 200 === $google_fonts_response['response']['code'] ) {
$google_fonts_json = wp_remote_retrieve_body( $google_fonts_response );
$google_fonts_json = et_core_parse_google_fonts_json( $google_fonts_json );
if ( ! empty( $google_fonts_json ) ) {
// Save Google Fonts Data, if it's not empty.
update_option( 'et_google_fonts_cache', $google_fonts_json );
}
} else {
et_debug( 'An unkown error has occured while trying to retrieve the fonts from the Google Fonts API. Please ensure your Google API Key is valid and active.' );
return;
}
}
endif;
if ( ! function_exists( 'et_builder_get_google_fonts' ) ) :
/**
* Return google fonts.
*/
function et_builder_get_google_fonts() {
// Google Fonts disabled.
if ( ! et_core_use_google_fonts() ) {
return array();
}
et_builder_google_fonts_sync();
$google_fonts_cache = get_option( 'et_google_fonts_cache', array() );
$google_fonts_cache = is_array( $google_fonts_cache ) ? $google_fonts_cache : et_core_parse_google_fonts_json( $google_fonts_cache );
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() );
}
endif;
/**
* 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
* ajax-admin.php nature.
*
* @param string $name conditional tag name.
* @param array $conditional_tags 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 'true' === $conditional_tags[ $name ] ? 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 ( ! isset( $_POST['et_fb_retrieve_library_modules_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( $_POST['et_fb_retrieve_library_modules_nonce'] ), 'et_fb_retrieve_library_modules_nonce' ) ) {
die( -1 );
}
if ( ! current_user_can( 'edit_posts' ) ) {
die( -1 );
}
$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.
$post_type = 'post';
}
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 );
} else {
$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;
$next_page = 'none';
if ( 0 !== $start_from && empty( $templates_data ) ) {
$templates_data_processed = array();
} else {
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' ) );
} else {
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'] ) ? sanitize_text_field( $_POST['post_type'] ) : false;
if ( empty( $post_type ) || ! isset( $post_types[ $post_type ] ) ) {
wp_send_json_error();
}
$posts_list = array();
$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' );
if ( empty( $posts ) ) {
wp_send_json_error();
}
foreach ( $posts as $post ) {
// Check if page builder is activated.
if ( ! et_pb_is_pagebuilder_used( $post->ID ) ) {
continue;
}
// Only include posts that the user is allowed to edit.
if ( ! current_user_can( 'edit_post', $post->ID ) ) {
continue;
}
// Skip for post has no title.
if ( empty( $post->post_title ) ) {
continue;
}
$posts_list[ $post->ID ] = array(
'id' => $post->ID,
'title' => $post->post_title,
'link' => array(
'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' );
/**
* Return supported font formats.
*
* @return mixed|void
*/
function et_pb_get_supported_font_formats() {
return apply_filters( 'et_pb_supported_font_formats', array( 'ttf', 'otf' ) );
}
/***
* AJAX Callback :: Process uploaded custom font.
*/
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 );
$fonts_array = array();
foreach ( $supported_font_files as $format ) {
if ( isset( $_FILES[ 'et_pb_font_file_' . $format ] ) ) {
// phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- This is file input.
$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' ) ) :
/**
* Save droploaded images to WP Media Library.
*/
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 ) ) {
die( -1 );
}
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 );
}
endif;
add_action( 'wp_ajax_et_builder_droploader_process', 'et_builder_droploader_process' );
/**
* Add allowed mime types and file extensions for font files.
*
* @return array
*/
function et_pb_filter_upload_mimes_custom_fonts() {
return array(
'otf' => 'application/x-font-opentype',
'ttf' => 'application/x-font-ttf',
'woff' => 'application/font-woff',
'woff2' => 'application/font-woff2',
'eot' => 'application/vnd.ms-fontobject',
);
}
/**
* Save the font-file.
*
* @param array $font_files font files.
* @param string $font_name font name.
* @param array $font_settings font settings.
*
* @return array
*/
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 = '';
$uploaded_files = array(
'font_file' => array(),
'font_url' => array(),
);
foreach ( $font_files as $ext => $font_file ) {
// Try to upload font file.
// phpcs:ignore ET.Sniffs.DangerousFunctions.ET_handle_upload -- test_type is enabled and proper type and extension checking are implemented.
$upload = wp_handle_upload(
$font_file,
array(
'test_size' => false,
'test_form' => false,
'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'] ) ) {
// phpcs:ignore ET.Sniffs.DangerousFunctions.ET_handle_upload -- test_type is enabled and proper type and extension checking are implemented.
$upload = wp_handle_upload(
$font_file,
array(
'test_size' => false,
'test_form' => false,
'mimes' => array(
'otf' => $mime_type,
),
)
);
}
}
}
if ( ! empty( $upload['error'] ) ) {
$uploaded_files_error = $upload['error'];
} else {
$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,
);
}
/**
* Remove custom font.
*
* @param string $font_name Font name to remove.
*
* @return array
*/
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 );
}
} else {
$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,
);
}
/**
* Delete a font file.
*
* @param string $font_file font file path.
*
* @return bool
*/
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 ) ) {
return false;
}
// 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, true ) ) {
return false;
}
// 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 ) ) {
return false;
}
// now that all checks have passed, the file can be safely deleted.
return unlink( $file_realpath );
}
/**
* Set fonts upload dir.
*
* @param array $directory directory path.
*
* @return mixed
*/
function et_pb_set_fonts_upload_dir( $directory ) {
$directory['path'] = $directory['basedir'] . '/et-fonts';
$directory['url'] = $directory['baseurl'] . '/et-fonts';
$directory['subdir'] = '/et-fonts';
return $directory;
}
/**
* Return unsynced global settings,
*
* @param string $post_type Post type.
* @param array $shortcode_data Shortcode data.
*
* @return array
*/
function et_pb_get_unsynced_legacy_options( $post_type, $shortcode_data ) {
if ( ! isset( $shortcode_data['attrs']['saved_tabs'] ) && 'all' === $shortcode_data['attrs']['saved_tabs'] ) {
return array();
}
// get all options.
$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.
*
* @param string $link The link to be be prepared for ssl.
*
* @return string|string[]
*/
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 );
}
return $link;
}
if ( ! function_exists( 'et_fb_get_builder_url' ) ) :
/**
* Create a VB/BFB url.
*
* @param string $url Post url.
* @param string $builder 'vb' or 'bfb'.
* @param bool $is_new_page Whether the page is new or not.
* @param bool $custom_page_id page id.
*
* @return string.
*/
function et_fb_get_builder_url( $url = false, $builder = 'vb', $is_new_page = false, $custom_page_id = false ) {
$args = array(
'et_fb' => '1',
'et_bfb' => 'bfb' === $builder ? '1' : false,
'PageSpeed' => 'off',
);
if ( 'bfb' === $builder && $is_new_page ) {
global $post;
$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) get_option( 'page_for_posts' ) === (int) $custom_page_id ? (int) $custom_page_id : 'empty';
// phpcs:ignore WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF.
$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';
if ( $custom_page_id ) {
$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_id = get_the_ID();
$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(), true );
$is_unqueryable = $is_third_party_post_type && in_array(
$post_type,
get_post_types( array( 'publicly_queryable' => false ) ),
true
);
// These post id & post type query strings should only be added if current post type
// has false publicly_queryable setting.
if ( $post_id && $post_type && is_user_logged_in() && $is_unqueryable ) {
$args['et_post_id'] = $post_id;
$args['et_post_id_nonce'] = wp_create_nonce( 'et_post_id_' . $post_id );
$args['et_post_type'] = $post_type;
$args['et_post_type_nonce'] = wp_create_nonce( 'et_post_type_' . $post_type );
}
}
return add_query_arg( $args, et_fb_prepare_ssl_link( $url ? $url : get_the_permalink() ) );
}
endif;
if ( ! function_exists( 'et_fb_get_vb_url' ) ) :
/**
* Create a VB url.
*
* @param string $url Post url.
* @return string.
*/
function et_fb_get_vb_url( $url = false ) {
return et_fb_get_builder_url( $url );
}
endif;
if ( ! function_exists( 'et_fb_get_bfb_url' ) ) :
/**
* Create a BFB url.
*
* @param string $url Post url.
* @param bool $is_new_page Whether the page is new or not.
* @param bool $custom_page_id page id.
*
* @return string.
*/
function et_fb_get_bfb_url( $url = false, $is_new_page = false, $custom_page_id = false ) {
return et_fb_get_builder_url( $url, 'bfb', $is_new_page, $custom_page_id );
}
endif;
if ( ! function_exists( 'et_builder_options' ) ) :
/**
* Filterable options for backend and visual builder. Designed to be filtered
* by theme/plugin since builder is shared accross Divi, Extra, and Divi Builder.
*
* @return array builder options values
*/
function et_builder_options() {
return apply_filters(
'et_builder_options',
array(
'all_buttons_icon' => 'yes', // Default appearance of button icon.
)
);
}
endif;
if ( ! function_exists( 'et_builder_option' ) ) :
/**
* Get specific builder option (fetched from et_builder_options()).
*
* @param string $name option name.
* @return mixed builder option value
*/
function et_builder_option( $name ) {
$options = et_builder_options();
$option = isset( $options[ $name ] ) ? $options[ $name ] : false;
return apply_filters( "et_builder_option_{$name}", $option );
}
endif;
/**
* Pass thru semantical previously escaped acknowledgement
*
* @deprecated {@see et_core_esc_previously()}
*
* @since 3.17.1 Deprecated
*
* @param string $passthru value being passed through.
* @return string
*/
function et_esc_previously( $passthru ) {
et_debug( "You're Doing It Wrong! Attempted to call " . __FUNCTION__ . '(), use et_core_esc_previously() instead.' );
return $passthru;
}
/**
* Pass thru semantical escaped by WordPress core acknowledgement
*
* @deprecated {@see et_core_esc_wp()}
*
* @since 3.17.1 Deprecated
*
* @param string $passthru value being passed through.
*
* @return string
*/
function et_esc_wp( $passthru ) {
et_debug( "You're Doing It Wrong! Attempted to call " . __FUNCTION__ . '(), use et_core_esc_wp() instead.' );
return $passthru;
}
/**
* Pass thru semantical intentionally unescaped acknowledgement.
*
* @deprecated {@see et_core_intentionally_unescaped()}
*
* @since 3.17.1 Deprecated
*
* @param string $passthru value being passed through.
* @param string $excuse excuse the value is allowed to be unescaped.
* @return string
*/
function et_intentionally_unescaped( $passthru, $excuse ) {
et_debug( "You're Doing It Wrong! Attempted to call " . __FUNCTION__ . '(), use et_core_intentionally_unescaped() instead.' );
// Add valid excuses as they arise.
$valid_excuses = array(
'cap_based_sanitized',
'fixed_string',
'react_jsx',
'underscore_template',
);
if ( ! in_array( $excuse, $valid_excuses, true ) ) {
et_debug( "You're Doing It Wrong! This is not a valid excuse to not escape the passed value." );
}
return $passthru;
}
/**
* Sanitize value depending on user capability.
*
* @deprecated {@see et_core_sanitize_value_by_cap()}
*
* @since 3.17.1 Deprecated
*
* @param string $passthru value being passed through.
* @param callable $sanitize_function santization function.
* @param string $cap WP capability name.
*
* @return string value being passed through.
*/
function et_sanitize_value_by_cap( $passthru, $sanitize_function = 'et_sanitize_html_input_text', $cap = 'unfiltered_html' ) {
et_debug( "You're Doing It Wrong! Attempted to call " . __FUNCTION__ . '(), use et_core_sanitize_value_by_cap() instead.' );
if ( ! current_user_can( $cap ) ) {
$passthru = $sanitize_function( $passthru );
}
return $passthru;
}
/**
* Pass thru semantical intentionally unsanitized acknowledgement.
*
* @deprecated {@see et_core_intentinally_unsanitized()}
*
* @since 3.17.1 Deprecated
*
* @param string $passthru value being passed through.
* @param string $excuse excuse the value is allowed to be unsanitized.
* @return string
*/
function et_intentionally_unsanitized( $passthru, $excuse ) {
et_debug( "You're Doing It Wrong! Attempted to call " . __FUNCTION__ . '(), use et_core_intentionally_unsanitized() instead.' );
// Add valid excuses as they arise.
$valid_excuses = array();
if ( ! in_array( $excuse, $valid_excuses, true ) ) {
et_debug( "You're Doing It Wrong! This is not a valid excuse to not sanitize the passed value." );
}
return $passthru;
}
/**
* Prevent delimiter-separated string from having duplicate item.
*
* @param string $string_list delimiter-separated string.
* @param string $delimiter delimiter.
* @return string filtered delimiter-separated string.
*/
function et_prevent_duplicate_item( $string_list, $delimiter ) {
$list = explode( $delimiter, $string_list );
return implode( $delimiter, array_unique( $list ) );
}
/**
* Determining whether unminified scripts should be loaded or not.
*
* @since 4.6.2 Removes static $should_load to ensure it's filtered with latest value.
*
* @return bool
*
* @deprecated ??
*/
function et_load_unminified_scripts() {
$is_script_debug = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG;
return apply_filters( 'et_load_unminified_scripts', $is_script_debug );
}
/**
* Determining whether unminified styles should be loaded or not
*
* @since 4.6.2 Removes static $should_load to ensure it's filtered with latest value.
*
* @deprecated ??
*/
function et_load_unminified_styles() {
$is_script_debug = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG;
return apply_filters( 'et_load_unminified_styles', $is_script_debug );
}
/**
* Enable / Disable classic editor based on saved option in Theme Options page.
* Only applies to versions of WordPress that have the Gutenberg editor.
*
* @since 3.18
*
* @param bool $enable Whether to enable or disable.
*
* @return bool
*/
function et_builder_enable_classic_editor( $enable ) {
if ( 'on' === et_get_option( 'et_enable_classic_editor', 'off' ) ) {
return true;
}
return $enable;
}
if ( version_compare( $GLOBALS['wp_version'], '5.0-beta', '>=' ) ) {
add_filter( 'et_builder_enable_classic_editor', 'et_builder_enable_classic_editor' );
}
/**
* Check whether the BFB is enabled.
*
* @since 3.18
*
* @return bool
*/
function et_builder_bfb_enabled() {
return apply_filters( 'et_builder_bfb_enabled', false );
}
/**
* Check whether BFB is activated for this site or not.
*
* @since 3.28
*
* @return bool
*/
function et_builder_bfb_activated() {
$bfb_settings = get_option( 'et_bfb_settings' );
$enabled = isset( $bfb_settings['enable_bfb'] ) && 'on' === $bfb_settings['enable_bfb'];
return $enabled;
}
/**
* Check whether the VB is loaded through TB.
*
* @since 4.0
*
* @return bool
*/
function et_builder_tb_enabled() {
// Layout Block uses abstracted visual builder on modal originally introduced in TB. However,
// TB needs different Divi capability, hence adjust it for Layout Block Builder.
$is_layout_block = ET_GB_Block_Layout::is_layout_block_preview();
$builder_capability = $is_layout_block ? 'use_visual_builder' : 'theme_builder';
// phpcs:ignore WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF.
return et_core_is_fb_enabled() && et_pb_is_allowed( $builder_capability ) && isset( $_GET['et_tb'] ) && '1' === $_GET['et_tb'];
}
/**
* Check if the current screen is the Theme Builder administration screen.
*
* @since 4.0
*
* @return bool
*/
function et_builder_is_tb_admin_screen() {
global $pagenow;
// phpcs:ignore WordPress.Security.NonceVerification -- this is generic read-only, bool returning helper function, and not a state changing action that is susceptible to CSRF attack.
return is_admin() && 'admin.php' === $pagenow && isset( $_GET['page'] ) && 'et_theme_builder' === $_GET['page'];
}
/**
* Check if the current screen is the Divi Onboarding administration screen.
*
* @since 4.26.0
*
* @return bool
*/
function et_builder_is_et_onboarding_page() {
// phpcs:ignore WordPress.Security.NonceVerification -- this is generic read-only, bool returning helper function, and not a state changing action that is susceptible to CSRF attack.
return is_admin() && isset( $_GET['page'] ) && 'et_onboarding' === $_GET['page'];
}
if ( ! function_exists( 'et_builder_filter_bfb_enabled' ) ) :
/**
* Theme implementation for BFB enabled check.
*
* @since 3.18
*
* @return bool
*/
function et_builder_filter_bfb_enabled() {
global $pagenow;
$enabled = et_builder_bfb_activated();
// phpcs:disable WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF.
if ( is_admin() && ! in_array( $pagenow, array( 'post.php', 'post-new.php', 'admin-ajax.php' ), true ) ) {
$enabled = false;
} elseif ( ! is_admin() && ! isset( $_GET['et_bfb'] ) ) {
$enabled = false;
} elseif ( ! et_pb_is_allowed( 'use_visual_builder' ) ) {
$enabled = false;
}
// phpcs:enable
return $enabled;
}
endif;
if ( ! function_exists( 'et_builder_is_fresh_install' ) ) :
/**
* Get whether the builder is freshly installed.
*
* @since 3.18
*
* @return bool
*/
function et_builder_is_fresh_install() {
return apply_filters( 'et_builder_is_fresh_install', false );
}
endif;
if ( ! function_exists( 'et_builder_filter_is_fresh_install' ) ) :
/**
* Theme implementation for fresh install check.
*
* @since 3.18
*
* @return bool
*/
function et_builder_filter_is_fresh_install() {
global $shortname;
return false === et_get_option( $shortname . '_logo' );
}
endif;
/**
* Determine whether current request is AJAX request for loading BB data
*
* @since 3.28
*
* @todo remove & replace this function with `et_builder_is_loading_data()` once PR #6325 is merged
*
* @return bool
*/
function et_builder_is_loading_bb_data() {
// phpcs:ignore WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF.
return isset( $_POST['action'] ) && in_array( $_POST['action'], array( 'et_pb_get_backbone_templates', 'et_pb_get_backbone_template' ), true );
}
/**
* Determine whether current request is classic builder (BB) edit page.
*
* @since 3.28
*
* @return bool
*/
function et_builder_is_bb_page() {
// BB Is definitely on backend.
if ( ! is_admin() ) {
return false;
}
// BB page is on either post new or edit post page in backend.
global $pagenow;
if ( ! in_array( $pagenow, array( 'post.php', 'post-new.php' ), true ) ) {
return false;
}
// If BFB is activated, this is definitely not BB page.
if ( et_builder_bfb_enabled() ) {
return false;
}
// Check if current post type has builder activated.
// phpcs:disable WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF.
if ( 'post-new.php' === $pagenow ) {
$post_type = isset( $_GET['post_type'] ) ? sanitize_text_field( $_GET['post_type'] ) : 'post';
} else {
$post_id = isset( $_GET['post'] ) ? sanitize_text_field( $_GET['post'] ) : false;
$post_type = get_post_type( $post_id );
}
// phpcs:enable
return et_builder_enabled_for_post_type( $post_type );
}
if ( ! function_exists( 'et_builder_toggle_bfb' ) ) :
/**
* Toggle BFB.
*
* @since 3.18
*
* @param bool $enable Whether to enable or disable bfb.
*
* @return void
*/
function et_builder_toggle_bfb( $enable ) {
do_action( 'et_builder_toggle_bfb', $enable );
}
endif;
if ( ! function_exists( 'et_builder_action_toggle_bfb' ) ) :
/**
* Theme implementation for BFB toggle.
*
* @since 3.18
*
* @param bool $enable Whether to enable or disable BFB.
*
* @return void
*/
function et_builder_action_toggle_bfb( $enable ) {
$bfb_value = $enable ? 'on' : 'off';
et_update_option( '', $bfb_value, true, 'et_bfb_settings', 'enable_bfb' );
}
endif;
if ( ! function_exists( 'et_builder_show_bfb_welcome_modal' ) ) :
/**
* Show the BFB welcome modal.
*
* @since 3.18
*
* @return void
*/
function et_builder_show_bfb_welcome_modal() {
global $pagenow;
// Cancel if BFB is not enabled yet.
if ( ! et_builder_bfb_enabled() ) {
return;
}
// Cancel if current request is not editing screen.
if ( ! in_array( $pagenow, array( 'post.php', 'post-new.php' ), true ) ) {
return;
}
// Cancel if current edit screen use Gutenberg. `use_block_editor_for_post_type()` was added
// after v5.0 so check for its existance first in case current WP version is below 5.0.
if ( function_exists( 'use_block_editor_for_post_type' ) && use_block_editor_for_post_type( get_post_type() ) ) {
return;
}
// Cancel if current edit screen doesn't use builder.
if ( ! et_pb_is_pagebuilder_used() ) {
return;
}
// Cancel if assigned transient doesn't exist.
if ( ! get_transient( 'et_builder_show_bfb_welcome_modal' ) ) {
return;
}
// Clear Builder assets cache to avoid double reloading of BFB after theme update.
et_fb_delete_builder_assets();
// Clear Builder assets cache to avoid double reloading of BFB after theme update.
et_fb_delete_builder_assets();
delete_transient( 'et_builder_show_bfb_welcome_modal' );
?>
);
' : '';
return sprintf(
'
%3$s
',
esc_attr( $outer_id ),
esc_attr( $outer_classes ),
et_core_intentionally_unescaped( $dbp_compat_wrapper_open, 'fixed_string' )
);
}
/**
* Get the opening wrappers for individual builder-powered layouts.
*
* @since 4.0
*
* @return string
*/
function et_builder_get_layout_opening_wrapper( $post_type = '' ) {
$post_type = ! empty( $post_type ) ? $post_type : get_post_type();
$layout_class = array( 'et-l' );
$el = 'div';
$layout_id = '';
switch ( $post_type ) {
case ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE:
$el = 'header';
$layout_class[] = 'et-l--header';
break;
case ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE:
$layout_class[] = 'et-l--body';
break;
case ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE:
$el = 'footer';
$layout_class[] = 'et-l--footer';
break;
default:
$layout_class[] = 'et-l--post';
break;
}
$layout_id = apply_filters( 'et_builder_layout_id', $layout_id, $post_type );
$layout_id = ! empty( $layout_id ) ? sprintf( 'id="%s" ', esc_attr( $layout_id ) ) : '';
$layout_class = apply_filters( 'et_builder_layout_class', $layout_class );
$layout_classes = implode( ' ', $layout_class );
$inner_class = apply_filters( 'et_builder_inner_content_class', array( 'et_builder_inner_content' ) );
$inner_classes = implode( ' ', $inner_class );
return sprintf(
'<%3$s %4$sclass="%1$s">
',
esc_attr( $layout_classes ),
esc_attr( $inner_classes ),
esc_attr( $el ),
et_core_esc_previously( $layout_id )
);
}
/**
* Get the closing wrappers for individual builder-powered layouts.
*
* @since 4.0
*
* @return string
*/
function et_builder_get_layout_closing_wrapper( $post_type = '' ) {
$post_type = ! empty( $post_type ) ? $post_type : get_post_type();
$el = 'div';
switch ( $post_type ) {
case ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE:
$el = 'header';
break;
case ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE:
$el = 'footer';
break;
}
return sprintf(
'
%1$s>
',
esc_attr( $el )
);
}
/**
* Get the closing wrappers for builder-powered content.
*
* @since 4.0
*
* @return string
*/
function et_builder_get_builder_content_closing_wrapper() {
$is_dbp = et_is_builder_plugin_active();
$dbp_compat_wrapper_close = $is_dbp ? '
' : '';
return sprintf(
'
%1$s
',
et_core_intentionally_unescaped( $dbp_compat_wrapper_close, 'fixed_string' )
);
}
/**
* Wrap post builder content.
*
* @since 3.10
*
* @param string $content The post content.
*
* @return string
*/
function et_builder_add_builder_content_wrapper( $content ) {
// phpcs:ignore WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF.
$is_bfb_new_page = isset( $_GET['is_new_page'] ) && '1' === $_GET['is_new_page'];
$has_layout_block = has_block( 'divi/layout', get_the_ID() );
if ( ! et_pb_is_pagebuilder_used( get_the_ID() ) && ! is_et_pb_preview() && ! $is_bfb_new_page && ! $has_layout_block ) {
return $content;
}
// Divi builder layout should only be used in singular template unless we are rendering
// a theme builder layout as they can appear on any page.
if ( ! is_singular() && ! $is_bfb_new_page && ! et_theme_builder_is_layout_post_type( get_post_type( get_the_ID() ) ) ) {
return $content;
}
$content = et_builder_get_layout_opening_wrapper() . $content . et_builder_get_layout_closing_wrapper();
/**
* Filter whether to add the outer builder content wrapper or not.
*
* @since 4.0
*
* @param bool $wrap
*/
$post_id = is_singular() ? get_the_ID() : 0;
$is_custom_post_type = et_builder_post_is_of_custom_post_type( $post_id ) && ! ET_Builder_Element::is_theme_builder_layout();
$should_content_be_wrapped = $is_custom_post_type || et_is_builder_plugin_active() || et_core_is_fb_enabled();
$wrap = apply_filters( 'et_builder_add_outer_content_wrap', $should_content_be_wrapped );
if ( $wrap ) {
$content = et_builder_get_builder_content_opening_wrapper() . $content . et_builder_get_builder_content_closing_wrapper();
}
return $content;
}
add_filter( 'the_content', 'et_builder_add_builder_content_wrapper' );
add_filter( 'et_builder_render_layout', 'et_builder_add_builder_content_wrapper' );
/**
* Wraps a copy of a css selector and then returns both selectors.
* Wrapping a copy of a selector instead of the original is necessary for selectors
* that target elements both inside AND outside the wrapper element.
*
* @since 3.10
* @since 4.6.6 New $inside_selectors parameter to extend default inside selector.
*
* @param string $selector CSS selector to wrap.
* @param string $suffix Selector partial to add to the wrapped selector after the wrapper (a space will be added first).
* @param boolean $clone Duplicate the selector, wrap the duplicate, and then return both selectors. Default `true`.
* @param mixed $inside_selectors Additional inside builder element selectors.
*
* @return string
*/
function et_builder_maybe_wrap_css_selector( $selector, $suffix = '', $clone = true, $inside_selectors = '' ) {
static $should_wrap_selectors = array();
$post_id = ET_Builder_Element::get_theme_builder_layout_id();
if ( ! isset( $should_wrap_selectors[ $post_id ] ) ) {
$is_builder_used = et_pb_is_pagebuilder_used( $post_id ) || has_block( 'divi/layout', get_the_ID() );
$should_wrap_selectors[ $post_id ] = et_is_builder_plugin_active() || et_builder_is_custom_post_type_archive() || ( $is_builder_used && ( et_builder_post_is_of_custom_post_type( $post_id ) || et_theme_builder_is_layout_post_type( get_post_type( $post_id ) ) ) );
}
if ( is_bool( $suffix ) ) {
$clone = $suffix;
$suffix = '';
}
if ( ! $should_wrap_selectors[ $post_id ] ) {
return trim( "{$selector} {$suffix}" );
}
$wrapper = ET_BUILDER_CSS_PREFIX;
$result = '';
if ( $clone ) {
$result .= $suffix ? "{$selector} {$suffix}, " : "{$selector}, ";
}
// By default, only selector that starts with `.et_pb` or `.et_fb` is considered as
// inside builder element. $inside_selectors param allow us to extend it and it would
// be useful for 3rd party extensions that use Divi Module Elements on their modules.
//
// Default inside builder element pattern as the first alternative, matches:
// - \.et[_-] : Start with .et- or .et_
// - (?:pb|fb)[_-] : Followed by one of pb-, pb_, fb-, fb_.
$inside_selector_pattern = '\.et[_-](?:pb|fb)[_-]';
if ( ! empty( $inside_selectors ) ) {
if ( is_array( $inside_selectors ) ) {
$inside_selectors = implode( '|', $inside_selectors );
}
$inside_selector_pattern .= "|{$inside_selectors}";
}
// Elements selector pattern.
// - (html[^ ]*)? : 1st group (html). Match html followed by non empty string
// - (body[^ ]*)? : 2nd group (body). Match body followed by non empty string
// - (.*?) : 3rd group (outside). Match any character.
// - ([^ ]*(?:inside).+) : 4th group (inside). Match one of inside builder element alternatives from $inside_selector_pattern.
// - (?: *) : Non capturing group. Match any space character.
$elements_selector_pattern = '/^(html[^ ]*)?(?: *)(body[^ ]*)?(?: *)(.*?)(?: *)([^ ]*(?:' . $inside_selector_pattern . ').+)/';
if ( $suffix ) {
// $suffix param allows caller to split selector into two parts (1. outside builder and 2. inside builder)
// so that it can be wrapped properly. It was implemented before the regex solution below.
if ( preg_match( '/et_fb_preview|et_fb_desktop_mode/', $selector ) ) {
// Selector targets html element using a custom class.
$result .= "{$selector} {$wrapper} {$suffix}";
} else {
// Selector targets body element either directly or using a custom class.
$result .= "{$selector}{$wrapper} {$suffix}";
}
} elseif ( preg_match( $elements_selector_pattern, $selector, $matches ) ) {
// The selector includes elements outside builder content so we can't just prepend the wrapper to it.
list( $_, $html, $body, $outside_builder, $inside_builder ) = $matches;
$parts = array_filter(
array(
$html,
// Intentionally glued together to produce "body.et-db", for example.
$body . ET_BUILDER_CSS_WRAPPER_PREFIX,
$outside_builder,
ET_BUILDER_CSS_LAYOUT_PREFIX,
$inside_builder,
)
);
$result .= implode( ' ', $parts );
} else {
$result .= "{$wrapper} {$selector}";
}
return trim( $result );
}
/**
* Wrapper for {@see et_builder_maybe_wrap_css_selector()} to support multiple selectors
* at once (eg. selector1, selector2, selector3)
*
* @since 3.10
* @since 4.6.6 New $inside_selectors parameter to extend default inside selector.
*
* @param string $selector CSS selectors to wrap.
* @param bool $clone {@see et_builder_maybe_wrap_css_selector()}.
* @param mixed $inside_selectors Additional inside builder element selectora.
*
* @return string
*/
function et_builder_maybe_wrap_css_selectors( $selector, $clone = true, $inside_selectors = '' ) {
static $should_wrap_selectors = array();
$post_id = ET_Builder_Element::get_theme_builder_layout_id();
$wrap_post_id = $post_id;
if ( ! isset( $should_wrap_selectors[ $post_id ] ) ) {
if ( et_theme_builder_is_layout_post_type( get_post_type( $post_id ) ) ) {
$main_post_id = ET_Post_Stack::get_main_post_id();
if ( $main_post_id ) {
$wrap_post_id = $main_post_id;
}
}
// GB editor + layout block is considered using builder.
$is_builder_used = et_pb_is_pagebuilder_used( $wrap_post_id ) || has_block( 'divi/layout', get_the_ID() );
$should_wrap_selectors[ $post_id ] = et_is_builder_plugin_active() || et_builder_is_custom_post_type_archive() || ( $is_builder_used && et_builder_post_is_of_custom_post_type( $wrap_post_id ) );
}
if ( ! $should_wrap_selectors[ $post_id ] ) {
return $selector;
}
$selectors = explode( ',', $selector );
$result = array();
foreach ( $selectors as $css_selector ) {
$result[] = et_builder_maybe_wrap_css_selector( $css_selector, '', $clone, $inside_selectors );
}
return implode( ',', $result );
}
/**
* Unprepend code module content.
*
* @param string $content Code module content.
*
* @return string|string[]|null
*/
function _et_pb_code_module_unprep_content( $content ) {
// before we swap out the placeholders,
// remove all the tags and \n that wpautop added!
$content = preg_replace( '/\n/smi', '', $content );
$content = preg_replace( '/
/smi', '', $content );
$content = preg_replace( '/<\/p>/smi', '', $content );
$content = str_replace( '', ' ', $content );
// convert the ', '
', $content );
return $content;
}
/**
* A preg_replace_callback callback.
*
* @param array $matches matched elements.
*
* @return string|string[]
*/
function _et_pb_code_module_unprep_content_regex_cb( $matches ) {
$prepped_content = $matches[1];
$prepped_content = _et_pb_code_module_unprep_content( $prepped_content );
return str_replace( $matches[1], $prepped_content, $matches[0] );
}
/**
* Undo prepared code module content.
*
* @param string $content Content from which to remove prepended.
*
* @return string|string[]|null
*/
function et_pb_unprep_code_module_for_wpautop( $content ) {
$content = preg_replace_callback( '/\[et_pb_code.*?\](.*)\[\/et_pb_code\]/mis', '_et_pb_code_module_unprep_content_regex_cb', $content );
$content = preg_replace_callback( '/\[et_pb_fullwidth_code.*?\](.*)\[\/et_pb_fullwidth_code\]/mis', '_et_pb_code_module_unprep_content_regex_cb', $content );
return $content;
}
/**
* Prepare code modules for wpautop.
*
* @param string $content Content to be prep.
*
* @return string|string[]|null
*/
function _et_pb_code_module_prep_content( $content ) {
// convert tags into placeholder so wpautop will leave them alone.
$content = preg_replace( '| |', '', $content );
// convert tag to tag, so wpautop will leave them alone,
// *and* so that we can clearly spot the tags that wpautop adds
// so we can quickly remove them.
$content = preg_replace( '|
|', '', $content );
$content = preg_replace( '|<\/p>|', ' ', $content );
return $content;
}
/**
* The Callback preg_replace_callback used while preparing code module for autop.
*
* @param array $matches array of matched elements.
*
* @return string|string[]
*/
function _et_pb_code_module_prep_content_regex_cb( $matches ) {
$prepped_content = $matches[1];
$prepped_content = _et_pb_code_module_prep_content( $prepped_content );
return str_replace( $matches[1], $prepped_content, $matches[0] );
}
/**
* Prepare code module for autop.
*
* @param string $content the content.
*
* @return string|string[]|null
*/
function et_pb_prep_code_module_for_wpautop( $content ) {
$content = preg_replace_callback( '/\[et_pb_code(?:(?![^\]]*\/\])[^\]]*)\](.*?)\[\/et_pb_code\]/mis', '_et_pb_code_module_prep_content_regex_cb', $content );
$content = preg_replace_callback( '/\[et_pb_fullwidth_code(?:(?![^\]]*\/\])[^\]]*)\](.*?)\[\/et_pb_fullwidth_code\]/mis', '_et_pb_code_module_prep_content_regex_cb', $content );
return $content;
}
/**
* Determine whether dynamic asset exists or not.
*
* @param string $prefix Asset prefix.
* @param string|bool $post_type Asset post type.
*
* @return bool
*/
function et_fb_dynamic_asset_exists( $prefix, $post_type = false ) {
// Get post type if it isn't being defined.
if ( ! $post_type ) {
if ( wp_doing_ajax() ) {
// phpcs:ignore WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF.
$post_type = isset( $_REQUEST['et_post_type'] ) ? sanitize_text_field( $_REQUEST['et_post_type'] ) : 'post';
$post_type = sanitize_text_field( $post_type );
} else {
global $post;
$post_type = isset( $post->post_type ) ? $post->post_type : 'post';
}
}
$post_type = apply_filters( 'et_builder_cache_post_type', $post_type, $prefix );
$prefix = esc_attr( $prefix );
$cache = sprintf( '%s/%s', ET_Core_PageResource::get_cache_directory(), get_locale() );
$files = glob( sprintf( '%s/%s-%s-*.js', $cache, $prefix, $post_type ) );
return is_array( $files ) && count( $files ) > 0;
}
if ( ! function_exists( 'et_fb_delete_builder_assets' ) ) :
/**
* Delete builder cache.
*/
function et_fb_delete_builder_assets() {
$cache = ET_Core_PageResource::get_cache_directory();
// Old cache location, make sure we clean that one too.
$old_files = glob( sprintf( '%s/*.js', $cache ) );
$old_files = is_array( $old_files ) ? $old_files : array();
// New, per language location.
$new_files = glob( sprintf( '%s/*/*.js', $cache ) );
$new_files = is_array( $new_files ) ? $new_files : array();
// Modules cache.
$modules_files = glob( sprintf( '%s/*/*.data', $cache ) );
$modules_files = is_array( $modules_files ) ? $modules_files : array();
foreach ( array_merge( $old_files, $new_files, $modules_files ) as $file ) {
@unlink( $file ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- unlink may fail with the permissions denied error.
}
// Images data cache.
$image_cache_keys = array(
'image_srcset_sizes',
'image_responsive_metadata',
'attachment_id_by_url',
'attachment_size_by_url',
);
foreach ( $image_cache_keys as $image_cache_key ) {
$cache_file_name = ET_Core_Cache_File::get_cache_file_name( $image_cache_key );
if ( file_exists( $cache_file_name ) ) {
@unlink( $cache_file_name ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- unlink may fail with the permissions denied error.
}
}
/**
* Clear AJAX cache.
*
* @since 4.0.10
*/
do_action( 'et_builder_ajax_cache_clear' );
}
// Since Google data is included in static helpers, we have to delete assets
// whenever the option is updated to avoid Builder reloads.
add_action( 'update_option_et_google_api_settings', 'et_fb_delete_builder_assets' );
endif;
if ( ! function_exists( 'et_fb_enqueue_open_sans' ) ) :
/**
* Load Open Sans font.
*
* @deprecated See {@see et_builder_enqueue_open_sans()}
*/
function et_fb_enqueue_open_sans() {
et_builder_enqueue_open_sans();
}
endif;
/**
* Wrapper for et_core_portability_link() which does ET capability checks as well.
*
* @since 3.26
*
* @param string $context The context used to register the portability.
* @param string|array $attributes Query string or array of attributes. Default empty.
*
* @return string
*/
function et_builder_portability_link( $context, $attributes = array() ) {
global $shortname;
$product = (string) $shortname;
$context_caps = array(
'et_builder' => 'et_builder_portability',
'et_builder_layouts' => 'et_builder_layouts_portability',
"et_{$product}_mods" => "et_{$product}_mods_portability",
'et_pb_roles' => 'et_pb_roles_portability',
'epanel' => 'epanel_portability',
);
$cap = et_()->array_get( $context_caps, $context, '' );
if ( ! empty( $cap ) && ! et_pb_is_allowed( $cap ) ) {
return '';
}
return et_core_portability_link( $context, $attributes );
}
/**
* Get the list of all public post types.
*
* @since 3.26.7
*
* @return WP_Post_Type[]
*/
function et_builder_get_public_post_types() {
$cache_key = 'et_builder_get_public_post_types';
if ( ! et_core_cache_has( $cache_key ) ) {
$blocklist = array_merge(
array(
'et_pb_layout',
ET_THEME_BUILDER_TEMPLATE_POST_TYPE,
),
et_theme_builder_get_layout_post_types()
);
$all_post_types = get_post_types( array(), 'objects' );
$post_types = array();
foreach ( $all_post_types as $post_type ) {
if ( ! in_array( $post_type->name, $blocklist, true ) && et_builder_is_post_type_public( $post_type->name ) ) {
$post_types[ $post_type->name ] = $post_type;
}
}
et_core_cache_add( $cache_key, $post_types );
}
/**
* Filter array of public post types.
*
* @since 3.26.7
*
* @param WP_Post_Type[]
*/
return apply_filters( 'et_builder_get_public_post_types', et_core_cache_get( $cache_key ) );
}
/**
* Clear public post type cache whenever a custom post type is registered.
*
* @since 3.26.7
*
* @return void
*/
function et_builder_clear_get_public_post_types_cache() {
et_core_cache_delete( 'et_builder_get_public_post_types' );
}
add_action( 'registered_post_type', 'et_builder_clear_get_public_post_types_cache' );
if ( ! function_exists( 'et_filter_intermediate_image_sizes_advanced' ) ) :
/**
* Filters the image sizes to calculate responsive image height.
*
* @param array $sizes An associative array of image sizes.
* @param array $metadata An associative array of image metadata: width, height, file.
*
* @return array
*/
function et_filter_intermediate_image_sizes_advanced( $sizes, $metadata = array() ) {
// Bail early when the attachment metadata is empty.
if ( ! $metadata ) {
return $sizes;
}
foreach ( array_keys( $sizes ) as $size_key ) {
if ( strpos( $size_key, 'et-pb-image--responsive--' ) !== 0 ) {
continue;
}
$breakpoint = str_replace( 'et-pb-image--responsive--', '', $size_key );
$responsive_size = et_image_get_responsive_size( $metadata['width'], $metadata['height'], $breakpoint );
if ( $responsive_size && isset( $responsive_size['width'] ) && isset( $responsive_size['height'] ) ) {
$sizes[ $size_key ]['width'] = $responsive_size['width'];
$sizes[ $size_key ]['height'] = $responsive_size['height'];
} else {
unset( $sizes[ $size_key ] );
}
}
return $sizes;
}
endif;
add_filter( 'intermediate_image_sizes_advanced', 'et_filter_intermediate_image_sizes_advanced', 10, 2 );
if ( ! function_exists( 'et_action_sync_attachment_data_cache' ) ) :
/**
* Sync image data cache
*
* @since 3.29.3
*
* @param int $attachment_id Attachment ID.
* @param array $metadata Image metadata.
*
* @return void
*/
function et_action_sync_attachment_data_cache( $attachment_id, $metadata = null ) {
if ( ! $attachment_id ) {
return;
}
$url_full = wp_get_attachment_url( $attachment_id );
if ( ! $url_full ) {
return;
}
// Normalize image URL to remove the HTTP/S protocol.
$normalized_url_full = et_attachment_normalize_url( $url_full );
if ( ! $normalized_url_full ) {
return;
}
$normalized_urls = array(
$normalized_url_full => $normalized_url_full,
);
if ( is_null( $metadata ) ) {
$metadata = wp_get_attachment_metadata( $attachment_id );
}
if ( ! empty( $metadata ) ) {
foreach ( $metadata['sizes'] as $image_size ) {
$normalized_url = str_replace( basename( $normalized_url_full ), $image_size['file'], $normalized_url_full );
if ( ! isset( $normalized_urls[ $normalized_url ] ) ) {
$normalized_urls[ $normalized_url ] = $normalized_url;
}
}
}
$cache_keys = array(
'attachment_id_by_url',
'attachment_size_by_url',
'image_responsive_metadata',
'image_srcset_sizes',
);
foreach ( $cache_keys as $cache_key ) {
$cache = ET_Core_Cache_File::get( $cache_key );
// Skip if the cache data is empty.
if ( ! $cache ) {
continue;
}
foreach ( $normalized_urls as $normalized_url ) {
unset( $cache[ $normalized_url ] );
}
ET_Core_Cache_File::set( $cache_key, $cache );
}
}
endif;
add_action( 'delete_attachment', 'et_action_sync_attachment_data_cache' );
if ( ! function_exists( 'et_filter_wp_generate_attachment_metadata' ) ) :
/**
* Sync the cached srcset data when attachment meta data generated/updated.
*
* @since 3.27.1
*
* @param array $metadata An array of attachment meta data.
* @param int $attachment_id Current attachment ID.
*
* @return array
*/
function et_filter_wp_generate_attachment_metadata( $metadata, $attachment_id = 0 ) {
if ( $attachment_id ) {
et_action_sync_attachment_data_cache( $attachment_id, $metadata );
}
return $metadata;
}
endif;
add_filter( 'wp_generate_attachment_metadata', 'et_filter_wp_generate_attachment_metadata', 10, 2 );
/**
* Filter the main query paged arg to avoid pagination clashes with the Blog module pagination.
*
* @since 4.0
*
* @param WP_Query $query Query object.
*
* @return void
*/
function et_builder_filter_main_query_paged_for_blog_module( $query ) {
/**
* Utility which holds the current page number for the Blog module.
* Necessary to avoid clashes with the main query pagination.
*
* @var integer
*/
global $__et_blog_module_paged, $__et_portfolio_module_paged;
// phpcs:ignore WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF.
if ( ( isset( $_GET['et_blog'] ) || isset( $_GET['et_portfolio'] ) ) && $query->is_main_query() ) {
$__et_blog_module_paged = $query->get( 'paged' );
$__et_portfolio_module_paged = $query->get( 'paged' );
$query->set( 'paged', 0 );
}
}
add_filter( 'pre_get_posts', 'et_builder_filter_main_query_paged_for_blog_module' );
if ( ! function_exists( 'et_maybe_enable_embed_shortcode' ) ) :
/**
* Maybe enable [embed] shortcode at the content.
*
* @since 4.4.9
*
* @param string $content Content to search for shortcodes.
* @param boolean $is_content Whether the passed content is the content.
*
* @return string
*/
function et_maybe_enable_embed_shortcode( $content, $is_content ) {
if ( $is_content && has_shortcode( $content, 'embed' ) ) {
global $wp_embed;
$content = $wp_embed->run_shortcode( $content );
}
return $content;
}
endif;
/**
* Calculate value which has unit on it.
*
* Might need to group this style rendering related utils function if there are more of them
*
* @used-by ET_Builder_Module_Helper_Overlay::process_icon_font_size()
*
* @param string $value base value which has unit.
* @param int|float $multiplier multiplier (literally).
* @param bool|int|float $min_value minimum $value to do calculation. set to false to skip.
*
* @return string
*/
function et_builder_multiply_value_has_unit( $value, $multiplier, $min_value = false ) {
$number = (float) $value;
$unit = str_replace( $number, '', $value );
$should_calculate = false === $min_value || $min_value < $number;
$product = $should_calculate ? $number * (float) $multiplier : $min_value;
return (string) $product . $unit;
}
/**
* Register custom sidebars.
*
* @since 4.4.8 Moved from builder/functions.php, so it can be loaded on wp_ajax_save_widget().
*/
function et_builder_widgets_init() {
$et_pb_widgets = get_theme_mod( 'et_pb_widgets' );
$widget_areas = et_()->array_get( $et_pb_widgets, 'areas', array() );
if ( ! empty( $widget_areas ) ) {
foreach ( $widget_areas as $id => $name ) {
register_sidebar(
array(
'name' => sanitize_text_field( $name ),
'id' => sanitize_text_field( $id ),
'before_widget' => '
',
'after_widget' => '
',
'before_title' => '',
)
);
}
}
// Disable built-in's recent comments widget link styling because ET Themes don't need it.
if ( ! et_is_builder_plugin_active() ) {
add_filter( 'show_recent_comments_widget_style', '__return_false' );
}
}
// Call the widgets init at 'init' hook if Divi Builder plugin active because plugin
// loads the Divi builder at 'init' hook and 'widgets_init' is too early.
if ( et_is_builder_plugin_active() ) {
add_action( 'init', 'et_builder_widgets_init', 20 );
} else {
add_action( 'widgets_init', 'et_builder_widgets_init' );
}
/**
* For Dev Use
*/
function et_light_debug_backtrace() {
$debug_backtrace = debug_backtrace();
$keys = [ 'file', 'line', 'function', 'class' ];
$light_debug_backtrace = [];
foreach ( $debug_backtrace as $key => $value ) {
foreach ( $keys as $_key ) {
if ( isset( $value[ $_key ] ) ) {
$light_debug_backtrace[ $key ][ $_key ] = $value[ $_key ];
}
}
}
return array_slice( $light_debug_backtrace, 1 );
}
/**
* Get all global colors.
*
* @since 4.9.0
*
* @return array
*/
function et_builder_get_all_global_colors( $include_customizer = false ) {
if ( $include_customizer ) {
$primary_color = et_get_option( 'accent_color', '#2ea3f2' );
$secondary_color = et_get_option( 'secondary_accent_color', '#2ea3f2' );
$heading_color = et_get_option( 'header_color', '#666666' );
$body_color = et_get_option( 'font_color', '#666666' );
$saved_global_colors = et_get_option( 'et_global_colors' );
if ( empty( $saved_global_colors ) ) {
$saved_global_colors = [];
}
// Remove customizer global colors if exist for any reason.
// For example if user imported global colors on old version of Divi which doesn't support customizer colors.
$excluded_keys = [
'gcid-primary-color',
'gcid-secondary-color',
'gcid-heading-color',
'gcid-body-color',
];
foreach ( $excluded_keys as $excluded_key ) {
unset( $saved_global_colors[ $excluded_key ] );
}
return array_merge(
[
'gcid-primary-color' => [
'color' => $primary_color,
'active' => 'yes',
],
'gcid-secondary-color' => [
'color' => $secondary_color,
'active' => 'yes',
],
'gcid-heading-color' => [
'color' => $heading_color,
'active' => 'yes',
],
'gcid-body-color' => [
'color' => $body_color,
'active' => 'yes',
],
],
$saved_global_colors
);
}
return et_get_option( 'et_global_colors' );
}
/**
* Filters the auto-sizes for lazy loaded images to be disabled.
*
* This filter callback is introduced initially to remove additional "auto" value in `sizes` image attribute added by
* `wp_img_tag_add_auto_sizes` function.
*
* We have tried the CSS solution by overriding the `contain-intrinsic-size` value like WP 6.7 does to remove default
* behavior of auto-sizes for lazy loaded images, but it doesn't work as expected for all cases we have tested. So, we
* decided to disable the auto-sizes for lazy loaded images by filtering the `wp_img_tag_add_auto_sizes` hook.
*
* @since 4.27.4
*
* @param boolean $enabled Whether auto-sizes for lazy loaded images is enabled.
*/
add_filter( 'wp_img_tag_add_auto_sizes', '__return_false' );