Merged in feature/from-pantheon (pull request #16)
code from pantheon * code from pantheon
This commit is contained in:
@@ -0,0 +1,284 @@
|
||||
<?php
|
||||
/**
|
||||
* Ajax Cache.
|
||||
*
|
||||
* @package Builder
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to cache commonly used AJAX requests.
|
||||
*/
|
||||
class ET_Builder_Ajax_Cache {
|
||||
|
||||
/**
|
||||
* Instance of this class.
|
||||
*
|
||||
* @var ET_Builder_Ajax_Cache
|
||||
*/
|
||||
private static $_instance;
|
||||
|
||||
/**
|
||||
* Transient name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_transient = 'et_builder_ajax_cache';
|
||||
|
||||
/**
|
||||
* Flag to determine whether to save cache or not on `shutdown` hook.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $_dirty = false;
|
||||
|
||||
/**
|
||||
* List of all ajax cache.
|
||||
*
|
||||
* @var Array
|
||||
*/
|
||||
protected $_cache;
|
||||
|
||||
/**
|
||||
* ET_Builder_Ajax_Cache constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'et_builder_ajax_cache_clear', array( $this, 'clear' ), 10, 1 );
|
||||
add_action( 'shutdown', array( $this, 'save' ) );
|
||||
add_filter( 'et_builder_dynamic_asset_deps', array( $this, 'add_cache_dep' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether cache file exists or not.
|
||||
*
|
||||
* @since 4.0.10
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function file_exists() {
|
||||
$file = $this->get_file_name();
|
||||
return $file && et_()->WPFS()->is_readable( $file );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether cache is empty or not.
|
||||
*
|
||||
* @since 4.0.10
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_empty() {
|
||||
$this->load();
|
||||
return empty( $this->_cache );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue ajax cache as definitions dependency.
|
||||
*
|
||||
* @since 4.0.10
|
||||
*
|
||||
* @param array $deps Dependencies array.
|
||||
* @param string $key Script handle.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function add_cache_dep( $deps, $key ) {
|
||||
// Skip all static assets but definitions.
|
||||
if ( 'et-dynamic-asset-definitions' !== $key ) {
|
||||
return $deps;
|
||||
}
|
||||
|
||||
if ( ! $this->file_exists() && ! $this->write_file() ) {
|
||||
// Bail out if cache is empty and cannot write the file.
|
||||
return $deps;
|
||||
}
|
||||
|
||||
// Enqueue ajax cache as definitions dependency.
|
||||
$handle = 'et-ajax-cache';
|
||||
$deps[] = $handle;
|
||||
wp_register_script( $handle, $this->get_url(), array(), ET_BUILDER_VERSION, false );
|
||||
|
||||
return $deps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load cache.
|
||||
*
|
||||
* @since 4.0.10
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function load() {
|
||||
if ( is_array( $this->_cache ) ) {
|
||||
// Cache was already loaded.
|
||||
return;
|
||||
}
|
||||
|
||||
$this->_cache = get_transient( $this->_transient );
|
||||
|
||||
if ( ! is_array( $this->_cache ) ) {
|
||||
$this->_cache = array();
|
||||
$this->delete_file();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save cache.
|
||||
*
|
||||
* @since 4.0.10
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function save() {
|
||||
// Ensure cache is loaded.
|
||||
$this->load();
|
||||
|
||||
if ( $this->_dirty ) {
|
||||
set_transient( $this->_transient, $this->_cache );
|
||||
$this->delete_file();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write cache file.
|
||||
*
|
||||
* @since 4.0.10
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function write_file() {
|
||||
if ( $this->is_empty() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$file = $this->get_file_name();
|
||||
$cache = '';
|
||||
foreach ( $this->_cache as $key => $value ) {
|
||||
$cache = sprintf( '"%s":%s,', $key, $value );
|
||||
}
|
||||
$cache = sprintf( '{"ajaxCache":{%s}}', rtrim( $cache, ',' ) );
|
||||
$cache = sprintf( 'window.ETBuilderBackend=jQuery.extend(true,%s,window.ETBuilderBackend)', $cache );
|
||||
|
||||
et_()->WPFS()->put_contents( $file, $cache );
|
||||
return $this->file_exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete cache file.
|
||||
*
|
||||
* @since 4.0.10
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete_file() {
|
||||
if ( $this->file_exists() ) {
|
||||
et_()->WPFS()->delete( $this->get_file_name() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set cache key.
|
||||
*
|
||||
* @since 4.0.10
|
||||
*
|
||||
* @param string $key Cache key.
|
||||
* @param string $content Cache value.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set( $key, $content ) {
|
||||
$this->load();
|
||||
$this->_cache[ $key ] = wp_json_encode( $content );
|
||||
$this->_dirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset cache key.
|
||||
*
|
||||
* @since 4.0.10
|
||||
*
|
||||
* @param string $key Cache key.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unset_( $key ) {
|
||||
$this->load();
|
||||
if ( isset( $this->_cache[ $key ] ) ) {
|
||||
unset( $this->_cache[ $key ] );
|
||||
$this->_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear cache.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clear() {
|
||||
delete_transient( $this->_transient );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cache file name.
|
||||
*
|
||||
* @since 4.0.10
|
||||
*
|
||||
* @return string.
|
||||
*/
|
||||
public function get_file_name() {
|
||||
// Per language Cache due to some data being localized.
|
||||
$lang = is_admin() || et_fb_is_enabled() ? get_user_locale() : get_locale();
|
||||
$lang = trim( sanitize_file_name( $lang ), '.' );
|
||||
$prefix = 'ajax';
|
||||
$cache = et_()->path( et_core_cache_dir()->path, $lang );
|
||||
$files = glob( "{$cache}/{$prefix}-*.js" );
|
||||
$exists = is_array( $files ) && $files;
|
||||
|
||||
if ( $exists ) {
|
||||
return $files[0];
|
||||
}
|
||||
|
||||
wp_mkdir_p( $cache );
|
||||
|
||||
// Create uniq filename.
|
||||
$uniq = str_replace( '.', '', (string) microtime( true ) );
|
||||
$file = "{$cache}/{$prefix}-{$uniq}.js";
|
||||
|
||||
return et_()->WPFS()->is_writable( dirname( $file ) ) ? $file : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cache url.
|
||||
*
|
||||
* @since 4.0.10
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_url() {
|
||||
$file = $this->get_file_name();
|
||||
$lang = basename( dirname( $file ) );
|
||||
$name = basename( $file );
|
||||
return et_()->path( et_core_cache_dir()->url, $lang, $name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the class instance.
|
||||
*
|
||||
* @since 4.0.10
|
||||
*
|
||||
* @return ET_Builder_Ajax_Cache
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( ! self::$_instance ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ET_Builder_Ajax_Cache::instance();
|
||||
@@ -0,0 +1,757 @@
|
||||
<?php
|
||||
/**
|
||||
* Compatibility for Gutenberg.
|
||||
*
|
||||
* @package Builder
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Editing GB blocks inside Divi.
|
||||
*
|
||||
* @since 3.18 Added support for WP 5.0
|
||||
* @since 3.10.2
|
||||
*/
|
||||
class ET_Builder_Block_Editor_Integration {
|
||||
|
||||
/**
|
||||
* Regex to match gallery in block editor.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_gb_gallery_regexp = '/<ul class="wp-block-gallery[^"]*?">.*?<\/ul>/mis';
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* ET_Builder_Block_Editor_Integration constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->init_hooks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the post can be edited in the block editor.
|
||||
*
|
||||
* @param mixed $post Post ID or WP_Post object.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function _can_edit_post( $post ) {
|
||||
if ( function_exists( 'gutenberg_can_edit_post' ) ) {
|
||||
return gutenberg_can_edit_post( $post );
|
||||
}
|
||||
|
||||
// In case WordPress is lower than version 5.0.
|
||||
if ( ! function_exists( 'use_block_editor_for_post' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return use_block_editor_for_post( $post );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether a post type is compatible with the block editor.
|
||||
*
|
||||
* @param string $type The post type.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function _can_edit_post_type( $type ) {
|
||||
if ( function_exists( 'gutenberg_can_edit_post_type' ) ) {
|
||||
return gutenberg_can_edit_post_type( $type );
|
||||
}
|
||||
|
||||
// In case WordPress is lower than version 5.0.
|
||||
if ( ! function_exists( 'use_block_editor_for_post_type' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return use_block_editor_for_post_type( $type );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current editor is set to load Gutenberg.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function _is_block_editor_page() {
|
||||
if ( function_exists( 'is_gutenberg_page' ) ) {
|
||||
return is_gutenberg_page();
|
||||
}
|
||||
|
||||
// In case WordPress is lower than version 5.0.
|
||||
if ( ! function_exists( 'use_block_editor_for_post' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return use_block_editor_for_post( get_the_ID() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter on map_meta_cap.
|
||||
*
|
||||
* @param array $caps Capabilities.
|
||||
* @param string $cap Capability to check.
|
||||
* @param string $user_id User ID.
|
||||
* @param array $args Additional args.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function map_meta_cap( $caps, $cap, $user_id, $args ) {
|
||||
// This only needs to run once,.
|
||||
remove_filter( 'map_meta_cap', array( $this, 'map_meta_cap' ), 10 );
|
||||
if (
|
||||
// GB checks for 'edit_post' so do nothing in all other cases.
|
||||
'edit_post' !== $cap ||
|
||||
// Ignore the case where Divi wasn't used to edit the post.
|
||||
! et_pb_is_pagebuilder_used( $args[0] )
|
||||
) {
|
||||
return $caps;
|
||||
}
|
||||
// We need to add `do_not_allow` for superadmins.
|
||||
$caps = array( 'do_not_allow' );
|
||||
|
||||
return $caps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user capabilities that is relevant to block editor integration
|
||||
*
|
||||
* @since 4.1.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_current_user_capabilities() {
|
||||
/**
|
||||
* Make relevant capabilities filterable should the need to check for more caps arises
|
||||
*
|
||||
* @since 4.1.0
|
||||
*
|
||||
* @param array user capabilities
|
||||
*/
|
||||
$relevant_capabilities = array(
|
||||
'divi_library',
|
||||
'use_visual_builder',
|
||||
);
|
||||
$relevant_capabilities = apply_filters( 'et_block_editor_relevant_capabilities', $relevant_capabilities );
|
||||
|
||||
$capabilities = array();
|
||||
|
||||
foreach ( $relevant_capabilities as $cap_name ) {
|
||||
$capabilities[ $cap_name ] = et_pb_is_allowed( $cap_name );
|
||||
}
|
||||
|
||||
return $capabilities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter used to disable GB for certain post types.
|
||||
*
|
||||
* @param bool $can_edit Whether post type can be editable with gutenberg or not.
|
||||
* @param string $post_type Post type name.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function gutenberg_can_edit_post_type( $can_edit, $post_type ) {
|
||||
// The tricky part here is that GB doesn't pass the post ID to this filter but only its type
|
||||
// but we need the ID to determine whether the post has been edited with Divi.
|
||||
// Since GB uses `current_user_can( 'edit_post', $post->ID )` right after call this filter,
|
||||
// We hook into `map_meta_cap` (which gets passed the ID) and do our checks there.
|
||||
add_filter( 'map_meta_cap', array( $this, 'map_meta_cap' ), 10, 4 );
|
||||
|
||||
return $can_edit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue our GB compatibility bundle.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue_block_editor_assets() {
|
||||
// Load script dependencies that is used by builder on top window. These dependencies
|
||||
// happen to be the exact same scripts required by BFB top window's scripts.
|
||||
et_bfb_enqueue_scripts_dependencies();
|
||||
|
||||
// Enqueue open sans.
|
||||
et_builder_enqueue_open_sans();
|
||||
|
||||
// Enqueue integration & blocks scripts.
|
||||
$deps = array(
|
||||
'jquery',
|
||||
'et_bfb_admin_date_addon_js',
|
||||
'wp-hooks',
|
||||
);
|
||||
et_fb_enqueue_bundle( 'et-builder-gutenberg', 'gutenberg.js', $deps );
|
||||
|
||||
// Enqueue top window style.
|
||||
wp_register_style(
|
||||
'et-fb-top-window',
|
||||
ET_BUILDER_URI . '/frontend-builder/assets/css/fb-top-window.css',
|
||||
array(),
|
||||
ET_BUILDER_VERSION
|
||||
);
|
||||
|
||||
// Enqueue integration & blocks styles.
|
||||
$deps = array(
|
||||
'et-fb-top-window',
|
||||
);
|
||||
et_fb_enqueue_bundle( 'et-builder-gutenberg', 'gutenberg.css', $deps );
|
||||
|
||||
// this enqueue bundle.css.
|
||||
et_builder_enqueue_assets_main();
|
||||
|
||||
$post_id = get_the_ID();
|
||||
$post_type = get_post_type();
|
||||
$enabled_for_post_type = et_builder_enabled_for_post_type( $post_type );
|
||||
$updates_options = get_site_option( 'et_automatic_updates_options', array() );
|
||||
$et_account = array(
|
||||
'et_username' => et_()->array_get( $updates_options, 'username', '' ),
|
||||
'et_api_key' => et_()->array_get( $updates_options, 'api_key', '' ),
|
||||
'status' => get_site_option( 'et_account_status', 'not_active' ),
|
||||
);
|
||||
|
||||
// Set helpers needed by our own Gutenberg bundle.
|
||||
$gutenberg = array(
|
||||
'helpers' => array(
|
||||
'postID' => $post_id,
|
||||
'postType' => $post_type,
|
||||
'is3rdPartyPostType' => et_builder_is_post_type_custom( $post_type ) ? 'yes' : 'no',
|
||||
'vbUrl' => et_fb_get_vb_url(),
|
||||
'builderUsed' => et_pb_is_pagebuilder_used( $post_id ),
|
||||
'scriptDebug' => defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG,
|
||||
'canToggle' => et_pb_is_allowed( 'divi_builder_control' ) && $enabled_for_post_type,
|
||||
'isEnabled' => $enabled_for_post_type,
|
||||
'i18n' => array(
|
||||
'placeholder' => array(
|
||||
'block' => array(
|
||||
'title' => esc_html__( 'Divi Builder', 'et_builder' ),
|
||||
'description' => esc_html__( 'The Divi Builder is activated on this page. To edit your page using the builder, click the Edit With The Divi Builder button.', 'et_builder' ),
|
||||
),
|
||||
'render' => array(
|
||||
'title' => array(
|
||||
'new' => esc_html__( 'Build Your Layout Using Divi', 'et_builder' ),
|
||||
'old' => esc_html__( 'This Layout Is Built With Divi', 'et_builder' ),
|
||||
),
|
||||
'divi' => array(
|
||||
'new' => esc_html__( 'Use Divi Builder', 'et_builder' ),
|
||||
'old' => esc_html__( 'Edit With The Divi Builder', 'et_builder' ),
|
||||
),
|
||||
'default' => esc_html__( 'Use Default Editor', 'et_builder' ),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Loaded into ETBlockUserStore.
|
||||
'capabilities' => $this->get_current_user_capabilities(),
|
||||
|
||||
// Loaded into ETBlockLibraryStore.
|
||||
'etAccount' => $et_account,
|
||||
|
||||
// Loaded into ETBlockSettingsStore.
|
||||
'conditions' => array(
|
||||
'isRtl' => is_rtl(),
|
||||
),
|
||||
'constants' => array(
|
||||
'emptyLayout' => '[et_pb_section admin_label="section"][et_pb_row admin_label="row"][/et_pb_row][/et_pb_section]',
|
||||
),
|
||||
'nonces' => array(
|
||||
'et_builder_library_get_layouts_data' => wp_create_nonce( 'et_builder_library_get_layouts_data' ),
|
||||
'et_builder_library_update_account' => wp_create_nonce( 'et_builder_library_update_account' ),
|
||||
'et_block_layout_preview' => wp_create_nonce( 'et_block_layout_preview' ),
|
||||
'et_rest_get_layout_content' => wp_create_nonce( 'et_rest_get_layout_content' ),
|
||||
'et_rest_process_builder_edit_data' => wp_create_nonce( 'et_rest_process_builder_edit_data' ),
|
||||
),
|
||||
'urls' => array(
|
||||
'adminAjax' => admin_url( 'admin-ajax.php' ),
|
||||
'diviLibrary' => ET_BUILDER_DIVI_LIBRARY_URL,
|
||||
'home' => home_url( '/' ),
|
||||
),
|
||||
/**
|
||||
* Make DOM selectors list filterable so third party can modified it if needed
|
||||
*
|
||||
* @since 4.1.0
|
||||
*
|
||||
* @param array list of selectors
|
||||
*/
|
||||
'selectors' => apply_filters(
|
||||
'et_gb_selectors',
|
||||
array(
|
||||
'pageLayoutSelect' => '#et_pb_page_layout',
|
||||
)
|
||||
),
|
||||
/**
|
||||
* Make Content Widhts settings filterable so third party can modified it if needed
|
||||
*
|
||||
* @since 4.1.0
|
||||
*
|
||||
* @param array content width configurations
|
||||
*/
|
||||
'contentWidths' => apply_filters(
|
||||
'et_gb_content_widths',
|
||||
array(
|
||||
// Intentionally set null for default and undefined if no saved content width found
|
||||
// unless `et_gb_content_widths` is being filtered to handle Divi Builder Plugin
|
||||
// situation which might not have deifined content width.
|
||||
'default' => null,
|
||||
'current' => get_post_meta( $post_id, '_et_gb_content_width', true ),
|
||||
'min' => 320, // Min content width (small smartphone width).
|
||||
'max' => 2880, // Max content width (15" laptop * 2).
|
||||
)
|
||||
),
|
||||
);
|
||||
wp_localize_script( 'et-builder-gutenberg', 'et_builder_gutenberg', $gutenberg );
|
||||
|
||||
// Set translated strings for the scripts.
|
||||
wp_set_script_translations( 'et-builder-gutenberg', 'et_builder', ET_BUILDER_DIR . 'languages' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new Divi page
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_new_button() {
|
||||
global $typenow;
|
||||
if ( ! $this->_can_edit_post_type( $typenow ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$edit = 'post-new.php';
|
||||
$edit .= 'post' !== $typenow ? "?post_type=$typenow" : '';
|
||||
|
||||
// Create a nonce to auto activate VB on a new Auto Draft.
|
||||
$url = add_query_arg( 'et_fb_new_vb_nonce', wp_create_nonce( 'et_fb_new_vb_nonce' ), admin_url( $edit ) );
|
||||
$button = sprintf( '<a href="%s">%s</a>', esc_url( $url ), 'Divi' );
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var menu = document.querySelector('#split-page-title-action .dropdown');
|
||||
|
||||
if (menu) {
|
||||
menu.insertAdjacentHTML('afterbegin', '<?php echo et_core_esc_previously( $button ); ?>');
|
||||
return;
|
||||
}
|
||||
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* This filter allows VB to be directly activated for Auto Drafts.
|
||||
*
|
||||
* @param object $post Auto Draft post.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function auto_draft( $post ) {
|
||||
// phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- The nonce value is used only for comparision in the `wp_verify_nonce`.
|
||||
if ( ! isset( $_GET['et_fb_new_vb_nonce'] ) || ! wp_verify_nonce( $_GET['et_fb_new_vb_nonce'], 'et_fb_new_vb_nonce' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save the draft.
|
||||
wp_update_post(
|
||||
array(
|
||||
'ID' => $post->ID,
|
||||
'post_status' => 'draft',
|
||||
)
|
||||
);
|
||||
|
||||
// Add VB activation nonce.
|
||||
$url = add_query_arg(
|
||||
'et_fb_activation_nonce',
|
||||
wp_create_nonce( 'et_fb_activation_nonce_' . $post->ID ),
|
||||
et_fb_prepare_ssl_link( get_permalink( $post ) )
|
||||
);
|
||||
|
||||
// Set post meta to `off` or else `et_builder_set_content_activation` won't work...
|
||||
update_post_meta( $post->ID, '_et_pb_use_builder', 'off' );
|
||||
|
||||
wp_safe_redirect( $url );
|
||||
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add 'Edit With Divi Editor' links
|
||||
*
|
||||
* @param array $actions Currently defined actions for the row.
|
||||
* @param object $post Current post object.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function add_edit_link( $actions, $post ) {
|
||||
// Maybe change this with et_fb_current_user_can_save or equivalent.
|
||||
|
||||
if ( ! $this->_can_edit_post( $post ) || ! et_builder_enabled_for_post_type( $post->post_type ) ) {
|
||||
return $actions;
|
||||
}
|
||||
|
||||
if ( (int) get_option( 'page_for_posts' ) === $post->ID ) {
|
||||
// Post is assigned as the blog page so it does not have editable content.
|
||||
return $actions;
|
||||
}
|
||||
|
||||
$post_id = $post->ID;
|
||||
$is_divi_library = 'et_pb_layout' === get_post_type( $post_id );
|
||||
$edit_url = $is_divi_library ? get_edit_post_link( $post_id, 'raw' ) : get_permalink( $post_id );
|
||||
|
||||
if ( et_pb_is_pagebuilder_used( $post_id ) ) {
|
||||
$edit_url = et_fb_get_vb_url( $edit_url );
|
||||
} else {
|
||||
if ( ! et_pb_is_allowed( 'divi_builder_control' ) ) {
|
||||
// Do not add Divi activation link when user lacks `Toggle Divi Builder` capability.
|
||||
return $actions;
|
||||
}
|
||||
$edit_url = add_query_arg(
|
||||
array(
|
||||
'et_fb_activation_nonce' => wp_create_nonce( 'et_fb_activation_nonce_' . $post_id ),
|
||||
),
|
||||
$edit_url
|
||||
);
|
||||
}
|
||||
|
||||
$edit_action = array(
|
||||
'divi' => sprintf(
|
||||
'<a href="%s" aria-label="%s">%s</a>',
|
||||
esc_url( $edit_url ),
|
||||
esc_attr(
|
||||
sprintf(
|
||||
__( 'Edit “%s” in Divi', 'et_builder' ),
|
||||
_draft_or_post_title( $post->ID )
|
||||
)
|
||||
),
|
||||
esc_html__( 'Edit With Divi', 'et_builder' )
|
||||
),
|
||||
);
|
||||
|
||||
$actions = array_merge( $actions, $edit_action );
|
||||
|
||||
// I'm leaving this here in case we wanna change item position.
|
||||
// $edit_offset = array_search( 'edit', array_keys( $actions ), true );
|
||||
// $actions = array_merge(
|
||||
// array_slice( $actions, 0, $edit_offset + 1 ),
|
||||
// $edit_action,
|
||||
// array_slice( $actions, $edit_offset + 1 )
|
||||
// );.
|
||||
|
||||
return $actions;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add filters needed to show our extra row action.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_edit_link_filters() {
|
||||
// For hierarchical post types.
|
||||
add_filter( 'page_row_actions', array( $this, 'add_edit_link' ), 10, 2 );
|
||||
// For non-hierarchical post types.
|
||||
add_filter( 'post_row_actions', array( $this, 'add_edit_link' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add 'Divi' to post states when builder is enabled for it.
|
||||
*
|
||||
* @param array $post_states Existing post states.
|
||||
* @param object $post Current post object.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function display_post_states( $post_states, $post ) {
|
||||
// Make sure that $post_states is an array. Third party plugin might modify $post_states and makes it null
|
||||
// which create various issue (i.e. Piklist + Having a page configured as a static page).
|
||||
if ( ! is_array( $post_states ) ) {
|
||||
$post_states = array();
|
||||
}
|
||||
|
||||
if ( et_pb_is_pagebuilder_used( $post->ID ) ) {
|
||||
// Remove Gutenberg if existing.
|
||||
$key = array_search( 'Gutenberg', $post_states, true );
|
||||
if ( false !== $key ) {
|
||||
unset( $post_states[ $key ] );
|
||||
}
|
||||
// GB devs didn't allow this to be translated so why should we ?
|
||||
$post_states[] = 'Divi';
|
||||
}
|
||||
|
||||
return $post_states;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that Divi enabled CPTs support 'custom-fields'.
|
||||
*
|
||||
* @since 3.19.12
|
||||
*/
|
||||
public function ensure_post_type_supports() {
|
||||
$post_types = et_builder_get_builder_post_types();
|
||||
|
||||
foreach ( $post_types as $post_type ) {
|
||||
if ( ! post_type_supports( $post_type, 'custom-fields' ) ) {
|
||||
add_post_type_support( $post_type, 'custom-fields' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter update_post_metadata return value from during a REST API update
|
||||
* when meta value isn't changed.
|
||||
*
|
||||
* @param mixed $result Previous result.
|
||||
* @param int $object_id Post ID.
|
||||
* @param string $meta_key Meta key.
|
||||
* @param mixed $meta_value Meta value.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function update_post_metadata( $result, $object_id, $meta_key, $meta_value ) {
|
||||
if ( ! in_array( $meta_key, array( '_et_pb_use_builder', '_et_pb_old_content' ), true ) ) {
|
||||
// Only act if it's one of our metas.
|
||||
return $result;
|
||||
}
|
||||
if ( get_metadata( 'post', $object_id, $meta_key, true ) === $meta_value ) {
|
||||
// Return true instead of false so silly WP REST API call won't die on us....
|
||||
return true;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove empty Divi GB placeholder when processing shortcode.
|
||||
*
|
||||
* @param string $post_content Raw post content (shortcode).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function et_fb_load_raw_post_content( $post_content ) {
|
||||
// Replace empty placeholder with no content so page creation will
|
||||
// still work in this case.
|
||||
return '<!-- wp:divi/placeholder /-->' === $post_content ? '' : $post_content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a single GB gallery to shortcode.
|
||||
*
|
||||
* @param string $gallery Post content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function gb_gallery_to_shortcode( $gallery ) {
|
||||
|
||||
$gallery = is_array( $gallery ) ? $gallery[0] : $gallery;
|
||||
$ids = preg_match_all( '/data-id="(\d+)"/i', $gallery, $matches ) ? $matches[1] : array();
|
||||
$columns = preg_match( '/<ul class="wp-block-gallery columns-(\d)[^"]*?">/i', $gallery, $matches ) ? $matches[1] : 3;
|
||||
$shortcode = sprintf(
|
||||
'[gallery columns="%s" ids="%s"]',
|
||||
intval( $columns ),
|
||||
implode( ',', array_map( 'intval', $ids ) )
|
||||
);
|
||||
|
||||
return $shortcode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert all GB galleries to shortcodes.
|
||||
*
|
||||
* @param string $content Post content.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function gb_galleries_to_shortcodes( $content ) {
|
||||
return preg_replace_callback(
|
||||
$this->_gb_gallery_regexp,
|
||||
array( $this, 'gb_gallery_to_shortcode' ),
|
||||
$content
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a specified post's content for GB gallery and, if present, return the first
|
||||
*
|
||||
* @param string $gallery Gallery data and srcs parsed from the expanded shortcode.
|
||||
* @param int|WP_Post $post Post ID or object.
|
||||
*
|
||||
* @return string|array Gallery data and srcs parsed from the expanded shortcode.
|
||||
*/
|
||||
public function get_post_gallery( $gallery, $post ) {
|
||||
if ( $gallery ) {
|
||||
return $gallery;
|
||||
}
|
||||
|
||||
$content = get_post_field( 'post_content', $post );
|
||||
if ( empty( $content ) ) {
|
||||
return $gallery;
|
||||
}
|
||||
|
||||
if ( preg_match( $this->_gb_gallery_regexp, $content, $matches ) ) {
|
||||
// Found a GB gallery.
|
||||
if ( apply_filters( 'et_gb_gallery_to_shortcode', true ) ) {
|
||||
// Return as shortcode.
|
||||
return do_shortcode( $this->gb_gallery_to_shortcode( $matches[0] ) );
|
||||
}
|
||||
// Return it as is.
|
||||
return $matches[0];
|
||||
}
|
||||
|
||||
return $gallery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete first GB gallery in content
|
||||
*
|
||||
* @param string $content Content.
|
||||
* @param bool $deleted Whether a gallery has been already deleted or not.
|
||||
* @return string
|
||||
*/
|
||||
public function et_delete_post_gallery( $content, $deleted ) {
|
||||
if ( $deleted ) {
|
||||
// If a gallery was already removed, do nothing.
|
||||
return $content;
|
||||
}
|
||||
return preg_replace( $this->_gb_gallery_regexp, '', $content, 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove custom style from our metabox when GB is showing it.
|
||||
*
|
||||
* @param string $post_type Post type.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_meta_boxes( $post_type ) {
|
||||
$is_block_editor_page = $this->_is_block_editor_page();
|
||||
$is_metabox_exist = function_exists( 'et_single_settings_meta_box' );
|
||||
$is_builder_enabled = et_builder_enabled_for_post_type( $post_type );
|
||||
$is_metabox_allowed = et_pb_is_allowed( 'page_options' );
|
||||
|
||||
if ( $is_block_editor_page && $is_metabox_exist && $is_builder_enabled && $is_metabox_allowed ) {
|
||||
// Change our metabox id so that no custom style is applied.
|
||||
remove_meta_box( 'et_settings_meta_box', $post_type, 'side' );
|
||||
add_meta_box(
|
||||
'et_settings_meta_box_gutenberg',
|
||||
esc_html__( 'Divi Page Settings', 'Divi' ),
|
||||
'et_single_settings_meta_box',
|
||||
$post_type,
|
||||
'side',
|
||||
'high'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook into REST API page call.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rest_insert_page() {
|
||||
add_filter( 'update_post_metadata', array( $this, 'update_post_metadata' ), 10, 4 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom auth function for meta updates via REST API.
|
||||
*
|
||||
* @param boolean $allowed True if allowed to view the meta field by default, false if else.
|
||||
* @param string $meta_key The meta key.
|
||||
* @param int $id Post ID.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function meta_auth( $allowed, $meta_key, $id ) {
|
||||
return current_user_can( 'edit_post', $id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook methods to WordPress
|
||||
* Latest plugin version: 1.5
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init_hooks() {
|
||||
if ( is_admin() ) {
|
||||
add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_block_editor_assets' ), 4 );
|
||||
add_action( 'admin_print_scripts-edit.php', array( $this, 'add_new_button' ), 10 );
|
||||
add_action( 'admin_init', array( $this, 'add_edit_link_filters' ) );
|
||||
|
||||
// Only need to add this filter is the nonce is present in the url request
|
||||
// nonce value will be checked in the filter itself.
|
||||
// phpcs:ignore WordPress.Security.NonceVerification -- This is just check, therefore nonce verification not required.
|
||||
if ( isset( $_GET['et_fb_new_vb_nonce'] ) ) {
|
||||
add_action( 'new_to_auto-draft', array( $this, 'auto_draft' ), 1 );
|
||||
}
|
||||
add_filter( 'display_post_states', array( $this, 'display_post_states' ), 10, 2 );
|
||||
} else {
|
||||
// If "Enable Divi Gallery" option is enabled.
|
||||
if ( apply_filters( 'et_gb_gallery_to_shortcode', false ) === true ) {
|
||||
// Converts GB galleries to shortcodes.
|
||||
add_filter( 'the_content', array( $this, 'gb_galleries_to_shortcodes' ), 1 );
|
||||
}
|
||||
if ( apply_filters( 'et_gb_gallery_include_in_get_post_gallery', false ) === true ) {
|
||||
// Makes sure `get_post_gallery` returns a GB gallery if no shortcode is found.
|
||||
add_filter( 'get_post_gallery', array( $this, 'get_post_gallery' ), 10, 2 );
|
||||
}
|
||||
// This filter gets called when Divi removes first gallery shortcode from
|
||||
// a gallery post (as in post format). We hook into that to ensure that the first GB gallery
|
||||
// is deleted if nothing else was.
|
||||
add_filter( 'et_delete_post_gallery', array( $this, 'et_delete_post_gallery' ), 10, 2 );
|
||||
// Provide other code a simple way to access the conversion function via this custom filter.
|
||||
add_filter( 'et_gb_galleries_to_shortcodes', array( $this, 'gb_galleries_to_shortcodes' ) );
|
||||
}
|
||||
|
||||
add_filter( 'et_fb_load_raw_post_content', array( $this, 'et_fb_load_raw_post_content' ) );
|
||||
add_filter( 'init', array( $this, 'ensure_post_type_supports' ), 999999 );
|
||||
|
||||
// This is one of the most idiot things I had to do ever and its due to
|
||||
// a 10 month old-yet not fixed WP bug: https://core.trac.wordpress.org/ticket/42069
|
||||
// TLDR: when updating a post with meta via WP REST API, `update_metadata` should only
|
||||
// be called for metas whose value changed.
|
||||
// However, the equality check is fooled by values including characters that are
|
||||
// slashed or converted to entities, like " or <.
|
||||
// `update_metadata` is then called and returns `false` (because value didn't change) which results
|
||||
// in REST API page update to abort with a 500 error code....
|
||||
// To fix the issue, we hook into REST API page update and force `update_metadata` to return `true`
|
||||
// when value didn't change (only applied to our own meta keys).
|
||||
add_action( 'rest_insert_page', array( $this, 'rest_insert_page' ) );
|
||||
|
||||
// Need to deal with our metabox styling when inside GB.
|
||||
add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ), 10, 1 );
|
||||
|
||||
// To register the post metas is needed because we want to change their value within our custom GB blocks
|
||||
// Editing a post meta via REST API is allowed by default unless its key is protected (starts with `_`)
|
||||
// which is the case here so we also need to create a custom auth function.
|
||||
$auth = array( $this, 'meta_auth' );
|
||||
$args = array(
|
||||
'auth_callback' => $auth,
|
||||
'show_in_rest' => true,
|
||||
'single' => true,
|
||||
'type' => 'string',
|
||||
);
|
||||
register_meta( 'post', '_et_pb_use_builder', $args );
|
||||
$args = array(
|
||||
'auth_callback' => $auth,
|
||||
'show_in_rest' => true,
|
||||
'single' => true,
|
||||
'type' => 'string',
|
||||
);
|
||||
register_meta( 'post', '_et_pb_old_content', $args );
|
||||
$args = array(
|
||||
'auth_callback' => $auth,
|
||||
'show_in_rest' => true,
|
||||
'single' => true,
|
||||
'type' => 'string',
|
||||
);
|
||||
register_meta( 'post', '_et_gb_content_width', $args );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( et_core_is_gutenberg_active() ) {
|
||||
new ET_Builder_Block_Editor_Integration();
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
/**
|
||||
* Block Templates Compatibility.
|
||||
*
|
||||
* @package Builder
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Block Templates Compatibility Class.
|
||||
*
|
||||
* @since 4.9.8
|
||||
*/
|
||||
class ET_Builder_Block_Templates {
|
||||
/**
|
||||
* Instance of `ET_Builder_Block_Templates`.
|
||||
*
|
||||
* @var ET_Builder_Block_Templates
|
||||
*/
|
||||
private static $_instance;
|
||||
|
||||
/**
|
||||
* ET_Builder_Block_Templates constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->set_query_templates_filters();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the class instance.
|
||||
*
|
||||
* @since 4.9.8
|
||||
*
|
||||
* @return ET_Builder_Block_Templates
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( ! self::$_instance ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set query templates filters to override block templates.
|
||||
*
|
||||
* @since 4.9.8
|
||||
*/
|
||||
public function set_query_templates_filters() {
|
||||
// Bail early if current active builder is not DBP.
|
||||
if ( ! et_is_builder_plugin_active() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Bail early if `locate_block_template` function doesn't exists (WP 5.8).
|
||||
if ( ! function_exists( 'locate_block_template' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add those filters only when current active theme supports `block-templates` or
|
||||
// has block templates index HTML.
|
||||
if ( ! current_theme_supports( 'block-templates' ) && ! is_readable( get_stylesheet_directory() . '/block-templates/index.html' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of possible hook names:
|
||||
* - `404_template`
|
||||
* - `archive_template`
|
||||
* - `attachment_template` (Not Included)
|
||||
* - `author_template`
|
||||
* - `category_template`
|
||||
* - `date_template`
|
||||
* - `embed_template` (Not Included)
|
||||
* - `frontpage_template`
|
||||
* - `home_template`
|
||||
* - `index_template`
|
||||
* - `page_template`
|
||||
* - `paged_template`
|
||||
* - `privacypolicy_template`
|
||||
* - `search_template`
|
||||
* - `single_template`
|
||||
* - `singular_template`
|
||||
* - `tag_template`
|
||||
* - `taxonomy_template`
|
||||
*
|
||||
* However we don't include `attachment`, `paged`, and `embed` because they are not
|
||||
* modified or attached to TB tempates.
|
||||
*/
|
||||
$template_types = array(
|
||||
'404_template',
|
||||
'archive_template',
|
||||
'author_template',
|
||||
'category_template',
|
||||
'date_template',
|
||||
'frontpage_template',
|
||||
'home_template',
|
||||
'index_template',
|
||||
'page_template',
|
||||
'privacypolicy_template',
|
||||
'search_template',
|
||||
'single_template',
|
||||
'singular_template',
|
||||
'tag_template',
|
||||
'taxonomy_template',
|
||||
);
|
||||
|
||||
foreach ( $template_types as $template ) {
|
||||
add_filter( $template, array( $this, 'get_custom_query_template' ), 30, 3 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get pre-defined query template to override block template (modified default template
|
||||
* or custom template).
|
||||
*
|
||||
* @since 4.9.8
|
||||
*
|
||||
* @param string $template Path to the template. See locate_template().
|
||||
* @param string $type Sanitized filename without extension.
|
||||
* @param array $templates A list of template candidates, in descending order of priority.
|
||||
*
|
||||
* @return string Modified path to the template.
|
||||
*/
|
||||
public function get_custom_query_template( $template, $type, $templates ) {
|
||||
// Bail early if there is no TB templates for current page request.
|
||||
if ( empty( et_theme_builder_get_template_layouts() ) ) {
|
||||
return $template;
|
||||
}
|
||||
|
||||
// 1. Restore - Get pre-defined query template.
|
||||
$original_template = $template;
|
||||
$template = locate_template( $templates );
|
||||
|
||||
// If the `locate_template` return empty path because there is no template or theme
|
||||
// theme compat found, use builder block template canvas.
|
||||
if ( empty( $template ) && 'template-canvas.php' === basename( $original_template ) ) {
|
||||
$template = ET_BUILDER_DIR . 'templates/block-template-canvas.php';
|
||||
}
|
||||
|
||||
// 2. Remove hooks added for template canvas (block template).
|
||||
// Remove viewport meta tag.
|
||||
if ( function_exists( '_block_template_viewport_meta_tag' ) ) {
|
||||
remove_action( 'wp_head', '_block_template_viewport_meta_tag', 0 );
|
||||
}
|
||||
|
||||
// Render conditional title tag for `title-tag` support.
|
||||
add_action( 'wp_head', '_wp_render_title_tag', 1 );
|
||||
|
||||
// Remove unconditional title tag.
|
||||
if ( function_exists( '_block_template_render_title_tag' ) ) {
|
||||
remove_action( 'wp_head', '_block_template_render_title_tag', 1 );
|
||||
}
|
||||
|
||||
return $template;
|
||||
}
|
||||
}
|
||||
|
||||
ET_Builder_Block_Templates::instance();
|
||||
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
/**
|
||||
* Classic Editor Enabler.
|
||||
*
|
||||
* @package Builder
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'ET_Builder_Classic_Editor' ) ) :
|
||||
/**
|
||||
* Load classic editor and disable Gutenberg/Block Editor
|
||||
*
|
||||
* Adapted from Classic Editor plugin by WordPress Contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU
|
||||
* General Public License version 2, as published by the Free Software Foundation. You may NOT assume
|
||||
* that you can use any other version of the GPL.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
||||
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* Classic Editor
|
||||
*
|
||||
* Copyright 2018 by WordPress Contributors
|
||||
*
|
||||
* Classic Editor is released under the GPL-2.0+
|
||||
*/
|
||||
class ET_Builder_Classic_Editor {
|
||||
/**
|
||||
* Instance of `ET_Builder_Classic_Editor`.
|
||||
*
|
||||
* @var ET_Builder_Classic_Editor
|
||||
*/
|
||||
private static $_instance;
|
||||
|
||||
/**
|
||||
* ET_Builder_Classic_Editor constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'init', array( $this, 'register_actions' ), 20 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the class instance.
|
||||
*
|
||||
* @since 3.18
|
||||
*
|
||||
* @return ET_Builder_Classic_Editor
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( ! self::$_instance ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add & remove necessary actions and filters needed to load Classic Editor back
|
||||
* These filters are based on Classic Editor plugin to ensure required filters & actions needed
|
||||
* to load Classic Editor on Gutenberg / Block Editor (WordPress 5.0). All conditiononal Block Editor
|
||||
* loader based on query string has been removed.
|
||||
*
|
||||
* @since 3.18
|
||||
*/
|
||||
public function register_actions() {
|
||||
$gutenberg = has_filter( 'replace_editor', 'gutenberg_init' );
|
||||
$block_editor = version_compare( $GLOBALS['wp_version'], '5.0-beta', '>' );
|
||||
|
||||
if ( ! $gutenberg && ! $block_editor ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load classic editor.
|
||||
// phpcs:ignore WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF.
|
||||
$enable_classic_editor = apply_filters( 'et_builder_enable_classic_editor', isset( $_GET['et_classic_editor'] ) );
|
||||
|
||||
if ( $block_editor && $enable_classic_editor ) {
|
||||
add_filter( 'use_block_editor_for_post_type', '__return_false', 100 );
|
||||
}
|
||||
|
||||
if ( $gutenberg && $enable_classic_editor ) {
|
||||
// gutenberg.php.
|
||||
remove_action( 'admin_menu', 'gutenberg_menu' );
|
||||
remove_action( 'admin_notices', 'gutenberg_build_files_notice' );
|
||||
remove_action( 'admin_notices', 'gutenberg_wordpress_version_notice' );
|
||||
remove_action( 'admin_init', 'gutenberg_redirect_demo' );
|
||||
|
||||
remove_filter( 'replace_editor', 'gutenberg_init' );
|
||||
|
||||
// lib/client-assets.php.
|
||||
remove_action( 'wp_enqueue_scripts', 'gutenberg_register_scripts_and_styles', 5 );
|
||||
remove_action( 'admin_enqueue_scripts', 'gutenberg_register_scripts_and_styles', 5 );
|
||||
remove_action( 'wp_enqueue_scripts', 'gutenberg_common_scripts_and_styles' );
|
||||
remove_action( 'admin_enqueue_scripts', 'gutenberg_common_scripts_and_styles' );
|
||||
|
||||
// lib/compat.php.
|
||||
remove_filter( 'wp_refresh_nonces', 'gutenberg_add_rest_nonce_to_heartbeat_response_headers' );
|
||||
|
||||
// lib/rest-api.php.
|
||||
remove_action( 'rest_api_init', 'gutenberg_register_rest_routes' );
|
||||
remove_action( 'rest_api_init', 'gutenberg_add_taxonomy_visibility_field' );
|
||||
|
||||
remove_filter( 'rest_request_after_callbacks', 'gutenberg_filter_oembed_result' );
|
||||
remove_filter( 'registered_post_type', 'gutenberg_register_post_prepare_functions' );
|
||||
remove_filter( 'register_post_type_args', 'gutenberg_filter_post_type_labels' );
|
||||
|
||||
// lib/meta-box-partial-page.php.
|
||||
remove_action( 'do_meta_boxes', 'gutenberg_meta_box_save', 1000 );
|
||||
remove_action( 'submitpost_box', 'gutenberg_intercept_meta_box_render' );
|
||||
remove_action( 'submitpage_box', 'gutenberg_intercept_meta_box_render' );
|
||||
remove_action( 'edit_page_form', 'gutenberg_intercept_meta_box_render' );
|
||||
remove_action( 'edit_form_advanced', 'gutenberg_intercept_meta_box_render' );
|
||||
|
||||
remove_filter( 'redirect_post_location', 'gutenberg_meta_box_save_redirect' );
|
||||
remove_filter( 'filter_gutenberg_meta_boxes', 'gutenberg_filter_meta_boxes' );
|
||||
}
|
||||
|
||||
if ( $gutenberg && $enable_classic_editor ) {
|
||||
// gutenberg.php.
|
||||
remove_action( 'admin_init', 'gutenberg_add_edit_link_filters' );
|
||||
remove_action( 'admin_print_scripts-edit.php', 'gutenberg_replace_default_add_new_button' );
|
||||
|
||||
remove_filter( 'body_class', 'gutenberg_add_responsive_body_class' );
|
||||
remove_filter( 'admin_url', 'gutenberg_modify_add_new_button_url' );
|
||||
|
||||
// lib/compat.php.
|
||||
remove_action( 'admin_enqueue_scripts', 'gutenberg_check_if_classic_needs_warning_about_blocks' );
|
||||
|
||||
// lib/register.php.
|
||||
remove_action( 'edit_form_top', 'gutenberg_remember_classic_editor_when_saving_posts' );
|
||||
|
||||
remove_filter( 'redirect_post_location', 'gutenberg_redirect_to_classic_editor_when_saving_posts' );
|
||||
remove_filter( 'get_edit_post_link', 'gutenberg_revisions_link_to_editor' );
|
||||
remove_filter( 'wp_prepare_revision_for_js', 'gutenberg_revisions_restore' );
|
||||
remove_filter( 'display_post_states', 'gutenberg_add_gutenberg_post_state' );
|
||||
|
||||
// lib/plugin-compat.php.
|
||||
remove_filter( 'rest_pre_insert_post', 'gutenberg_remove_wpcom_markdown_support' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
||||
|
||||
ET_Builder_Classic_Editor::instance();
|
||||
@@ -0,0 +1,419 @@
|
||||
<?php
|
||||
/**
|
||||
* Handle error report
|
||||
*
|
||||
* @package Builder
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
// get_plugins() is only available on dashboard; Manually require it needed.
|
||||
if ( ! function_exists( 'get_plugins' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to send an error report.
|
||||
*/
|
||||
class ET_Builder_Error_Report {
|
||||
/**
|
||||
* Instance of `ET_Core_Data_Utils`.
|
||||
*
|
||||
* @var ET_Core_Data_Utils
|
||||
*/
|
||||
protected static $_;
|
||||
|
||||
/**
|
||||
* Instance of `ET_Builder_Error_Report`.
|
||||
*
|
||||
* @var ET_Builder_Error_Report
|
||||
*/
|
||||
private static $_instance;
|
||||
|
||||
/**
|
||||
* ET_Builder_Error_Report constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'wp_ajax_et_fb_error_report', array( 'ET_Builder_Error_Report', 'endpoint' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get json_decode data and stripslashes if needed.
|
||||
*
|
||||
* @since 3.24
|
||||
*
|
||||
* @param string $data Data to be decoded.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function json_decode_maybe_stripslashes( $data ) {
|
||||
$decoded = json_decode( $data, true );
|
||||
if ( null === $decoded ) {
|
||||
$decoded = json_decode( stripslashes( $data ), true );
|
||||
}
|
||||
return $decoded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the class instance.
|
||||
*
|
||||
* @since 3.21.4
|
||||
*
|
||||
* @return ET_Builder_Error_Report
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( ! self::$_instance ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
self::$_ = ET_Core_Data_Utils::instance();
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information sent for error reporting
|
||||
*
|
||||
* @since 3.21.4
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_debug_info() {
|
||||
$info = array(
|
||||
'user' => array(
|
||||
'role',
|
||||
),
|
||||
'errors' => array(
|
||||
'error_message',
|
||||
'error_message_stack',
|
||||
'error_stack',
|
||||
'component_info',
|
||||
'notes',
|
||||
),
|
||||
'page' => array(
|
||||
'post_type',
|
||||
'builder_settings',
|
||||
'builder_history',
|
||||
'preferences',
|
||||
),
|
||||
'installation' => array(
|
||||
'product_name',
|
||||
'product_version',
|
||||
'builder_version',
|
||||
'wp_version',
|
||||
'installed_plugins',
|
||||
'active_plugins',
|
||||
'must_use_plugins',
|
||||
),
|
||||
);
|
||||
|
||||
// If the site uses divi builder plugin, provide the theme information.
|
||||
if ( et_is_builder_plugin_active() ) {
|
||||
array_unshift(
|
||||
$info['installation'],
|
||||
'theme_name'
|
||||
);
|
||||
}
|
||||
|
||||
// If the site uses child theme, provide the child theme information.
|
||||
if ( is_child_theme() ) {
|
||||
array_unshift(
|
||||
$info['installation'],
|
||||
'is_child_theme',
|
||||
'child_theme_name',
|
||||
'child_theme_version'
|
||||
);
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current product name
|
||||
*
|
||||
* @since 3.21.4
|
||||
*
|
||||
* @return string|bool
|
||||
*/
|
||||
protected function _get_product() {
|
||||
if ( et_is_builder_plugin_active() ) {
|
||||
return 'divi-builder';
|
||||
}
|
||||
|
||||
if ( function_exists( 'et_divi_fonts_url' ) ) {
|
||||
return 'Divi';
|
||||
}
|
||||
|
||||
if ( function_exists( 'et_extra_fonts_url' ) ) {
|
||||
return 'Extra';
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get debug item value
|
||||
*
|
||||
* @since 3.21.4
|
||||
*
|
||||
* @param string $info_name debug info item name.
|
||||
* @param object $post alias for $_POST.
|
||||
*
|
||||
* @return string|array|object
|
||||
*/
|
||||
protected function _get_debug_value( $info_name, $post ) {
|
||||
switch ( $info_name ) {
|
||||
case 'role':
|
||||
$current_user = wp_get_current_user();
|
||||
$value = esc_html( implode( ', ', $current_user->roles ) );
|
||||
break;
|
||||
|
||||
case 'error_message':
|
||||
case 'error_message_stack':
|
||||
case 'error_stack':
|
||||
case 'notes':
|
||||
case 'post_type':
|
||||
// this will be saved into a text report, no need to convert entities.
|
||||
$value = self::$_->array_get( $post, $info_name, '' );
|
||||
break;
|
||||
|
||||
case 'latest_content':
|
||||
case 'loaded_content':
|
||||
$value = et_fb_process_to_shortcode( self::$_->array_get( $post, $info_name, array() ) );
|
||||
break;
|
||||
|
||||
case 'builder_settings':
|
||||
case 'builder_history':
|
||||
case 'component_info':
|
||||
$value = wp_json_encode( self::$_->array_get( $post, $info_name, array() ) );
|
||||
break;
|
||||
|
||||
case 'preferences':
|
||||
$value = array();
|
||||
foreach ( et_fb_app_preferences() as $name => $preference ) {
|
||||
$value[ $name ] = $preference['value'];
|
||||
}
|
||||
$value = wp_json_encode( $value );
|
||||
break;
|
||||
|
||||
case 'product_name':
|
||||
$value = $this->_get_product();
|
||||
break;
|
||||
|
||||
case 'product_version':
|
||||
$value = et_is_builder_plugin_active() ?
|
||||
self::$_->array_get( get_plugin_data( WP_PLUGIN_DIR . '/divi-builder/divi-builder.php' ), 'Version', '' ) :
|
||||
et_get_theme_version();
|
||||
|
||||
$value = esc_html( $value );
|
||||
break;
|
||||
|
||||
case 'builder_version':
|
||||
$value = ET_BUILDER_PRODUCT_VERSION;
|
||||
break;
|
||||
|
||||
case 'wp_version':
|
||||
$value = esc_html( get_bloginfo( 'version' ) );
|
||||
break;
|
||||
|
||||
case 'installed_plugins':
|
||||
$all_plugins = get_plugins();
|
||||
$value = wp_json_encode( array_keys( $all_plugins ), true );
|
||||
break;
|
||||
|
||||
case 'active_plugins':
|
||||
$all_plugins = get_plugins();
|
||||
$active_plugins_saved = get_option( 'active_plugins' );
|
||||
$active_plugins_keys = is_array( $active_plugins_saved ) ? $active_plugins_saved : array();
|
||||
$active_plugins = array_intersect_key( $all_plugins, array_flip( $active_plugins_keys ) );
|
||||
$value = wp_json_encode( $active_plugins, true );
|
||||
break;
|
||||
|
||||
case 'must_use_plugins':
|
||||
$value = wp_json_encode( get_mu_plugins(), true );
|
||||
break;
|
||||
|
||||
case 'theme_name':
|
||||
case 'child_theme_name':
|
||||
$value = esc_html( wp_get_theme()->get( 'Name' ) );
|
||||
break;
|
||||
|
||||
case 'theme_version':
|
||||
case 'child_theme_version':
|
||||
$value = esc_html( wp_get_theme()->get( 'Version' ) );
|
||||
break;
|
||||
|
||||
case 'is_child_theme':
|
||||
$value = is_child_theme() ? 'yes' : 'no';
|
||||
break;
|
||||
|
||||
default:
|
||||
$value = '';
|
||||
break;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get error report content
|
||||
*
|
||||
* @since 3.21.4
|
||||
*
|
||||
* @param string $data Report data.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _get_report_content( $data ) {
|
||||
$report_content = '';
|
||||
|
||||
$debug_info = self::get_debug_info();
|
||||
|
||||
$report_content = array();
|
||||
|
||||
foreach ( $debug_info as $items_title => $debug_items ) {
|
||||
$item_key = 'group_title-' . $items_title;
|
||||
$items_title = ucwords( $items_title );
|
||||
|
||||
$report_content[ $item_key ] = $items_title;
|
||||
|
||||
foreach ( $debug_items as $debug_item ) {
|
||||
$item_value = et_core_esc_previously( $this->_get_debug_value( $debug_item, $data, 'array' ) );
|
||||
|
||||
$report_content[ $debug_item ] = $item_value;
|
||||
}
|
||||
}
|
||||
|
||||
return $report_content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get attachment data as string to be passed into endpoint
|
||||
*
|
||||
* @since 3.21.4
|
||||
*
|
||||
* @param string $data Report data.
|
||||
* @param string $field Debug info item name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function _get_exported_layout_content( $data, $field ) {
|
||||
// phpcs:disable WordPress.Security.NonceVerification -- Nonce has been verified in the {@see self::endpoint()}.
|
||||
// Set faux $_POST value that is required by portability.
|
||||
$_POST['post'] = isset( $_POST['post_id'] ) ? sanitize_text_field( $_POST['post_id'] ) : '';
|
||||
$_POST['content'] = self::$_instance->_get_debug_value( $field, $data );
|
||||
|
||||
// Remove page value if it is equal to `false`, avoiding paginated images not accidentally triggered.
|
||||
if ( isset( $_POST['page'] ) && false === $_POST['page'] ) {
|
||||
unset( $_POST['page'] );
|
||||
}
|
||||
|
||||
$portability = et_core_portability_load( 'et_builder' );
|
||||
// Export the content.
|
||||
$result = $portability->export( true );
|
||||
// Delete temp files or else the same content will be used for all exports.
|
||||
$portability->delete_temp_files( 'et_core_export' );
|
||||
return $result;
|
||||
// phpcs:enable
|
||||
}
|
||||
|
||||
/**
|
||||
* Endpoint for sending error report request
|
||||
*
|
||||
* @since 3.21.4
|
||||
*/
|
||||
public static function endpoint() {
|
||||
// Check for valid permission. Only administrator role can send error report.
|
||||
if ( ! et_core_security_check_passed( 'manage_options', 'et_fb_send_error_report' ) ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => esc_html__( 'You do not have valid permission to send error report', 'et_builder' ),
|
||||
)
|
||||
);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// Check valid post id.
|
||||
$post_id = self::$_->array_get( $_POST, 'post_id', false );
|
||||
|
||||
if ( ! $post_id ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => esc_html__( 'No valid post id found', 'et_builder' ),
|
||||
)
|
||||
);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// Check report data.
|
||||
$data = self::$_->array_get( $_POST, 'data', false );
|
||||
|
||||
if ( ! $data ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => esc_html__( 'No valid report data found', 'et_builder' ),
|
||||
)
|
||||
);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// Check for Elegant Themes username & API Key.
|
||||
$updates_options = get_site_option( 'et_automatic_updates_options', array() );
|
||||
$et_username = self::$_->array_get( $updates_options, 'username', '' );
|
||||
$et_api_key = self::$_->array_get( $updates_options, 'api_key', '' );
|
||||
|
||||
if ( '' === $et_username || '' === $et_api_key ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => esc_html__( 'No Elegant Themes username or API key found', 'et_builder' ),
|
||||
)
|
||||
);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// Check for account status.
|
||||
$et_account_status = get_site_option( 'et_account_status', 'not_active' );
|
||||
|
||||
if ( 'active' !== $et_account_status ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'message' => esc_html__( 'Your Elegant Themes account is inactive', 'et_builder' ),
|
||||
)
|
||||
);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
$data = self::json_decode_maybe_stripslashes( $data );
|
||||
$et_endpoint = apply_filters( 'et_builder_report_endpoint', 'https://www.elegantthemes.com/api/reportV2.php' );
|
||||
|
||||
// Crafting reports and send to end endpoint.
|
||||
$request_settings = array(
|
||||
'timeout' => 30,
|
||||
'body' => array(
|
||||
'username' => $et_username,
|
||||
'api_key' => $et_api_key,
|
||||
'error_report' => self::$_instance->_get_report_content( $data ),
|
||||
'site_url' => site_url(),
|
||||
'attachments' => array(
|
||||
'latest' => self::$_instance->_get_exported_layout_content( $data, 'latest_content' ),
|
||||
'loaded' => self::$_instance->_get_exported_layout_content( $data, 'loaded_content' ),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$request = wp_remote_post( $et_endpoint, $request_settings );
|
||||
$request_response_code = wp_remote_retrieve_response_code( $request );
|
||||
$request_body = wp_remote_retrieve_body( $request );
|
||||
|
||||
if ( 200 === $request_response_code ) {
|
||||
wp_send_json_success();
|
||||
} else {
|
||||
wp_send_json_error( json_decode( $request_body ) );
|
||||
}
|
||||
wp_die();
|
||||
}
|
||||
}
|
||||
|
||||
ET_Builder_Error_Report::instance();
|
||||
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
/**
|
||||
* Cached common translation.
|
||||
*
|
||||
* @package Builder
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Commonly used translations.
|
||||
*/
|
||||
class ET_Builder_I18n {
|
||||
|
||||
/**
|
||||
* Retrieve a commonly used translation.
|
||||
*
|
||||
* @since 4.4.9
|
||||
*
|
||||
* @param string $key Translation key.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get( $key ) {
|
||||
// phpcs:disable PSR2.ControlStructures.SwitchDeclaration.SpaceBeforeColonCASE
|
||||
// phpcs:disable PSR2.ControlStructures.SwitchDeclaration.BodyOnNextLineCASE
|
||||
switch ( $key ) {
|
||||
// To avoid breaking tests:
|
||||
// 1. Do not remove `i18-list-begin` / `i18-list-end` tags.
|
||||
// 2. One traslation per line.
|
||||
// 3. `et_builder` Text Domain only.
|
||||
// 4. No comments / empty lines.
|
||||
// 5. Keep the list ordered, if can't do with your IDE, switch to Emacs.
|
||||
// i18-list-begin.
|
||||
case 'Admin Label' : return esc_html__( 'Admin Label', 'et_builder' );
|
||||
case 'Advanced' : return esc_html__( 'Advanced', 'et_builder' );
|
||||
case 'After' : return esc_html__( 'After', 'et_builder' );
|
||||
case 'Background' : return esc_html__( 'Background', 'et_builder' );
|
||||
case 'Before' : return esc_html__( 'Before', 'et_builder' );
|
||||
case 'Blur' : return esc_html__( 'Blur', 'et_builder' );
|
||||
case 'Body' : return esc_html__( 'Body', 'et_builder' );
|
||||
case 'Bottom Center' : return esc_html__( 'Bottom Center', 'et_builder' );
|
||||
case 'Bottom Left' : return esc_html__( 'Bottom Left', 'et_builder' );
|
||||
case 'Bottom Right' : return esc_html__( 'Bottom Right', 'et_builder' );
|
||||
case 'Bottom' : return esc_html__( 'Bottom', 'et_builder' );
|
||||
case 'Button' : return esc_html__( 'Button', 'et_builder' );
|
||||
case 'Cancel' : return esc_html__( 'Cancel', 'et_builder' );
|
||||
case 'Center Center' : return esc_html__( 'Center Center', 'et_builder' );
|
||||
case 'Center Left' : return esc_html__( 'Center Left', 'et_builder' );
|
||||
case 'Center Right' : return esc_html__( 'Center Right', 'et_builder' );
|
||||
case 'Center' : return esc_html__( 'Center', 'et_builder' );
|
||||
case 'Circle' : return esc_html__( 'Circle', 'et_builder' );
|
||||
case 'Color Burn' : return esc_html__( 'Color Burn', 'et_builder' );
|
||||
case 'Color Dodge' : return esc_html__( 'Color Dodge', 'et_builder' );
|
||||
case 'Color' : return esc_html__( 'Color', 'et_builder' );
|
||||
case 'Content' : return esc_html__( 'Content', 'et_builder' );
|
||||
case 'Custom CSS' : return esc_html__( 'Custom CSS', 'et_builder' );
|
||||
case 'Dark' : return esc_html__( 'Dark', 'et_builder' );
|
||||
case 'Darken' : return esc_html__( 'Darken', 'et_builder' );
|
||||
case 'Default' : return esc_html__( 'Default', 'et_builder' );
|
||||
case 'Design' : return esc_html__( 'Design', 'et_builder' );
|
||||
case 'Desktop' : return esc_html__( 'Desktop', 'et_builder' );
|
||||
case 'Difference' : return esc_html__( 'Difference', 'et_builder' );
|
||||
case 'Disc' : return esc_html__( 'Disc', 'et_builder' );
|
||||
case 'Down' : return esc_html__( 'Down', 'et_builder' );
|
||||
case 'Ease' : return esc_html__( 'Ease', 'et_builder' );
|
||||
case 'Ease-In' : return esc_html__( 'Ease-In', 'et_builder' );
|
||||
case 'Ease-In-Out' : return esc_html__( 'Ease-In-Out', 'et_builder' );
|
||||
case 'Ease-Out' : return esc_html__( 'Ease-Out', 'et_builder' );
|
||||
case 'Elements' : return esc_html__( 'Elements', 'et_builder' );
|
||||
case 'Exclusion' : return esc_html__( 'Exclusion', 'et_builder' );
|
||||
case 'Expand' : return esc_html__( 'Expand', 'et_builder' );
|
||||
case 'Fade' : return esc_html__( 'Fade', 'et_builder' );
|
||||
case 'Flip' : return esc_html__( 'Flip', 'et_builder' );
|
||||
case 'Hard Light' : return esc_html__( 'Hard Light', 'et_builder' );
|
||||
case 'Hue' : return esc_html__( 'Hue', 'et_builder' );
|
||||
case 'Image' : return esc_html__( 'Image', 'et_builder' );
|
||||
case 'Inside' : return esc_html__( 'Inside', 'et_builder' );
|
||||
case 'Layout' : return esc_html__( 'Layout', 'et_builder' );
|
||||
case 'Left' : return esc_html__( 'Left', 'et_builder' );
|
||||
case 'Light' : return esc_html__( 'Light', 'et_builder' );
|
||||
case 'Lighten' : return esc_html__( 'Lighten', 'et_builder' );
|
||||
case 'Linear' : return esc_html__( 'Linear', 'et_builder' );
|
||||
case 'Link' : return esc_html__( 'Link', 'et_builder' );
|
||||
case 'Luminosity' : return esc_html__( 'Luminosity', 'et_builder' );
|
||||
case 'Main Element' : return esc_html__( 'Main Element', 'et_builder' );
|
||||
case 'Multiply' : return esc_html__( 'Multiply', 'et_builder' );
|
||||
case 'No' : return esc_html__( 'No', 'et_builder' );
|
||||
case 'None' : return esc_html__( 'None', 'et_builder' );
|
||||
case 'Normal' : return esc_html__( 'Normal', 'et_builder' );
|
||||
case 'Off' : return esc_html__( 'Off', 'et_builder' );
|
||||
case 'On' : return esc_html__( 'On', 'et_builder' );
|
||||
case 'Outside' : return esc_html__( 'Outside', 'et_builder' );
|
||||
case 'Overlay' : return esc_html__( 'Overlay', 'et_builder' );
|
||||
case 'Phone' : return esc_html__( 'Phone', 'et_builder' );
|
||||
case 'Position' : return esc_html__( 'Position', 'et_builder' );
|
||||
case 'Radial' : return esc_html__( 'Radial', 'et_builder' );
|
||||
case 'Right' : return esc_html__( 'Right', 'et_builder' );
|
||||
case 'Saturation' : return esc_html__( 'Saturation', 'et_builder' );
|
||||
case 'Screen' : return esc_html__( 'Screen', 'et_builder' );
|
||||
case 'Sizing' : return esc_html__( 'Sizing', 'et_builder' );
|
||||
case 'Slide' : return esc_html__( 'Slide', 'et_builder' );
|
||||
case 'Soft Light' : return esc_html__( 'Soft Light', 'et_builder' );
|
||||
case 'Space' : return esc_html__( 'Space', 'et_builder' );
|
||||
case 'Square' : return esc_html__( 'Square', 'et_builder' );
|
||||
case 'Tablet' : return esc_html__( 'Tablet', 'et_builder' );
|
||||
case 'Text' : return esc_html__( 'Text', 'et_builder' );
|
||||
case 'Title' : return esc_html__( 'Title', 'et_builder' );
|
||||
case 'Top Center' : return esc_html__( 'Top Center', 'et_builder' );
|
||||
case 'Top Left' : return esc_html__( 'Top Left', 'et_builder' );
|
||||
case 'Top Right' : return esc_html__( 'Top Right', 'et_builder' );
|
||||
case 'Top' : return esc_html__( 'Top', 'et_builder' );
|
||||
case 'Up' : return esc_html__( 'Up', 'et_builder' );
|
||||
case 'Upload an image' : return esc_attr__( 'Upload an image', 'et_builder' );
|
||||
case 'Visibility' : return esc_attr__( 'Visibility', 'et_builder' );
|
||||
case 'Yes' : return esc_html__( 'Yes', 'et_builder' );
|
||||
// i18-list-end.
|
||||
}
|
||||
// phpcs:enable
|
||||
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,294 @@
|
||||
<?php
|
||||
|
||||
class ET_Builder_Global_Presets_History {
|
||||
const CUSTOM_DEFAULTS_HISTORY_OPTION = 'builder_custom_defaults_history';
|
||||
const GLOBAL_PRESETS_HISTORY_OPTION = 'builder_global_presets_history';
|
||||
const GLOBAL_PRESETS_HISTORY_LENGTH = 100;
|
||||
|
||||
private static $instance;
|
||||
|
||||
private function __construct() {
|
||||
$this->_register_ajax_callbacks();
|
||||
$this->_register_hooks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns instance of the singleton class
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @return ET_Builder_Global_Presets_History
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( ! isset( self::$instance ) ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
private function _register_ajax_callbacks() {
|
||||
add_action(
|
||||
'wp_ajax_et_builder_save_global_presets_history',
|
||||
array(
|
||||
$this,
|
||||
'ajax_save_global_presets_history',
|
||||
)
|
||||
);
|
||||
add_action(
|
||||
'wp_ajax_et_builder_retrieve_global_presets_history',
|
||||
array(
|
||||
$this,
|
||||
'ajax_retrieve_global_presets_history',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private function _register_hooks() {
|
||||
add_action( 'et_builder_modules_loaded', array( $this, 'migrate_custom_defaults_history' ), 99 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles AJAX requests to save history of Global Presets settings changes
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function ajax_save_global_presets_history() {
|
||||
// Allow saving Global Presets for admins and support elevated users only
|
||||
if ( ! et_core_security_check_passed( 'switch_themes', 'et_builder_save_global_presets_history' ) ) {
|
||||
wp_send_json_error(
|
||||
array(
|
||||
'code' => 'et_forbidden',
|
||||
'message' => esc_html__( 'You do not have sufficient permissions to edit Divi Presets.', 'et_builder' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$history = json_decode( stripslashes( $_POST['history'] ) );
|
||||
|
||||
if ( self::sanitize_and_validate( $history ) ) {
|
||||
$current_settings = $history->history[ $history->index ];
|
||||
et_update_option( ET_Builder_Global_Presets_Settings::GLOBAL_PRESETS_OPTION, $current_settings->settings );
|
||||
et_update_option( self::GLOBAL_PRESETS_HISTORY_OPTION, $history );
|
||||
ET_Core_PageResource::remove_static_resources( 'all', 'all' );
|
||||
|
||||
if ( et_get_option( ET_Builder_Global_Presets_Settings::CUSTOM_DEFAULTS_UNMIGRATED_OPTION, false ) ) {
|
||||
et_delete_option( ET_Builder_Global_Presets_Settings::CUSTOM_DEFAULTS_UNMIGRATED_OPTION );
|
||||
et_fb_delete_builder_assets();
|
||||
}
|
||||
|
||||
ET_Builder_Ajax_Cache::instance()->unset_( 'ET_Builder_Global_Presets_History' );
|
||||
|
||||
wp_send_json_success();
|
||||
} else {
|
||||
et_core_die( esc_html__( 'Global History data is corrupt.', 'et_builder' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles AJAX requests to retrieve history of Global Presets settings changes
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function ajax_retrieve_global_presets_history() {
|
||||
if ( ! et_core_security_check_passed( 'edit_posts', 'et_builder_retrieve_global_presets_history' ) ) {
|
||||
wp_send_json_error();
|
||||
}
|
||||
|
||||
$history = $this->_get_global_presets_history();
|
||||
|
||||
ET_Builder_Ajax_Cache::instance()->set( 'ET_Builder_Global_Presets_History', $history );
|
||||
|
||||
wp_send_json_success( $history );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new Global Presets settings history record
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @param {Object} $defaults
|
||||
*/
|
||||
public function add_global_history_record( $defaults ) {
|
||||
if ( empty( $defaults ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$new_record = (object) array(
|
||||
'settings' => $defaults,
|
||||
'time' => time() * 1000,
|
||||
'label' => esc_html__( 'Imported From Layout', 'et_builder' ),
|
||||
);
|
||||
|
||||
$history = $this->_get_global_presets_history();
|
||||
$history_index = (int) $history->index;
|
||||
|
||||
$history->history = array_slice( $history->history, 0, $history_index + 1 );
|
||||
array_push( $history->history, $new_record );
|
||||
$history->index++;
|
||||
|
||||
if ( count( $history->history ) > self::GLOBAL_PRESETS_HISTORY_LENGTH ) {
|
||||
$history->history = array_slice( $history->history, -self::GLOBAL_PRESETS_HISTORY_LENGTH );
|
||||
$history->index = min( $history->index, self::GLOBAL_PRESETS_HISTORY_LENGTH - 1 );
|
||||
}
|
||||
|
||||
et_update_option( self::GLOBAL_PRESETS_HISTORY_OPTION, $history );
|
||||
ET_Core_PageResource::remove_static_resources( 'all', 'all' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs validation and sanitizing history object.
|
||||
* Returns false if data is invalid or corrupt.
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @param $data
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function sanitize_and_validate( &$data ) {
|
||||
if ( ! is_object( $data ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$properties = array(
|
||||
'history',
|
||||
'index',
|
||||
);
|
||||
|
||||
foreach ( $properties as $property ) {
|
||||
if ( ! property_exists( $data, $property ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! is_array( $data->history ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ( $data->history as &$record ) {
|
||||
if ( ! is_object( $record ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$properties = array(
|
||||
'settings',
|
||||
'time',
|
||||
'label',
|
||||
);
|
||||
|
||||
if ( count( (array) $record ) !== count( $properties ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ( $properties as $property ) {
|
||||
if ( ! property_exists( $record, $property ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $record->settings as &$module ) {
|
||||
if ( ! is_object( $module ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! is_numeric( $record->time ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$record->label = sanitize_text_field( $record->label );
|
||||
}
|
||||
|
||||
$data->index = sanitize_text_field( $data->index );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles theme version rollback.
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @param string $product_name - The short name of the product rolling back.
|
||||
* @param string $rollback_from_version
|
||||
* @param string $rollback_to_version
|
||||
*/
|
||||
public function after_version_rollback( $product_name, $rollback_from_version, $rollback_to_version ) {
|
||||
if ( ! isset( ET_Builder_Global_Presets_Settings::$allowed_products[ $product_name ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( 0 > version_compare( $rollback_to_version, ET_Builder_Global_Presets_Settings::$allowed_products[ $product_name ] ) ) {
|
||||
et_delete_option( self::GLOBAL_PRESETS_HISTORY_OPTION );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Global Presets history object from DB
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
private function _get_global_presets_history() {
|
||||
$history = et_get_option( self::GLOBAL_PRESETS_HISTORY_OPTION, false );
|
||||
if ( ! $history ) {
|
||||
$history = (object) array(
|
||||
'history' => array(),
|
||||
'index' => - 1,
|
||||
);
|
||||
}
|
||||
|
||||
return $history;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates Custom Defaults history format to Global Presets history format
|
||||
*
|
||||
* @since 4.5.0
|
||||
*/
|
||||
public static function migrate_custom_defaults_history() {
|
||||
if ( et_is_builder_plugin_active() || ET_Builder_Global_Presets_Settings::are_custom_defaults_migrated() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$history = et_get_option( self::CUSTOM_DEFAULTS_HISTORY_OPTION, false );
|
||||
|
||||
if ( ! $history ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$all_modules = ET_Builder_Element::get_modules();
|
||||
$migrated_history = (object) array();
|
||||
$migrated_history->history = array();
|
||||
|
||||
foreach ( $history->history as $record ) {
|
||||
$migrated_record = (object) array();
|
||||
$migrated_record->settings = (object) array();
|
||||
|
||||
foreach ( $record->settings as $module => $settings ) {
|
||||
$migrated_record->settings->$module = ET_Builder_Global_Presets_Settings::generate_module_initial_presets_structure( $module, $all_modules );
|
||||
|
||||
foreach ( $settings as $setting => $value ) {
|
||||
$migrated_record->settings->$module->presets->_initial->settings->$setting = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$migrated_record->time = $record->time;
|
||||
$migrated_record->label = $record->label;
|
||||
|
||||
$migrated_history->history[] = $migrated_record;
|
||||
}
|
||||
|
||||
$migrated_history->index = $history->index;
|
||||
|
||||
et_update_option( self::GLOBAL_PRESETS_HISTORY_OPTION, $migrated_history );
|
||||
}
|
||||
}
|
||||
|
||||
ET_Builder_Global_Presets_History::instance();
|
||||
@@ -0,0 +1,578 @@
|
||||
<?php
|
||||
|
||||
class ET_Builder_Global_Presets_Settings {
|
||||
const CUSTOM_DEFAULTS_OPTION = 'builder_custom_defaults';
|
||||
const CUSTOM_DEFAULTS_UNMIGRATED_OPTION = 'builder_custom_defaults_unmigrated';
|
||||
const CUSTOMIZER_SETTINGS_MIGRATED_FLAG = 'customizer_settings_migrated_flag';
|
||||
|
||||
const GLOBAL_PRESETS_OPTION = 'builder_global_presets';
|
||||
const CUSTOM_DEFAULTS_MIGRATED_FLAG = 'custom_defaults_migrated_flag';
|
||||
const MODULE_PRESET_ATTRIBUTE = '_module_preset';
|
||||
const MODULE_INITIAL_PRESET_ID = '_initial';
|
||||
|
||||
/**
|
||||
* @var array - The list of the product short names we allowing to do a Module Customizer settings migration rollback
|
||||
*/
|
||||
public static $allowed_products = array(
|
||||
'divi' => '4.5',
|
||||
'extra' => '4.5',
|
||||
);
|
||||
|
||||
// Migration phase two settings
|
||||
public static $phase_two_settings = array(
|
||||
'body_font_size',
|
||||
'captcha_font_size',
|
||||
'caption_font_size',
|
||||
'filter_font_size',
|
||||
'form_field_font_size',
|
||||
'header_font_size',
|
||||
'meta_font_size',
|
||||
'number_font_size',
|
||||
'percent_font_size',
|
||||
'price_font_size',
|
||||
'sale_badge_font_size',
|
||||
'sale_price_font_size',
|
||||
'subheader_font_size',
|
||||
'title_font_size',
|
||||
'toggle_font_size',
|
||||
'icon_size',
|
||||
'padding',
|
||||
'custom_padding',
|
||||
);
|
||||
|
||||
protected static $_module_additional_slugs = array(
|
||||
'et_pb_section' => array(
|
||||
'et_pb_section_fullwidth',
|
||||
'et_pb_section_specialty',
|
||||
),
|
||||
'et_pb_slide' => array(
|
||||
'et_pb_slide_fullwidth',
|
||||
),
|
||||
'et_pb_column' => array(
|
||||
'et_pb_column_specialty',
|
||||
),
|
||||
);
|
||||
|
||||
protected static $_module_types_conversion_map = array(
|
||||
'et_pb_section' => '_convert_section_type',
|
||||
'et_pb_column' => '_convert_column_type',
|
||||
'et_pb_column_inner' => '_convert_column_type',
|
||||
'et_pb_slide' => '_convert_slide_type',
|
||||
);
|
||||
|
||||
protected static $_module_import_types_conversion_map = array(
|
||||
'et_pb_section_specialty' => 'et_pb_section',
|
||||
'et_pb_section_fullwidth' => 'et_pb_section',
|
||||
'et_pb_column_inner' => 'et_bp_column',
|
||||
'et_pb_slide_fullwidth' => 'et_pb_slide',
|
||||
'et_pb_column_specialty' => 'et_pb_column',
|
||||
);
|
||||
|
||||
protected static $_instance;
|
||||
protected $_settings;
|
||||
|
||||
protected function __construct() {
|
||||
$global_presets = et_get_option( self::GLOBAL_PRESETS_OPTION, (object) array(), '', true );
|
||||
|
||||
$this->_settings = $this->_normalize_global_presets( $global_presets );
|
||||
|
||||
$this->_register_hooks();
|
||||
}
|
||||
|
||||
protected function _register_hooks() {
|
||||
add_action( 'et_after_version_rollback', array( $this, 'after_version_rollback' ), 10, 3 );
|
||||
add_action( 'et_builder_modules_loaded', array( $this, 'migrate_custom_defaults' ), 100 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns instance of the singleton class
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @return ET_Builder_Global_Presets_Settings
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( ! isset( self::$_instance ) ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of additional module slugs used to separate Global Presets settings.
|
||||
* For example defaults for sections must be separated depends on the section type (regular, fullwidth or specialty).
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @param $module_slug - The module slug for which additional slugs are looked up
|
||||
*
|
||||
* @return array - The list of the additional slugs
|
||||
*/
|
||||
public function get_module_additional_slugs( $module_slug ) {
|
||||
if ( ! empty( self::$_module_additional_slugs[ $module_slug ] ) ) {
|
||||
return self::$_module_additional_slugs[ $module_slug ];
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns builder Global Presets settings.
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function get_global_presets() {
|
||||
return $this->_settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the gives preset ID exists
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @param string $module_slug
|
||||
* @param string $preset_id
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_module_preset_exist( $module_slug, $preset_id ) {
|
||||
return isset( $this->_settings->{$module_slug}->presets->{$preset_id} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a default preset ID for the given module type
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @param string $module_slug
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_module_default_preset_id( $module_slug ) {
|
||||
return isset( $this->_settings->{$module_slug}->default )
|
||||
? $this->_settings->{$module_slug}->default
|
||||
: self::MODULE_INITIAL_PRESET_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the module preset ID
|
||||
* If the preset ID doesn't exist it will return the default preset ID
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @param string $module_slug
|
||||
* @param array $module_attrs
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_module_preset_id( $module_slug, $module_attrs ) {
|
||||
$preset_id = et_()->array_get( $module_attrs, self::MODULE_PRESET_ATTRIBUTE, false );
|
||||
|
||||
if ( ! $preset_id || ! $this->is_module_preset_exist( $module_slug, $preset_id ) ) {
|
||||
return $this->get_module_default_preset_id( $module_slug );
|
||||
}
|
||||
|
||||
return $preset_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the module preset by the given preset ID
|
||||
* Returns an empty object if no preset found
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @param string $module_slug
|
||||
* @param string $preset_id
|
||||
*
|
||||
* @return stdClass
|
||||
*/
|
||||
public function get_module_preset( $module_slug, $preset_id ) {
|
||||
if ( isset( $this->_settings->{$module_slug}->presets->{$preset_id} ) ) {
|
||||
return (object) $this->_settings->{$module_slug}->presets->{$preset_id};
|
||||
}
|
||||
|
||||
return (object) array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Global Presets settings for the particular module.
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @param string $module_slug - The module slug
|
||||
* @param array $attrs - The module attributes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_module_presets_settings( $module_slug, $attrs ) {
|
||||
$result = array();
|
||||
|
||||
$real_preset_id = $this->get_module_preset_id( $module_slug, $attrs );
|
||||
|
||||
if ( isset( $this->_settings->{$module_slug}->presets->{$real_preset_id}->settings ) ) {
|
||||
$result = (array) $this->_settings->{$module_slug}->presets->{$real_preset_id}->settings;
|
||||
}
|
||||
|
||||
$result = $this->maybe_set_global_colors( $result );
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Global Presets settings with global colors injected.
|
||||
*
|
||||
* @since ??
|
||||
*
|
||||
* @param array $attrs - The module attributes.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function maybe_set_global_colors( $attrs ) {
|
||||
if ( empty( $attrs['global_colors_info'] ) ) {
|
||||
return $attrs;
|
||||
}
|
||||
|
||||
$gc_info = json_decode( $attrs['global_colors_info'], true );
|
||||
|
||||
foreach ( $gc_info as $color_id => $option_names ) {
|
||||
foreach ( $option_names as $option_name ) {
|
||||
$attrs[ $option_name ] = $color_id;
|
||||
}
|
||||
}
|
||||
|
||||
return $attrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether customizer settings migrated or not
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_customizer_migrated() {
|
||||
return et_get_option( self::CUSTOMIZER_SETTINGS_MIGRATED_FLAG, false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether Custom Defaults settings migrated or not
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function are_custom_defaults_migrated() {
|
||||
return et_get_option( self::CUSTOM_DEFAULTS_MIGRATED_FLAG, false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates Module Customizer settings to Custom Defaults
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @param array $defaults - The list of modules default settings
|
||||
*/
|
||||
public function migrate_customizer_settings( $defaults ) {
|
||||
$template_directory = get_template_directory();
|
||||
|
||||
require_once $template_directory . '/includes/module-customizer/migrations.php';
|
||||
|
||||
$migrations = ET_Module_Customizer_Migrations::instance();
|
||||
|
||||
list (
|
||||
$custom_defaults,
|
||||
$custom_defaults_unmigrated,
|
||||
) = $migrations->migrate( $defaults );
|
||||
|
||||
et_update_option( self::CUSTOM_DEFAULTS_OPTION, (object) $custom_defaults );
|
||||
et_update_option( self::CUSTOMIZER_SETTINGS_MIGRATED_FLAG, true );
|
||||
|
||||
if ( ! empty( $custom_defaults_unmigrated ) ) {
|
||||
et_update_option( self::CUSTOM_DEFAULTS_UNMIGRATED_OPTION, (object) $custom_defaults_unmigrated );
|
||||
} else {
|
||||
et_update_option( self::CUSTOM_DEFAULTS_UNMIGRATED_OPTION, false );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates `_initial` module presets structure
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @param string $module_slug
|
||||
* @param array $all_modules
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public static function generate_module_initial_presets_structure( $module_slug, $all_modules ) {
|
||||
$structure = (object) array();
|
||||
$module_slug_converted = isset( self::$_module_import_types_conversion_map[ $module_slug ] )
|
||||
? self::$_module_import_types_conversion_map[ $module_slug ]
|
||||
: $module_slug;
|
||||
|
||||
$preset_name = isset( $all_modules[ $module_slug_converted ]->name )
|
||||
? sprintf( esc_html__( '%s Preset', 'et_builder' ), $all_modules[ $module_slug_converted ]->name )
|
||||
: esc_html__( 'Preset', 'et_builder' );
|
||||
|
||||
$structure->default = '_initial';
|
||||
$structure->presets = (object) array();
|
||||
$structure->presets->_initial = (object) array();
|
||||
$structure->presets->_initial->name = et_core_esc_previously( "{$preset_name} 1" );
|
||||
$structure->presets->_initial->created = 0;
|
||||
$structure->presets->_initial->updated = 0;
|
||||
$structure->presets->_initial->version = ET_BUILDER_PRODUCT_VERSION;
|
||||
$structure->presets->_initial->settings = (object) array();
|
||||
|
||||
return $structure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts Custom Defaults to the new Global Presets format
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @param object $custom_defaults - The previous Custom Defaults
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public static function migrate_custom_defaults_to_global_presets( $custom_defaults ) {
|
||||
$all_modules = ET_Builder_Element::get_modules();
|
||||
$presets = (object) array();
|
||||
|
||||
foreach ( $custom_defaults as $module => $settings ) {
|
||||
$presets->$module = self::generate_module_initial_presets_structure( $module, $all_modules );
|
||||
|
||||
foreach ( $settings as $setting => $value ) {
|
||||
$presets->$module->presets->_initial->settings->$setting = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $presets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates existing Custom Defaults to the Global Presets structure
|
||||
*
|
||||
* @since 4.5.0
|
||||
*/
|
||||
public function migrate_custom_defaults() {
|
||||
if ( self::are_custom_defaults_migrated() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Re-run migration to Global Presets if a user has not yet saved any presets.
|
||||
if ( et_is_builder_plugin_active() && ! empty( (array) $this->_settings ) ) {
|
||||
et_update_option( self::CUSTOM_DEFAULTS_MIGRATED_FLAG, true );
|
||||
return;
|
||||
}
|
||||
|
||||
$custom_defaults = et_get_option( self::CUSTOM_DEFAULTS_OPTION, false );
|
||||
|
||||
if ( ! $custom_defaults ) {
|
||||
$custom_defaults = (object) array();
|
||||
}
|
||||
|
||||
$global_presets = self::migrate_custom_defaults_to_global_presets( $custom_defaults );
|
||||
|
||||
et_update_option( self::GLOBAL_PRESETS_OPTION, $global_presets );
|
||||
$this->_settings = $global_presets;
|
||||
|
||||
et_update_option( self::CUSTOM_DEFAULTS_MIGRATED_FLAG, true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles theme version rollback.
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @param string $product_name - The short name of the product rolling back.
|
||||
* @param string $rollback_from_version
|
||||
* @param string $rollback_to_version
|
||||
*/
|
||||
public function after_version_rollback( $product_name, $rollback_from_version, $rollback_to_version ) {
|
||||
if ( ! isset( self::$allowed_products[ $product_name ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( 0 > version_compare( $rollback_to_version, self::$allowed_products[ $product_name ] ) ) {
|
||||
et_delete_option( self::CUSTOM_DEFAULTS_MIGRATED_FLAG );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts module type (slug).
|
||||
* Used to separate Global Presets settings for modules sharing the same slug but having different meaning
|
||||
* For example: Regular, Fullwidth and Specialty section types
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @param string $type - The module type (slug)
|
||||
* @param array $attrs - The module attributes
|
||||
*
|
||||
* @return string - The converted module type (slug)
|
||||
*/
|
||||
public function maybe_convert_module_type( $type, $attrs ) {
|
||||
if ( isset( self::$_module_types_conversion_map[ $type ] ) ) {
|
||||
// @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found
|
||||
$type = call_user_func_array(
|
||||
array( $this, self::$_module_types_conversion_map[ $type ] ),
|
||||
array( $attrs, $type )
|
||||
);
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts Section module slug to appropriate slug used in Global Presets
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @param array $attrs - The section attributes
|
||||
*
|
||||
* @return string - The converted section type depends on the section attributes
|
||||
*/
|
||||
protected function _convert_section_type( $attrs ) {
|
||||
if ( isset( $attrs['fullwidth'] ) && 'on' === $attrs['fullwidth'] ) {
|
||||
return 'et_pb_section_fullwidth';
|
||||
}
|
||||
|
||||
if ( isset( $attrs['specialty'] ) && 'on' === $attrs['specialty'] ) {
|
||||
return 'et_pb_section_specialty';
|
||||
}
|
||||
|
||||
return 'et_pb_section';
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts Slide module slug to appropriate slug used in Global Presets
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @return string - The converted slide type depends on the parent slider type
|
||||
*/
|
||||
protected function _convert_slide_type() {
|
||||
global $et_pb_slider_parent_type;
|
||||
|
||||
if ( 'et_pb_fullwidth_slider' === $et_pb_slider_parent_type ) {
|
||||
return 'et_pb_slide_fullwidth';
|
||||
}
|
||||
|
||||
return 'et_pb_slide';
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts Column module slug to appropriate slug used in Global Presets
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @return string - The converted column type
|
||||
*/
|
||||
protected function _convert_column_type( $attrs, $type ) {
|
||||
global $et_pb_parent_section_type;
|
||||
|
||||
if ( 'et_pb_column_inner' === $type ) {
|
||||
return 'et_pb_column';
|
||||
}
|
||||
|
||||
if ( 'et_pb_specialty_section' === $et_pb_parent_section_type
|
||||
|| ( isset( $attrs['specialty_columns'] ) && '' !== $attrs['specialty_columns'] ) ) {
|
||||
return 'et_pb_column_specialty';
|
||||
}
|
||||
|
||||
return 'et_pb_column';
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters Global Presets setting to avoid non plain values like arrays or objects
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @param $value - The Global Presets setting value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected static function _filter_global_presets_setting_value( $value ) {
|
||||
return ! is_object( $value ) && ! is_array( $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs Global Presets format normalization.
|
||||
* Usually used to cast format from array to object
|
||||
* Also used to normalize global colors
|
||||
*
|
||||
* @since 4.5.0
|
||||
*
|
||||
* @param $presets - The object representing Global Presets settings
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
protected function _normalize_global_presets( $presets ) {
|
||||
$result = (object) array();
|
||||
|
||||
foreach ( $presets as $module => $preset_structure ) {
|
||||
if ( isset( $preset_structure->presets ) ) {
|
||||
$result->$module = (object) array();
|
||||
$result->$module->presets = (object) array();
|
||||
|
||||
foreach ( $preset_structure->presets as $preset_id => $preset ) {
|
||||
$result->$module->presets->$preset_id = (object) array();
|
||||
$result->$module->presets->$preset_id->name = $preset->name;
|
||||
$result->$module->presets->$preset_id->created = $preset->created;
|
||||
$result->$module->presets->$preset_id->updated = $preset->updated;
|
||||
$result->$module->presets->$preset_id->version = $preset->version;
|
||||
|
||||
if ( isset( $preset->settings ) ) {
|
||||
$result->$module->presets->$preset_id->settings = (object) array();
|
||||
|
||||
$settings_filtered = array_filter(
|
||||
(array) $preset->settings,
|
||||
array(
|
||||
$this,
|
||||
'_filter_global_presets_setting_value',
|
||||
)
|
||||
);
|
||||
|
||||
// Since we still support PHP 5.2 we can't use `array_filter` with array keys
|
||||
// So check if defaults have empty key
|
||||
if ( isset( $settings_filtered[''] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( $settings_filtered as $setting_name => $value ) {
|
||||
$result->$module->presets->$preset_id->settings->$setting_name = $value;
|
||||
}
|
||||
|
||||
// Insert correct global color IDs for affected settings.
|
||||
$global_colors_info = isset( $settings_filtered['global_colors_info'] ) ? json_decode( $settings_filtered['global_colors_info'], true ) : array();
|
||||
|
||||
if ( ! empty( $global_colors_info ) ) {
|
||||
foreach ( $global_colors_info as $color_id => $options_list ) {
|
||||
if ( empty( $options_list ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( $options_list as $global_color_option ) {
|
||||
if ( isset( $result->$module->presets->$preset_id->settings->$global_color_option ) ) {
|
||||
$result->$module->presets->$preset_id->settings->$global_color_option = $color_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$result->$module->presets->$preset->settings = (object) array();
|
||||
}
|
||||
}
|
||||
|
||||
$result->$module->default = $preset_structure->default;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
ET_Builder_Global_Presets_Settings::instance();
|
||||
@@ -0,0 +1,550 @@
|
||||
<?php
|
||||
/**
|
||||
* Gutenberg editor typography.
|
||||
*
|
||||
* @package Builder
|
||||
* @subpackage Gutenberg
|
||||
* @since 4.7.6
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
/**
|
||||
* Class use theme's chosen fonts in Gutenberg editor.
|
||||
*
|
||||
* Class ET_GB_Editor_Typography
|
||||
*/
|
||||
class ET_GB_Editor_Typography {
|
||||
|
||||
/**
|
||||
* `ET_GB_Editor_Typography` instance.
|
||||
*
|
||||
* @var ET_GB_Editor_Typography
|
||||
*/
|
||||
private static $_instance;
|
||||
|
||||
/**
|
||||
* TB's body layout post
|
||||
*
|
||||
* @var WP_Post
|
||||
*/
|
||||
private $_body_layout_post;
|
||||
|
||||
/**
|
||||
* The `et_pb_post_content` shortcode content extracted from the TB's body layout post content
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_post_content_shortcode;
|
||||
|
||||
/**
|
||||
* The `et_pb_post_title shortcode` content extracted from the TB's body layout post content
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_post_title_shortcode;
|
||||
|
||||
/**
|
||||
* CSS selector to target text content inside GB editor
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_body_selector;
|
||||
|
||||
/**
|
||||
* CSS selector to target post title inside GB editor
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $_heading_selector;
|
||||
|
||||
/**
|
||||
* List of HTML element used in post content.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $body_selectors;
|
||||
|
||||
/**
|
||||
* List of HTML heading levels.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $heading_selectors;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* ET_GB_Editor_Typography constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
if ( ! et_core_is_gutenberg_active() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->register_hooks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get class instance.
|
||||
*
|
||||
* @return object class instance.
|
||||
*/
|
||||
public static function instance() {
|
||||
|
||||
if ( null === self::$_instance ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register hooks
|
||||
*/
|
||||
public function register_hooks() {
|
||||
add_action( 'admin_footer', array( $this, 'enqueue_block_typography_styles' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the class.
|
||||
*/
|
||||
private function _initialize() {
|
||||
global $post;
|
||||
|
||||
// Bail early if no post found.
|
||||
if ( empty( $post ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::$body_selectors = array( 'p', 'ol', 'ul', 'dl', 'dt' );
|
||||
$this->_body_selector = self::_generate_selectors( self::$body_selectors, '.editor-styles-wrapper .wp-block .wp-block-freeform ' );
|
||||
$this->_body_selector .= ',' . self::_generate_selectors( self::$body_selectors, '.block-editor-block-list__layout ', '.wp-block' );
|
||||
|
||||
self::$heading_selectors = array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' );
|
||||
$this->_heading_selector = self::_generate_selectors( self::$heading_selectors, '.editor-styles-wrapper .wp-block .wp-block-freeform ' );
|
||||
$this->_heading_selector .= ',' . self::_generate_selectors( self::$heading_selectors, '.editor-styles-wrapper ', '.rich-text' );
|
||||
$this->_heading_selector .= ',.edit-post-visual-editor__post-title-wrapper .editor-post-title__block .editor-post-title__input';
|
||||
|
||||
$tb_layouts = et_theme_builder_get_template_layouts( ET_Theme_Builder_Request::from_post( $post->ID ) );
|
||||
|
||||
// Bail if body layouts is not set current post.
|
||||
if ( ! isset( $tb_layouts[ ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$body_layout = $tb_layouts[ ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE ];
|
||||
$body_layout_id = et_()->array_get( $body_layout, 'id' );
|
||||
$this->_body_layout_post = get_post( $body_layout_id );
|
||||
|
||||
$this->_initialize_shortcode( '_post_content_shortcode', et_theme_builder_get_post_content_modules() );
|
||||
$this->_initialize_shortcode( '_post_title_shortcode', array( 'et_pb_post_title' ) );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the et_pb_post_content and et_pb_post_title shortcode from the body layout post content.
|
||||
*
|
||||
* @param string $prop {@see self::$_post_content_shortcode} or {@see self::$_post_title_shortcode} property.
|
||||
* @param array $tagnames Shortcode tagnames.
|
||||
*/
|
||||
private function _initialize_shortcode( $prop, $tagnames ) {
|
||||
$regex = get_shortcode_regex( $tagnames );
|
||||
|
||||
if ( preg_match_all( "/$regex/", $this->_body_layout_post->post_content, $matches ) ) {
|
||||
$post_title_shortcodes = et_()->array_get( $matches, '0' );
|
||||
|
||||
// Take the style from the first Post Title module that has the title enabled.
|
||||
foreach ( $post_title_shortcodes as $post_title_shortcode ) {
|
||||
if ( false === strpos( $post_title_shortcode, 'title="off"' ) ) {
|
||||
$this->{$prop} = $post_title_shortcode;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
} elseif ( preg_match_all( "/$regex/", $this->_body_layout_post->post_content, $matches, PREG_SET_ORDER ) ) {
|
||||
$this->{$prop} = et_()->array_get(
|
||||
$matches,
|
||||
'0.0'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print GB typography style.
|
||||
*/
|
||||
public function enqueue_block_typography_styles() {
|
||||
|
||||
if ( ! ( method_exists( get_current_screen(), 'is_block_editor' ) && get_current_screen()->is_block_editor() ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->_initialize();
|
||||
|
||||
$styles = '';
|
||||
|
||||
$styles .= $this->get_body_styles();
|
||||
$styles .= $this->get_title_styles();
|
||||
$styles .= $this->get_tb_styles();
|
||||
|
||||
// phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion -- Inline style.
|
||||
wp_register_style( 'divi-block-editor-styles', false );
|
||||
wp_enqueue_style( 'divi-block-editor-styles' );
|
||||
wp_add_inline_style( 'divi-block-editor-styles', $styles );
|
||||
|
||||
// Enqueue google fonts.
|
||||
et_builder_print_font();
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the post content style.
|
||||
*/
|
||||
public function get_body_styles() {
|
||||
|
||||
$body_styles = '';
|
||||
|
||||
$body_font = esc_html( et_get_option( 'body_font' ) );
|
||||
|
||||
if ( ! empty( $body_font ) && 'none' !== $body_font ) {
|
||||
et_builder_enqueue_font( $body_font );
|
||||
$font_family = et_builder_get_font_family( $body_font );
|
||||
|
||||
$body_styles .= et_builder_generate_css_style(
|
||||
array(
|
||||
'style' => 'font-family',
|
||||
'value' => str_replace( 'font-family: ', '', $font_family ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$body_font_height = esc_html( et_get_option( 'body_font_height' ) );
|
||||
|
||||
if ( ! empty( $body_font_height ) ) {
|
||||
$body_styles .= et_builder_generate_css_style(
|
||||
array(
|
||||
'style' => 'line-height',
|
||||
'value' => $body_font_height,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$body_font_size = esc_html( et_get_option( 'body_font_size' ) );
|
||||
|
||||
if ( ! empty( $body_font_size ) ) {
|
||||
$body_styles .= et_builder_generate_css_style(
|
||||
array(
|
||||
'style' => 'font-size',
|
||||
'value' => $body_font_size,
|
||||
'suffix' => 'px',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$body_font_color = esc_html( et_get_option( 'font_color' ) );
|
||||
|
||||
if ( ! empty( $body_font_color ) ) {
|
||||
$body_styles .= et_builder_generate_css_style(
|
||||
array(
|
||||
'style' => 'color',
|
||||
'value' => $body_font_color,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! empty( $body_styles ) ) {
|
||||
$body_styles = sprintf( '%1$s { %2$s }', $this->_body_selector, $body_styles );
|
||||
}
|
||||
|
||||
$link_color = esc_html( et_get_option( 'link_color' ) );
|
||||
|
||||
if ( ! empty( $link_color ) ) {
|
||||
$body_styles .= et_builder_generate_css(
|
||||
array(
|
||||
'style' => 'color',
|
||||
'value' => $link_color,
|
||||
'selector' => '.block-editor-block-list__layout .wp-block a, .editor-styles-wrapper .wp-block .wp-block-freeform a',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $body_styles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print post title styles.
|
||||
*/
|
||||
public function get_title_styles() {
|
||||
|
||||
$title_styles = '';
|
||||
|
||||
$heading_font = esc_html( et_get_option( 'heading_font' ) );
|
||||
|
||||
// Fallback to the body font.
|
||||
if ( empty( $heading_font ) || 'none' === $heading_font ) {
|
||||
$heading_font = esc_html( et_get_option( 'body_font' ) );
|
||||
}
|
||||
|
||||
if ( ! empty( $heading_font ) && 'none' !== $heading_font ) {
|
||||
et_builder_enqueue_font( $heading_font );
|
||||
$font_family = et_builder_get_font_family( $heading_font );
|
||||
|
||||
$title_styles .= et_builder_generate_css_style(
|
||||
array(
|
||||
'style' => 'font-family',
|
||||
'value' => str_replace( 'font-family: ', '', $font_family ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$body_header_spacing = esc_html( et_get_option( 'body_header_spacing' ) );
|
||||
|
||||
if ( ! empty( $body_header_spacing ) ) {
|
||||
$title_styles .= et_builder_generate_css_style(
|
||||
array(
|
||||
'style' => 'letter-spacing',
|
||||
'value' => $body_header_spacing,
|
||||
'suffix' => 'px',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$header_color = esc_html( et_get_option( 'header_color' ) );
|
||||
|
||||
if ( ! empty( $header_color ) ) {
|
||||
$title_styles .= et_builder_generate_css_style(
|
||||
array(
|
||||
'style' => 'color',
|
||||
'value' => $header_color,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$body_header_height = esc_html( et_get_option( 'body_header_height' ) );
|
||||
|
||||
if ( ! empty( $body_header_height ) && '1' !== $body_header_height ) {
|
||||
$title_styles .= et_builder_generate_css_style(
|
||||
array(
|
||||
'style' => 'line-height',
|
||||
'value' => $body_header_height,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$body_header_style = esc_html( et_get_option( 'body_header_style' ) );
|
||||
|
||||
if ( ! empty( $body_header_style ) ) {
|
||||
// Convert string into array.
|
||||
$styles_array = explode( '|', $body_header_style );
|
||||
|
||||
if ( in_array( 'bold', $styles_array, true ) ) {
|
||||
$title_styles .= et_builder_generate_css_style(
|
||||
array(
|
||||
'style' => 'font-weight',
|
||||
'value' => 'bold',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( in_array( 'italic', $styles_array, true ) ) {
|
||||
$title_styles .= et_builder_generate_css_style(
|
||||
array(
|
||||
'style' => 'font-style',
|
||||
'value' => 'italic',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( in_array( 'uppercase', $styles_array, true ) ) {
|
||||
$title_styles .= et_builder_generate_css_style(
|
||||
array(
|
||||
'style' => 'text-transform',
|
||||
'value' => 'uppercase',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( in_array( 'underline', $styles_array, true ) ) {
|
||||
$title_styles .= et_builder_generate_css_style(
|
||||
array(
|
||||
'style' => 'text-decoration',
|
||||
'value' => 'underline',
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$body_header_size = esc_html( et_get_option( 'body_header_size' ) );
|
||||
|
||||
if ( ! empty( $title_styles ) ) {
|
||||
$title_styles = sprintf( '%1$s { %2$s }', $this->_heading_selector, $title_styles );
|
||||
}
|
||||
|
||||
if ( ! empty( $body_header_size ) ) {
|
||||
$title_styles .= ',' . et_builder_generate_css(
|
||||
array(
|
||||
'style' => 'font-size',
|
||||
'value' => $body_header_size,
|
||||
'suffix' => 'px',
|
||||
'selector' => '.editor-styles-wrapper .wp-block .wp-block-freeform h1',
|
||||
)
|
||||
);
|
||||
|
||||
$title_styles .= ',' . et_builder_generate_css(
|
||||
array(
|
||||
'style' => 'font-size',
|
||||
'value' => intval( $body_header_size * .86 ),
|
||||
'suffix' => 'px',
|
||||
'selector' => '.editor-styles-wrapper .wp-block .wp-block-freeform h2',
|
||||
)
|
||||
);
|
||||
|
||||
$title_styles .= ',' . et_builder_generate_css(
|
||||
array(
|
||||
'style' => 'font-size',
|
||||
'value' => intval( $body_header_size * .73 ),
|
||||
'suffix' => 'px',
|
||||
'selector' => '.editor-styles-wrapper .wp-block .wp-block-freeform h3',
|
||||
)
|
||||
);
|
||||
|
||||
$title_styles .= ',' . et_builder_generate_css(
|
||||
array(
|
||||
'style' => 'font-size',
|
||||
'value' => intval( $body_header_size * .6 ),
|
||||
'suffix' => 'px',
|
||||
'selector' => '.editor-styles-wrapper .wp-block .wp-block-freeform h4',
|
||||
)
|
||||
);
|
||||
|
||||
$title_styles .= ',' . et_builder_generate_css(
|
||||
array(
|
||||
'style' => 'font-size',
|
||||
'value' => intval( $body_header_size * .53 ),
|
||||
'suffix' => 'px',
|
||||
'selector' => '.editor-styles-wrapper .wp-block .wp-block-freeform h5',
|
||||
)
|
||||
);
|
||||
|
||||
$title_styles .= ',' . et_builder_generate_css(
|
||||
array(
|
||||
'style' => 'font-size',
|
||||
'value' => intval( $body_header_size * .47 ),
|
||||
'suffix' => 'px',
|
||||
'selector' => '.editor-styles-wrapper .wp-block .wp-block-freeform h6',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $title_styles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print TB's style.
|
||||
*/
|
||||
public function get_tb_styles() {
|
||||
|
||||
if ( empty( $this->_post_content_shortcode ) && empty( $this->_post_title_shortcode ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! class_exists( 'ET_Builder_Element' ) ) {
|
||||
require_once ET_BUILDER_DIR . 'class-et-builder-value.php';
|
||||
require_once ET_BUILDER_DIR . 'class-et-builder-element.php';
|
||||
require_once ET_BUILDER_DIR . 'ab-testing.php';
|
||||
et_builder_init_global_settings();
|
||||
et_builder_add_main_elements();
|
||||
et_builder_settings_init();
|
||||
ET_Builder_Element::set_media_queries();
|
||||
}
|
||||
|
||||
// To generate the styles from the shortcode, this do_shortcode will intialize et_pb_post_content and et_pb_post_title modules classes.
|
||||
ob_start();
|
||||
do_shortcode( $this->_post_title_shortcode . $this->_post_content_shortcode );
|
||||
ob_end_clean();
|
||||
|
||||
// Get style generated by modules.
|
||||
$tb_style = ET_Builder_Element::get_style();
|
||||
|
||||
$have_post_content_style = preg_match( '/\.et_pb_post_content_0 { (.*) }/', $tb_style, $matches );
|
||||
if ( $have_post_content_style && isset( $matches[1] ) ) {
|
||||
$et_pb_post_content_styles = explode( ';', $matches[1] );
|
||||
$typography_properties = array(
|
||||
'color',
|
||||
'font-family',
|
||||
'font-size',
|
||||
'font-weight',
|
||||
'font-style',
|
||||
'text-align',
|
||||
'text-shadow',
|
||||
'letter-spacing',
|
||||
'line-height',
|
||||
'text-transform',
|
||||
'text-decoration',
|
||||
'text-decoration-style',
|
||||
);
|
||||
|
||||
$post_content_style = '';
|
||||
|
||||
foreach ( $et_pb_post_content_styles as $et_pb_post_content_style ) {
|
||||
$style = explode( ':', $et_pb_post_content_style ); // explode CSS property and value.
|
||||
$css_property = trim( et_()->array_get( $style, '0' ) );
|
||||
if ( in_array( $css_property, $typography_properties, true ) ) {
|
||||
$post_content_style .= $css_property . ':' . et_()->array_get( $style, '1' ) . ';';
|
||||
}
|
||||
}
|
||||
|
||||
$tb_style = $this->_body_selector . ' {' . $post_content_style . '}' . $tb_style;
|
||||
}
|
||||
|
||||
foreach ( self::$heading_selectors as $heading_selector ) {
|
||||
$tb_style = str_replace( ".et_pb_post_content_0 $heading_selector ", ".editor-styles-wrapper .wp-block .wp-block-freeform $heading_selector ,.editor-styles-wrapper $heading_selector.rich-text ", $tb_style );
|
||||
}
|
||||
|
||||
$tb_style = str_replace( '.et_pb_post_content_0 a ', '.editor-styles-wrapper .wp-block .wp-block-freeform a,.block-editor-block-list__layout .wp-block a ', $tb_style );
|
||||
$tb_style = str_replace( array( '.et_pb_post_content_0 ul li ', '.et_pb_post_content_0.et_pb_post_content ul li' ), '.editor-styles-wrapper .wp-block .wp-block-freeform ul li,.block-editor-block-list__layout ul li ', $tb_style );
|
||||
$tb_style = str_replace( array( '.et_pb_post_content_0 ol li ', '.et_pb_post_content_0.et_pb_post_content ol li' ), '.editor-styles-wrapper .wp-block .wp-block-freeform ol li,.block-editor-block-list__layout ol li ', $tb_style );
|
||||
$tb_style = str_replace( array( 'et_pb_post_content_0 ul ', '.et_pb_post_content_0.et_pb_post_content ul ' ), '.editor-styles-wrapper .wp-block .wp-block-freeform ul.wp-block,.block-editor-block-list__layout ul.wp-block ', $tb_style );
|
||||
$tb_style = str_replace( array( '.et_pb_post_content_0 ol ', '.et_pb_post_content_0.et_pb_post_content ol ' ), '.editor-styles-wrapper .wp-block .wp-block-freeform ol.wp-block,.block-editor-block-list__layout ol.wp-block ', $tb_style );
|
||||
$tb_style = str_replace( array( '.et_pb_post_content_0 blockquote ', '.et_pb_post_content_0.et_pb_post_content blockquote' ), '.editor-styles-wrapper .wp-block .wp-block-freeform ol,.block-editor-block-list__layout blockquote ', $tb_style );
|
||||
|
||||
// Replace the post title style selectors with editor's post title selector.
|
||||
$tb_style = str_replace( array( '.et_pb_post_title_0 .entry-title', '.et_pb_post_title_0 .et_pb_title_container h1.entry-title, .et_pb_post_title_0 .et_pb_title_container h2.entry-title, .et_pb_post_title_0 .et_pb_title_container h3.entry-title, .et_pb_post_title_0 .et_pb_title_container h4.entry-title, .et_pb_post_title_0 .et_pb_title_container h5.entry-title, .et_pb_post_title_0 .et_pb_title_container h6.entry-title' ), '.edit-post-visual-editor__post-title-wrapper .editor-post-title__block .editor-post-title__input', $tb_style );
|
||||
|
||||
// Enqueue fonts.
|
||||
$fonts_regex = '/font-family:\s+[\'"]([a-zA-Z0-9\s]+)[\'"]/';
|
||||
$has_fonts = preg_match_all( $fonts_regex, $tb_style, $matches, PREG_SET_ORDER );
|
||||
if ( false !== $has_fonts && isset( $match[1] ) ) {
|
||||
foreach ( $matches as $match ) {
|
||||
et_builder_enqueue_font( $match[1] );
|
||||
}
|
||||
}
|
||||
|
||||
return $tb_style;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate css selectors from prefixes and suffixes.
|
||||
*
|
||||
* @param array $selectors Selectors list.
|
||||
* @param string $prefix Selector prefix.
|
||||
* @param string $suffix Selector suffix.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function _generate_selectors( $selectors, $prefix, $suffix = '' ) {
|
||||
$prepared_selectors = '';
|
||||
|
||||
foreach ( (array) $selectors as $selector ) {
|
||||
$prepared_selectors .= $prefix . $selector . $suffix . ',';
|
||||
}
|
||||
|
||||
return rtrim( $prepared_selectors, ',' );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Initialize ET_GB_Editor_Typography.
|
||||
ET_GB_Editor_Typography::instance();
|
||||
@@ -0,0 +1,676 @@
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class ET_GB_Block_Layout {
|
||||
/**
|
||||
* @var ET_GB_Block_Layout
|
||||
*/
|
||||
private static $_instance;
|
||||
|
||||
private $block_name = 'divi/layout';
|
||||
|
||||
function __construct() {
|
||||
if ( ! et_core_is_gutenberg_active() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->register_block();
|
||||
$this->register_hooks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get class instance
|
||||
*
|
||||
* @since 4.1.0
|
||||
*
|
||||
* @return object class instance
|
||||
*/
|
||||
public static function instance() {
|
||||
if ( null === self::$_instance ) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register block
|
||||
*
|
||||
* @since 4.1.0
|
||||
*/
|
||||
public function register_block() {
|
||||
register_block_type(
|
||||
$this->block_name,
|
||||
array(
|
||||
'attributes' => array(
|
||||
'layoutContent' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register hooks
|
||||
*
|
||||
* @since 4.1.0
|
||||
*/
|
||||
public function register_hooks() {
|
||||
// Admin screen
|
||||
add_action( 'admin_init', array( $this, 'register_portability_on_builder_page' ) );
|
||||
|
||||
// Block preview inside gutenberg
|
||||
add_action( 'template_include', array( $this, 'register_preview_template' ) );
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_block_preview_styles_scripts' ), 15 );
|
||||
add_action( 'wp_footer', array( $this, 'enqueue_block_preview_footer_styles_scripts' ) );
|
||||
add_action( 'pre_get_posts', array( $this, 'modify_layout_content_condition' ), 20 );
|
||||
|
||||
add_filter( 'body_class', array( $this, 'add_body_classnames' ) );
|
||||
add_filter( 'et_pb_section_data_attributes', array( $this, 'add_section_boxshadow_attributes' ), 10, 3 );
|
||||
add_filter( 'the_content', array( $this, 'modify_layout_content_output' ), 1 );
|
||||
add_filter( 'get_post_metadata', array( $this, 'modify_layout_content_builder_meta' ), 10, 4 );
|
||||
add_filter( 'et_fb_load_raw_post_content', array( $this, 'modify_layout_content_visual_builder_raw_post_content' ) );
|
||||
add_filter( 'et_builder_render_layout', array( $this, 'modify_theme_builder_body_layout' ), 7 );
|
||||
|
||||
// Block rendering on frontend
|
||||
add_filter( 'render_block', array( $this, 'render_block' ), 100, 2 );
|
||||
add_filter( 'get_the_excerpt', array( $this, 'get_the_post_excerpt' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current request is Divi Layout preview for block request. Layout block preview page
|
||||
* is only valid for logged in user with edit_posts cap with query string for activating block
|
||||
* layout preview and its nonce to verify it.
|
||||
*
|
||||
* Initially, is_singular() check existed but reusable block at `wp_block` CPT and any other CPT
|
||||
* that has no frontend due to its post type registration sets `public` attribute to `false`
|
||||
* renders layout block preview at non singular page makes is_singular() check need to be dropped
|
||||
*
|
||||
* @since 4.1.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_layout_block_preview() {
|
||||
return isset( $_GET['et_block_layout_preview'] ) && et_core_security_check(
|
||||
'edit_posts',
|
||||
'et_block_layout_preview',
|
||||
'et_block_layout_preview_nonce',
|
||||
'_GET',
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current builder shortcode rendering is done inside layout block
|
||||
*
|
||||
* @since 4.1.0
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_layout_block() {
|
||||
global $et_is_layout_block;
|
||||
|
||||
// Ensure the returned value is bool
|
||||
return $et_is_layout_block ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register portability which is needed to import premade and saved Layout via Divi Library;
|
||||
* Portability is intentionally disabled on builder page by `et_builder_should_load_framework()`
|
||||
* nevertheless excluding GB there doesn't work because it is being too early before any
|
||||
* GB check is hooked. Thus Register another portability for GB + builder page
|
||||
*
|
||||
* @since 4.1.0
|
||||
*/
|
||||
public function register_portability_on_builder_page() {
|
||||
global $pagenow;
|
||||
|
||||
// No permission, can't load library UI in the first place
|
||||
if ( ! et_pb_is_allowed( 'divi_library' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Exit if current page is not saved edit page
|
||||
$is_edit_page = 'post.php' === $pagenow && isset( $_GET['post'] );
|
||||
|
||||
if ( ! $is_edit_page ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$post_id = intval( $_GET['post'] );
|
||||
|
||||
// Exit if current page doesn't use Gutenberg
|
||||
if ( ! use_block_editor_for_post_type( get_post_type( $post_id ) ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Exit if current page doesn't use builder
|
||||
if ( ! et_pb_is_pagebuilder_used( $post_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Register portability
|
||||
et_core_portability_register(
|
||||
'et_builder',
|
||||
array(
|
||||
'name' => esc_html__( 'Divi Builder Layout', 'et_builder' ),
|
||||
'type' => 'post',
|
||||
'view' => true,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter rendered blocks on FE.
|
||||
*
|
||||
* @since 4.1.0
|
||||
* @since ?? Filter core/post-excerpt rendered output.
|
||||
*
|
||||
* @param string $block_content Saved & serialized block data.
|
||||
* @param array $block Block info.
|
||||
*/
|
||||
public function render_block( $block_content, $block ) {
|
||||
// Core - Post Excerpt block.
|
||||
if ( 'core/post-excerpt' === $block['blockName'] ) {
|
||||
return $this->get_rendered_post_excerpt( $block_content, true );
|
||||
}
|
||||
|
||||
// Divi - Layout block.
|
||||
if ( 'divi/layout' === $block['blockName'] ) {
|
||||
global $et_is_layout_block;
|
||||
|
||||
// Set flag.
|
||||
$et_is_layout_block = true;
|
||||
|
||||
// Render block content's shortcode. Block content actually can be rendered without this
|
||||
// method and only depending to WordPress' `do_shortcode` hooked into `the_content`. However
|
||||
// layout block need to set global for detecting that shortcode is rendered inside layout
|
||||
// block hence the early shortcode rendering between global variables.
|
||||
$block_content = do_shortcode( $block_content );
|
||||
|
||||
// Reset flag.
|
||||
$et_is_layout_block = false;
|
||||
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter post excerpt of REST API request.
|
||||
*
|
||||
* @since 4.9.10
|
||||
* @since ?? Only filter post excerpt rendered from REST API request. This API request
|
||||
* is being used by Block Editor.
|
||||
*
|
||||
* @param string $post_excerpt Current post excerpt rendered.
|
||||
*
|
||||
* @return string Modified post excerpt.
|
||||
*/
|
||||
public function get_the_post_excerpt( $post_excerpt ) {
|
||||
// Bail early if current request is not REST API request.
|
||||
if ( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) {
|
||||
return $post_excerpt;
|
||||
}
|
||||
|
||||
return $this->get_rendered_post_excerpt( $post_excerpt );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get rendered post excerpt built with builder. Always return rendered $post_excerpt
|
||||
* because it's already wrapped with Post Excerpt block wrapper.
|
||||
*
|
||||
* @since ??
|
||||
*
|
||||
* @param string $post_excerpt Current rendered post excerpt.
|
||||
* @param boolean $is_wrapped Whether the post excerpt is wrapped or not.
|
||||
*
|
||||
* @return string Old or new rendered post excerpt.
|
||||
*/
|
||||
public function get_rendered_post_excerpt( $post_excerpt, $is_wrapped = false ) {
|
||||
// Bail early if no global post. Need to get the post here due to some issues with
|
||||
// 3rd party plugins regarding missing 2nd arg on the `get_the_excerpt` filter.
|
||||
$post = get_post();
|
||||
if ( empty( $post ) ) {
|
||||
return $post_excerpt;
|
||||
}
|
||||
|
||||
if ( ! empty( $post->post_excerpt ) ) {
|
||||
return $post_excerpt;
|
||||
}
|
||||
|
||||
// Bail early if Builder framework is not loaded. There are some cases where 3rd
|
||||
// party plugins run scan without visiting theme functions file.
|
||||
if ( ! function_exists( 'et_builder_load_framework' ) ) {
|
||||
return $post_excerpt;
|
||||
}
|
||||
|
||||
if ( ! et_pb_is_pagebuilder_used( $post->ID ) ) {
|
||||
return $post_excerpt;
|
||||
}
|
||||
|
||||
static $et_rendered_post_excerpt = array();
|
||||
|
||||
// Bail early if current post is already processed.
|
||||
if ( isset( $et_rendered_post_excerpt[ $post->ID ] ) ) {
|
||||
return $et_rendered_post_excerpt[ $post->ID ];
|
||||
}
|
||||
|
||||
// 1. Ensure all the ET shortcode are registered.
|
||||
if ( ! did_action( 'et_builder_ready' ) ) {
|
||||
// When the `get_the_excerpt` filter is called by Query Loop block on the FE,
|
||||
// the `ET_Builder_Element` class is loaded properly but no ET shortcode is
|
||||
// registered yet. In this case, we can call `et_builder_init_global_settings`
|
||||
// & `et_builder_add_main_elements` methods directly. However, this class is not
|
||||
// loaded on the Block Editor, so we have to load all related files manually
|
||||
// before we can call those methods to register the shortcode.
|
||||
if ( ! class_exists( 'ET_Builder_Element' ) ) {
|
||||
require_once ET_BUILDER_DIR . 'class-et-builder-value.php';
|
||||
require_once ET_BUILDER_DIR . 'class-et-builder-element.php';
|
||||
require_once ET_BUILDER_DIR . 'ab-testing.php';
|
||||
}
|
||||
|
||||
et_builder_init_global_settings();
|
||||
et_builder_add_main_elements();
|
||||
et_builder_settings_init();
|
||||
}
|
||||
|
||||
// 2. Generate Builder post excerpt.
|
||||
// WordPress post excerpt length comes from `excerpt_length` filter. And, it's
|
||||
// words based length, not characters based length.
|
||||
$excerpt_length = apply_filters( 'excerpt_length', 55 );
|
||||
$new_post_excerpt = et_core_intentionally_unescaped( wpautop( et_delete_post_first_video( truncate_post( $excerpt_length, false, $post, true, true ) ) ), 'html' );
|
||||
|
||||
// 3. Ensure to return the block wrapper if the $post_excerpt is already wrapped.
|
||||
if ( $is_wrapped ) {
|
||||
$wrapper = '/(<p class="wp-block-post-excerpt__excerpt">)(.*?)(<a|<\/p>)/';
|
||||
$new_post_excerpt = wp_strip_all_tags( $new_post_excerpt );
|
||||
$new_post_excerpt = preg_replace( $wrapper, "$1{$new_post_excerpt}$3", $post_excerpt );
|
||||
}
|
||||
|
||||
$et_rendered_post_excerpt[ $post->ID ] = $new_post_excerpt;
|
||||
|
||||
return $new_post_excerpt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite template path if current request is Divi Layout block preview
|
||||
*
|
||||
* @since 4.1.0
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function register_preview_template( $template ) {
|
||||
if ( self::is_layout_block_preview() ) {
|
||||
// disable admin bar
|
||||
show_admin_bar( false );
|
||||
|
||||
// Use layout block specific template for render layout block preview (headerless
|
||||
// and footerless templates). BFB template was initialy used for this for DRY reason
|
||||
// but its #page-container-bfb causing styling issues
|
||||
return ET_BUILDER_DIR . 'templates/block-layout-preview.php';
|
||||
}
|
||||
|
||||
// return original template
|
||||
return $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Theme Builder's template settings of current (layout block preview) page
|
||||
*
|
||||
* @since 4.3.4
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_preview_tb_template() {
|
||||
// Identify current request, and get applicable TB template for current page
|
||||
$request = ET_Theme_Builder_Request::from_current();
|
||||
$templates = et_theme_builder_get_theme_builder_templates( true );
|
||||
$settings = et_theme_builder_get_flat_template_settings_options();
|
||||
$tb_template = $request->get_template( $templates, $settings );
|
||||
|
||||
// Define template properties as variables for readability
|
||||
$template_id = et_()->array_get( $tb_template, 'id', 0 );
|
||||
$layout_id = et_()->array_get( $tb_template, 'layouts.body.id', 0 );
|
||||
$layout_enabled = et_()->array_get( $tb_template, 'layouts.body.enabled', false );
|
||||
$layout_override = et_()->array_get( $tb_template, 'layouts.body.override', false );
|
||||
$has_layout = $layout_id && $layout_enabled && $layout_override;
|
||||
|
||||
return array(
|
||||
'layout_id' => $layout_id,
|
||||
'layout_enabled' => $layout_enabled,
|
||||
'template_id' => $template_id,
|
||||
'has_layout' => $has_layout,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Early scripts and styles queue for Layout Block Preview page
|
||||
* Need to queue early because `et-builder-modules-script` uses localize scripts on this method
|
||||
*
|
||||
* @since 4.4.1 Compatibility fixes for WP 5.4
|
||||
* @since 4.1.0
|
||||
*/
|
||||
public function enqueue_block_preview_styles_scripts() {
|
||||
if ( ! self::is_layout_block_preview() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( et_fb_enabled() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Enqueue frame helper if it hasn't been queued
|
||||
if ( ! wp_script_is( 'et-frame-helpers', 'enqueued' ) ) {
|
||||
wp_enqueue_script(
|
||||
'et-frame-helpers',
|
||||
ET_BUILDER_URI . '/frontend-builder/build/frame-helpers.js',
|
||||
array(),
|
||||
ET_BUILDER_VERSION
|
||||
);
|
||||
}
|
||||
|
||||
// frontend-builder-scripts.js handle might change when scripts are minified
|
||||
$builder_script_handle = apply_filters(
|
||||
'et_builder_modules_script_handle',
|
||||
'et-builder-modules-script'
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
$builder_script_handle,
|
||||
'ETBlockLayoutModulesScript',
|
||||
array(
|
||||
// blockId is dash separated alphanumeric uuid value
|
||||
'blockId' => sanitize_title( et_()->array_get( $_POST, 'et_editor_block_id', 0 ) ),
|
||||
|
||||
// Make layout shortcode available for ajax pagination request by outputting it
|
||||
// as JS params so the pagination ajax request can have identical page on next
|
||||
// request. Thus any custom script has no business being here: as long as the same
|
||||
// module shortcode exist on next page it should be okay. Hence, kses em all
|
||||
// regardless user capability to reduce security concern
|
||||
'layoutContent' => wp_kses_post( et_()->array_get( $_POST, 'et_layout_block_layout_content', '' ) ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Late scripts and styles queue for Layout Block Preview page
|
||||
* Need to queue late because localize script needs to populate settings from rendered modules
|
||||
*
|
||||
* @since 4.4.1 Renamed into `enqueue_block_preview_footer_styles_scripts`. Localize script
|
||||
* value which is used by `et-builder-modules-script` is queued on earlier hook
|
||||
* @since 4.1.0
|
||||
*/
|
||||
public function enqueue_block_preview_footer_styles_scripts() {
|
||||
if ( ! self::is_layout_block_preview() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Frontend preview adjustment should only be called on layout block preview frame
|
||||
// and shouldn't be called on layout block builder
|
||||
if ( ! et_fb_enabled() ) {
|
||||
wp_enqueue_script(
|
||||
'et-block-layout-preview',
|
||||
ET_BUILDER_URI . '/scripts/block-layout-frontend-preview.js',
|
||||
array( 'jquery' ),
|
||||
ET_BUILDER_PRODUCT_VERSION
|
||||
);
|
||||
}
|
||||
|
||||
wp_localize_script(
|
||||
'et-block-layout-preview',
|
||||
'ETBlockLayoutPreview',
|
||||
array(
|
||||
// blockId is dash separated alphanumeric uuid value
|
||||
'blockId' => sanitize_title( et_()->array_get( $_POST, 'et_editor_block_id', 0 ) ),
|
||||
|
||||
// Exposed module settings for layout block preview for making nescessary adjustments
|
||||
'assistiveSettings' => ET_Builder_Element::get_layout_block_assistive_settings(),
|
||||
|
||||
// Exposed Divi breakpoint minimum widths
|
||||
'breakpointMinWidths' => et_pb_responsive_options()->get_breakpoint_min_widths(),
|
||||
|
||||
// Divi style mode
|
||||
'styleModes' => array(
|
||||
'desktop',
|
||||
'tablet',
|
||||
'phone',
|
||||
'hover',
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
// Disabled link modal, originally added for classic builder preview
|
||||
wp_enqueue_style(
|
||||
'et-block-layout-preview-style',
|
||||
ET_BUILDER_URI . '/styles/preview-layout-block.css',
|
||||
array(),
|
||||
ET_BUILDER_VERSION
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add builder classname on body class if layout block exist on the page
|
||||
*
|
||||
* @since 4.1.0
|
||||
*
|
||||
* @param array classname
|
||||
*
|
||||
* @return array modified classname
|
||||
*/
|
||||
public function add_body_classnames( $classes ) {
|
||||
if ( self::is_layout_block_preview() ) {
|
||||
$classes[] = 'et-db';
|
||||
$classes[] = 'et-block-layout-preview';
|
||||
}
|
||||
|
||||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add box shadow's highest offset value if box shadow is used on section so block preview area
|
||||
* can adjust its padding to make section's box shadow previewable on block preview
|
||||
*
|
||||
* @since 4.1.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function add_section_boxshadow_attributes( $attributes, $props, $render_count ) {
|
||||
$box_shadow_style = et_()->array_get( $props, 'box_shadow_style', '' );
|
||||
|
||||
// Only apply on layout block and box shadow is set
|
||||
if ( ! self::is_layout_block_preview() || '' === $box_shadow_style || 'none' === $box_shadow_style ) {
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
// List of box shadow attribute that might affect how tall the box shadow is
|
||||
$spread = et_()->array_get( $props, 'box_shadow_spread', '' );
|
||||
$blur = et_()->array_get( $props, 'box_shadow_blur', '' );
|
||||
$vertical = et_()->array_get( $props, 'box_shadow_vertical', '' );
|
||||
|
||||
$values = array(
|
||||
'spread' => absint( $spread ),
|
||||
'blur' => absint( $blur ),
|
||||
'vertical' => absint( $vertical ),
|
||||
);
|
||||
|
||||
// Sort attributes; there's no way to safely convert all unit (em, rem, etc) into one
|
||||
// specific unit accurately, so this assumes that all values are in px
|
||||
asort( $values );
|
||||
|
||||
// Point to the last array
|
||||
end( $values );
|
||||
|
||||
// Get last array keys
|
||||
$highest_attribute_key = key( $values );
|
||||
|
||||
// Add attribute with higest value into DOM data-* attribute so it can be referenced
|
||||
$attributes['box-shadow-offset'] = et_()->array_get( $props, 'box_shadow_' . $highest_attribute_key, '' );
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify layout content condition. Preview template should consider itself is_single = true
|
||||
*
|
||||
* @since 4.1.0
|
||||
* @since 4.4.1 don't overwrite `p` and `post_type` query vars if homepage displays static page
|
||||
*
|
||||
* @param object
|
||||
*/
|
||||
public function modify_layout_content_condition( $query ) {
|
||||
if ( $query->is_main_query() && self::is_layout_block_preview() ) {
|
||||
// Set to `false` to avoid home specific classname and attribute being printed. This is
|
||||
// specifically needed on CPT which is not publicly queryable / doesn't have frontend
|
||||
// page such as reusable block's `wp_block` CPT
|
||||
$query->is_home = false;
|
||||
|
||||
// Set to `true` so `#et-boc` wrapper is correctly added
|
||||
$query->is_single = true;
|
||||
$query->is_singular = true;
|
||||
|
||||
// Query name doesn't exist while post_id is passed via query string means current
|
||||
// layout block preview is rendered on CPT that doesn't publicly queryable / doesn't
|
||||
// have registered frontend page such `wp_block`. Manually set post id and post type
|
||||
// to avoid current query fetches ALL posts on `post` post type. However, bail if
|
||||
// current page has page_id. This means the current page, most likely homepage URL when
|
||||
// it has `name` query like this, renders static page on its homepage URL (defined at
|
||||
// dashboard > settings > your homepage display). Not doing this will cause current
|
||||
// homepage has incorrect post type and id which leads to 404 preview page which
|
||||
// will prevent the preview page from being rendered
|
||||
if ( ! isset( $query->query['name'] ) && 0 === $query->get( 'page_id' ) ) {
|
||||
if ( isset( $_GET['et_post_id'] ) ) {
|
||||
$query->set( 'p', intval( $_GET['et_post_id'] ) );
|
||||
}
|
||||
|
||||
if ( isset( $_GET['et_post_type'] ) ) {
|
||||
$query->set( 'post_type', sanitize_text_field( $_GET['et_post_type'] ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify layout content content output based on layout shortcode layout sent over POST for
|
||||
* previewing layout block on gutenberg editor
|
||||
*
|
||||
* @since 4.1.0
|
||||
*
|
||||
* @param string $content post's content
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function modify_layout_content_output( $content ) {
|
||||
if ( self::is_layout_block_preview() && is_main_query() ) {
|
||||
$content = et_()->array_get( $_POST, 'et_layout_block_layout_content', '' );
|
||||
|
||||
// If user don't have posting unfiltered html capability, strip scripts
|
||||
if ( ! current_user_can( 'unfiltered_html' ) ) {
|
||||
$content = wp_kses_post( $content );
|
||||
}
|
||||
|
||||
return wp_unslash( $content );
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify post meta for enabling builder status and disabling static css if current request is
|
||||
* layout block preview
|
||||
*
|
||||
* @since 4.1.0
|
||||
*
|
||||
* @param null $value
|
||||
* @param int $object_id
|
||||
* @param string $meta_key
|
||||
* @param bool $single
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function modify_layout_content_builder_meta( $value, $object_id, $meta_key, $single ) {
|
||||
// Force enable builder on layout block preview page request
|
||||
if ( '_et_pb_use_builder' === $meta_key && self::is_layout_block_preview() ) {
|
||||
return 'on';
|
||||
}
|
||||
|
||||
// Force disable static CSS on layout block preview page request so static CSS doesn't cache
|
||||
// incorrect stylesheet and break layout block styling
|
||||
if ( '_et_pb_static_css_file' === $meta_key && self::is_layout_block_preview() ) {
|
||||
return 'off';
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify raw post content for visual builder for layout content edit screen
|
||||
*
|
||||
* @since 4.1.0
|
||||
*
|
||||
* @param string $post_content
|
||||
*
|
||||
* @return string modified post content
|
||||
*/
|
||||
public function modify_layout_content_visual_builder_raw_post_content( $post_content ) {
|
||||
if ( self::is_layout_block_preview() ) {
|
||||
// Explicitly set post_id value based on query string because layout block's edit
|
||||
// window of CPT that has no frontend page such as reusable block's `wp_block` CPT
|
||||
// might use other / last post loop for rendering visual builder structure since its
|
||||
// own post data isn't publicly queryable
|
||||
$post_id = intval( et_()->array_get( $_GET, 'et_post_id', get_the_ID() ) );
|
||||
$block_id = sanitize_title( et_()->array_get( $_GET, 'blockId' ) );
|
||||
|
||||
$key = "_et_block_layout_preview_{$block_id}";
|
||||
$post_content = wp_unslash( get_post_meta( $post_id, $key, true ) );
|
||||
}
|
||||
|
||||
return $post_content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify Theme Builder body layout that is used on layout block preview
|
||||
*
|
||||
* @since 4.3.4
|
||||
*
|
||||
* @param string $content
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function modify_theme_builder_body_layout( $content ) {
|
||||
// Skip if current request isn't layout block preview
|
||||
if ( ! self::is_layout_block_preview() ) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
// Get `et_pb_post_content` shortcode inside layout by pulling regex and matching it.
|
||||
// `et_pb_post_content` has to exist, otherwise `the_content()` won't be rendered; Return
|
||||
// plain `et_pb_post_content` shortcode if current layout doesn't have any
|
||||
$post_content_regex = get_shortcode_regex( et_theme_builder_get_post_content_modules() );
|
||||
|
||||
preg_match_all( "/$post_content_regex/", $content, $post_content_module, PREG_SET_ORDER );
|
||||
|
||||
$post_content_shortcode = et_()->array_get(
|
||||
$post_content_module,
|
||||
'0.0',
|
||||
'[et_pb_post_content][/et_pb_post_content]'
|
||||
);
|
||||
|
||||
// Return `et_pb_post_content` wrapped by section > row > column which has no unwanted
|
||||
// styling. TB body layout might have any module imaginable while in context of layout block
|
||||
// preview, only `et_pb_post_content` matters because its typography setting can override
|
||||
// default typography styling
|
||||
return '[et_pb_section admin_label="section" custom_padding="0px|0px|0px|0px"]
|
||||
[et_pb_row admin_label="row" custom_padding="0px|0px|0px|0px" custom_margin="0px|0px|0px|0px" width="100%"]
|
||||
[et_pb_column type="4_4"]' . $post_content_shortcode . '[/et_pb_column]
|
||||
[/et_pb_row]
|
||||
[/et_pb_section]';
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize ET_GB_Block_Layout
|
||||
ET_GB_Block_Layout::instance();
|
||||
@@ -0,0 +1,261 @@
|
||||
<?php
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Class ET_GB_Utils_Conversion
|
||||
*
|
||||
* Handling Gutenberg serialized content conversion into builder shortcode layout
|
||||
*/
|
||||
class ET_GB_Utils_Conversion {
|
||||
// Populate all layout block which is placed inside other block. Layout block contains
|
||||
// section which has to be the first level element once converted into VB content
|
||||
private $deep_layout_blocks = array();
|
||||
|
||||
// Layout list. Layout block got its own section. Others are concatenated into text module
|
||||
private $layout_list = array();
|
||||
|
||||
// Temporary variable to hold non layout block into one
|
||||
private $text_module_content = '';
|
||||
|
||||
// Serialized layout
|
||||
private $shortcode_layout = '';
|
||||
|
||||
/**
|
||||
* Check if given block is layout block
|
||||
*
|
||||
* @since 4.1.0
|
||||
*
|
||||
* @todo being set as static so it is easier to be used outside this class. If being used quite
|
||||
* frequently, probably consider wrap this into function. Not needed at the moment tho
|
||||
*
|
||||
* @param array $block Parsed block.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_layout_block( $block = array() ) {
|
||||
$block_name = et_()->array_get( $block, 'blockName', '' );
|
||||
|
||||
return 'divi/layout' === $block_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given block is reusable block
|
||||
*
|
||||
* @since 4.1.0
|
||||
*
|
||||
* @todo being set as static so it is easier to be used outside this class. If being used quite
|
||||
* frequently, probably consider wrap this into function. Not needed at the moment tho
|
||||
*
|
||||
* @param array $block Parsed block.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_reusable_block( $block = array() ) {
|
||||
$block_name = et_()->array_get( $block, 'blockName', '' );
|
||||
|
||||
return 'core/block' === $block_name && et_()->array_get( $block, 'attrs.ref' ) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reusable block's parsed content. NOTE: WordPress has built in `render_block_core_block()`
|
||||
* but it renders the block and its content instead of parse its content.
|
||||
*
|
||||
* @since 4.1.0
|
||||
*
|
||||
* @see render_block_core_block()
|
||||
*
|
||||
* @todo being set as static so it is easier to be used outside this class. If being used quite
|
||||
* frequently, probably consider wrap this into function. Not needed at the moment tho
|
||||
*
|
||||
* @param array $block Parsed block.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_reusable_block_content( $block ) {
|
||||
$block_id = et_()->array_get( $block, 'attrs.ref' );
|
||||
$block_data = get_post( $block_id );
|
||||
|
||||
if ( ! $block_data || 'wp_block' !== $block_data->post_type || 'publish' !== $block_data->post_status ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return parse_blocks( $block_data->post_content );
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse reusable block by getting its content and append it as innerBlocks
|
||||
*
|
||||
* @since 4.1.0
|
||||
*
|
||||
* @param array Parsed block.
|
||||
*
|
||||
* @return array Modified parsed block.
|
||||
*/
|
||||
public static function parse_reusable_block( $block ) {
|
||||
$reusable_block_data = self::get_reusable_block_content( $block );
|
||||
$block['innerBlocks'] = array_merge( $block['innerBlocks'], $reusable_block_data );
|
||||
|
||||
// Unset reusable block's ref attribute so reusable block content is no longer fetched
|
||||
unset( $block['attrs']['ref'] );
|
||||
|
||||
// Change block into group so its content is being rendered
|
||||
$block['blockName'] = 'core/group';
|
||||
|
||||
// Recreate innerContent which is used by block parser to render innerBlock.
|
||||
// See: `render_block()`'s `$block['innerContent'] as $chunk` loop
|
||||
$block['innerContent'] = array_merge(
|
||||
array( '<div class="wp-block-group"><div class="wp-block-group__inner-container">' ),
|
||||
array_fill( 0, count( $block['innerBlocks'] ), null ),
|
||||
array( '</div></div>' )
|
||||
);
|
||||
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull layout block that is located deep inside inner blocks. Layout block contains section;
|
||||
* in builder, section has to be on the first level of document
|
||||
*
|
||||
* @since 4.1.0
|
||||
*
|
||||
* @param array $block Parsed block.
|
||||
*/
|
||||
private function pull_layout_block( $block ) {
|
||||
// Pull and populate layout block. Layout block contains section(s) so it should be rendered
|
||||
// on first level layout, below Gutenberg content inside text module
|
||||
if ( self::is_layout_block( $block ) ) {
|
||||
// Pull layout block and populate list of layout block located on inner blocks
|
||||
$this->deep_layout_blocks[] = $block;
|
||||
|
||||
// Remove innerContent and innerHTML value because inner block can't be simply removed
|
||||
// due to nested block rendering relies on `$block['innerContent']` making cross reference
|
||||
// on `$block['innerBlocks']` and removing them causes error (see: `render_block()`'s
|
||||
// `$block['innerContent'] as $chunk` loop). Thus, set deep layout block's content empty
|
||||
// so it doesn't get rendered
|
||||
$block['innerHTML'] = '';
|
||||
$block['innerContent'] = array();
|
||||
|
||||
return $block;
|
||||
}
|
||||
|
||||
// Reusable block's content is not saved inside block; Thus Get reusable block's content,
|
||||
// append it as innerBlock, and pull layout block if exist.
|
||||
if ( self::is_reusable_block( $block ) ) {
|
||||
$block = self::parse_reusable_block( $block );
|
||||
}
|
||||
|
||||
// Recursively loop over block then pull Layout Block
|
||||
if ( ! empty( $block['innerBlocks'] ) ) {
|
||||
$block['innerBlocks'] = array_map(
|
||||
array( $this, 'pull_layout_block' ),
|
||||
$block['innerBlocks']
|
||||
);
|
||||
}
|
||||
|
||||
return $block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert serialized block into shortcode layout
|
||||
*
|
||||
* @since 4.1.0
|
||||
*
|
||||
* @param string $serialized_block
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function block_to_shortcode( $serialized_block = '' ) {
|
||||
// Wrapper div needs to be trimmed
|
||||
$layout_open_tag = '<div class="wp-block-divi-layout">';
|
||||
$layout_open_length = strlen( $layout_open_tag );
|
||||
$layout_close_tag = '</div>';
|
||||
$layout_close_length = strlen( $layout_close_tag );
|
||||
|
||||
// Parsed blocks
|
||||
$blocks = parse_blocks( $serialized_block );
|
||||
|
||||
// Loop blocks
|
||||
foreach ( $blocks as $block ) {
|
||||
if ( self::is_layout_block( $block ) ) {
|
||||
// Append currently populated non-Layout Block into one before layout block is appended
|
||||
if ( ! empty( $this->text_module_content ) ) {
|
||||
$this->layout_list[] = $this->text_module_content;
|
||||
|
||||
// Reset text module content so next non-layout block is placed below current layout block
|
||||
$this->text_module_content = '';
|
||||
}
|
||||
|
||||
$this->layout_list[] = $block;
|
||||
} else {
|
||||
// Reusable block's content is not saved inside block; Thus Get reusable block's
|
||||
// content, append it as innerBlock, and pull layout block if exist.
|
||||
if ( self::is_reusable_block( $block ) ) {
|
||||
$block = self::parse_reusable_block( $block );
|
||||
}
|
||||
|
||||
// Pull any Layout Block inside nested block if there's any
|
||||
if ( ! empty( $block['innerBlocks'] ) ) {
|
||||
$block['innerBlocks'] = array_map(
|
||||
array( $this, 'pull_layout_block' ),
|
||||
$block['innerBlocks']
|
||||
);
|
||||
}
|
||||
|
||||
// Populate block into temporary text module content buffer
|
||||
$this->text_module_content .= render_block( $block );
|
||||
}
|
||||
}
|
||||
|
||||
// Populate remaining non-layout block into layout list
|
||||
if ( ! empty( $this->text_module_content ) ) {
|
||||
$this->layout_list[] = $this->text_module_content;
|
||||
|
||||
// Reset
|
||||
$this->text_module_content = '';
|
||||
}
|
||||
|
||||
// Loop over populated content and render it into shortcode layout
|
||||
foreach ( array_merge( $this->layout_list, $this->deep_layout_blocks ) as $item ) {
|
||||
if ( self::is_layout_block( $item ) ) {
|
||||
$shortcode_layout = trim( et_()->array_get( $item, 'innerHTML', '' ) );
|
||||
|
||||
// Remove layout content opening <div>
|
||||
if ( $layout_open_tag === substr( $shortcode_layout, 0, $layout_open_length ) ) {
|
||||
$shortcode_layout = substr( $shortcode_layout, $layout_open_length );
|
||||
}
|
||||
|
||||
// Remove layout content closing </div>
|
||||
if ( $layout_close_tag === substr( $shortcode_layout, ( 0 - $layout_close_length ) ) ) {
|
||||
$shortcode_layout = substr( $shortcode_layout, 0, ( 0 - $layout_close_length ) );
|
||||
}
|
||||
|
||||
$this->shortcode_layout .= $shortcode_layout;
|
||||
} else {
|
||||
$text_module = '[et_pb_text]' . $item . '[/et_pb_text]';
|
||||
$column = '[et_pb_column type="4_4"]' . $text_module . '[/et_pb_column]';
|
||||
$row = '[et_pb_row admin_label="row"]' . $column . '[/et_pb_row]';
|
||||
$this->shortcode_layout .= '[et_pb_section admin_label="section"]' . $row . '[/et_pb_section]';
|
||||
}
|
||||
}
|
||||
|
||||
return $this->shortcode_layout;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert gutenberg block layout into shortcode.
|
||||
* NOTE: There is JS version for activation via Gutenberg. See: `convertBlockToShortcode()`
|
||||
*
|
||||
* @since 4.1.0
|
||||
*
|
||||
* @param string $post_content Post content / serialized block.
|
||||
*
|
||||
* @return string Shortcode layout.
|
||||
*/
|
||||
function et_builder_convert_block_to_shortcode( $post_content ) {
|
||||
$conversion = new ET_GB_Utils_Conversion();
|
||||
|
||||
return $conversion->block_to_shortcode( $post_content );
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
/**
|
||||
* Generalized dynamic content implementation to make it usable for WooCommerce Modules.
|
||||
*
|
||||
* @package Divi
|
||||
* @subpackage Builder
|
||||
*/
|
||||
|
||||
/**
|
||||
* Handle ajax requests to resolve post content.
|
||||
*
|
||||
* @since 3.17.2
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function et_builder_ajax_resolve_post_content() {
|
||||
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( $_POST['nonce'] ), 'et_fb_resolve_post_content' ) ) { // phpcs:ignore ET.Sniffs.ValidatedSanitizedInput -- The nonce value is used only for comparision in the `wp_verify_nonce`.
|
||||
et_core_die();
|
||||
}
|
||||
|
||||
$_ = ET_Core_Data_Utils::instance();
|
||||
$post_id = isset( $_POST['post_id'] ) ? (int) $_POST['post_id'] : 0;
|
||||
// phpcs:disable ET.Sniffs.ValidatedSanitizedInput -- All values from `$_POST['groups']` and `$_POST['overrides']` arrays value are being sanitized before use in following foreach loop.
|
||||
$groups = isset( $_POST['groups'] ) && is_array( $_POST['groups'] ) ? $_POST['groups'] : array();
|
||||
$overrides = isset( $_POST['overrides'] ) && is_array( $_POST['overrides'] ) ? $_POST['overrides'] : array();
|
||||
// phpcs:enable
|
||||
$overrides = array_map( 'wp_kses_post', $overrides );
|
||||
$post = get_post( $post_id );
|
||||
|
||||
$invalid_permissions = ! current_user_can( 'edit_post', $post_id );
|
||||
$invalid_post = null === $post;
|
||||
|
||||
if ( $invalid_permissions || $invalid_post ) {
|
||||
et_core_die();
|
||||
}
|
||||
|
||||
$response = array();
|
||||
|
||||
foreach ( $groups as $hash => $field_group ) {
|
||||
$group = sanitize_text_field( isset( $field_group['group'] ) ? (string) $field_group['group'] : '' );
|
||||
$field = isset( $field_group['field'] ) ? sanitize_text_field( (string) $field_group['field'] ) : '';
|
||||
$settings = isset( $field_group['settings'] ) && is_array( $field_group['settings'] ) ? wp_unslash( $field_group['settings'] ) : array();
|
||||
$settings = array_map( 'wp_kses_post', $settings );
|
||||
$is_content = $_->array_get( $field_group, 'attribute' ) === 'content';
|
||||
$response[ $hash ] = apply_filters( "et_builder_resolve_{$group}_post_content_field", $field, $settings, $post_id, $overrides, $is_content );
|
||||
}
|
||||
|
||||
wp_send_json_success( $response );
|
||||
}
|
||||
add_action( 'wp_ajax_et_builder_resolve_post_content', 'et_builder_ajax_resolve_post_content' );
|
||||
|
||||
/**
|
||||
* List terms for a given post.
|
||||
*
|
||||
* @since 3.17.2
|
||||
*
|
||||
* @param array $terms List of terms.
|
||||
* @param boolean $link Whether return link or label.
|
||||
* @param string $separator Terms separators.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function et_builder_list_terms( $terms, $link = true, $separator = ' | ' ) {
|
||||
$output = array();
|
||||
|
||||
foreach ( $terms as $term ) {
|
||||
$label = esc_html( $term->name );
|
||||
|
||||
if ( $link ) {
|
||||
$label = sprintf(
|
||||
'<a href="%1$s">%2$s</a>',
|
||||
esc_url( get_term_link( $term ) ),
|
||||
et_core_esc_previously( $label )
|
||||
);
|
||||
}
|
||||
|
||||
$output[] = $label;
|
||||
}
|
||||
|
||||
return implode( esc_html( $separator ), $output );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the title for the current page be it a post, a tax archive, search etc.
|
||||
*
|
||||
* @since 4.0
|
||||
*
|
||||
* @param integer $post_id Post id.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function et_builder_get_current_title( $post_id = 0 ) {
|
||||
if ( 0 === $post_id ) {
|
||||
$post_id = get_the_ID();
|
||||
}
|
||||
|
||||
$post_id = (int) $post_id;
|
||||
|
||||
if ( ! ET_Builder_Element::is_theme_builder_layout() || is_singular() ) {
|
||||
return get_the_title( $post_id );
|
||||
}
|
||||
|
||||
if ( is_front_page() ) {
|
||||
return __( 'Home', 'et_builder' );
|
||||
}
|
||||
|
||||
if ( is_home() ) {
|
||||
return __( 'Blog', 'et_builder' );
|
||||
}
|
||||
|
||||
if ( is_404() ) {
|
||||
return __( 'No Results Found', 'et_builder' );
|
||||
}
|
||||
|
||||
if ( is_search() ) {
|
||||
return sprintf( __( 'Results for "%1$s"', 'et_builder' ), get_search_query() );
|
||||
}
|
||||
|
||||
if ( is_author() ) {
|
||||
return get_the_author();
|
||||
}
|
||||
|
||||
if ( is_post_type_archive() ) {
|
||||
return post_type_archive_title( '', false );
|
||||
}
|
||||
|
||||
if ( is_category() || is_tag() || is_tax() ) {
|
||||
return single_term_title( '', false );
|
||||
}
|
||||
|
||||
return get_the_archive_title();
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
/**
|
||||
* Ajax service which searches through posts.
|
||||
*
|
||||
* @package Divi
|
||||
* @subpackage Builder
|
||||
*/
|
||||
|
||||
/**
|
||||
* Handle ajax requests to search for posts.
|
||||
*
|
||||
* @since 3.26.7
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function et_builder_ajax_search_posts() {
|
||||
et_core_security_check( 'edit_posts', 'et_builder_search_posts', 'nonce', '_GET' );
|
||||
|
||||
$current_page = isset( $_GET['page'] ) ? (int) $_GET['page'] : 0;
|
||||
$current_page = max( $current_page, 1 );
|
||||
$post_type = isset( $_GET['post_type'] ) ? sanitize_text_field( $_GET['post_type'] ) : '';
|
||||
$value = isset( $_GET['value'] ) ? sanitize_text_field( $_GET['value'] ) : '';
|
||||
$search = isset( $_GET['search'] ) ? sanitize_text_field( $_GET['search'] ) : '';
|
||||
$prepend_value = (int) $value > 0;
|
||||
$results_per_page = 20;
|
||||
$results = array(
|
||||
'results' => array(),
|
||||
'meta' => array(),
|
||||
);
|
||||
$include_current_post = '1' === (string) et_()->array_get( $_GET, 'include_current_post', '0' );
|
||||
$include_latest_post = '1' === (string) et_()->array_get( $_GET, 'include_latest_post', '0' );
|
||||
|
||||
$public_post_types = et_builder_get_public_post_types();
|
||||
|
||||
if ( ! isset( $public_post_types[ $post_type ] ) ) {
|
||||
$post_type = 'post';
|
||||
}
|
||||
|
||||
$post_type_object = get_post_type_object( $post_type );
|
||||
$post_type_label = $post_type_object ? $post_type_object->labels->singular_name : '';
|
||||
|
||||
$query = array(
|
||||
'post_type' => $post_type,
|
||||
'posts_per_page' => $results_per_page,
|
||||
'post_status' => 'attachment' === $post_type ? 'inherit' : 'publish',
|
||||
's' => $search,
|
||||
'orderby' => 'date',
|
||||
'order' => 'desc',
|
||||
'paged' => $current_page,
|
||||
);
|
||||
|
||||
if ( $prepend_value ) {
|
||||
$value_post = get_post( $value );
|
||||
|
||||
if ( $value_post && 'publish' === $value_post->post_status && $value_post->post_type === $post_type ) {
|
||||
$results['results'][] = array(
|
||||
'value' => $value,
|
||||
'label' => et_core_intentionally_unescaped( wp_strip_all_tags( $value_post->post_title ), 'react_jsx' ),
|
||||
'meta' => array(
|
||||
'post_type' => et_core_intentionally_unescaped( $post_type_label, 'react_jsx' ),
|
||||
),
|
||||
);
|
||||
|
||||
// We will manually prepend the current value so we need to reduce the number of results.
|
||||
$query['posts_per_page'] -= 1;
|
||||
$query['post__not_in'] = array( $value );
|
||||
}
|
||||
}
|
||||
|
||||
if ( 'attachment' === $post_type ) {
|
||||
add_filter( 'posts_join', 'et_builder_ajax_search_posts_query_join' );
|
||||
add_filter( 'posts_where', 'et_builder_ajax_search_posts_query_where' );
|
||||
}
|
||||
|
||||
$posts = new WP_Query( $query );
|
||||
|
||||
if ( 'attachment' === $post_type ) {
|
||||
remove_filter( 'posts_join', 'et_builder_ajax_search_posts_query_join' );
|
||||
remove_filter( 'posts_where', 'et_builder_ajax_search_posts_query_where' );
|
||||
}
|
||||
|
||||
if ( $include_current_post && ! empty( $posts->posts ) ) {
|
||||
$current_post_type = sanitize_text_field( et_()->array_get( $_GET, 'current_post_type', 'post' ) );
|
||||
$current_post_type = isset( $public_post_types[ $current_post_type ] ) ? $current_post_type : 'post';
|
||||
$current_post_type_object = get_post_type_object( $current_post_type );
|
||||
$current_post_type_label = $current_post_type_object ? $current_post_type_object->labels->singular_name : '';
|
||||
|
||||
$results['results'][] = array(
|
||||
'value' => 'current',
|
||||
// Translators: %1$s: Post type singular name.
|
||||
'label' => et_core_intentionally_unescaped( sprintf( __( 'This %1$s', 'et_builder' ), $current_post_type_label ), 'react_jsx' ),
|
||||
'meta' => array(
|
||||
'post_type' => et_core_intentionally_unescaped( $current_post_type_label, 'react_jsx' ),
|
||||
),
|
||||
);
|
||||
|
||||
$query['posts_per_page'] -= 1;
|
||||
}
|
||||
|
||||
if ( $include_latest_post && ! empty( $posts->posts ) ) {
|
||||
$results['results'][] = array(
|
||||
'value' => 'latest',
|
||||
// Translators: %1$s: Post type singular name.
|
||||
'label' => et_core_intentionally_unescaped(
|
||||
sprintf(
|
||||
__( 'Latest %1$s', 'et_builder' ),
|
||||
$post_type_label
|
||||
),
|
||||
'react_jsx'
|
||||
),
|
||||
'meta' => array(
|
||||
'post_type' => et_core_intentionally_unescaped( $post_type_label, 'react_jsx' ),
|
||||
),
|
||||
);
|
||||
|
||||
$query['posts_per_page'] -= 1;
|
||||
}
|
||||
|
||||
foreach ( $posts->posts as $post ) {
|
||||
$results['results'][] = array(
|
||||
'value' => (int) $post->ID,
|
||||
'label' => et_core_intentionally_unescaped( wp_strip_all_tags( $post->post_title ), 'react_jsx' ),
|
||||
'meta' => array(
|
||||
'post_type' => et_core_intentionally_unescaped( $post_type_label, 'react_jsx' ),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
$results['meta']['pagination'] = array(
|
||||
'results' => array(
|
||||
'per_page' => (int) $results_per_page,
|
||||
'total' => (int) $posts->found_posts,
|
||||
),
|
||||
'pages' => array(
|
||||
'current' => (int) $current_page,
|
||||
'total' => (int) $posts->max_num_pages,
|
||||
),
|
||||
);
|
||||
|
||||
wp_send_json_success( $results );
|
||||
}
|
||||
add_action( 'wp_ajax_et_builder_search_posts', 'et_builder_ajax_search_posts' );
|
||||
|
||||
/**
|
||||
* Join the parent post for attachments queries.
|
||||
*
|
||||
* @since 3.27.3
|
||||
*
|
||||
* @param string $join The JOIN clause of the query.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function et_builder_ajax_search_posts_query_join( $join ) {
|
||||
global $wpdb;
|
||||
|
||||
$join .= " LEFT JOIN `$wpdb->posts` AS `parent` ON `parent`.`ID` = `$wpdb->posts`.`post_parent` ";
|
||||
|
||||
return $join;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter attachments based on the parent post status, if any.
|
||||
*
|
||||
* @since 3.27.3
|
||||
*
|
||||
* @param string $where The WHERE clause of the query.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function et_builder_ajax_search_posts_query_where( $where ) {
|
||||
global $wpdb;
|
||||
|
||||
$public_post_types = array_keys( et_builder_get_public_post_types() );
|
||||
|
||||
// Add an empty value to:
|
||||
// - Avoid syntax error for `IN ()` when there are no public post types.
|
||||
// - Cause the query to only return posts with no parent when there are no public post types.
|
||||
$public_post_types[] = '';
|
||||
|
||||
$where .= $wpdb->prepare(
|
||||
' AND (
|
||||
`parent`.`ID` IS NULL OR (
|
||||
`parent`.`post_status` = %s
|
||||
AND
|
||||
`parent`.`post_type` IN (' . implode( ',', array_fill( 0, count( $public_post_types ), '%s' ) ) . ')
|
||||
)
|
||||
)',
|
||||
array_merge( array( 'publish' ), $public_post_types )
|
||||
);
|
||||
|
||||
return $where;
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
/**
|
||||
* Function collection related to window.
|
||||
*
|
||||
* @package Divi
|
||||
* @sub-package Builder
|
||||
* @since 4.6.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get scroll location of all preview mode of all builder context
|
||||
* These are sorted by the time it was added to Divi (older to newer)
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function et_builder_get_builder_scroll_locations() {
|
||||
return array(
|
||||
// Frontend - what user sees.
|
||||
'fe' => array(
|
||||
'desktop' => 'app',
|
||||
'tablet' => 'app',
|
||||
'phone' => 'app',
|
||||
),
|
||||
|
||||
// Visual Builder - The most complex one
|
||||
// It used to use "faux responsive" while smaller breakpoints are simulated using more
|
||||
// specific CSS; but since true responsive which is derivation of BFB is introduced,
|
||||
// builder is rendered inside iframe on actual window width; To keep it seamless it needs
|
||||
// some trick and switching scroll location, hence these more complex scroll location.
|
||||
'vb' => array(
|
||||
'desktop' => 'app',
|
||||
'tablet' => 'top',
|
||||
'phone' => 'top',
|
||||
'zoom' => 'top',
|
||||
'wireframe' => 'app',
|
||||
),
|
||||
|
||||
// New Builder Experience - The Backend Frontend Builder (BFB)
|
||||
// Loads builder app inside iframe, but need to avoid the iframe having vertical scroll
|
||||
// for UX reason. User only need to scroll the main window's scroll hence the builder
|
||||
// app is rendered on its 100vh height and all scroll locations are located on top window.
|
||||
'bfb' => array(
|
||||
'desktop' => 'top',
|
||||
'tablet' => 'top',
|
||||
'phone' => 'top',
|
||||
'zoom' => 'top',
|
||||
'wireframe' => 'top',
|
||||
),
|
||||
|
||||
// Theme Builder
|
||||
// Builder is rendered on modal with 100vh on app window; all scroll is on top window.
|
||||
'tb' => array(
|
||||
'desktop' => 'top',
|
||||
'tablet' => 'top',
|
||||
'phone' => 'top',
|
||||
'zoom' => 'top',
|
||||
'wireframe' => 'top',
|
||||
),
|
||||
|
||||
// Layout Block's Builder
|
||||
// Reusing theme builder component, hence the shared characteristics.
|
||||
'lbb' => array(
|
||||
'desktop' => 'top',
|
||||
'tablet' => 'top',
|
||||
'phone' => 'top',
|
||||
'zoom' => 'top',
|
||||
'wireframe' => 'top',
|
||||
),
|
||||
|
||||
// Layout Block Preview
|
||||
// Preview Layout Block's frontend appearance inside Gutenberg block; similar to BFB but
|
||||
// what is being rendered is frontend component. Hence it displays 100vh preview height
|
||||
// for UX reason and all scroll happpens in top window.
|
||||
'lbp' => array(
|
||||
'desktop' => 'top',
|
||||
'tablet' => 'top',
|
||||
'phone' => 'top',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get window scroll location
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function et_builder_get_window_scroll_locations() {
|
||||
return array( 'app', 'top' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current builder type
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @return string app|top
|
||||
*/
|
||||
function et_builder_get_current_builder_type() {
|
||||
$type = 'fe';
|
||||
|
||||
if ( ET_GB_Block_Layout::is_layout_block_preview() ) {
|
||||
$type = 'lbp';
|
||||
|
||||
// Layout Block builder reuses Theme Builder's modal component.
|
||||
if ( et_builder_tb_enabled() ) {
|
||||
$type = 'lbb';
|
||||
}
|
||||
} elseif ( et_builder_tb_enabled() ) {
|
||||
$type = 'tb';
|
||||
} elseif ( et_builder_bfb_enabled() ) {
|
||||
$type = 'bfb';
|
||||
} elseif ( et_core_is_fb_enabled() ) {
|
||||
$type = 'vb';
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get scroll location on all breakpoints of current builder type
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function et_builder_get_onload_scroll_locations() {
|
||||
$builder_scroll_locations = et_builder_get_builder_scroll_locations();
|
||||
$builder_type = et_builder_get_current_builder_type();
|
||||
|
||||
return et_()->array_get( $builder_scroll_locations, $builder_type, array( 'desktop' => 'app' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get on page load scroll location of current builder type
|
||||
*
|
||||
* @since 4.6.0
|
||||
*
|
||||
* @return string app|top
|
||||
*/
|
||||
function et_builder_get_onload_scroll_location() {
|
||||
$builder_scroll_locations = et_builder_get_builder_scroll_locations();
|
||||
$builder_type = et_builder_get_current_builder_type();
|
||||
|
||||
// Default view mode doesn't change and consistent scroll location on all modes / breakpoint.
|
||||
if ( in_array( $builder_type, array( 'fe', 'lbp' ), true ) ) {
|
||||
return et_()->array_get( $builder_scroll_locations, "{$builder_type}.desktop" );
|
||||
}
|
||||
|
||||
// Default view mode might be changed via app preference modal.
|
||||
$app_preferences = et_fb_app_preferences_settings();
|
||||
$default_view_mode = et_()->array_get( $app_preferences, 'view_mode.default' );
|
||||
$view_mode = et_get_option( 'et_fb_pref_view_mode', $default_view_mode );
|
||||
|
||||
return et_()->array_get( $builder_scroll_locations, "{$builder_type}.{$view_mode}", 'app' );
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user