Commit realizado el 12:13:52 08-04-2024

This commit is contained in:
Pagina Web Monito
2024-04-08 12:13:55 -04:00
commit 0c33094de9
7815 changed files with 1365694 additions and 0 deletions

View File

@@ -0,0 +1,344 @@
<?php
/**
* ET_Theme_Builder_Local_Library_Item class
*
* @package Builder
* @subpackage ThemeBuilder
* @since 4.18.0
*/
/**
* Class used to implement local library in the theme builder.
* */
class ET_Theme_Builder_Local_Library_Item {
/**
* Data util.
*
* @var ET_Core_Data_Utils
*/
protected static $_;
/**
* The library item post.
*
* @var WP_Post|null
*/
public $item_post = null;
/**
* The library item type. i.e template and preset.
*
* @deprecated Use get_item_type() instead. This should be private variable, eventually.
*
* @var string
*/
public $item_type;
/**
* The contructor.
*
* @since 4.18.0
*
* @param integer|null $item_id Iem post id.
*/
public function __construct( $item_id = null ) {
if ( $item_id ) {
$this->init_item( $item_id );
}
self::$_ = ET_Core_Data_Utils::instance();
}
/**
* Returns the Library Item type.
*
* @return string
*/
public function get_item_type() {
return $this->item_type;
}
/**
* Init item.
*
* @param integer $item_id Item post id.
*/
public function init_item( $item_id ) {
// Initalize item post.
$item_post = et_theme_builder_get_library_item_post( $item_id );
if ( is_wp_error( $item_post ) ) {
return new WP_Error( 'et_library_item_not_exists', __( 'Library Item does not exist.', 'et_builder' ) );
}
$this->item_post = $item_post;
$item_type = et_theme_builder_get_library_item_type( $item_post );
if ( is_wp_error( $item_post ) ) {
return $item_type;
}
$this->item_type = $item_type;
}
/**
* Use the local library item.
*
* @param array $args Item details.
*/
public function use_library_item( $args = [] ) {
if ( ! $this->item_post || ! $this->item_type ) {
return new WP_Error(
'no_library_item_found',
esc_html__( 'No library item found', 'et_builder' ),
array(
'status' => 400,
)
);
}
switch ( $this->item_type ) {
case ET_THEME_BUILDER_ITEM_TEMPLATE:
return $this->use_template();
case ET_THEME_BUILDER_ITEM_SET:
return $this->use_preset( $args );
// `default` case is already handled in the above `if` statement.
}
}
// phpcs:disable Squiz.Commenting.FunctionComment.ParamCommentFullStop -- Respecting punctuation.
/**
* Use local library template.
*
* @param array $global_layouts Optional. Array containing the necessary params.
* $params = [
* 'header' => (int|string) Header Layout ID. `use_global` string when TB global layout (relink option) is to be used.
* 'body' => (int|string) Body Layout ID. `use_global` string when TB global layout (relink option) is to be used.
* 'footer' => (int|string) Footer Layout ID. `use_global` string when TB global layout (relink option) is to be used.
* ]
*/
public function use_template( $global_layouts = [] ) {
$template_data = array(
'layouts' => et_theme_builder_create_layouts_from_library_template( $this->item_post, $global_layouts ),
'settings' => et_theme_builder_get_template_settings( $this->item_post->ID, true ),
);
return $template_data;
}
// phpcs:enable
/**
* Populate Data to implement Use Preset functionality in TB.
*
* @param array $args Additional arguments.
*
* @return array
*/
public function use_preset( $args = [] ) {
$preset = [];
$_ = et_();
$override_default_website_template = $_->array_get( $args, 'override_default_website_template' );
$incoming_layout_duplicate_decision = $_->array_get( $args, 'incoming_layout_duplicate_decision' );
$global_layouts = [];
$default_template_id = 0;
$maybe_default_template_id = get_post_meta( $this->item_post->ID, '_et_default_template_id', true );
$default_template_id = is_string( $maybe_default_template_id ) ? absint( $maybe_default_template_id ) : 0;
if ( 'duplicate' === $incoming_layout_duplicate_decision || $override_default_website_template ) {
if ( $default_template_id > 0 ) {
// $context is raw, because get_post()->post_content by default uses `raw`.
$default_template = get_post( $default_template_id );
$global_layouts = et_theme_builder_create_layouts_from_library_template( $default_template );
$preset[ $default_template_id ] = [
'layouts' => $global_layouts,
'settings' => et_theme_builder_get_template_settings( $default_template_id, true ),
];
}
if ( ! $override_default_website_template ) {
// Layouts should be created for each templates when `Import as static layouts` is clicked.
$global_layouts = [];
}
}
$template_ids = get_post_meta( $this->item_post->ID, '_et_template_id', false );
foreach ( $template_ids as $maybe_template_id ) {
$template_id = absint( $maybe_template_id );
if ( $default_template_id === $template_id && 'relink' !== $incoming_layout_duplicate_decision ) {
continue;
}
if ( $default_template_id === $template_id && 'relink' === $incoming_layout_duplicate_decision ) {
foreach ( [ 'header', 'body', 'footer' ] as $layout_type ) {
if ( '1' === get_post_meta( $template_id, "_et_{$layout_type}_layout_global", true ) ) {
$global_layouts[ $layout_type ] = 'use_global';
}
}
}
$library_item = new self( $template_id );
$preset[ $template_id ] = $library_item->use_template( $global_layouts );
}
return $preset;
}
/**
* Returns TRUE when the given Preset ID contains a global layout.
*
* @return bool
*/
public function has_global_layouts() {
if ( ET_THEME_BUILDER_ITEM_SET !== $this->item_type ) {
return false;
}
$has_global_layouts = '1' === get_post_meta( $this->item_post->ID, '_et_has_global_layouts', true );
return $has_global_layouts;
}
/**
* Returns TRUE when the given Preset ID contains a default template.
*
* @return bool
*/
public function has_default_template() {
if ( ET_THEME_BUILDER_ITEM_SET !== $this->item_type ) {
return false;
}
$has_default_template = '1' === get_post_meta( $this->item_post->ID, '_et_has_default_template', true );
return $has_default_template;
}
/**
* Gets the default template ID when the $item_type is preset.
*
* @return bool
*/
public function get_default_template_id() {
if ( ! $this->has_default_template() ) {
return 0;
}
$template_ids = get_post_meta( $this->item_post->ID, '_et_template_id', false );
$default_template_id = 0;
foreach ( $template_ids as $template_id ) {
$is_default = (bool) get_post_meta( $template_id, '_et_default', true );
if ( $is_default ) {
$default_template_id = $template_id;
} else {
continue;
}
return $default_template_id;
}
}
/**
* Get the theme builder id.
*
* @since 4.18.0
*
* @return int The theme builder id.
*/
public function get_theme_builder_id() {
return $this->theme_builder_id;
}
/**
* Gets the item field.
*
* @param string $field_name Database field name.
* @param string $context Refer get_post_field() for context.
* @param string $default Default value to return when actual value does not exist.
* @return string
*/
public function get_item_field( $field_name = 'post_title', $context = 'display', $default = '' ) {
if ( is_a( $this->item_post, 'WP_Post' ) ) {
return get_post_field( $field_name, $this->item_post->ID, $context );
} else {
return $default;
}
}
/**
* Gets the item title field.
*
* @param string $context Refer get_post_field() for context.
* @param string $default Default value to return when actual value does not exist.
* @return string
*/
public function get_item_title( $context = 'display', $default = '' ) {
return $this->get_item_field( 'post_title', $context );
}
/**
* Gets the item title field formatted to be displayed in Theme Builder.
*
* @return int|WP_Error Valid Post ID on success. 0 or WP_Error on failure.
*/
public function duplicate_template_item() {
$template_meta_keys = array(
'_et_autogenerated_title',
'_et_enabled',
'_et_header_layout_enabled',
'_et_body_layout_enabled',
'_et_footer_layout_enabled',
'_et_template_title',
'_et_use_on',
'_et_exclude_from',
'_et_header_layout_global',
'_et_body_layout_global',
'_et_footer_layout_global',
'_et_set_template',
'_et_default',
);
foreach ( $template_meta_keys as $key ) {
$post_meta_value = get_post_meta( $this->item_post->ID, $key, true );
// `empty()` must NOT be used because meta value may contain '0' and the post meta will be skipped during duplication.
if ( isset( $post_meta_value ) && '' !== $post_meta_value ) {
$template_meta_input[ $key ] = $post_meta_value;
}
}
$new_item = array(
'post_title' => $this->item_post->post_title,
'post_content' => wp_slash( $this->item_post->post_content ),
'post_status' => 'publish',
'post_type' => $this->item_post->post_type,
'meta_input' => $template_meta_input,
'tax_input' => array(
'et_tb_item_type' => ET_THEME_BUILDER_ITEM_TEMPLATE,
),
);
return wp_insert_post( $new_item );
}
/**
* Duplicates the Library Item.
*
* @return int
*/
public function duplicate_item() {
$item_duplication_function = "duplicate_{$this->get_item_type()}_item";
$duplicated_item_id = $this->$item_duplication_function();
return $duplicated_item_id;
}
}

View File

@@ -0,0 +1,166 @@
<?php
/**
* File containing Local Library Item Editor class.
*
* @package Builder
* @subpackage ThemeBuilder
* @since 4.18.0
*/
/**
* Local Library Item Editor class.
*/
class ET_Theme_Builder_Local_Library_Item_Editor {
/**
* Hold the class instance.
*
* @var ET_Theme_Builder_Local_Library_Item_Editor[]
*/
private static $_instances;
/**
* Interim Theme Builder Id.
*
* @var int
*/
protected static $_theme_builder_id;
/**
* Library Item.
*
* @var ET_Theme_Builder_Local_Library_Item
*/
public $item;
/**
* Class contructor.
*
* @param int $item_id Item Id.
*/
public function __construct( $item_id ) {
if ( ! self::$_theme_builder_id ) {
self::$_theme_builder_id = et_theme_builder_insert_library_theme_builder();
}
$this->item = new ET_Theme_Builder_Local_Library_Item( $item_id );
}
/**
* Get the singleton instance.
*
* @param int $item_id Item Id.
*
* @return ET_Theme_Builder_Local_Library_Item_Editor
*/
public static function instance( $item_id ) {
if ( ! isset( self::$_instances[ $item_id ] ) ) {
self::$_instances[ $item_id ] = new ET_Theme_Builder_Local_Library_Item_Editor( $item_id );
}
return self::$_instances[ $item_id ];
}
/**
* Gets the interim Theme Builder Id for the current request.
*
* @return int
*/
public static function get_interim_theme_builder_id() {
return self::$_theme_builder_id;
}
/**
* Init Library Template Item.
*/
public function init_library_template_item_editor() {
if ( ! isset( self::$_theme_builder_id ) ) {
return false;
}
// Insert template.
$template_id = et_theme_builder_create_template_from_library_template( $this->item->item_post );
if ( ! $template_id ) {
return false;
}
add_post_meta( self::$_theme_builder_id, '_et_template', $template_id );
}
/**
* Init Library Template Item.
*/
public function init_library_set_item_editor() {
if ( ! isset( self::$_theme_builder_id ) ) {
return false;
}
$template_ids = get_post_meta( $this->item->item_post->ID, '_et_template_id', false );
$default_template_item_id = (int) get_post_meta( $this->item->item_post->ID, '_et_default_template_id', true );
$global_layouts = array();
foreach ( $template_ids as $maybe_template_id ) {
$template_item_id = absint( $maybe_template_id );
$template_item = new ET_Theme_Builder_Local_Library_Item( $template_item_id );
if ( ! isset( $template_item->item_post ) || ! is_a( $template_item->item_post, 'WP_Post' ) ) {
continue;
}
// Insert template.
$template_id = et_theme_builder_create_template_from_library_template( $template_item->item_post, $global_layouts );
if ( ! $template_id ) {
continue;
}
if ( $template_item_id === $default_template_item_id ) {
$global_layouts = array(
'body' => (int) get_post_meta( $template_id, '_et_body_layout_id', true ),
'header' => (int) get_post_meta( $template_id, '_et_header_layout_id', true ),
'footer' => (int) get_post_meta( $template_id, '_et_footer_layout_id', true ),
);
}
add_post_meta( self::$_theme_builder_id, '_et_template', $template_id );
}
}
/**
* Init Library Item.
*/
public function init_library_item_editor() {
if ( ! is_a( $this->item, 'ET_Theme_Builder_Local_Library_Item' ) ) {
return false;
}
$item_type = $this->item->get_item_type();
if ( ET_THEME_BUILDER_ITEM_TEMPLATE === $item_type ) {
$this->init_library_template_item_editor();
} elseif ( ET_THEME_BUILDER_ITEM_SET === $item_type ) {
$this->init_library_set_item_editor();
}
}
/**
* Gets the item title field formatted to be displayed in Theme Builder.
*
* @return string
*/
public function get_library_item_editor_item_title() {
if ( ET_THEME_BUILDER_ITEM_SET === $this->item->get_item_type() ) {
return sprintf(
'%1$s: %2$s',
esc_html_x( 'Edit Set', 'Edit Set using Theme Builder', 'et_builder' ),
$this->item->get_item_title()
);
} elseif ( ET_THEME_BUILDER_ITEM_TEMPLATE === $this->item->get_item_type() ) {
return sprintf(
'%1$s: %2$s',
esc_html_x( 'Edit Template', 'Edit Template using Theme Builder', 'et_builder' ),
$this->item->get_item_title()
);
}
}
}

View File

@@ -0,0 +1,159 @@
<?php
/**
* Divi Theme Builder Item Library.
*
* @since 4.18.0
*
* @package Builder
*/
/**
* Core class used to implement TB Item library.
*
* Register TB Item post type and its taxonomies.
*/
class ET_Builder_TBItem_Library {
/**
* Instance of `ET_Builder_TBItem_Library`.
*
* @var ET_Builder_TBItem_Library
*/
private static $_instance;
/**
* Instance of `ET_Core_Data_Utils`.
*
* @var ET_Core_Data_Utils
*/
protected static $_;
/**
* List of i18n strings.
*
* @var mixed[]
*/
protected static $_i18n;
/**
* ET_Builder_Post_Taxonomy_TBItemCategory instance.
*
* Shall be used for querying `et_tb_item` taxonomy.
*
* @var ET_Builder_Post_Taxonomy_TBItemCategory
*/
public $item_categories;
/**
* ET_Builder_Post_Taxonomy_TBItemTag instance.
*
* Shall be used for querying `et_tb_item` taxonomy .
*
* @var ET_Builder_Post_Taxonomy_TBItemTag
*/
public $item_tags;
/**
* ET_Builder_Post_Taxonomy_TBItemType instance.
*
* Shall be used for querying `et_tb_item` taxonomy .
*
* @var ET_Builder_Post_Taxonomy_TBItemType
*/
public $item_types;
/**
* ET_Builder_Post_Type_TBItem instance.
*
* Shall be used for querying `et_tb_item` posts .
*
* @var ET_Builder_Post_Type_TBItem
*/
public $items;
/**
* Class constructor.
*/
public function __construct() {
$this->_instance_check();
$this->_register_cpt_and_taxonomies();
self::$_ = ET_Core_Data_Utils::instance();
$root_directory = defined( 'ET_BUILDER_PLUGIN_ACTIVE' ) ? ET_BUILDER_PLUGIN_DIR : get_template_directory();
self::$_i18n = require $root_directory . '/cloud/i18n/library.php';
}
/**
* Gets a translated string from {@see self::$_i18n}.
*
* @param string $string The untranslated string.
* @param string $path Optional path for nested strings.
*
* @return string The translated string if found, the original string otherwise.
*/
public static function __( $string, $path = '' ) {
$path .= $path ? ".{$string}" : $string;
return self::$_->array_get( self::$_i18n, $path, $string );
}
/**
* Dies if an instance already exists.
*/
protected function _instance_check() {
if ( self::$_instance ) {
et_error( 'Multiple instances are not allowed!' );
wp_die();
}
}
/**
* Registers the Theme Builder Library's custom post type and its taxonomies.
*/
protected function _register_cpt_and_taxonomies() {
$files = [
ET_THEME_BUILDER_DIR . 'post/type/TBItem.php',
ET_THEME_BUILDER_DIR . 'post/taxonomy/TBItemType.php',
ET_THEME_BUILDER_DIR . 'post/query/TBItems.php',
ET_BUILDER_DIR . 'post/type/Layout.php',
ET_BUILDER_DIR . 'post/taxonomy/LayoutCategory.php',
ET_BUILDER_DIR . 'post/taxonomy/LayoutTag.php',
ET_BUILDER_DIR . 'post/query/Layouts.php',
];
if ( ! $files ) {
return;
}
foreach ( $files as $file ) {
require_once $file;
}
$this->items = ET_Builder_Post_Type_TBItem::instance();
$this->item_categories = ET_Builder_Post_Taxonomy_LayoutCategory::instance();
$this->item_tags = ET_Builder_Post_Taxonomy_LayoutTag::instance();
$this->item_types = ET_Builder_Post_Taxonomy_TBItemType::instance();
// We manually call register_all() now to ensure the CPT and taxonomies are registered
// at exactly the same point during the request that they were in prior releases.
ET_Builder_Post_Type_TBItem::register_all( 'builder' );
}
/**
* Returns the ET_Builder_TBItem_Library instance.
*
* @return ET_Builder_TBItem_Library
*/
public static function instance() {
if ( ! self::$_instance ) {
self::$_instance = new self();
}
return self::$_instance;
}
}
ET_Builder_TBItem_Library::instance();

View File

@@ -0,0 +1,26 @@
<?php
class ET_Theme_Builder_Api_Errors {
const UNKNOWN = 'unknown';
const PORTABILITY_INCORRECT_CONTEXT = 'incorrect_context';
const PORTABILITY_REQUIRE_INCOMING_LAYOUT_DUPLICATE_DECISION = 'require_incoming_layout_duplicate_decision';
const PORTABILITY_IMPORT_PRESETS_FAILURE = 'import_presets_failure';
const PORTABILITY_IMPORT_INVALID_FILE = 'invalid_file';
/**
* Get map of all error codes.
*
* @since 4.0
*
* @return string[]
*/
public static function getMap() {
return array(
'unknown' => self::UNKNOWN,
'portabilityIncorrectContext' => self::PORTABILITY_INCORRECT_CONTEXT,
'portabilityRequireIncomingLayoutDuplicateDecision' => self::PORTABILITY_REQUIRE_INCOMING_LAYOUT_DUPLICATE_DECISION,
'portabilityImportPresetsFailure' => self::PORTABILITY_IMPORT_PRESETS_FAILURE,
'portabilityImportInvalidFile' => self::PORTABILITY_IMPORT_INVALID_FILE,
);
}
}

View File

@@ -0,0 +1,444 @@
<?php
class ET_Theme_Builder_Request {
/**
* Type constants.
*/
const TYPE_FRONT_PAGE = 'front_page';
const TYPE_404 = '404';
const TYPE_SEARCH = 'search';
const TYPE_SINGULAR = 'singular';
const TYPE_POST_TYPE_ARCHIVE = 'archive';
const TYPE_TERM = 'term';
const TYPE_AUTHOR = 'author';
const TYPE_DATE = 'date';
/**
* Requested object type.
*
* @var string
*/
protected $type = '';
/**
* Requested object subtype.
*
* @var string
*/
protected $subtype = '';
/**
* Requested object id.
*
* @var integer
*/
protected $id = 0;
/**
* Create a request object based on the current request.
*
* @since 4.0
*
* @return ET_Theme_Builder_Request|null
*/
public static function from_current() {
$is_extra_layout_home = 'layout' === get_option( 'show_on_front' ) && is_home();
if ( $is_extra_layout_home || is_front_page() ) {
return new self( self::TYPE_FRONT_PAGE, '', get_queried_object_id() );
}
if ( is_404() ) {
return new self( self::TYPE_404, '', 0 );
}
if ( is_search() ) {
return new self( self::TYPE_SEARCH, '', 0 );
}
$id = get_queried_object_id();
$object = get_queried_object();
$page_for_posts = (int) get_option( 'page_for_posts' );
$is_blog_page = 0 !== $page_for_posts && is_page( $page_for_posts );
if ( is_singular() ) {
return new self( self::TYPE_SINGULAR, get_post_type( $id ), $id );
}
if ( $is_blog_page || is_home() ) {
return new self( self::TYPE_POST_TYPE_ARCHIVE, 'post', $id );
}
if ( is_category() || is_tag() || is_tax() ) {
return new self( self::TYPE_TERM, $object->taxonomy, $id );
}
if ( is_post_type_archive() ) {
return new self( self::TYPE_POST_TYPE_ARCHIVE, $object->name, $id );
}
if ( is_author() ) {
return new self( self::TYPE_AUTHOR, '', $id );
}
if ( is_date() ) {
return new self( self::TYPE_DATE, '', 0 );
}
return null;
}
/**
* Create a request object based on a post id.
*
* @since 4.0
*
* @param integer $post_id
*
* @return ET_Theme_Builder_Request
*/
public static function from_post( $post_id ) {
if ( (int) get_option( 'page_on_front' ) === $post_id ) {
return new self( self::TYPE_FRONT_PAGE, '', $post_id );
}
if ( (int) get_option( 'page_for_posts' ) === $post_id ) {
return new self( self::TYPE_POST_TYPE_ARCHIVE, 'post', $post_id );
}
return new self( self::TYPE_SINGULAR, get_post_type( $post_id ), $post_id );
}
/**
* Constructor.
*
* @since 4.0
*
* @param string $type Type.
* @param string $subtype Subtype.
* @param integer $id ID.
*/
public function __construct( $type, $subtype, $id ) {
$this->type = $type;
$this->subtype = $subtype;
$this->id = $id;
}
/**
* Get the requested object type.
*
* @since 4.0
*
* @return string
*/
public function get_type() {
return $this->type;
}
/**
* Get the requested object subtype.
*
* @since 4.0
*
* @return string
*/
public function get_subtype() {
return $this->subtype;
}
/**
* Get the requested object id.
*
* @since 4.0
*
* @return string
*/
public function get_id() {
return $this->id;
}
/**
* Get the top ancestor of a setting based on its id. Takes the setting itself
* if it has no ancestors.
* Returns an empty array if the setting is not found.
*
* @since 4.0
*
* @param array $flat_settings Flat settings.
* @param string $setting_id Setting ID.
*
* @return array
*/
protected function _get_template_setting_ancestor( $flat_settings, $setting_id ) {
$id = $setting_id;
if ( ! isset( $flat_settings[ $id ] ) ) {
// If the setting is not found, check if a valid parent exists.
$parent_id = explode( ET_THEME_BUILDER_SETTING_SEPARATOR, $id );
array_pop( $parent_id );
$parent_id[] = '';
$parent_id = implode( ET_THEME_BUILDER_SETTING_SEPARATOR, $parent_id );
$id = $parent_id;
}
if ( ! isset( $flat_settings[ $id ] ) ) {
// The setting is still not found - bail.
return array();
}
return $flat_settings[ $id ];
}
/**
* Get $a or $b depending on which template setting has a higher priority.
* Handles cases such as category settings with equal priority but in a ancestor-child relationship.
* Returns an empty string if neither setting is found.
*
* @since 4.0
*
* @param array $flat_settings Flat settings.
* @param string $a First template setting.
* @param string $b Second template setting.
*
* @return string
*/
protected function _get_higher_priority_template_setting( $flat_settings, $a, $b ) {
$map = array_flip( array_keys( $flat_settings ) );
$a_ancestor = $this->_get_template_setting_ancestor( $flat_settings, $a );
$b_ancestor = $this->_get_template_setting_ancestor( $flat_settings, $b );
$a_found = ! empty( $a_ancestor );
$b_found = ! empty( $b_ancestor );
if ( ! $a_found || ! $b_found ) {
if ( $a_found ) {
return $a;
}
if ( $b_found ) {
return $b;
}
return '';
}
if ( $a_ancestor['priority'] !== $b_ancestor['priority'] ) {
// Priorities are not equal - use a simple comparison.
return $a_ancestor['priority'] >= $b_ancestor['priority'] ? $a : $b;
}
if ( $a_ancestor['id'] !== $b_ancestor['id'] ) {
// Equal priorities, but the ancestors are not the same - use the order in $flat_settings
// so we have a deterministic result even if $a and $b are swapped.
return $map[ $a_ancestor['id'] ] <= $map[ $b_ancestor['id'] ] ? $a : $b;
}
// Equal priorities, same ancestor.
$ancestor = $a_ancestor;
$a_pieces = explode( ET_THEME_BUILDER_SETTING_SEPARATOR, $a );
$b_pieces = explode( ET_THEME_BUILDER_SETTING_SEPARATOR, $b );
$separator = preg_quote( ET_THEME_BUILDER_SETTING_SEPARATOR, '/' );
// Hierarchical post types are a special case by spec since we have to take hierarchy into account.
// Test if the ancestor matches "singular:post_type:<post_type>:children:id:".
$id_pieces = array( 'singular', 'post_type', '[^' . $separator . ']+', 'children', 'id', '' );
$term_regex = '/^' . implode( $separator, $id_pieces ) . '$/';
if ( preg_match( $term_regex, $ancestor['id'] ) && is_post_type_hierarchical( $a_pieces[2] ) ) {
$a_post_id = (int) $a_pieces[5];
$b_post_id = (int) $b_pieces[5];
$a_post_ancestors = get_post_ancestors( $a_post_id );
$b_post_ancestors = get_post_ancestors( $b_post_id );
if ( in_array( $a_post_id, $b_post_ancestors, true ) ) {
// $b is a child of $a so it should take priority.
return $b;
}
if ( in_array( $b_post_id, $a_post_ancestors, true ) ) {
// $a is a child of $b so it should take priority.
return $a;
}
// neither $a nor $b is an ancestor to the other - continue the comparisons.
}
// Term archive listings are a special case by spec since we have to take hierarchy into account.
// Test if the ancestor matches "archive:taxonomy:<taxonomy>:term:id:".
$id_pieces = array( 'archive', 'taxonomy', '[^' . $separator . ']+', 'term', 'id', '' );
$term_regex = '/^' . implode( $separator, $id_pieces ) . '$/';
if ( preg_match( $term_regex, $ancestor['id'] ) && is_taxonomy_hierarchical( $a_pieces[2] ) ) {
$a_term_id = $a_pieces[5];
$b_term_id = $b_pieces[5];
if ( term_is_ancestor_of( $a_term_id, $b_term_id, $a_pieces[2] ) ) {
// $b is a child of $a so it should take priority.
return $b;
}
if ( term_is_ancestor_of( $b_term_id, $a_term_id, $a_pieces[2] ) ) {
// $a is a child of $b so it should take priority.
return $a;
}
// neither $a nor $b is an ancestor to the other - continue the comparisons.
}
// Find the first difference in the settings and compare it.
// The difference should be representing an id or a slug.
foreach ( $a_pieces as $index => $a_piece ) {
$b_piece = $b_pieces[ $index ];
if ( $b_piece === $a_piece ) {
continue;
}
if ( is_numeric( $a_piece ) ) {
$prioritized = (float) $a_piece <= (float) $b_piece ? $a : $b;
} else {
$prioritized = strcmp( $a, $b ) <= 0 ? $a : $b;
}
/**
* Filters the higher prioritized setting in a given pair that
* has equal built-in priority.
*
* @since 4.2
*
* @param string $prioritized_setting
* @param string $setting_a
* @param string $setting_b
* @param ET_Theme_Builder_Request $request
*/
return apply_filters( 'et_theme_builder_prioritized_template_setting', $prioritized, $a, $b, $this );
}
// We should only reach this point if $a and $b are equal so it doesn't
// matter which we return.
return $a;
}
/**
* Check if this request fulfills a template setting.
*
* @since 4.0
*
* @param array $flat_settings Flat settings.
* @param string $setting_id Setting ID.
*
* @return boolean
*/
protected function _fulfills_template_setting( $flat_settings, $setting_id ) {
$ancestor = $this->_get_template_setting_ancestor( $flat_settings, $setting_id );
$fulfilled = false;
if ( ! empty( $ancestor ) && isset( $ancestor['validate'] ) && is_callable( $ancestor['validate'] ) ) {
// @phpcs:ignore Generic.PHP.ForbiddenFunctions.Found
$fulfilled = call_user_func(
$ancestor['validate'],
$this->get_type(),
$this->get_subtype(),
$this->get_id(),
explode( ET_THEME_BUILDER_SETTING_SEPARATOR, $setting_id )
);
}
return $fulfilled;
}
/**
* Reduce callback for self::get_template() to get the highest priority template from all applicable ones.
*
* @since 4.0
*
* @param array $carry
* @param array $applicable_template
*
* @return array
*/
public function reduce_get_template( $carry, $applicable_template ) {
global $__et_theme_builder_request_flat_settings;
if ( empty( $carry ) ) {
return $applicable_template;
}
$higher = $this->_get_higher_priority_template_setting(
$__et_theme_builder_request_flat_settings,
$carry['top_setting_id'],
$applicable_template['top_setting_id']
);
return $carry['top_setting_id'] !== $higher ? $applicable_template : $carry;
}
/**
* Get the highest-priority template that should be applied for this request, if any.
*
* @since 4.0
*
* @param array $templates
* @param array $flat_settings
*
* @return array
*/
public function get_template( $templates, $flat_settings ) {
// Use a global variable to pass data to the reduce callback as we support PHP 5.2.
global $__et_theme_builder_request_flat_settings;
$applicable_templates = array();
foreach ( $templates as $template ) {
if ( ! $template['enabled'] ) {
continue;
}
foreach ( $template['exclude_from'] as $setting_id ) {
if ( $this->_fulfills_template_setting( $flat_settings, $setting_id ) ) {
// The setting is explicitly excluded - bail from testing the template any further.
continue 2;
}
}
$highest_priority = '';
foreach ( $template['use_on'] as $setting_id ) {
if ( $this->_fulfills_template_setting( $flat_settings, $setting_id ) ) {
$highest_priority = $this->_get_higher_priority_template_setting( $flat_settings, $highest_priority, $setting_id );
}
}
if ( '' !== $highest_priority ) {
$applicable_templates[] = array(
'template' => $template,
'top_setting_id' => $highest_priority,
);
}
}
$__et_theme_builder_request_flat_settings = $flat_settings;
$applicable_template = array_reduce( $applicable_templates, array( $this, 'reduce_get_template' ), array() );
$__et_theme_builder_request_flat_settings = array();
if ( ! empty( $applicable_template ) ) {
// Found the highest priority applicable template - return it.
return $applicable_template['template'];
}
$default_templates = et_()->array_pick( $templates, array( 'default' => true ) );
if ( ! empty( $default_templates ) ) {
$default_template = $default_templates[0];
if ( $default_template['enabled'] ) {
// Return the first default template. We don't expect there to be multiple ones but
// it is technically possible with direct database edits, for example.
return $default_template;
}
}
// No templates found at all - probably never used the Theme Builder.
return array();
}
}

View File

@@ -0,0 +1,327 @@
<?php
/**
* Class ET_Theme_Builder_Woocommerce_Product_Variable_Placeholder
*
* Variable product class extension for displaying WooCommerce placeholder on Theme Builder
*/
class ET_Theme_Builder_Woocommerce_Product_Variable_Placeholder extends WC_Product_Variable {
/**
* Cached upsells id
*
* @since 4.0.10
*
* @var array
*/
protected static $tb_upsells_id;
/**
* Cached product category ids
*
* @since 4.0.10
*
* @var array
*/
protected static $tb_category_ids;
/**
* Cached product tag ids
*
* @since 4.0.10
*
* @var array
*/
protected static $tb_tag_ids;
/**
* Cached attributes
*
* @since 4.0.10
*
* @var array
*/
protected static $tb_attributes;
/**
* Create pre-filled WC Product (variable) object which acts as placeholder generator in TB
*
* @since 4.0.10 Instead of empty product object that is set later, pre-filled default data properties
*
* @param int|WC_Product|object $product Product to init.
*/
public function __construct( $product = 0 ) {
// Pre-filled default data with placeholder value so everytime this product class is
// initialized, it already has sufficient data to be displayed on Theme Builder
$this->data = array(
'name' => esc_html( 'Product name', 'et_builder' ),
'slug' => 'product-name',
'date_created' => current_time( 'timestamp' ),
'date_modified' => null,
'status' => 'publish',
'featured' => false,
'catalog_visibility' => 'visible',
'description' => esc_html( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris bibendum eget dui sed vehicula. Suspendisse potenti. Nam dignissim at elit non lobortis. Cras sagittis dui diam, a finibus nibh euismod vestibulum. Integer sed blandit felis. Maecenas commodo ante in mi ultricies euismod. Morbi condimentum interdum luctus. Mauris iaculis interdum risus in volutpat. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Praesent cursus odio eget cursus pharetra. Aliquam lacinia lectus a nibh ullamcorper maximus. Quisque at sapien pulvinar, dictum elit a, bibendum massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris non pellentesque urna.', 'et_builder' ),
'short_description' => esc_html( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris bibendum eget dui sed vehicula. Suspendisse potenti. Nam dignissim at elit non lobortis.', 'et_builder' ),
'sku' => 'product-name',
'price' => '75',
'regular_price' => '80',
'sale_price' => '65',
'date_on_sale_from' => null,
'date_on_sale_to' => null,
'total_sales' => '0',
'tax_status' => 'taxable',
'tax_class' => '',
'manage_stock' => true,
'stock_quantity' => 50,
'stock_status' => 'instock',
'backorders' => 'no',
'low_stock_amount' => 2,
'sold_individually' => false,
'weight' => 2,
'length' => '',
'width' => 2,
'height' => 2,
'upsell_ids' => array(),
'cross_sell_ids' => array(),
'parent_id' => 0,
'reviews_allowed' => true,
'purchase_note' => '',
'attributes' => array(),
'default_attributes' => array(),
'menu_order' => 0,
'post_password' => '',
'virtual' => false,
'downloadable' => false,
'category_ids' => array(),
'tag_ids' => array(),
'shipping_class_id' => 0,
'downloads' => array(),
'image_id' => '',
'gallery_image_ids' => array(),
'download_limit' => -1,
'download_expiry' => -1,
'rating_counts' => array(
4 => 2,
),
'average_rating' => '4.00',
'review_count' => 2,
'recent_product_ids' => null,
);
parent::__construct( $product );
}
/**
* Get internal type.
* Define custom internal type so custom data store can be used to bypass database value retrieval
*
* @since 4.0.10
*
* @return string
*/
public function get_type() {
return 'tb-placeholder';
}
/**
* Get placeholder product as available variation. The method is basically identical to
* `WC_Product_Variable->get_available_variation()` except for the checks which are removed
* so placeholder value can be passed
*
* @since 4.3.3
*
* @param int|object $variation not needed since it will be overwritten by placeholder variation
* but it needs to be kept for compatibility with base class' method
*
* @return array
*/
function get_available_variation( $variation = 0 ) {
$variation = new ET_Theme_Builder_Woocommerce_Product_Variation_Placeholder();
$show_variation_price = apply_filters( 'woocommerce_show_variation_price', $variation->get_price() === '' || $this->get_variation_sale_price( 'min' ) !== $this->get_variation_sale_price( 'max' ) || $this->get_variation_regular_price( 'min' ) !== $this->get_variation_regular_price( 'max' ), $this, $variation );
// Set variation id; Prevent $product->get_id() returns falsey which usually triggers wc_product_get()
// in WC add ons; Valid $product->get_id() makes global $product being used most of the time
$variation->set_id( $this->get_id() );
// Set current product id as variation parent id so $product->get_parent_id() returns
// valid value (mostly when being called by WC add-ons). The absence of this value (in TB)
// triggers new `wc_get_product()` which most likely returned unwanted output
$variation->set_prop( 'parent_id', $this->get_id() );
// Returned array properties are identical to `WC_Product_Variable->get_available_variation()`
return apply_filters(
'woocommerce_available_variation',
array(
'attributes' => $variation->get_variation_attributes(),
'availability_html' => wc_get_stock_html( $variation ),
'backorders_allowed' => $variation->backorders_allowed(),
'dimensions' => $variation->get_dimensions( false ),
'dimensions_html' => wc_format_dimensions( $variation->get_dimensions( false ) ),
'display_price' => wc_get_price_to_display( $variation ),
'display_regular_price' => wc_get_price_to_display( $variation, array( 'price' => $variation->get_regular_price() ) ),
'image' => wc_get_product_attachment_props( $variation->get_image_id() ),
'image_id' => $variation->get_image_id(),
'is_downloadable' => $variation->is_downloadable(),
'is_in_stock' => $variation->is_in_stock(),
'is_purchasable' => $variation->is_purchasable(),
'is_sold_individually' => $variation->is_sold_individually() ? 'yes' : 'no',
'is_virtual' => $variation->is_virtual(),
'max_qty' => 0 < $variation->get_max_purchase_quantity() ? $variation->get_max_purchase_quantity() : '',
'min_qty' => $variation->get_min_purchase_quantity(),
'price_html' => $show_variation_price ? '<span class="price">' . $variation->get_price_html() . '</span>' : '',
'sku' => $variation->get_sku(),
'variation_description' => wc_format_content( $variation->get_description() ),
'variation_id' => $variation->get_id(),
'variation_is_active' => $variation->variation_is_active(),
'variation_is_visible' => $variation->variation_is_visible(),
'weight' => $variation->get_weight(),
'weight_html' => wc_format_weight( $variation->get_weight() ),
),
$this,
$variation
);
}
/**
* Add to cart's <select> requires variable product type and get_available_variations() method
* outputting product->children value. Filtering get_available_variations() can't be done so
* extending WC_Product_Variable and set fixed value for get_available_variations() method
*
* @since 4.5.7 Introduced $return arg to fix compatibility issue {@link https://github.com/elegantthemes/Divi/issues/20985}
* @since 4.3.3 `Replaced ET_Theme_Builder_Woocommerce_Product_Variable_Placeholder` with
* `ET_Theme_Builder_Woocommerce_Product_Variation_Placeholder` (which is now
* called at `get_available_variations()` method and similar to
* `WC_Product_Variation`'s method with no check). It has all variation-required
* methods and properties which makes it more reliable when WC add-ons are used
* @since 4.0.1
*
* @return array
*/
public function get_available_variations( $return = 'array' ) {
return array(
$this->get_available_variation(),
);
}
/**
* Display Divi's placeholder image in WC image in TB
*
* @since 4.0.10
*
* @param string not used but need to be declared to prevent incompatible declaration error
* @param array not used but need to be declared to prevent incompatible declaration error
* @param bool not used but need to be declared to prevent incompatible declaration error
*
* @return string
*/
public function get_image( $size = 'woocommerce_thumbnail', $attr = array(), $placeholder = true ) {
return et_builder_wc_placeholder_img();
}
/**
* Set product upsells id for TB's woocommerceComponent. This can't be called during class
* initialization and need to be called BEFORE `woocommerce_product_class` filter callback
* to avoid infinite loop
*
* @since 4.0.10
*
* @param array $args
*/
public static function set_tb_upsells_ids( $args = array() ) {
$defaults = array(
'limit' => 4,
);
$args = wp_parse_args( $args, $defaults );
// Get recent products for upsells product; Any product will do since its purpose is
// for visual preview only
$recent_products_query = new WC_Product_Query( $args );
$recent_product_ids = array();
foreach ( $recent_products_query->get_products() as $recent_product ) {
$recent_product_ids[] = $recent_product->get_id();
}
// Set up upsells id product
self::$tb_upsells_id = $recent_product_ids;
}
/**
* Get upsells id
*
* @since 4.0.10
*
* @param string not used but need to be declared to prevent incompatible declaration error
*
* @return array
*/
public function get_upsell_ids( $context = 'view' ) {
// Bypass database value retrieval and simply pulled cached value from property
return is_array( self::$tb_upsells_id ) ? self::$tb_upsells_id : array();
}
/**
* Get attributes
*
* @since 4.0.10
*
* @param string not used but need to be declared to prevent incompatible declaration error
*
* @return array
*/
public function get_attributes( $context = 'view' ) {
if ( ! is_null( self::$tb_attributes ) ) {
return self::$tb_attributes;
}
// Initialize color attribute
$colors = new WC_Product_Attribute();
$colors->set_id( 1 );
$colors->set_name( 'color' );
$colors->set_options( array( 'Black', 'White', 'Gray' ) );
$colors->set_position( 1 );
$colors->set_visible( 1 );
$colors->set_variation( 1 );
// Initialize size attribute
$sizes = new WC_Product_Attribute();
$sizes->set_id( 2 );
$sizes->set_name( 'size' );
$sizes->set_options( array( 'S', 'M', 'L', 'XL' ) );
$sizes->set_position( 1 );
$sizes->set_visible( 1 );
$sizes->set_variation( 1 );
self::$tb_attributes = array(
'pa_color' => $colors,
'pa_size' => $sizes,
);
return self::$tb_attributes;
}
/**
* Get variation price
*
* @since 4.0.10
*
* @param bool not used but need to be declared to prevent incompatible declaration error
*
* @return array
*/
public function get_variation_prices( $for_display = false ) {
return array(
'price' => array( $this->data['price'] ),
'regular_price' => array( $this->data['regular_price'] ),
'sale_price' => array( $this->data['sale_price'] ),
);
}
}
/**
* Render default product variable add to cart UI for tb-placeholder product type
*
* @since 4.0.10
*/
add_action( 'woocommerce_tb-placeholder_add_to_cart', 'woocommerce_variable_add_to_cart', 30 );

View File

@@ -0,0 +1,53 @@
<?php
/**
* Register data store for ET_Theme_Builder_Woocommerce_Product_Variable_Placeholder_Data_Store_CPT
* which aims to bypass database value retrieval and simply returns default value as placeholder
*
* @since 4.0.10
*/
class ET_Theme_Builder_Woocommerce_Product_Variable_Placeholder_Data_Store_CPT extends WC_Product_Variable_Data_Store_CPT implements WC_Object_Data_Store_Interface, WC_Product_Variable_Data_Store_Interface {
/**
* Basically the original read() method with one exception: retruns default value (which is
* placeholder value) and remove all database value retrieval mechanism so any add-ons
* on TB refers to TB placeholder product data
*
* @since 4.0.10
*
* @param WC_Product $product Product object.
*/
public function read( &$product ) {
$product->set_defaults();
}
/**
* Register product type data store
*
* @since 4.0.10
* @since 4.3.3 register the store for tb-placeholder-variation product
*
* @param array $stores
*
* @return array
*/
public static function register_store( $stores ) {
$stores['product-tb-placeholder'] = 'ET_Theme_Builder_Woocommerce_Product_Variable_Placeholder_Data_Store_CPT';
// Placeholder variation store requirement is identical to placeholder variable, which is
// loading defaults as value and skipping database value retrieval; thus best keep thing
// simple and reuse it for tb-placeholder-variation
$stores['product-tb-placeholder-variation'] = 'ET_Theme_Builder_Woocommerce_Product_Variable_Placeholder_Data_Store_CPT';
return $stores;
}
}
/**
* Register product tb-placeholder's store
*
* @since 4.0.10
*/
add_filter(
'woocommerce_data_stores',
array( 'ET_Theme_Builder_Woocommerce_Product_Variable_Placeholder_Data_Store_CPT', 'register_store' )
);

View File

@@ -0,0 +1,21 @@
<?php
/**
* Class ET_Theme_Builder_Woocommerce_Product_Variation_Placeholder
*
* Display variation (child of variable) placeholder product on Theme Builder. This needs to be
* explicitly defined in case WC add-ons relies on any of variation's method.
*/
class ET_Theme_Builder_Woocommerce_Product_Variation_Placeholder extends WC_Product_Variation {
/**
* Get internal type.
* Define custom internal type so custom data store can be used to bypass database value retrieval
*
* @since 4.3.3
*
* @return string
*/
public function get_type() {
return 'tb-placeholder-variation';
}
}

View File

@@ -0,0 +1,265 @@
<?php
/**
* Load portability used in the Theme Builder admin page.
*
* @since 4.0
*
* @return void
*/
function et_theme_builder_load_portability() {
if ( ! et_pb_is_allowed( 'theme_builder' ) ) {
return;
}
et_core_load_component( 'portability' );
et_core_portability_register(
'et_theme_builder',
array(
'name' => esc_html__( 'Divi Theme Builder', 'et_builder' ),
'type' => 'theme_builder',
'view' => 'et_theme_builder' === et_()->array_get( $_GET, 'page' ), // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- No need to use nonce.
)
);
}
add_action( 'admin_init', 'et_theme_builder_load_portability' );
/**
* Register the Theme Builder admin page.
*
* @since 4.0
*
* @param string $parent
*
* @return void
*/
function et_theme_builder_add_admin_page( $parent ) {
if ( ! et_pb_is_allowed( 'theme_builder' ) ) {
return;
}
// We register the page with the 'edit_others_posts' capability since it's the lowest
// requirement to use VB and we already checked for the theme_builder ET cap.
add_submenu_page(
$parent,
esc_html__( 'Theme Builder', 'et_builder' ),
esc_html__( 'Theme Builder', 'et_builder' ),
'edit_others_posts',
'et_theme_builder',
'et_theme_builder_admin_page'
);
}
/**
* Enqueue Theme Builder assets.
*
* @since 4.0
*
* @return void
*/
function et_theme_builder_enqueue_scripts() {
if ( ! et_builder_is_tb_admin_screen() ) {
return;
}
$role_capabilities = et_pb_get_role_settings();
$user_role = et_pb_get_current_user_role();
et_builder_enqueue_open_sans();
et_fb_enqueue_bundle( 'et-theme-builder', 'theme-builder.css', array( 'et-core-admin' ) );
et_builder_enqueue_assets_head();
et_builder_enqueue_assets_main();
global $wp_version;
$ver = ET_BUILDER_VERSION;
$root = ET_BUILDER_URI;
if ( version_compare( substr( $wp_version, 0, 3 ), '4.5', '<' ) ) {
$dep = array( 'jquery-ui-compat' );
wp_register_script( 'jquery-ui-compat', "{$root}/scripts/ext/jquery-ui-1.10.4.custom.min.js", array( 'jquery' ), $ver, true );
} else {
$dep = array( 'jquery-ui-datepicker' );
}
wp_register_script( 'jquery-ui-datepicker-addon', "{$root}/scripts/ext/jquery-ui-timepicker-addon.js", $dep, $ver, true );
wp_register_script( 'react-tiny-mce', "{$root}/frontend-builder/assets/vendors/tinymce.min.js" );
$asset_ver = ET_BUILDER_VERSION;
$frame_helpers_id = 'et-frame-helpers';
$frame_helpers_path = ET_BUILDER_DIR . '/frontend-builder/build/frame-helpers.js';
$frame_helpers_url = ET_BUILDER_URI . '/frontend-builder/build/frame-helpers.js';
if ( ! file_exists( $frame_helpers_path ) ) {
// Load "hot" from webpack-dev-server.
$site_url = wp_parse_url( get_site_url() );
$frame_helpers_url = "{$site_url['scheme']}://{$site_url['host']}:31495/frame-helpers.js";
}
wp_register_script( $frame_helpers_id, $frame_helpers_url, array(), $asset_ver );
$asset_id = 'et-theme-builder';
$asset_path = ET_BUILDER_DIR . '/frontend-builder/build/theme-builder.js';
$asset_uri = ET_BUILDER_URI . '/frontend-builder/build/theme-builder.js';
$dependencies = array(
'jquery',
'jquery-ui-sortable',
'jquery-ui-datepicker-addon',
'react',
'react-dom',
'react-tiny-mce',
'et-core-admin',
'wp-hooks',
'et-frame-helpers',
);
if ( ! wp_script_is( 'wp-hooks', 'registered' ) ) {
// Use bundled wp-hooks script when WP < 5.0
wp_enqueue_script( 'wp-hooks', ET_BUILDER_URI . '/frontend-builder/assets/backports/hooks.js', array(), $asset_ver, false );
}
et_fb_enqueue_react();
if ( ! file_exists( $asset_path ) ) {
// Load "hot" from webpack-dev-server.
$site_url = wp_parse_url( get_site_url() );
$asset_uri = "{$site_url['scheme']}://{$site_url['host']}:31495/theme-builder.js";
}
wp_enqueue_script( $asset_id, $asset_uri, $dependencies, $asset_ver, true );
// Strip 'validate' key from settings as it is used server-side only.
$default_settings = et_theme_builder_get_template_settings_options();
foreach ( $default_settings as $group_key => $group ) {
foreach ( $group['settings'] as $setting_key => $setting ) {
unset( $default_settings[ $group_key ]['settings'][ $setting_key ]['validate'] );
}
}
// Library item editor.
$theme_builder_id = 0;
$library_item_title = '';
$is_item_editor = et_theme_builder_library_is_item_editor();
if ( $is_item_editor ) {
$item_id = et_theme_builder_get_item_id();
$item_editor = ET_Theme_Builder_Local_Library_Item_Editor::instance( $item_id );
if ( null !== $item_editor->item->item_post ) {
$theme_builder_id = $item_editor->get_interim_theme_builder_id();
$library_item_title = $item_editor->get_library_item_editor_item_title();
}
}
$preloaded_settings = et_theme_builder_get_template_settings_options_for_preloading( $theme_builder_id );
foreach ( $preloaded_settings as $setting_key => $setting ) {
unset( $preloaded_settings[ $setting_key ]['validate'] );
}
$preferences = et_fb_app_preferences();
$animation = et_()->array_get( $preferences, 'builder_animation.value', 'true' );
$animation = true === $animation || 'true' === $animation;
$i18n = require ET_BUILDER_DIR . 'frontend-builder/i18n.php';
wp_localize_script(
'et-theme-builder',
'et_theme_builder_bundle',
array(
'config' => array(
'distPath' => ET_BUILDER_URI . '/frontend-builder/build/',
'api' => admin_url( 'admin-ajax.php' ),
'apiErrors' => ET_Theme_Builder_Api_Errors::getMap(),
'themeBuilderURL' => admin_url( 'admin.php?page=et_theme_builder' ),
'diviLibraryCustomTabs' => apply_filters( 'et_builder_library_modal_custom_tabs', array(), 'theme-builder' ),
// phpcs:disable WordPress.Arrays.MultipleStatementAlignment -- It fails to correctly identify the required spaces before double arrow in the nonces list`.
'nonces' => array(
'et_builder_library_get_layouts_data' => wp_create_nonce( 'et_builder_library_get_layouts_data' ),
'et_theme_builder_library_get_items_data' => wp_create_nonce( 'et_theme_builder_library_get_items_data' ),
'et_theme_builder_library_update_terms' => wp_create_nonce( 'et_theme_builder_library_update_terms' ),
'et_theme_builder_library_get_item' => wp_create_nonce( 'et_theme_builder_library_get_item' ),
'et_theme_builder_api_duplicate_layout' => wp_create_nonce( 'et_theme_builder_api_duplicate_layout' ),
'et_theme_builder_api_create_layout' => wp_create_nonce( 'et_theme_builder_api_create_layout' ),
'et_theme_builder_api_get_layout_url' => wp_create_nonce( 'et_theme_builder_api_get_layout_url' ),
'et_theme_builder_api_save' => wp_create_nonce( 'et_theme_builder_api_save' ),
'et_theme_builder_api_drop_autosave' => wp_create_nonce( 'et_theme_builder_api_drop_autosave' ),
'et_theme_builder_api_get_template_settings' => wp_create_nonce( 'et_theme_builder_api_get_template_settings' ),
'et_theme_builder_api_reset' => wp_create_nonce( 'et_theme_builder_api_reset' ),
'et_theme_builder_api_export_theme_builder' => wp_create_nonce( 'et_theme_builder_api_export_theme_builder' ),
'et_theme_builder_api_import_theme_builder' => wp_create_nonce( 'et_theme_builder_api_import_theme_builder' ),
'et_builder_library_update_account' => wp_create_nonce( 'et_builder_library_update_account' ),
'et_theme_builder_library_update_item' => wp_create_nonce( 'et_theme_builder_library_update_item' ),
'et_theme_builder_library_save_temp_layout' => wp_create_nonce( 'et_theme_builder_library_save_temp_layout' ),
'et_theme_builder_library_remove_temp_layout' => wp_create_nonce( 'et_theme_builder_library_remove_temp_layout' ),
'et_theme_builder_library_toggle_cloud_status' => wp_create_nonce( 'et_theme_builder_library_toggle_cloud_status' ),
'et_theme_builder_api_save_template_to_library' => wp_create_nonce( 'et_theme_builder_api_save_template_to_library' ),
'et_theme_builder_api_save_preset_to_library' => wp_create_nonce( 'et_theme_builder_api_save_preset_to_library' ),
'et_theme_builder_api_get_terms' => wp_create_nonce( 'et_theme_builder_api_get_terms' ),
'et_theme_builder_api_use_library_item' => wp_create_nonce( 'et_theme_builder_api_use_library_item' ),
'et_theme_builder_trash_theme_builder' => wp_create_nonce( 'et_theme_builder_trash_theme_builder' ),
'et_theme_builder_library_item_edit' => wp_create_nonce( 'et_theme_builder_library_item_edit' ),
'et_theme_builder_api_get_library_item' => wp_create_nonce( 'et_theme_builder_api_get_library_item' ),
'et_pb_preview_nonce' => wp_create_nonce( 'et_pb_preview_nonce' ),
'et_theme_builder_library_get_set_items' => wp_create_nonce( 'et_theme_builder_library_get_set_items' ),
'et_theme_builder_get_preset_default_template_id' => wp_create_nonce( 'et_theme_builder_get_preset_default_template_id' ),
'saveDomainToken' => wp_create_nonce( 'et_builder_ajax_save_domain_token' ),
'et_theme_builder_library_clear_temp_data' => wp_create_nonce( 'et_theme_builder_library_clear_temp_data' ),
'et_theme_builder_library_get_cloud_token' => wp_create_nonce( 'et_theme_builder_library_get_cloud_token' ),
),
// phpcs:enable
'site_url' => get_site_url(),
'rtl' => is_rtl(),
'animation' => $animation,
'templateSettings' => array(
'default' => $default_settings,
'preloaded' => $preloaded_settings,
),
'etAccount' => et_core_get_et_account(),
'capabilities' => isset( $role_capabilities[ $user_role ] ) ? $role_capabilities[ $user_role ] : array(),
'templates' => array(
'hasDraft' => ! $theme_builder_id && 0 !== et_theme_builder_get_theme_builder_post_id( false, false ),
'live' => et_theme_builder_get_theme_builder_templates( true, $theme_builder_id ),
'draft' => et_theme_builder_get_theme_builder_templates( false, $theme_builder_id ),
),
'localLibrary' => array(
'templateCategories' => et_theme_builder_get_terms( 'layout_category' ),
'templateTags' => et_theme_builder_get_terms( 'layout_tag' ),
'themeBuilderId' => $theme_builder_id,
'libraryItemTitle' => ! empty( $library_item_title ) ? $library_item_title : '',
),
'site_domain' => isset( $home_url['host'] ) ? untrailingslashit( $home_url['host'] ) : '/',
'domainToken' => get_option( 'et_server_domain_token', '' ),
'verticalMenu' => array(
'showTooltip' => false,
),
),
'i18n' => array(
'generic' => $i18n['generic'],
'portability' => $i18n['portability'],
'library' => $i18n['library'],
'themeBuilder' => $i18n['themeBuilder'],
),
)
);
// Load Library and Cloud.
et_builder_load_library();
ET_Cloud_App::load_js();
if ( et_pb_is_allowed( 'divi_ai' ) ) {
ET_AI_App::load_js();
}
}
add_action( 'admin_enqueue_scripts', 'et_theme_builder_enqueue_scripts' );
/**
* Render the Theme Builder admin page.
*
* @since 4.0
*
* @return void
*/
function et_theme_builder_admin_page() {
echo '<div id="et-theme-builder"></div>';
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,26 @@
<?php
/**
* Theme Builder Library constants.
*
* @package Builder
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
if ( ! defined( 'ET_THEME_BUILDER_ITEM_SET' ) ) {
define( 'ET_THEME_BUILDER_ITEM_SET', 'set' );
}
if ( ! defined( 'ET_THEME_BUILDER_ITEM_TEMPLATE' ) ) {
define( 'ET_THEME_BUILDER_ITEM_TEMPLATE', 'template' );
}
if ( ! defined( 'ET_THEME_BUILDER_EDITOR_STANDARD' ) ) {
define( 'ET_THEME_BUILDER_EDITOR_STANDARD', 'standard' );
}
if ( ! defined( 'ET_THEME_BUILDER_TAXONOMY_TYPE' ) ) {
define( 'ET_THEME_BUILDER_TAXONOMY_TYPE', 'et_tb_item_type' );
}

View File

@@ -0,0 +1,216 @@
<?php
/**
* Resolve placeholder content for built-in dynamic content fields for Theme Builder layouts.
*
* @since 4.0
*
* @param string $content Content.
* @param string $name Name.
* @param array $settings Settings.
* @param integer $post_id Post ID.
* @param string $context Context.
* @param array $overrides Overrides.
*
* @return string
*/
function et_theme_builder_filter_resolve_default_dynamic_content( $content, $name, $settings, $post_id, $context, $overrides ) {
$post_type = get_post_type( $post_id );
if ( ! et_theme_builder_is_layout_post_type( $post_type ) && ! is_et_theme_builder_template_preview() ) {
return $content;
}
$placeholders = array(
'post_title' => __( 'Your Dynamic Post Title Will Display Here', 'et_builder' ),
'post_excerpt' => __( 'Your dynamic post excerpt will display here. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus auctor urna eleifend diam eleifend sollicitudin a fringilla turpis. Curabitur lectus enim.', 'et_builder' ),
'post_date' => time(),
'post_comment_count' => 12,
'post_categories' => array(
__( 'Category 1', 'et_builder' ),
__( 'Category 2', 'et_builder' ),
__( 'Category 3', 'et_builder' ),
),
'post_tags' => array(
__( 'Tag 1', 'et_builder' ),
__( 'Tag 2', 'et_builder' ),
__( 'Tag 3', 'et_builder' ),
),
'post_author' => array(
'display_name' => __( 'John Doe', 'et_builder' ),
'first_last_name' => __( 'John Doe', 'et_builder' ),
'last_first_name' => __( 'Doe, John', 'et_builder' ),
'first_name' => __( 'John', 'et_builder' ),
'last_name' => __( 'Doe', 'et_builder' ),
'nickname' => __( 'John', 'et_builder' ),
'username' => __( 'johndoe', 'et_builder' ),
),
'post_author_bio' => __( 'Your dynamic author bio will display here. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus auctor urna eleifend diam eleifend sollicitudin a fringilla turpis. Curabitur lectus enim.', 'et_builder' ),
'post_featured_image' => ET_BUILDER_PLACEHOLDER_LANDSCAPE_IMAGE_DATA,
'term_description' => __( 'Your dynamic category description will display here. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus auctor urna eleifend diam eleifend sollicitudin a fringilla turpis. Curabitur lectus enim.', 'et_builder' ),
'site_logo' => 'https://www.elegantthemes.com/img/divi.png',
);
$_ = et_();
$def = 'et_builder_get_dynamic_attribute_field_default';
$wrapped = false;
switch ( $name ) {
case 'post_title':
$content = esc_html( $placeholders[ $name ] );
break;
case 'post_excerpt':
$words = (int) $_->array_get( $settings, 'words', $def( $post_id, $name, 'words' ) );
$read_more = $_->array_get( $settings, 'read_more_label', $def( $post_id, $name, 'read_more_label' ) );
$content = esc_html( $placeholders[ $name ] );
if ( $words > 0 ) {
$content = wp_trim_words( $content, $words );
}
if ( ! empty( $read_more ) ) {
$content .= sprintf(
' <a href="%1$s">%2$s</a>',
'#',
esc_html( $read_more )
);
}
break;
case 'post_date':
$format = $_->array_get( $settings, 'date_format', $def( $post_id, $name, 'date_format' ) );
$custom_format = $_->array_get( $settings, 'custom_date_format', $def( $post_id, $name, 'custom_date_format' ) );
if ( 'default' === $format ) {
$format = strval( get_option( 'date_format' ) );
}
if ( 'custom' === $format ) {
$format = $custom_format;
}
$content = esc_html( date( $format, $placeholders[ $name ] ) );
break;
case 'post_comment_count':
$link = $_->array_get( $settings, 'link_to_comments_page', $def( $post_id, $name, 'link_to_comments_page' ) );
$link = 'on' === $link;
$content = esc_html( $placeholders[ $name ] );
if ( $link ) {
$content = sprintf(
'<a href="%1$s">%2$s</a>',
'#',
et_core_esc_previously( et_builder_wrap_dynamic_content( $post_id, $name, $content, $settings ) )
);
$wrapped = true;
}
break;
case 'post_categories': // Intentional fallthrough.
case 'post_tags':
$link = $_->array_get( $settings, 'link_to_term_page', $def( $post_id, $name, 'link_to_category_page' ) );
$link = 'on' === $link;
$url = '#';
$separator = $_->array_get( $settings, 'separator', $def( $post_id, $name, 'separator' ) );
$separator = ! empty( $separator ) ? $separator : $def( $post_id, $name, 'separator' );
$content = $placeholders[ $name ];
foreach ( $content as $index => $item ) {
$content[ $index ] = esc_html( $item );
if ( $link ) {
$content[ $index ] = sprintf(
'<a href="%1$s" target="%2$s">%3$s</a>',
esc_url( $url ),
esc_attr( '_blank' ),
et_core_esc_previously( $content[ $index ] )
);
}
}
$content = implode( esc_html( $separator ), $content );
break;
case 'post_link':
$text = $_->array_get( $settings, 'text', $def( $post_id, $name, 'text' ) );
$custom_text = $_->array_get( $settings, 'custom_text', $def( $post_id, $name, 'custom_text' ) );
$label = 'custom' === $text ? $custom_text : $placeholders['post_title'];
$content = sprintf(
'<a href="%1$s">%2$s</a>',
'#',
esc_html( $label )
);
break;
case 'post_author':
$name_format = $_->array_get( $settings, 'name_format', $def( $post_id, $name, 'name_format' ) );
$link = $_->array_get( $settings, 'link', $def( $post_id, $name, 'link' ) );
$link = 'on' === $link;
$label = isset( $placeholders[ $name ][ $name_format ] ) ? $placeholders[ $name ][ $name_format ] : '';
$url = '#';
$content = esc_html( $label );
if ( $link && ! empty( $url ) ) {
$content = sprintf(
'<a href="%1$s" target="%2$s">%3$s</a>',
esc_url( $url ),
esc_attr( '_blank' ),
et_core_esc_previously( $content )
);
}
break;
case 'post_author_bio':
$content = esc_html( $placeholders[ $name ] );
break;
case 'term_description':
$content = esc_html( $placeholders[ $name ] );
break;
case 'post_link_url':
$content = '#';
break;
case 'post_author_url':
$content = '#';
break;
case 'post_featured_image':
$content = et_core_intentionally_unescaped( $placeholders[ $name ], 'fixed_string' );
break;
case 'site_logo':
if ( empty( $content ) ) {
$content = esc_url( $placeholders[ $name ] );
} else {
$wrapped = true;
}
break;
default:
// Avoid unhandled cases being wrapped twice by the default resolve and this one.
$wrapped = true;
break;
}
if ( $_->starts_with( $name, 'custom_meta_' ) ) {
$meta_key = substr( $name, strlen( 'custom_meta_' ) );
$meta_value = get_post_meta( $post_id, $meta_key, true );
if ( empty( $meta_value ) ) {
$content = et_builder_get_dynamic_content_custom_field_label( $meta_key );
} else {
$wrapped = true;
}
}
if ( ! $wrapped ) {
$content = et_builder_wrap_dynamic_content( $post_id, $name, $content, $settings );
$wrapped = true;
}
return $content;
}
add_filter( 'et_builder_resolve_dynamic_content', 'et_theme_builder_filter_resolve_default_dynamic_content', 11, 6 );

View File

@@ -0,0 +1,15 @@
<?php
$layouts = et_theme_builder_get_template_layouts();
?>
<?php get_header(); ?>
<?php
et_theme_builder_frontend_render_body(
$layouts[ ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE ]['id'],
$layouts[ ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE ]['enabled'],
$layouts[ ET_THEME_BUILDER_TEMPLATE_POST_TYPE ]
);
?>
<?php
get_footer();

View File

@@ -0,0 +1,25 @@
<?php
$layouts = et_theme_builder_get_template_layouts();
?>
<?php
et_theme_builder_frontend_render_footer(
$layouts[ ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE ]['id'],
$layouts[ ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE ]['enabled'],
$layouts[ ET_THEME_BUILDER_TEMPLATE_POST_TYPE ]
);
?>
<?php if ( et_core_is_fb_enabled() && et_theme_builder_is_layout_post_type( get_post_type() ) ) : ?>
<?php // Hide the footer when we are editing a TB layout. ?>
<div class="et-tb-fb-footer" style="display: none;">
<?php wp_footer(); ?>
</div>
<?php else : ?>
<?php wp_footer(); ?>
<?php endif; ?>
<?php if ( ! et_is_builder_plugin_active() && 'on' === et_get_option( 'divi_back_to_top', 'false' ) ) : ?>
<span class="et_pb_scroll_top et-pb-icon"></span>
<?php endif; ?>
</body>
</html>

View File

@@ -0,0 +1,23 @@
<?php
$layouts = et_theme_builder_get_template_layouts();
?>
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
<?php echo $tb_theme_head; ?>
<?php do_action( 'et_theme_builder_template_head' ); ?>
<?php wp_head(); ?>
</head>
<body <?php body_class(); ?>>
<?php
wp_body_open();
et_theme_builder_frontend_render_header(
$layouts[ ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE ]['id'],
$layouts[ ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE ]['enabled'],
$layouts[ ET_THEME_BUILDER_TEMPLATE_POST_TYPE ]
);
?>

View File

@@ -0,0 +1,671 @@
<?php
/**
* Remove the admin bar from the VB when used from the Theme Builder.
*
* @since 4.0
*
* @return void
*/
function et_theme_builder_frontend_disable_admin_bar() {
if ( et_builder_tb_enabled() ) {
add_filter( 'show_admin_bar', '__return_false' );
}
}
add_filter( 'wp', 'et_theme_builder_frontend_disable_admin_bar' );
/**
* Add body classes depending on which areas are overridden by TB.
*
* @since 4.0
*
* @param array $classes
*
* @return string[]
*/
function et_theme_builder_frontend_add_body_classes( $classes ) {
if ( et_builder_bfb_enabled() ) {
// Do not add any classes in BFB.
return $classes;
}
$layouts = et_theme_builder_get_template_layouts();
if ( ! empty( $layouts ) ) {
$classes[] = 'et-tb-has-template';
if ( $layouts[ ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE ]['override'] ) {
$classes[] = 'et-tb-has-header';
if ( ! $layouts[ ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE ]['enabled'] ) {
$classes[] = 'et-tb-header-disabled';
}
}
if ( $layouts[ ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE ]['override'] ) {
$classes[] = 'et-tb-has-body';
if ( ! $layouts[ ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE ]['enabled'] ) {
$classes[] = 'et-tb-body-disabled';
}
}
if ( $layouts[ ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE ]['override'] ) {
$classes[] = 'et-tb-has-footer';
if ( ! $layouts[ ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE ]['enabled'] ) {
$classes[] = 'et-tb-footer-disabled';
}
}
}
return $classes;
}
add_filter( 'body_class', 'et_theme_builder_frontend_add_body_classes', 9 );
/**
* Conditionally override the template being loaded by WordPress based on what the user
* has created in their Theme Builder.
* The header and footer are always dealt with as a pair - if the header is replaced the footer is replaced a well.
*
* @since 4.0
*
* @param string $template
*
* @return string
*/
function et_theme_builder_frontend_override_template( $template ) {
$layouts = et_theme_builder_get_template_layouts();
$page_template = locate_template( 'page.php' );
$override_header = et_theme_builder_overrides_layout( ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE );
$override_body = et_theme_builder_overrides_layout( ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE );
$override_footer = et_theme_builder_overrides_layout( ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE );
$is_visual_builder = isset( $_GET['et_fb'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Value is not used
$is_theme_builder = et_builder_tb_enabled();
if ( ( $override_header || $override_body || $override_footer ) && et_core_is_fb_enabled() ) {
// When cached assets/definitions do not exist, a VB/BFB page will generate them inline.
// This would normally happen later but not in the case of TB because, due to early
// `the_content()` calls, `maybe_rebuild_option_template()` would be invoked before
// saving definitions/assets resulting in them including resolved option templates instead
// of placeholders.
$post_type = get_post_type();
et_fb_get_dynamic_asset( 'helpers', $post_type );
et_fb_get_dynamic_asset( 'definitions', $post_type );
}
if ( $override_header || $override_footer ) {
// wp-version >= 5.2
remove_action( 'wp_body_open', 'wp_admin_bar_render', 0 );
add_action( 'get_header', 'et_theme_builder_frontend_override_header' );
add_action( 'get_footer', 'et_theme_builder_frontend_override_footer' );
}
et_theme_builder_frontend_enqueue_styles( $layouts );
// For other themes than Divi, use 'frontend-body-template.php'.
if ( $override_body && ! function_exists( 'et_divi_fonts_url' ) ) {
return ET_THEME_BUILDER_DIR . 'frontend-body-template.php';
}
if ( $override_body && ( $is_theme_builder || ! $is_visual_builder || ! $layouts['et_template'] || ! et_pb_is_allowed( 'theme_builder' ) ) ) {
return ET_THEME_BUILDER_DIR . 'frontend-body-template.php';
}
if ( $override_body && ! is_home() ) {
return $page_template;
}
return $template;
}
// Priority of 98 so it can be overridden by BFB.
add_filter( 'template_include', 'et_theme_builder_frontend_override_template', 98 );
/**
* Enqueue any necessary TB layout styles.
*
* @since 4.0
*
* @param array $layouts
*
* @return void
*/
function et_theme_builder_frontend_enqueue_styles( $layouts ) {
if ( empty( $layouts ) ) {
return;
}
if ( ! is_singular() && ! et_core_is_fb_enabled() ) {
// Create styles managers so they can enqueue styles early enough.
// What styles are created and how they are enqueued:
// - In FE, singular post view:
// -> TB + Post Styles are combined into et-*-tb-{HEADER_ID}-tb-{BODY_ID}-tb-{FOOTER_ID}-{POST_ID}-*.css
//
// - In FE, non-singular post view:
// -> TB styles are separate with the usual filename: et-*-{LAYOUT_ID}-*.css
//
// - In FE, singular post view in VB so post-specific DC works:
// -> TB styles are separate with the current post ID prepended: et-*-tb-for-{POST_ID}-{LAYOUT_ID}-*.css.
if ( $layouts[ ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE ]['override'] ) {
ET_Builder_Element::setup_advanced_styles_manager( $layouts[ ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE ]['id'] );
}
if ( $layouts[ ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE ]['override'] ) {
ET_Builder_Element::setup_advanced_styles_manager( $layouts[ ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE ]['id'] );
}
if ( $layouts[ ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE ]['override'] ) {
ET_Builder_Element::setup_advanced_styles_manager( $layouts[ ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE ]['id'] );
}
}
}
/**
* Render a custom partial overriding the original one.
*
* @since 4.0
*
* @param string $partial
* @param string $action
* @param string $name
*
* @return void
*/
function et_theme_builder_frontend_override_partial( $partial, $name, $action = '' ) {
global $wp_filter;
$tb_theme_head = '';
/**
* Slightly adjusted version of WordPress core code in order to mimic behavior.
*
* @link https://core.trac.wordpress.org/browser/tags/5.0.3/src/wp-includes/general-template.php#L33
*/
$templates = array();
$name = (string) $name;
if ( '' !== $name ) {
$templates[] = "{$partial}-{$name}.php";
}
$templates[] = "{$partial}.php";
// Buffer and discard the original partial forcing a require_once so it doesn't load again later.
$buffered = ob_start();
if ( $buffered ) {
$actions = array();
if ( ! empty( $action ) ) {
// Skip any partial-specific actions so they don't run twice.
$actions = et_()->array_get( $wp_filter, $action, array() );
unset( $wp_filter[ $action ] );
}
locate_template( $templates, true, true );
$html = ob_get_clean();
if ( 'wp_head' === $action ) {
$tb_theme_head = et_theme_builder_extract_head( $html );
}
if ( ! empty( $action ) ) {
// Restore skipped actions.
$wp_filter[ $action ] = $actions;
}
}
require_once ET_THEME_BUILDER_DIR . "frontend-{$partial}-template.php";
}
/**
* Extract <head> tag contents.
*
* @since 4.0.8
*
* @param string $html
*
* @return string
*/
function et_theme_builder_extract_head( $html ) {
// We could use DOMDocument here to guarantee proper parsing but we need
// the most performant solution since we cannot reliably cache the result.
$head = array();
preg_match( '/^[\s\S]*?<head[\s\S]*?>([\s\S]*?)<\/head>[\s\S]*$/i', $html, $head );
return ! empty( $head[1] ) ? trim( $head[1] ) : '';
}
/**
* Override the default header template.
*
* @since 4.0
*
* @param string $name
*
* @return void
*/
function et_theme_builder_frontend_override_header( $name ) {
et_theme_builder_frontend_override_partial( 'header', $name, 'wp_head' );
}
/**
* Override the default footer template.
*
* @since 4.0
*
* @param string $name
*
* @return void
*/
function et_theme_builder_frontend_override_footer( $name ) {
et_theme_builder_frontend_override_partial( 'footer', $name, 'wp_footer' );
}
/**
* Filter builder content wrapping as Theme Builder Layouts are wrapped collectively instead of individually.
*
* @since 4.0
*
* @param bool $wrap
*
* @return bool
*/
function et_theme_builder_frontend_filter_add_outer_content_wrap( $wrap ) {
$override_header = et_theme_builder_overrides_layout( ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE );
$override_footer = et_theme_builder_overrides_layout( ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE );
// Theme Builder layouts must not be individually wrapped as they are wrapped
// collectively, with the exception of the BFB or body layout.
if ( ( $override_header || $override_footer ) && ! et_builder_bfb_enabled() ) {
$wrap = false;
}
return $wrap;
}
add_filter( 'et_builder_add_outer_content_wrap', 'et_theme_builder_frontend_filter_add_outer_content_wrap' );
/**
* Render a template builder layout.
*
* Wrapper cases:
* 1. Header/Footer are replaced.
* => Common is open and closed. Header/Footer do not get opened/closed because
* Common is opened before them.
*
* 2. Body is replaced.
* => Common is NOT opened/closed. Body is open/closed.
*
* 3. Header/Body/Footer are replaced.
* => Common is open and closed. Header/Body/Footer do not get opened/closed because
* Common is opened before them.
*
* @since 4.0
*
* @param string $layout_type Layout Type.
* @param integer $layout_id Layout ID.
*
* @return void
*/
function et_theme_builder_frontend_render_layout( $layout_type, $layout_id ) {
if ( $layout_id <= 0 ) {
return;
}
$layout = get_post( $layout_id );
if ( null === $layout || $layout->post_type !== $layout_type ) {
return;
}
et_theme_builder_frontend_render_common_wrappers( $layout_type, true );
/**
* Fires after Theme Builder layout opening wrappers have been output but before any
* other processing has been done (e.g. replacing the current post).
*
* @since 4.0.10
*
* @param string $layout_type
* @param integer $layout_id
*/
do_action( 'et_theme_builder_after_layout_opening_wrappers', $layout_type, $layout_id );
ET_Builder_Element::begin_theme_builder_layout( $layout_id );
ET_Post_Stack::replace( $layout );
$is_visual_builder = isset( $_GET['et_fb'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Value is not used
$theme_builder_layouts = array( 'et_header_layout', 'et_footer_layout' );
// Do not pass header and footer content here if visual builder is loaded,
// they will be loaded inside the builder itself.
if ( et_pb_is_allowed( 'theme_builder' ) && $is_visual_builder && in_array( $layout_type, $theme_builder_layouts, true ) ) {
$post_content = '';
} else {
$post_content = get_the_content();
}
echo et_core_intentionally_unescaped( et_builder_render_layout( $post_content ), 'html' );
// Get dynamic content.
$has_dynamic_content = et_builder_get_dynamic_contents( get_the_content() );
// Handle style output.
if ( is_singular() && ! et_core_is_fb_enabled() ) {
$result = ET_Builder_Element::setup_advanced_styles_manager( ET_Post_Stack::get_main_post_id() );
} elseif ( is_tax() && ! empty( $has_dynamic_content ) ) {
// Set post id to 0 if its a taxonomy page.
// This is because of the dynamic content not working properly,
// With the theme builder cache.
$result = ET_Builder_Element::setup_advanced_styles_manager( 0 );
} else {
$result = ET_Builder_Element::setup_advanced_styles_manager( $layout->ID );
}
$advanced_styles_manager = $result['manager'];
if ( isset( $result['deferred'] ) ) {
$deferred_styles_manager = $result['deferred'];
}
// Pass styles to page resource which will handle their output.
/**
* Filters whether Critical CSS feature is enabled or not.
*
* @since 4.10.0
*
* @param bool $enabled Critical CSS enabled value.
*/
$is_critical_enabled = apply_filters( 'et_builder_critical_css_enabled', false );
if ( ET_Builder_Element::$forced_inline_styles || ! $advanced_styles_manager->has_file() || $advanced_styles_manager->forced_inline ) {
$custom = et_pb_get_page_custom_css( $layout->ID );
$critical = $is_critical_enabled ? ET_Builder_Element::get_style( false, $layout->ID, true ) . ET_Builder_Element::get_style( true, $layout->ID, true ) : [];
$styles = ET_Builder_Element::get_style( false, $layout->ID ) . ET_Builder_Element::get_style( true, $layout->ID );
if ( empty( $critical ) ) {
// No critical styles defined, just enqueue everything as usual.
$styles = $custom . $styles;
if ( ! empty( $styles ) ) {
if ( isset( $deferred_styles_manager ) ) {
$deferred_styles_manager->set_data( $styles, 40 );
} else {
$advanced_styles_manager->set_data( $styles, 40 );
}
}
} else {
// Add page css to the critical section.
$critical = $custom . $critical;
$advanced_styles_manager->set_data( $critical, 40 );
if ( ! empty( $styles ) ) {
// Defer everything else.
$deferred_styles_manager->set_data( $styles, 40 );
}
}
}
ET_Post_Stack::restore();
ET_Builder_Element::end_theme_builder_layout();
/**
* Fires before Theme Builder layout closing wrappers have been output and after any
* other processing has been done (e.g. replacing the current post).
*
* @since 4.0.10
*
* @param string $layout_type
* @param integer $layout_id
*/
do_action( 'et_theme_builder_before_layout_closing_wrappers', $layout_type, $layout_id );
et_theme_builder_frontend_render_common_wrappers( $layout_type, false );
}
/**
* Render a header layout.
*
* @since 4.0
*
* @param integer $layout_id The layout id or 0.
* @param boolean $layout_enabled
* @param integer $template_id The template id or 0.
*
* @return void
*/
function et_theme_builder_frontend_render_header( $layout_id, $layout_enabled, $template_id ) {
/**
* Fires before theme builder page wrappers are output.
* Example use case is to add opening wrapping html tags for the entire page.
*
* @since 4.0
*
* @param integer $layout_id The layout id or 0.
* @param bool $layout_enabled
* @param integer $template_id The template id or 0.
*/
do_action( 'et_theme_builder_template_before_page_wrappers', $layout_id, $layout_enabled, $template_id );
et_theme_builder_frontend_render_common_wrappers( 'common', true );
/**
* Fires before theme builder template header is output.
* Example use case is to add opening wrapping html tags for the header and/or the entire page.
*
* @since 4.0
*
* @param integer $layout_id The layout id or 0.
* @param bool $layout_enabled
* @param integer $template_id The template id or 0.
*/
do_action( 'et_theme_builder_template_before_header', $layout_id, $layout_enabled, $template_id );
if ( $layout_enabled ) {
et_theme_builder_frontend_render_layout( ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE, $layout_id );
}
/**
* Fires after theme builder template header is output.
* Example use case is to add closing wrapping html tags for the header.
*
* @since 4.0
*
* @param integer $layout_id The layout id or 0.
* @param bool $layout_enabled
* @param integer $template_id The template id or 0.
*/
do_action( 'et_theme_builder_template_after_header', $layout_id, $layout_enabled, $template_id );
}
/**
* Render a body layout.
*
* @since 4.0
*
* @param integer $layout_id The layout id or 0.
* @param boolean $layout_enabled
* @param integer $template_id The template id or 0.
*
* @return void
*/
function et_theme_builder_frontend_render_body( $layout_id, $layout_enabled, $template_id ) {
/**
* Fires before theme builder template body is output.
* Example use case is to add opening wrapping html tags for the body.
*
* @since 4.0
*
* @param integer $layout_id The layout id or 0.
* @param bool $layout_enabled
* @param integer $template_id The template id or 0.
*
* @return void
*/
do_action( 'et_theme_builder_template_before_body', $layout_id, $layout_enabled, $template_id );
if ( $layout_enabled ) {
et_theme_builder_frontend_render_layout( ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE, $layout_id );
}
/**
* Fires after theme builder template body is output.
* Example use case is to add closing wrapping html tags for the body.
*
* @since 4.0
*
* @param integer $layout_id The layout id or 0.
* @param bool $layout_enabled
* @param integer $template_id The template id or 0.
*
* @return void
*/
do_action( 'et_theme_builder_template_after_body', $layout_id, $layout_enabled, $template_id );
}
/**
* Render a footer layout.
*
* @since 4.0
*
* @param integer $layout_id The layout id or 0.
* @param boolean $layout_enabled
* @param integer $template_id The template id or 0.
*
* @return void
*/
function et_theme_builder_frontend_render_footer( $layout_id, $layout_enabled, $template_id ) {
/**
* Fires before theme builder template footer is output.
* Example use case is to add opening wrapping html tags for the footer.
*
* @since 4.0
*
* @param integer $layout_id The layout id or 0.
* @param bool $layout_enabled
* @param integer $template_id The template id or 0.
*
* @return void
*/
do_action( 'et_theme_builder_template_before_footer', $layout_id, $layout_enabled, $template_id );
if ( $layout_enabled ) {
et_theme_builder_frontend_render_layout( ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE, $layout_id );
}
/**
* Fires after theme builder template footer is output.
* Example use case is to add closing wrapping html tags for the footer and/or the entire page.
*
* @since 4.0
*
* @param integer $layout_id The layout id or 0.
* @param bool $layout_enabled
* @param integer $template_id The template id or 0.
*
* @return void
*/
do_action( 'et_theme_builder_template_after_footer', $layout_id, $layout_enabled, $template_id );
et_theme_builder_frontend_render_common_wrappers( 'common', false );
/**
* Fires after theme builder page wrappers are output.
* Example use case is to add closing wrapping html tags for the entire page.
*
* @since 4.0
*
* @param integer $layout_id The layout id or 0.
* @param bool $layout_enabled
* @param integer $template_id The template id or 0.
*/
do_action( 'et_theme_builder_template_after_page_wrappers', $layout_id, $layout_enabled, $template_id );
}
/**
* Open or close common builder wrappers (e.g. #et-boc) in order to avoid having triple wrappers - one for every layout.
*
* Useful
*
* @param $area
* @param $open
*/
function et_theme_builder_frontend_render_common_wrappers( $area, $open ) {
static $wrapper = '';
if ( $open ) {
// Open wrappers only if there are no other open wrappers already.
if ( '' === $wrapper ) {
$wrapper = $area;
echo et_builder_get_builder_content_opening_wrapper();
}
return;
}
if ( '' === $wrapper || $area !== $wrapper ) {
// Do not close wrappers if the opener does not match the current area.
return;
}
echo et_builder_get_builder_content_closing_wrapper();
}
/**
* Get the html representing the post content for the current post.
*
* @since 4.0
*
* @return string
*/
function et_theme_builder_frontend_render_post_content() {
static $__prevent_recursion = false;
global $wp_query;
if ( is_et_theme_builder_template_preview() ) {
return et_theme_builder_get_post_content_placeholder();
}
if ( ET_Builder_Element::get_theme_builder_layout_type() !== ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE ) {
// Prevent usage on non-body layouts.
return '';
}
if ( ! is_singular() ) {
// Do not output anything on non-singular pages.
return '';
}
$main_query_post = ET_Post_Stack::get_main_post();
if ( ! $main_query_post ) {
// Bail if there is no current post.
return '';
}
if ( true === $__prevent_recursion ) {
// Failsafe just in case.
return '';
}
$__prevent_recursion = true;
$html = '';
$buffered = ob_start();
if ( $buffered ) {
ET_Post_Stack::replace( $main_query_post );
ET_Builder_Element::begin_theme_builder_layout( get_the_ID() );
do_action_ref_array( 'loop_start', array( &$wp_query ) );
the_content();
do_action_ref_array( 'loop_end', array( &$wp_query ) );
ET_Builder_Element::end_theme_builder_layout();
ET_Post_Stack::restore();
$html = ob_get_clean();
}
$__prevent_recursion = false;
return $html;
}

View File

@@ -0,0 +1,981 @@
<?php
/**
* Save (template and preset) to the local library functionality.
*
* @package Builder.
*/
/**
* Gets the Library Item name.
*
* @param array $preferences Preferences set in the Save Builder Preset/Template modals.
* @param string $item_type Preset / Template item type.
*
* @return string
*/
function et_theme_builder_local_library_get_item_name( $preferences, $item_type ) {
$_ = et_();
$item_name = '';
if ( ! is_array( $preferences ) ) {
$preferences = [];
}
switch ( trim( $item_type ) ) {
case ET_THEME_BUILDER_ITEM_SET:
$item_name = isset( $preferences['set_name'] ) && '' !== $preferences['set_name']
? sanitize_text_field( $preferences['set_name'] )
: esc_html__( 'Divi Theme Builder Set', 'et_builder' );
break;
case ET_THEME_BUILDER_ITEM_TEMPLATE:
$item_name = isset( $preferences['template_name'] ) && '' !== $preferences['template_name']
? sanitize_text_field( $preferences['template_name'] )
: esc_html__( 'Divi Theme Builder Template', 'et_builder' );
break;
}
return $item_name;
}
/**
* Gets the layouts(shortcodes from header/body/footer area) information.
*
* @param array $template Template information from Save Builder Preset/Template modals.
*
* @return array
*/
function et_theme_builder_local_library_get_layouts( $template ) {
$_ = et_();
$header_id = (int) $_->array_get( $template, 'layouts.header.id', 0 );
$body_id = (int) $_->array_get( $template, 'layouts.body.id', 0 );
$footer_id = (int) $_->array_get( $template, 'layouts.footer.id', 0 );
// get_post_field returns empty string on failure.
$header_content = get_post_field( 'post_content', $header_id );
$body_content = get_post_field( 'post_content', $body_id );
$footer_content = get_post_field( 'post_content', $footer_id );
return [
'header' => $header_content,
'body' => $body_content,
'footer' => $footer_content,
];
}
/**
* Save a Theme Builder template to the local library.
*
* @since 4.18.0
* @param array $template Template.
* @param array $preferences Preferences for the save template.
*
* @return (integer|false) Return false on failure.
*/
function et_theme_builder_save_template_to_library( $template, $preferences = array() ) {
if ( ! current_user_can( 'edit_others_posts' ) ) {
wp_die();
}
$_ = et_();
$is_set_template = false;
if ( ! is_array( $preferences ) ) {
$preferences = [];
}
// Preferences.
if ( ! array_key_exists( 'item_type', $preferences ) ) {
$preferences = array_merge( $preferences, [ 'item_type' => ET_THEME_BUILDER_ITEM_TEMPLATE ] );
}
$template_name = sanitize_text_field( et_theme_builder_local_library_get_item_name( $preferences, ET_THEME_BUILDER_ITEM_TEMPLATE ) );
$title = sanitize_text_field( $_->array_get( $template, 'title', '' ) );
$enabled = (bool) $_->array_get( $template, 'enabled', true );
$header_enabled = (bool) $_->array_get( $template, 'layouts.header.enabled', true );
$body_enabled = (bool) $_->array_get( $template, 'layouts.body.enabled', true );
$footer_enabled = (bool) $_->array_get( $template, 'layouts.footer.enabled', true );
$autogenerated_title = (bool) $_->array_get( $template, 'autogenerated_title', true );
$use_on = array_map( 'sanitize_text_field', $_->array_get( $template, 'use_on', array() ) );
$exclude_from = array_map( 'sanitize_text_field', $_->array_get( $template, 'exclude_from', array() ) );
$is_default = (bool) $_->array_get( $template, 'default', false );
$preset_id = intval( $_->array_get( $preferences, 'preset_id', 0 ) );
$item_id = intval( $_->array_get( $template, 'item_id', 0 ) );
$status = sanitize_text_field( $_->array_get( $template, 'status', 'publish' ) );
// Layout's shortcode.
$layout_types = array( 'header', 'body', 'footer' );
$layout_shortcodes = array();
$global_layout_meta = array();
$layout_meta = array();
$update = false; // Editing the saved template?.
if ( $item_id ) {
$item = get_post( $item_id );
$update = $item && ET_TB_ITEM_POST_TYPE === $item->post_type && ( 'publish' === $item->post_status || $_->array_get( $preferences, 'force_update', false ) );
}
foreach ( $layout_types as $layout_type ) {
$layout_id = (int) $_->array_get( $template, "layouts.$layout_type.id", 0 );
$global_layout_id = (int) $_->array_get( $template, "global_layouts.$layout_type.id", 0 );
$is_global_layout = $layout_id && $global_layout_id && $layout_id === $global_layout_id;
if ( $is_default && ET_THEME_BUILDER_ITEM_SET === $_->array_get( $preferences, 'item_type', '' ) ) {
if ( $preset_id && $global_layout_id ) {
update_post_meta( $preset_id, '_et_has_global_layouts', '1' );
}
}
// Mark whether layout is global layout.
$global_layout_meta[ "_et_{$layout_type}_layout_global" ] = $is_global_layout ? '1' : '0';
$layout_meta[ "_et_{$layout_type}_layout_id" ] = $layout_id;
// Do not save the global layout conent if template is not a Default Website Template.
$layout_post = get_post( $layout_id );
if ( $layout_post ) {
$layout_content = $layout_post->post_content;
if ( ! empty( $layout_content ) ) {
$layout_shortcodes[ $layout_type ] = array(
'post_content' => $layout_content,
'post_meta' => et_core_get_post_builder_meta( $layout_id ),
);
}
}
}
$post_content = ! empty( $layout_shortcodes ) ? wp_slash( wp_json_encode( $layout_shortcodes ) ) : '';
if ( $update ) {
// Update template into the local library.
$post_id = wp_update_post(
array(
'ID' => $item_id,
'post_title' => $title,
'post_content' => $post_content,
)
);
} else {
// Insert template into the local library.
$is_set_template = ET_THEME_BUILDER_ITEM_SET === $_->array_get( $preferences, 'item_type', '' );
$post_id = wp_insert_post(
array(
'post_type' => ET_TB_ITEM_POST_TYPE,
'post_status' => $status,
'post_title' => $is_set_template ? $title : $template_name,
'post_content' => $post_content,
)
);
}
if ( 0 === $post_id || is_wp_error( $post_id ) ) {
return false;
}
$item_type = $_->array_get( $preferences, 'item_type', '' );
// Taxonomy: TB item type and selected category and tags.
if ( ET_THEME_BUILDER_ITEM_TEMPLATE === $item_type ) {
et_local_library_set_item_taxonomy( $post_id, $preferences );
} elseif ( ET_THEME_BUILDER_ITEM_SET === $item_type ) {
$template_preferences = [
'item_type' => ET_THEME_BUILDER_ITEM_TEMPLATE,
];
$is_set_template = true;
if ( $is_default && $preset_id > 0 ) {
update_post_meta( $preset_id, '_et_has_default_template', '1' );
}
et_local_library_set_item_taxonomy( $post_id, $template_preferences );
}
// Template meta.
$metas = array_merge(
array(
'_et_autogenerated_title' => $autogenerated_title ? '1' : '0',
'_et_default' => $is_default ? '1' : '0',
'_et_enabled' => $enabled ? '1' : '0',
'_et_header_layout_enabled' => $header_enabled ? '1' : '0',
'_et_body_layout_enabled' => $body_enabled ? '1' : '0',
'_et_footer_layout_enabled' => $footer_enabled ? '1' : '0',
'_et_template_title' => $title,
),
$global_layout_meta
);
if ( $is_set_template ) {
$metas = array_merge(
$metas,
[
'_et_set_template' => '1',
]
);
}
foreach ( $metas as $key => $value ) {
update_post_meta( $post_id, $key, $value );
}
// In a case of update, delete existing meta `_et_use_on` and `_et_exclude_from` before adding new.
if ( $update ) {
delete_post_meta( $post_id, '_et_use_on' );
delete_post_meta( $post_id, '_et_exclude_from' );
}
if ( $use_on ) {
$use_on_unique = array_unique( $use_on );
foreach ( $use_on_unique as $condition ) {
add_post_meta( $post_id, '_et_use_on', $condition );
}
}
if ( $exclude_from ) {
$exclude_from_unique = array_unique( $exclude_from );
foreach ( $exclude_from_unique as $condition ) {
add_post_meta( $post_id, '_et_exclude_from', $condition );
}
}
if ( $is_default ) {
update_post_meta( $preset_id, '_et_default_template_id', $post_id );
}
return $post_id;
}
/**
* Save a Theme Builder preset to the local library.
*
* @since 4.18.0
* @param array $templates List of templates.
* @param array $preferences Preset preferences.
*
* @return (integer|false) Returns Post ID on success and FALSE on failure.
*/
function et_theme_builder_save_preset_to_library( $templates, $preferences ) {
if ( ! current_user_can( 'edit_others_posts' ) ) {
wp_die();
}
if ( ! is_array( $templates ) ) {
return false;
}
$_ = et_();
$template_post_ids = [];
// Preferences.
if ( is_array( $preferences ) ) {
$preferences = array_merge( $preferences, [ 'item_type' => ET_THEME_BUILDER_ITEM_SET ] );
} else {
// Returning FALSE sends back wp_send_json_error().
return false;
}
/*
* List of all Template IDs as part of this preset.
*
* The following hierarchy will explain the relation between Presets, Templates & Layouts.
*
* - Preset
* - Templates
* - Layouts
*
* Each Template pertaining to this preset will be stored as `_et_template_id` post meta.
*
* SELECT post_id, meta_key, meta_value
* FROM wp_postmeta
* WHERE
* meta_key='_et_template_id' AND post_id={post_id};
*/
$template_post_ids = [];
$preset_name = et_theme_builder_local_library_get_item_name( $preferences, ET_THEME_BUILDER_ITEM_SET );
$post_id = wp_insert_post(
array(
'post_type' => ET_TB_ITEM_POST_TYPE,
'post_status' => et_()->array_get( $preferences, 'post_status', 'publish' ),
'post_title' => $preset_name,
)
);
if ( 0 === $post_id || is_wp_error( $post_id ) ) {
return false;
}
$preferences = array_merge( $preferences, [ 'preset_id' => $post_id ] );
// Insert template into the local library.
foreach ( $templates as $template ) {
$template_post_ids[] = et_theme_builder_save_template_to_library( $template, $preferences );
}
// Taxonomy: TB item type and selected category and tags.
et_local_library_set_item_taxonomy( $post_id, $preferences );
et_theme_builder_add_template_to_preset( $post_id, $template_post_ids );
return $post_id;
}
/**
* Add template to the saved preset.
*
* @param int $preset_id The preset id.
* @param array $template_post_ids List of the template post ids.
*
* @since 4.18.0
*/
function et_theme_builder_add_template_to_preset( $preset_id, $template_post_ids ) {
foreach ( $template_post_ids as $template_post_id ) {
if ( 0 === absint( $template_post_id ) ) {
continue;
}
// `add_post_meta` used to create new { key: value } pair using the same key.
add_post_meta( $preset_id, '_et_template_id', $template_post_id );
}
}
/**
* Retrieves an array of the terms in a given taxonomy in following format:
*
* $terms = array(
* '1' => array(
* 'id' => 1,
* 'name' => 'Uncategorized',
* 'slug' => 'uncategorized',
* 'count' => 10,
* ),
* );
*
* @param string $tax_name Taxonomy name.
*/
function et_theme_builder_get_terms( $tax_name ) {
$terms = get_terms( $tax_name, array( 'hide_empty' => false ) );
$terms_by_id = array();
if ( is_wp_error( $terms ) || empty( $terms ) ) {
return array();
}
foreach ( $terms as $term ) {
$term_id = $term->term_id;
$terms_by_id[ $term_id ]['id'] = $term_id;
$terms_by_id[ $term_id ]['name'] = $term->name;
$terms_by_id[ $term_id ]['slug'] = $term->slug;
$terms_by_id[ $term_id ]['count'] = $term->count;
}
return $terms_by_id;
}
/**
* Retrieves the library item type attached with library item.
*
* @since 4.18.0
*
* @param int|WP_Post $item Library item post ID or object.
* @return string|WP_Error The library item type. WP_Error on failure.
*/
function et_theme_builder_get_library_item_type( $item ) {
// Initalize item type.
$terms = get_the_terms( $item, 'et_tb_item_type' );
if ( ! $terms || is_wp_error( $terms ) ) {
return new WP_Error( 'et_theme_builder_no_item_type_attached', __( 'The library item type is not attached to a given item.', 'et_builder' ) );
}
$item_type = $terms[0]->slug;
// Allowed item types, i.e template and preset.
if ( ! in_array( $item_type, array( ET_THEME_BUILDER_ITEM_SET, ET_THEME_BUILDER_ITEM_TEMPLATE ), true ) ) {
return new WP_Error( 'et_theme_builder_invalid_item_type', __( 'Invalid library item type.', 'et_builder' ) );
}
return $item_type;
}
/**
* Retrieves library item post given a library post ID or post object.
*
* @since 4.18.0
*
* @param int|WP_Post $item Library item's post ID or WP_Post object.
* @return WP_Post|WP_Error The library item post object. WP_Error on failure.
*/
function et_theme_builder_get_library_item_post( $item ) {
// Initalize item post.
$item_post = get_post( $item );
if ( ! $item_post ) {
return new WP_Error( 'et_theme_builder_item_not_found', __( 'Library item not found', 'et_builder' ) );
}
$item_type = et_theme_builder_get_library_item_type( $item_post );
if ( is_wp_error( $item_type ) ) {
return $item_type;
}
return $item_post;
}
/**
* Create interim theme builder post for the local library editor.
*
* @since 4.18.0
*
* @return int|bool The post ID on success. The value false on failure.
*/
function et_theme_builder_insert_library_theme_builder() {
if ( ! current_user_can( 'edit_others_posts' ) ) {
wp_die();
}
$post_id = wp_insert_post(
array(
'post_type' => ET_THEME_BUILDER_THEME_BUILDER_POST_TYPE,
'post_status' => 'publish',
'post_title' => 'Library Theme Builder',
)
);
if ( ! $post_id || is_wp_error( $post_id ) ) {
return false;
}
// Mark the theme builder to use during template editing.
update_post_meta( $post_id, '_et_library_theme_builder', '1' );
return $post_id;
}
// phpcs:disable Squiz.Commenting.FunctionComment.ParamCommentFullStop -- Respecting punctuation.
/**
* Create a theme builder layouts from the template saved in the local library.
*
* @since 4.18.0
*
* @param WP_Post $template_post The template post.
* @param array $global_layouts Optional. Array containing the necessary params.
* $params = [
* 'header' => (int|string) Header Layout ID. `use_global` string when TB global layout (relink option) is to be used.
* 'body' => (int|string) Body Layout ID. `use_global` string when TB global layout (relink option) is to be used.
* 'footer' => (int|string) Footer Layout ID. `use_global` string when TB global layout (relink option) is to be used.
* ]
*
* @return array|WP_Error An array of the created layout post ids. WP_Error on failure.
*/
function et_theme_builder_create_layouts_from_library_template( $template_post, $global_layouts = [] ) {
if ( ! $template_post instanceof WP_Post || empty( $template_post->post_content ) ) {
return array();
}
$layouts = (array) json_decode( $template_post->post_content, true );
$template_settings = et_theme_builder_get_template_settings( $template_post->ID, false );
$is_default_template = '1' === $template_settings['_et_default'];
if ( empty( $layouts ) ) {
return new WP_Error(
'et_theme_builder_invalid_json',
esc_html__(
'Incorrect JSON string.',
'et-builder'
)
);
}
$new_layout_ids = array();
foreach ( $layouts as $layout_type => $layout ) {
$layout_post_content = et_()->array_get( $layout, 'post_content', '' );
// Skip if the layout `post_content` value is empty.
if ( empty( $layout_post_content ) ) {
continue;
}
if ( ! $is_default_template && '1' === $template_settings[ "_et_{$layout_type}_layout_global" ] && array_key_exists( $layout_type, $global_layouts ) ) {
$new_layout_ids[ $layout_type ] = $global_layouts[ $layout_type ];
continue;
}
// Insert layout.
$post_id = et_theme_builder_insert_layout(
array(
'post_type' => et_theme_builder_get_valid_layout_post_type( $layout_type ),
'post_content' => $layout_post_content,
)
);
if ( $post_id && ! is_wp_error( $post_id ) ) {
$new_layout_ids[ $layout_type ] = $post_id;
// Add post meta in the new layout from the layout `post_meta` value.
$layout_post_meta = et_()->array_get( $layout, 'post_meta', array() );
foreach ( $layout_post_meta as $post_meta ) {
update_post_meta( $post_id, $post_meta['key'], $post_meta['value'] );
}
}
}
return $new_layout_ids;
}
// phpcs:enable
/**
* Create template from the library item post.
*
* @since 4.18.0
*
* @param WP_Post $item_post The library item post object.
* @param array $global_layouts Array of global layouts.
* @return int|bool The template id on success. false on failure.
*/
function et_theme_builder_create_template_from_library_template( $item_post, $global_layouts = array() ) {
if ( ! current_user_can( 'edit_others_posts' ) ) {
wp_die();
}
// Insert template.
$template_id = wp_insert_post(
array(
'post_type' => ET_THEME_BUILDER_TEMPLATE_POST_TYPE,
'post_status' => 'publish',
'post_title' => $item_post->post_title,
)
);
if ( ! $template_id || is_wp_error( $template_id ) ) {
return false;
}
// Insert layouts.
$new_layouts_ids = et_theme_builder_create_layouts_from_library_template( $item_post, $global_layouts );
if ( is_wp_error( $new_layouts_ids ) ) {
return false;
}
foreach ( array( 'body', 'header', 'footer' ) as $layout_type ) {
$layout_post_id = et_()->array_get( $new_layouts_ids, $layout_type, 0 );
update_post_meta( $template_id, "_et_{$layout_type}_layout_id", $layout_post_id );
}
// Add template settings.
$template_settings = et_theme_builder_get_template_settings( $item_post->ID, false );
foreach ( $template_settings as $meta_key => $meta_value ) {
if ( is_array( $meta_value ) ) {
foreach ( $meta_value as $value ) {
if ( ! empty( $value ) ) {
add_post_meta( $template_id, $meta_key, $value );
}
}
} else {
add_post_meta( $template_id, $meta_key, $meta_value );
}
}
add_post_meta( $template_id, '_et_library_item_id', $item_post->ID );
return $template_id;
}
/**
* Retrive template settings.
*
* @since 4.18.0
*
* @param int $item_id The library item post ID.
* @param bool $formatted Whether to remove "_et" prefix from setting keys.
*
* @return array An array of template settings.
*/
function et_theme_builder_get_template_settings( $item_id, $formatted ) {
// Template settings meta.
$metas = array(
'_et_template_title' => true,
'_et_autogenerated_title' => true,
'_et_default' => true,
'_et_enabled' => true,
'_et_header_layout_enabled' => true,
'_et_body_layout_enabled' => true,
'_et_footer_layout_enabled' => true,
'_et_use_on' => false,
'_et_exclude_from' => false,
'_et_header_layout_global' => true,
'_et_body_layout_global' => true,
'_et_footer_layout_global' => true,
);
$settings = array();
foreach ( $metas as $meta_key => $is_single ) {
$key = $formatted ? substr( $meta_key, 4 ) : $meta_key;
$settings[ $key ] = get_post_meta( $item_id, $meta_key, $is_single );
}
return $settings;
}
/**
* Intalize the library item editor.
*/
function et_theme_builder_init_library_item_editor() {
if ( ! et_theme_builder_library_is_item_editor() ) {
return;
}
$tb_item_id = et_theme_builder_get_item_id();
$item_editor = ET_Theme_Builder_Local_Library_Item_Editor::instance( $tb_item_id );
$item_editor->init_library_item_editor();
}
add_action( 'admin_init', 'et_theme_builder_init_library_item_editor', 10 );
/**
* Return custom style from the passed layout meta.
*
* @since 4.18.0
*
* @param array $layout_meta All layout meta saved in the local library.
* @param string $layout_type The layout type i.e body, header, footer.
* @return string Page custom style.
*/
function et_theme_builder_get_preview_custom_css( $layout_meta, $layout_type ) {
$overflow = et_pb_overflow();
$selector_prefix = '.et-l--post';
switch ( $layout_type ) {
case 'header':
$selector_prefix = '.et-l--header';
break;
case 'body':
$selector_prefix = '.et-l--body';
break;
case 'footer':
$selector_prefix = '.et-l--footer';
break;
}
$page_settings = array();
foreach ( $layout_meta as $post_meta ) {
$page_settings[ $post_meta->key ] = $post_meta->value;
}
$selector_prefix = ' ' . ET_BUILDER_CSS_PREFIX . $selector_prefix;
$output = isset( $page_settings['_et_pb_custom_css'] ) ? $page_settings['_et_pb_custom_css'] : '';
if ( isset( $page_settings['_et_pb_light_text_color'] ) ) {
$output .= sprintf(
'%2$s .et_pb_bg_layout_dark { color: %1$s !important; }',
esc_html( $page_settings['_et_pb_light_text_color'] ),
esc_html( $selector_prefix )
);
}
if ( isset( $page_settings['_et_pb_dark_text_color'] ) ) {
$output .= sprintf(
'%2$s .et_pb_bg_layout_light { color: %1$s !important; }',
esc_html( $page_settings['_et_pb_dark_text_color'] ),
esc_html( $selector_prefix )
);
}
if ( isset( $page_settings['_et_pb_content_area_background_color'] ) ) {
$content_area_bg_selector = et_is_builder_plugin_active() ? $selector_prefix : ' .page.et_pb_pagebuilder_layout #main-content';
$output .= sprintf(
'%1$s { background-color: %2$s; }',
esc_html( $content_area_bg_selector ),
esc_html( $page_settings['_et_pb_content_area_background_color'] )
);
}
if ( isset( $page_settings['_et_pb_section_background_color'] ) ) {
$output .= sprintf(
'%2$s > .et_builder_inner_content > .et_pb_section { background-color: %1$s; }',
esc_html( $page_settings['_et_pb_section_background_color'] ),
esc_html( $selector_prefix )
);
}
$overflow_x = $overflow->get_value_x( $page_settings, '', '_et_pb_' );
$overflow_y = $overflow->get_value_y( $page_settings, '', '_et_pb_' );
if ( ! empty( $overflow_x ) ) {
$output .= sprintf(
'%2$s .et_builder_inner_content { overflow-x: %1$s; }',
esc_html( $overflow_x ),
esc_html( $selector_prefix )
);
}
if ( ! empty( $overflow_y ) ) {
$output .= sprintf(
'%2$s .et_builder_inner_content { overflow-y: %1$s; }',
esc_html( $overflow_y ),
esc_html( $selector_prefix )
);
}
if ( isset( $page_settings['_et_pb_page_z_index'] ) && '' !== $page_settings['_et_pb_page_z_index'] ) {
$output .= sprintf(
'%2$s .et_builder_inner_content { z-index: %1$s; }',
esc_html( $page_settings['et_pb_page_z_index'] ),
esc_html( '.et-db #et-boc .et-l' . $selector_prefix )
);
}
/**
* Filters page custom css.
*
* @since 4.18.0
*
* @param string $output
*/
return apply_filters( 'et_pb_page_custom_css', $output );
}
/**
* Render the saved template.
*
* @since 4.18.0
*
* @param int $item_id The saved library item id.
* @retun void
*/
function et_theme_builder_render_library_template_preview( $item_id ) {
$item = new ET_Theme_Builder_Local_Library_Item( $item_id );
$layouts = json_decode( $item->item_post->post_content );
$template_html = '';
$layout_post_types = [
'header' => ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE,
'body' => ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE,
'footer' => ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE,
];
foreach ( $layouts as $layout_type => $layout ) {
$layout_meta = $layout->post_meta;
$layout_post_type = $layout_post_types[ $layout_type ];
$layout_id = 0;
et_theme_builder_frontend_render_common_wrappers( $layout_type, true );
$template_html .= et_builder_get_layout_opening_wrapper( $layout_post_type );
ET_Builder_Element::begin_theme_builder_layout( $layout_id );
$template_html .= et_core_intentionally_unescaped( et_builder_render_layout( $layout->post_content ), 'html' );
$result = ET_Builder_Element::setup_advanced_styles_manager( $layout_id );
$advanced_styles_manager = $result['manager'];
$advanced_styles_manager->forced_inline = true;
if ( isset( $result['deferred'] ) ) {
$deferred_styles_manager = $result['deferred'];
}
$is_critical_enabled = apply_filters( 'et_builder_critical_css_enabled', false );
$custom = et_theme_builder_get_preview_custom_css( $layout_meta, $layout_type );
$critical = $is_critical_enabled ? ET_Builder_Element::get_style( false, $layout_id, true ) . ET_Builder_Element::get_style( true, $layout_id, true ) : [];
$styles = ET_Builder_Element::get_style( false, $layout_id ) . ET_Builder_Element::get_style( true, $layout_id );
if ( empty( $critical ) ) {
// No critical styles defined, just enqueue everything as usual.
$styles = $custom . $styles;
if ( ! empty( $styles ) ) {
if ( isset( $deferred_styles_manager ) ) {
$deferred_styles_manager->set_data( $styles, 40 );
} else {
$advanced_styles_manager->set_data( $styles, 40 );
}
}
} else {
// Add page css to the critical section.
$critical = $custom . $critical;
$advanced_styles_manager->set_data( $critical, 40 );
if ( ! empty( $styles ) ) {
// Defer everything else.
$deferred_styles_manager->set_data( $styles, 40 );
}
}
ET_Builder_Element::end_theme_builder_layout();
$template_html .= et_builder_get_layout_closing_wrapper( $layout_post_type );
}
return $template_html;
}
/**
* Check whether current page is library template preview page.
*
* @return bool
*/
function is_et_theme_builder_template_preview() {
global $wp_query;
// phpcs:ignore WordPress.Security.NonceVerification -- This function does not change any state, and is therefore not susceptible to CSRF.
return ( 'true' === $wp_query->get( 'et_pb_preview' ) && isset( $_GET['et_pb_preview_nonce'] ) && isset( $_GET['item_id'] ) );
}
/**
* Get updated global layouts by incoming layout duplicate decision.
*
* @param string $incoming_layout_duplicate_decision Layout duplicate decision opted by User.
* @param array $global_layouts Global layouts.
* @return array
*/
function et_theme_builder_get_global_layouts_by_incoming_layout_duplicate_decision( $incoming_layout_duplicate_decision, $global_layouts ) {
$updated_global_layouts = [];
if ( 0 === count( $global_layouts ) && 'relink' === $incoming_layout_duplicate_decision ) {
$theme_builder_templates = et_theme_builder_get_theme_builder_templates( true, false );
$layouts = [];
foreach ( $theme_builder_templates as $template ) {
if ( ! $template['default'] ) {
continue;
}
$layouts = $template['layouts'];
}
foreach ( $layouts as $layout_type => $layout ) {
$updated_global_layouts[ $layout_type ] = $layout['id'];
}
} else {
foreach ( $global_layouts as $layout_type => $layout_id ) {
$layout_post = get_post( $layout_id );
if ( ! $layout_post ) {
$updated_global_layouts[ $layout_type ] = 0;
} else {
$created_layout_id = wp_insert_post(
[
'post_content' => $layout_post->post_content,
'post_title' => $layout_post->post_title,
'post_type' => $layout_post->post_type,
'post_status' => $layout_post->post_status,
'post_name' => $layout_post->post_name,
]
);
$updated_global_layouts[ $layout_type ] = $created_layout_id;
$meta = et_core_get_post_builder_meta( $layout_id );
foreach ( $meta as $entry ) {
update_post_meta( $created_layout_id, $entry['key'], $entry['value'] );
}
}
}
}
return $updated_global_layouts;
}
/**
* Retrieve the item id from query parameters.
*
* @since 4.18.0
*
* @return int The template id or preset id.
*/
function et_theme_builder_get_item_id() {
// Editing library template.
$item_id = (int) et_()->array_get( $_GET, 'tb_template', 0 ); // phpcs:ignore WordPress.Security.NonceVerification -- Nonce verified in `et_builder_security_check`.
if ( $item_id ) {
return $item_id;
}
$item_id = (int) et_()->array_get( $_GET, 'tb_set', 0 ); // phpcs:ignore WordPress.Security.NonceVerification -- Nonce verified in `et_builder_security_check`.
return $item_id;
}
/**
* Update library items.
*
* @since 4.18.0
*
* @param int $item_id The item id.
* @param array $templates List of template ids.
*/
function et_theme_builder_update_library_item( $item_id, $templates ) {
$item_type = et_theme_builder_get_library_item_type( $item_id );
$global_layouts = array();
if ( ET_THEME_BUILDER_ITEM_SET === $item_type ) {
// Remove the templates from preset.
$old_template_ids = get_post_meta( $item_id, '_et_template_id' );
delete_post_meta( $item_id, '_et_template_id' );
// Find global layouts.
foreach ( $templates as $template ) {
if ( isset( $template['default'] ) && '1' === $template['default'] ) {
$global_layouts = $template['layouts'];
break;
}
}
}
// Save templates to library.
$template_post_ids = array();
foreach ( $templates as $template ) {
$template['global_layouts'] = $global_layouts;
$preferences = array(
'template_name' => $template['title'],
'force_update' => true,
);
$template_post_ids[] = et_theme_builder_save_template_to_library( $template, $preferences );
}
if ( ET_THEME_BUILDER_ITEM_SET === $item_type ) {
// Trash the removed template.
$removed_template_ids = array_diff( $old_template_ids, $template_post_ids );
if ( ! empty( $removed_template_ids ) ) {
foreach ( $removed_template_ids as $removed_template_id ) {
if ( current_user_can( 'delete_post', $removed_template_id ) && ET_TB_ITEM_POST_TYPE === get_post_type( $removed_template_id ) ) {
wp_trash_post( $removed_template_id );
}
}
}
// Add saved templates to preset.
et_theme_builder_add_template_to_preset( $item_id, $template_post_ids );
}
}
/**
* Determines if the library item editor is open.
*
* @return bool
*/
function et_theme_builder_library_is_item_editor() {
if ( ! et_builder_is_tb_admin_screen() ) {
return false;
}
$item_id = et_theme_builder_get_item_id();
if ( ! $item_id || ET_TB_ITEM_POST_TYPE !== get_post_type( $item_id ) ) {
return false;
}
return true;
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* Implement ET_Builder_Post_Query_TBItems to query `et_tb_item`.
*
* @since 4.18.0
*
* @package Builder
*/
/**
* Class to handle `et_tb_item` query.
*
* Think of this class as WP_Query for ET_TB_ITEM_POST_TYPE.
*/
class ET_Builder_Post_Query_TBItems extends ET_Core_Post_Query {
/**
* {@inheritDoc}
*
* @param string $taxonomy The taxonomy name.
* @param array $terms Taxonomy terms.
* @param bool $negate Whether to negate this tax query.
*/
protected function _add_tax_query( $taxonomy, $terms, $negate = null ) {
$args = self::$_->array_flatten( $terms );
$negate = $this->_reset_negate();
if ( ! $args ) {
return $this;
}
parent::_add_tax_query( $taxonomy, $args, $negate );
return $this;
}
}

View File

@@ -0,0 +1,70 @@
<?php
/**
* Init `et_tb_item` taxonomy.
*
* @since 4.18.0
*
* @package Builder
*/
/**
* Class to handle `et_tb_item` taxonomy.
*
* Registers TB Item's Category taxonomy.
*/
class ET_Builder_Post_Taxonomy_TBItemCategory extends ET_Core_Post_Taxonomy {
/**
* {@inheritDoc}
*
* @var string
*/
protected $_owner = 'builder';
/**
* {@inheritDoc}
*
* @var string
*/
public $name = 'et_tb_item_category';
/**
* {@inheritDoc}
*
* @var string
*/
protected function _get_args() {
return array(
'hierarchical' => true,
'show_ui' => false,
'show_admin_column' => true,
'query_var' => true,
'show_in_nav_menus' => false,
'publicly_queryable' => ET_Builder_Post_Type_TBItem::is_publicly_queryable(),
);
}
/**
* {@inheritDoc}
*/
protected function _get_labels() {
return array();
}
/**
* Get the class instance.
*
* @param string $type See {@see self::$wp_type} for accepted values. Default is 'taxonomy'.
* @param string $name The name/slug of the post object. Default is {@see self::$name}.
*
* @return ET_Builder_Post_Taxonomy_TBItemCategory|null
*/
public static function instance( $type = 'taxonomy', $name = 'et_tb_item_category' ) {
$instance = parent::instance( $type, $name );
if ( ! $instance ) {
$instance = new self();
}
return $instance;
}
}

View File

@@ -0,0 +1,70 @@
<?php
/**
* Init `et_tb_item` taxonomy.
*
* @since 4.18.0
*
* @package Builder
*/
/**
* Class to handle `et_tb_item` taxonomy.
*
* Registers TB Item's Tag taxonomy.
*/
class ET_Builder_Post_Taxonomy_TBItemTag extends ET_Core_Post_Taxonomy {
/**
* Taxonomy key.
*
* @var string
*/
public $name = 'et_tb_item_tag';
/**
* Owner name.
*
* @var string
*/
protected $_owner = 'builder';
/**
* Get the class instance.
*
* @param string $type See {@see self::$wp_type} for accepted values. Default is 'taxonomy'.
* @param string $name The name/slug of the post object. Default is {@see self::$name}.
*
* @return ET_Builder_Post_Taxonomy_TBItemTag|null
*/
public static function instance( $type = 'taxonomy', $name = 'et_tb_item_tag' ) {
$instance = parent::instance( $type, $name );
if ( ! $instance ) {
$instance = new self();
}
return $instance;
}
/**
* {@inheritDoc}
*/
protected function _get_args() {
return array(
'hierarchical' => false,
'show_ui' => false,
'show_admin_column' => true,
'query_var' => true,
'show_in_nav_menus' => false,
'publicly_queryable' => ET_Builder_Post_Type_TBItem::is_publicly_queryable(),
);
}
/**
* {@inheritDoc}
*/
protected function _get_labels() {
return array(
'name' => esc_html__( 'Tags', 'et_builder' ),
);
}
}

View File

@@ -0,0 +1,70 @@
<?php
/**
* Init `et_tb_item` taxonomy.
*
* @since 4.18.0
*
* @package Builder
*/
/**
* Class to handle `et_tb_item` taxonomy.
*
* Registers TB Item's Type taxonomy.
*/
class ET_Builder_Post_Taxonomy_TBItemType extends ET_Core_Post_Taxonomy {
/**
* {@inheritDoc}
*
* @var string
*/
protected $_owner = 'builder';
/**
* {@inheritDoc}
*
* @var string
*/
public $name = 'et_tb_item_type';
/**
* {@inheritDoc}
*/
protected function _get_args() {
return array(
'hierarchical' => false,
'show_ui' => false,
'show_admin_column' => true,
'query_var' => true,
'show_in_nav_menus' => false,
'publicly_queryable' => ET_Builder_Post_Type_TBItem::is_publicly_queryable(),
);
}
/**
* {@inheritDoc}
*/
protected function _get_labels() {
return array(
'name' => esc_html__( 'Type', 'et_builder' ),
);
}
/**
* Get the class instance.
*
* @param string $type See {@see self::$wp_type} for accepted values. Default is 'taxonomy'.
* @param string $name The name/slug of the post object. Default is {@see self::$name}.
*
* @return ET_Builder_Post_Taxonomy_TBItemType|null
*/
public static function instance( $type = 'taxonomy', $name = 'et_tb_item_type' ) {
$instance = parent::instance( $type, $name );
if ( ! $instance ) {
$instance = new self();
}
return $instance;
}
}

View File

@@ -0,0 +1,158 @@
<?php
/**
* Init `et_tb_item` taxonomy.
*
* @since 4.18.0
*
* @package Builder
*/
if ( ! defined( 'ET_TB_ITEM_POST_TYPE' ) ) {
define( 'ET_TB_ITEM_POST_TYPE', 'et_tb_item' );
}
require_once ET_THEME_BUILDER_DIR . 'post/query/TBItems.php';
/**
* Class to handle `et_tb_item` post type.
*
* Registers TB Item.
*/
class ET_Builder_Post_Type_TBItem extends ET_Core_Post_Type {
/**
* {@inheritDoc}
*
* @var string
*/
protected $_category_tax = 'layout_category';
/**
* {@inheritDoc}
*
* @var string
*/
protected $_owner = 'builder';
/**
* {@inheritDoc}
*
* @var string
*/
protected $_tag_tax = 'layout_tag';
/**
* {@inheritDoc}
*
* @var string
*/
public $name = ET_TB_ITEM_POST_TYPE;
/**
* {@inheritDoc}
*/
protected function _get_args() {
return array(
'can_export' => true,
'capability_type' => 'post',
'has_archive' => false,
'hierarchical' => false,
'map_meta_cap' => true,
'public' => false,
'publicly_queryable' => self::is_publicly_queryable(),
'query_var' => false,
'show_in_menu' => false,
'show_ui' => false,
'supports' => array(
'editor',
'excerpt',
'revisions',
'thumbnail',
'title',
),
'taxonomies' => array(
'layout_category',
'layout_tag',
'et_tb_item_type',
),
);
}
/**
* {@inheritDoc}
*/
protected function _get_labels() {
return array(
'add_new' => esc_html_x( 'Add New', 'Layout', 'et_builder' ),
'add_new_item' => esc_html__( 'Add New Theme Builder Item', 'et_builder' ),
'all_items' => esc_html__( 'All Theme Builder Items', 'et_builder' ),
'edit_item' => esc_html__( 'Edit Theme Builder Item', 'et_builder' ),
'name' => esc_html__( 'Theme Builder Items', 'et_builder' ),
'new_item' => esc_html__( 'New Theme Builder Item', 'et_builder' ),
'not_found' => esc_html__( 'Nothing found', 'et_builder' ),
'not_found_in_trash' => esc_html__( 'Nothing found in Trash', 'et_builder' ),
'parent_item_colon' => '',
'search_items' => esc_html__( 'Search Theme Builder Items', 'et_builder' ),
'singular_name' => et_builder_i18n( 'Theme Builder Item' ),
'view_item' => esc_html__( 'View Theme Builder Item', 'et_builder' ),
);
}
/**
* Get the class instance.
*
* @param string $type See {@see self::$wp_type} for accepted values. Default is 'cpt'.
* @param string $name The name/slug of the post object. Default is {@see self::$name}.
*
* @return self|null
*/
public static function instance( $type = 'cpt', $name = ET_TB_ITEM_POST_TYPE ) {
$instance = parent::instance( $type, $name );
if ( ! $instance ) {
$instance = new self();
}
return $instance;
}
/**
* Returns TRUE when a layout is Favorite.
*
* @param string $post_id Post ID.
*
* @return bool
*/
public function is_favorite( $post_id ) {
return 'favorite' === get_post_meta( $post_id, 'favorite_status', true );
}
/**
* Determines if TB Item Library's CPT and its taxonomies are publicly queryable for the current request.
*
* @return bool
*/
public static function is_publicly_queryable() {
// phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification -- Nonce not required.
// phpcs:disable WordPress.Security.NonceVerification.Recommended -- Nonce not required.
$get = $_GET;
// phpcs:ignore ET.Sniffs.ValidVariableName.VariableNotSnakeCase -- TB is an acronym.
$is_TB = ( 'et_theme_builder' === self::$_->array_get( $get, 'page' ) || wp_doing_ajax() );
// phpcs:enable
// phpcs:ignore ET.Sniffs.ValidVariableName.VariableNotSnakeCase -- TB is an acronym.
return $is_TB;
}
/**
* Returns an instance of ET_Builder_Post_Query_TBItems.
*
* The instance can then be used to get results.
*
* @see ET_Builder_Post_Query_TBItems::run()
*
* @return ET_Builder_Post_Query_TBItems
*/
public function query() {
return new ET_Builder_Post_Query_TBItems( $this->name, $this->_category_tax, $this->_tag_tax );
}
}

View File

@@ -0,0 +1,325 @@
<?php
/**
* Filters an object id for use in template settings validation functions.
*
* @since 4.2
*
* @param integer $id Object ID.
* @param string $type Type.
* @param string $subtype Subtype.
*
* @return integer
*/
function et_theme_builder_template_setting_filter_validation_object_id( $id, $type, $subtype ) {
/**
* Filters template settings object id for validation use.
*
* @since 4.2
*
* @param integer $id
* @param string $type
* @param string $subtype
*/
return apply_filters( 'et_theme_builder_template_setting_filter_validation_id', $id, $type, $subtype );
}
/**
* Validate homepage.
*
* @since 4.0
*
* @param string $type Type.
* @param string $subtype Subtype.
* @param integer $id ID.
* @param string[] $setting Setting.
*
* @return bool
*/
function et_theme_builder_template_setting_validate_homepage( $type, $subtype, $id, $setting ) {
return ET_Theme_Builder_Request::TYPE_FRONT_PAGE === $type;
}
/**
* Validate singular:post_type:<post_type>:all.
*
* @since 4.0
*
* @param string $type Type.
* @param string $subtype Subtype.
* @param integer $id ID.
* @param string[] $setting Setting.
*
* @return bool
*/
function et_theme_builder_template_setting_validate_singular_post_type_all( $type, $subtype, $id, $setting ) {
if ( ET_Theme_Builder_Request::TYPE_FRONT_PAGE === $type && 'page' === $setting[2] && $id === (int) get_option( 'page_on_front' ) ) {
// Cover the homepage as well.
return true;
}
return ET_Theme_Builder_Request::TYPE_SINGULAR === $type && $subtype === $setting[2];
}
/**
* Validate archive:post_type:<post_type>.
*
* @since 4.0
*
* @param string $type Type.
* @param string $subtype Subtype.
* @param integer $id ID.
* @param string[] $setting Setting.
*
* @return bool
*/
function et_theme_builder_template_setting_validate_archive_post_type( $type, $subtype, $id, $setting ) {
return ET_Theme_Builder_Request::TYPE_POST_TYPE_ARCHIVE === $type && $subtype === $setting[2];
}
/**
* Validate singular:post_type:<post_type>:id:<id>.
*
* @since 4.0
*
* @param string $type Type.
* @param string $subtype Subtype.
* @param integer $id ID.
* @param string[] $setting Setting.
*
* @return bool
*/
function et_theme_builder_template_setting_validate_singular_post_type_id( $type, $subtype, $id, $setting ) {
$object_id = et_theme_builder_template_setting_filter_validation_object_id( (int) $setting[4], 'post', $setting[2] );
return (
// Cover the special case where the post selected is assigned as the website homepage.
( ET_Theme_Builder_Request::TYPE_FRONT_PAGE === $type && $id === $object_id )
||
( ET_Theme_Builder_Request::TYPE_SINGULAR === $type && $id === $object_id )
);
}
/**
* Validate singular:post_type:<post_type>:children:id:<id>.
*
* @since 4.0
*
* @param string $type Type.
* @param string $subtype Subtype.
* @param integer $id ID.
* @param string[] $setting Setting.
*
* @return bool
*/
function et_theme_builder_template_setting_validate_singular_post_type_children_id( $type, $subtype, $id, $setting ) {
if ( ET_Theme_Builder_Request::TYPE_SINGULAR !== $type ) {
return false;
}
$object_id = et_theme_builder_template_setting_filter_validation_object_id( (int) $setting[5], 'post', $setting[2] );
return in_array( $object_id, get_post_ancestors( $id ), true );
}
/**
* Validate singular:taxonomy:<taxonomy>:term:id:<id>.
*
* @since 4.0
*
* @param string $type Type.
* @param string $subtype Subtype.
* @param integer $id ID.
* @param string[] $setting Setting.
*
* @return bool
*/
function et_theme_builder_template_setting_validate_singular_taxonomy_term_id( $type, $subtype, $id, $setting ) {
if ( ET_Theme_Builder_Request::TYPE_SINGULAR !== $type ) {
return false;
}
$taxonomy = $setting[2];
$object_id = et_theme_builder_template_setting_filter_validation_object_id( (int) $setting[5], 'taxonomy', $taxonomy );
return has_term( $object_id, $taxonomy, $id );
}
/**
* Validate archive:all.
*
* @since 4.0
*
* @param string $type Type.
* @param string $subtype Subtype.
* @param integer $id ID.
* @param string[] $setting Setting.
*
* @return bool
*/
function et_theme_builder_template_setting_validate_archive_all( $type, $subtype, $id, $setting ) {
$archives = array(
ET_Theme_Builder_Request::TYPE_POST_TYPE_ARCHIVE,
ET_Theme_Builder_Request::TYPE_TERM,
ET_Theme_Builder_Request::TYPE_AUTHOR,
ET_Theme_Builder_Request::TYPE_DATE,
);
return in_array( $type, $archives, true );
}
/**
* Validate archive:taxonomy:<taxonomy>:all.
*
* @since 4.0
*
* @param string $type Type.
* @param string $subtype Subtype.
* @param integer $id ID.
* @param string[] $setting Setting.
*
* @return bool
*/
function et_theme_builder_template_setting_validate_archive_taxonomy_all( $type, $subtype, $id, $setting ) {
return ET_Theme_Builder_Request::TYPE_TERM === $type && $subtype === $setting[2];
}
/**
* Validate archive:taxonomy:<taxonomy>:term:id:<id>.
*
* @since 4.0
*
* @param string $type Type.
* @param string $subtype Subtype.
* @param integer $id ID.
* @param string[] $setting Setting.
*
* @return bool
*/
function et_theme_builder_template_setting_validate_archive_taxonomy_term_id( $type, $subtype, $id, $setting ) {
$taxonomy = $setting[2];
$object_id = et_theme_builder_template_setting_filter_validation_object_id( (int) $setting[5], 'post', $taxonomy );
if ( ET_Theme_Builder_Request::TYPE_TERM === $type && $subtype === $taxonomy ) {
// Exact match.
if ( $id === $object_id ) {
return true;
}
// Specified setting term id is an ancestor of the request term id ($id).
if ( term_is_ancestor_of( $object_id, $id, $taxonomy ) ) {
return true;
}
}
return false;
}
/**
* Validate archive:user:all.
*
* @since 4.0
*
* @param string $type Type.
* @param string $subtype Subtype.
* @param integer $id ID.
* @param string[] $setting Setting.
*
* @return bool
*/
function et_theme_builder_template_setting_validate_archive_user_all( $type, $subtype, $id, $setting ) {
return ET_Theme_Builder_Request::TYPE_AUTHOR === $type;
}
/**
* Validate archive:user:id:<id>.
*
* @since 4.0
*
* @param string $type Type.
* @param string $subtype Subtype.
* @param integer $id ID.
* @param string[] $setting Setting.
*
* @return bool
*/
function et_theme_builder_template_setting_validate_archive_user_id( $type, $subtype, $id, $setting ) {
return ET_Theme_Builder_Request::TYPE_AUTHOR === $type && $id === (int) $setting[3];
}
/**
* Validate archive:user:role:<role>.
*
* @since 4.0.10
*
* @param string $type Type.
* @param string $subtype Subtype.
* @param integer $id ID.
* @param string[] $setting Setting.
*
* @return bool
*/
function et_theme_builder_template_setting_validate_archive_user_role( $type, $subtype, $id, $setting ) {
$user = get_userdata( $id );
if ( ! $user ) {
return false;
}
if ( 'administrator' === $setting[3] && is_super_admin( $user->ID ) ) {
// Superadmins may:
// - have a low-level role assigned in the current site
// - not be added to the site at all
// in either case they are treated as administrators so we have to handle this edge case.
return true;
}
return ET_Theme_Builder_Request::TYPE_AUTHOR === $type && in_array( $setting[3], $user->roles, true );
}
/**
* Validate archive:date:all.
*
* @since 4.0
*
* @param string $type Type.
* @param string $subtype Subtype.
* @param integer $id ID.
* @param string[] $setting Setting.
*
* @return bool
*/
function et_theme_builder_template_setting_validate_archive_date_all( $type, $subtype, $id, $setting ) {
return ET_Theme_Builder_Request::TYPE_DATE === $type;
}
/**
* Validate search.
*
* @since 4.0
*
* @param string $type Type.
* @param string $subtype Subtype.
* @param integer $id ID.
* @param string[] $setting Setting.
*
* @return bool
*/
function et_theme_builder_template_setting_validate_search( $type, $subtype, $id, $setting ) {
return ET_Theme_Builder_Request::TYPE_SEARCH === $type;
}
/**
* Validate 404.
*
* @since 4.0
*
* @param string $type Type.
* @param string $subtype Subtype.
* @param integer $id ID.
* @param string[] $setting Setting.
*
* @return bool
*/
function et_theme_builder_template_setting_validate_404( $type, $subtype, $id, $setting ) {
return ET_Theme_Builder_Request::TYPE_404 === $type;
}

View File

@@ -0,0 +1,453 @@
<?php
/**
* Local Library API.
*
* @since 4.21.0
*
* @package Divi
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* ET_Theme_Builder_Library_Local utility class.
*
* Item can be a layout, a template, a theme option, a code snippet, etc.
*
* @since 4.21.0
*
* @return void
*/
class ET_Theme_Builder_Library_Local extends ET_Item_Library_Local {
/**
* Gets the class instance.
*
* @since 4.21.0
*
* @return ET_Item_Library_Local
*/
public static function instance() {
if ( ! self::$_instance ) {
self::$_instance = new self();
}
return self::$_instance;
}
/**
* Constructor.
*/
public function __construct() {
$this->post_type = ET_TB_ITEM_POST_TYPE;
$this->exceptional_processes = array(
'delete',
'delete_permanently',
'duplicate',
'duplicate_and_delete',
);
}
/**
* Gets the library items.
*
* @param string $item_type Item type.
* @return array
*/
public function get_library_items( $item_type ) {
$_ = ET_Core_Data_Utils::instance();
$tb_items = ET_Builder_Post_Type_TBItem::instance();
$tb_item_tags = ET_Builder_Post_Taxonomy_LayoutTag::instance();
$tb_item_categories = ET_Builder_Post_Taxonomy_LayoutCategory::instance();
$tb_item_types = ET_Builder_Post_Taxonomy_TBItemType::instance();
$item_categories = array();
$item_tags = array();
$item_types = array();
$items = array();
$index = 0;
$set_template_flag = '1';
$query_posts = $tb_items
->query()
->not()->with_meta( '_et_set_template', $set_template_flag )
->run(
array(
'post_status' => array( 'publish', 'trash' ),
'orderby' => 'name',
'fields' => 'ids',
)
);
$post_ids = $_->array_sort_by( is_array( $query_posts ) ? $query_posts : array( $query_posts ), 'post_name' );
foreach ( $post_ids as $post_id ) {
$item = new stdClass();
$post = get_post( $post_id );
$item->id = $post->ID;
$item->index = $index;
$item->date = $post->post_date;
$types = wp_get_post_terms( $item->id, $tb_item_types->name );
if ( ! $types ) {
continue;
}
$item->type = $types[0]->slug;
if ( $item_type !== $item->type ) {
continue;
}
$title = html_entity_decode( $post->post_title );
// check if current user can edit library item.
$can_edit_post = current_user_can( 'edit_post', $item->id );
if ( $title ) {
// Remove periods since we use dot notation to retrieve translation.
str_replace( '.', '', $title );
$item->name = et_core_intentionally_unescaped( $title, 'react_jsx' );
}
$item->slug = $post->post_name;
$item->url = esc_url( wp_make_link_relative( get_permalink( $post ) ) );
$item->short_name = '';
$item->is_default = get_post_meta( $item->id, '_et_default', true );
$item->description = et_theme_builder_library_get_item_description( $item->id, $item->is_default );
$item->is_favorite = $tb_items->is_favorite( $item->id );
$item->isTrash = 'trash' === $post->post_status; // phpcs:ignore ET.Sniffs.ValidVariableName.UsedPropertyNotSnakeCase -- This is valid format for the property in the Cloud App.
$item->isReadOnly = ! $can_edit_post; // phpcs:ignore ET.Sniffs.ValidVariableName.UsedPropertyNotSnakeCase -- This is valid format for the property in the Cloud App.
$item->categories = array();
$item->category_ids = array();
$item->tags = array();
$item->tag_ids = array();
$this->process_item_taxonomy(
$post,
$item,
$index,
$item_categories,
$tb_item_categories->name,
'category'
);
$this->process_item_taxonomy(
$post,
$item,
$index,
$item_tags,
$tb_item_tags->name,
'tag'
);
$item->item_items = et_theme_builder_library_get_item_items( $item_type, $post->ID );
$items[] = $item;
$index++;
}
return array(
'categories' => $this->get_processed_terms( $tb_item_categories->name ),
'tags' => $this->get_processed_terms( $tb_item_tags->name ),
'items' => $items,
);
}
/**
* Performs item exceptional updates.
*
* @param array $payload Payload.
* @param array $updated_data Updated data.
*
* @since 4.21.0
*
* @return array
*/
private function _perform_item_exceptional_updates( $payload, $updated_data ) {
if ( empty( $payload['item_id'] ) || empty( $payload['update_details'] ) ) {
return false;
}
$update_details = $payload['update_details'];
if ( empty( $update_details['updateType'] ) ) {
return false;
}
$item_id = absint( $payload['item_id'] );
$update_type = sanitize_text_field( $update_details['updateType'] );
$title = isset( $update_details['itemName'] ) ? sanitize_text_field( $update_details['itemName'] ) : '';
$favorite_status = 'on' === sanitize_text_field( $update_details['favoriteStatus'] ) ? 'favorite' : '';
$item_type = $update_details['itemType'];
$type_template = ET_THEME_BUILDER_ITEM_TEMPLATE === $item_type;
$type_set = ET_THEME_BUILDER_ITEM_SET === $item_type;
$_ = ET_Core_Data_Utils::instance();
$tb_item_types = ET_Builder_Post_Taxonomy_TBItemType::instance();
$et_builder_categories = ET_Builder_Post_Taxonomy_LayoutCategory::instance();
$et_builder_tags = ET_Builder_Post_Taxonomy_LayoutTag::instance();
switch ( $update_type ) {
case 'delete':
if ( $type_set ) {
$templates = get_post_meta( $item_id, '_et_template_id', false );
foreach ( $templates as $template ) {
$template_id = is_string( $template ) ? absint( $template ) : 0;
if ( current_user_can( 'delete_post', $template_id ) && get_post_type( $template_id ) === $this->post_type ) {
wp_trash_post( $template_id );
}
}
}
if ( current_user_can( 'delete_post', $item_id ) && get_post_type( $item_id ) === $this->post_type ) {
wp_trash_post( $item_id );
}
break;
case 'delete_permanently':
if ( $type_set ) {
$templates = get_post_meta( $item_id, '_et_template_id', false );
foreach ( $templates as $template ) {
$template_id = is_string( $template ) ? absint( $template ) : 0;
if ( current_user_can( 'delete_post', $item_id ) && get_post_type( $item_id ) === $this->post_type ) {
wp_delete_post( $item_id, true );
}
}
}
if ( current_user_can( 'delete_post', $item_id ) && get_post_type( $item_id ) === $this->post_type ) {
wp_delete_post( $item_id, true );
}
break;
case 'duplicate':
case 'duplicate_and_delete':
if ( ! current_user_can( 'edit_others_posts' ) ) {
return;
}
$meta_input = array( 'favorite_status' => $favorite_status );
$content_details = $_->array_get( $update_details, 'content', null );
$is_item_from_cloud = isset( $content_details );
$tax_input = array(
$et_builder_categories->name => $updated_data['categories'],
$et_builder_tags->name => $updated_data['tags'],
$tb_item_types->name => $item_type,
);
if ( $is_item_from_cloud ) {
$meta_input['_et_has_default_template'] = (int) filter_var( $content_details['has_default_template'], FILTER_VALIDATE_BOOLEAN );
$meta_input['_et_has_global_layouts'] = (int) filter_var( $content_details['has_global_layouts'], FILTER_VALIDATE_BOOLEAN );
if ( $type_set ) {
$set_id = et_theme_builder_library_insert_post( $title, '', $tax_input, $meta_input );
$meta_input = array(); // Reset.
}
$templates = $_->array_get( $content_details, 'templates', array() );
$portability = et_core_portability_load( 'et_theme_builder' );
// Import global colors.
$layouts = $_->array_get( $content_details, 'layouts', [] );
foreach ( $layouts as $layout ) {
if ( ! empty( $layout['global_colors'] ) ) {
$portability->import_global_colors( $layout['global_colors'] );
}
}
// Import presets.
$presets_json = $_->array_get( $content_details, 'presets', '' );
if ( ! empty( $presets_json ) ) {
$presets = json_decode( stripslashes( $presets_json ), true );
$portability->import_global_presets( $presets );
}
foreach ( $templates as $template ) {
/**
* $layouts_reference: content -> templates -> layouts
* $layouts_detail: content -> layouts
*/
$layouts_reference = $_->array_get( $template, 'layouts', array() );
$layouts_detail = $_->array_get( $content_details, 'layouts', array() );
$full_layout = array();
$meta_input['_et_template_title'] = sanitize_text_field( $_->array_get( $template, 'title', '' ) );
$use_on = array_map( 'sanitize_text_field', $_->array_get( $template, 'use_on', array() ) );
$exclude_from = array_map( 'sanitize_text_field', $_->array_get( $template, 'exclude_from', array() ) );
$meta_input['_et_autogenerated_title'] = (int) filter_var( $template['autogenerated_title'], FILTER_VALIDATE_BOOLEAN );
$meta_input['_et_default'] = (int) filter_var( $template['default'], FILTER_VALIDATE_BOOLEAN );
$meta_input['_et_enabled'] = (int) filter_var( $template['enabled'], FILTER_VALIDATE_BOOLEAN );
if ( isset( $layouts_reference['header'] ) ) {
$layout_id = (int) $layouts_reference['header']['id'];
$is_enabled = $layouts_reference['header']['enabled'];
$meta_input['_et_header_layout_enabled'] = (int) filter_var( $is_enabled, FILTER_VALIDATE_BOOLEAN );
if ( $layout_id ) {
$is_global = $layouts_detail[ $layout_id ]['theme_builder']['is_global'];
$full_layout['header']['post_content'] = wp_unslash( $layouts_detail[ $layout_id ]['data'][ $layout_id ] );
$meta_input['_et_header_layout_global'] = (int) filter_var( $is_global, FILTER_VALIDATE_BOOLEAN );
}
} else {
// if area is empty and is not explicitly disabled save it as enabled.
$meta_input['_et_header_layout_enabled'] = 1;
}
if ( isset( $layouts_reference['body'] ) ) {
$layout_id = (int) $layouts_reference['body']['id'];
$is_enabled = $layouts_reference['body']['enabled'];
$meta_input['_et_body_layout_enabled'] = (int) filter_var( $is_enabled, FILTER_VALIDATE_BOOLEAN );
if ( $layout_id ) {
$is_global = $layouts_detail[ $layout_id ]['theme_builder']['is_global'];
$full_layout['body']['post_content'] = wp_unslash( $layouts_detail[ $layout_id ]['data'][ $layout_id ] );
$meta_input['_et_body_layout_global'] = (int) filter_var( $is_global, FILTER_VALIDATE_BOOLEAN );
}
} else {
// if area is empty and is not explicitly disabled save it as enabled.
$meta_input['_et_body_layout_enabled'] = 1;
}
if ( isset( $layouts_reference['footer'] ) ) {
$layout_id = (int) $layouts_reference['footer']['id'];
$is_enabled = $layouts_reference['footer']['enabled'];
$meta_input['_et_footer_layout_enabled'] = (int) filter_var( $is_enabled, FILTER_VALIDATE_BOOLEAN );
if ( $layout_id ) {
$is_global = $layouts_detail[ $layout_id ]['theme_builder']['is_global'];
$full_layout['footer']['post_content'] = wp_unslash( $layouts_detail[ $layout_id ]['data'][ $layout_id ] );
$meta_input['_et_footer_layout_global'] = (int) filter_var( $is_global, FILTER_VALIDATE_BOOLEAN );
}
} else {
// if area is empty and is not explicitly disabled save it as enabled.
$meta_input['_et_footer_layout_enabled'] = 1;
}
$title = $type_set ? $meta_input['_et_template_title'] : $title;
$content = wp_json_encode( $full_layout );
$new_id = et_theme_builder_library_insert_post( $title, wp_slash( $content ), $tax_input, $meta_input );
foreach ( $use_on as $condition ) {
add_post_meta( $new_id, '_et_use_on', sanitize_text_field( $condition ) );
}
foreach ( $exclude_from as $condition ) {
add_post_meta( $new_id, '_et_exclude_from', sanitize_text_field( $condition ) );
}
if ( $type_set ) {
add_post_meta( $new_id, '_et_set_template', 1 );
add_post_meta( $set_id, '_et_template_id', $new_id );
if ( $meta_input['_et_default'] ) {
add_post_meta( $set_id, '_et_default_template_id', $new_id );
}
}
}
} else {
/**
* For local item duplication.
*/
if ( $type_template ) {
$meta_input = array_merge(
$meta_input,
et_theme_builder_get_template_settings( $item_id, false )
);
$use_on = $meta_input['_et_use_on'];
$exclude_from = $meta_input['_et_exclude_from'];
// Remove from post meta insertion.
unset( $meta_input['_et_use_on'], $meta_input['_et_exclude_from'] );
} else {
$meta_keys = array(
'_et_has_global_layouts',
'_et_has_default_template',
);
foreach ( $meta_keys as $key ) {
$meta_input[ $key ] = get_post_meta( $item_id, $key, true );
}
}
$content = get_the_content( null, false, $item_id );
$new_id = et_theme_builder_library_insert_post( $title, wp_slash( $content ), $tax_input, $meta_input );
if ( $type_template ) {
foreach ( $use_on as $condition ) {
add_post_meta( $new_id, '_et_use_on', sanitize_text_field( $condition ) );
}
foreach ( $exclude_from as $condition ) {
add_post_meta( $new_id, '_et_exclude_from', sanitize_text_field( $condition ) );
}
} else {
$template_ids = get_post_meta( $item_id, '_et_template_id', false );
$template_map = [];
foreach ( $template_ids as $maybe_template_id ) {
$template_id = absint( $maybe_template_id );
$library_item = new ET_Theme_Builder_Local_Library_Item( $template_id );
$duplicated_template_id = $library_item->duplicate_item();
$template_map[ $template_id ] = $duplicated_template_id;
}
$maybe_default_template_id = get_post_meta( $item_id, '_et_default_template_id', true );
$default_template_id = absint( $maybe_default_template_id );
if ( 0 !== $default_template_id ) {
$duplicated_default_template_id = $template_map[ $default_template_id ];
update_post_meta( $new_id, '_et_default_template_id', $duplicated_default_template_id );
}
foreach ( $template_map as $duplicated_template_id ) {
add_post_meta( $new_id, '_et_template_id', $duplicated_template_id );
}
}
}
$updated_data['newItem'] = isset( $set_id ) ? $set_id : $new_id;
break;
}
$updated_data['updateType'] = $update_type;
return $updated_data;
}
/**
* Updates the library item.
*
* @param array $payload Payload.
*
* @return array
*/
public function perform_item_update( $payload ) {
$updated_data = $this->_perform_item_common_updates( $payload );
if ( ! empty( $this->exceptional_processes ) ) {
$updated_data = $this->_perform_item_exceptional_updates( $payload, $updated_data );
}
return $updated_data;
}
}

View File

@@ -0,0 +1,751 @@
<?php
/**
* Retrieve (template and preset) local library functionality.
*
* @package Builder.
*/
/**
* Get Library Item items.
*
* Item refers to any cloud item, preset for example.
* Item Items refers to any cloud item's items. Preset's items are templates.
*
* @since 4.18.0
*
* @param string $item_type Item type.
* @param int $item_id Item Id.
*
* @return array
*/
function et_theme_builder_library_get_item_items( $item_type, $item_id ) {
if ( ! in_array( $item_type, array( 'set', 'template' ), true ) ) {
return;
}
$items = array();
$callback = "et_theme_builder_library_get_{$item_type}_items_data";
if ( is_callable( $callback ) ) {
$items = $callback( $item_id );
}
return $items;
}
/**
* Get theme builder library item `use on` or `exclude from` data.
*
* @since 4.18.0
*
* @param int $post_id Post ID.
* @param string $key Meta key (_et_use_on OR _et_exclude_from).
* @param array $meta_values Meta Values.
*
* @return array
*/
function et_theme_builder_library_get_item_use_or_exclude_data( $post_id, $key, $meta_values = [] ) {
$data = array();
$template_options = array();
if ( 0 < $post_id && empty( $meta_values ) ) {
$meta_values = get_post_meta( $post_id, $key ) ? get_post_meta( $post_id, $key ) : array();
}
// Flatten the template settings options.
foreach ( et_theme_builder_get_template_settings_options() as $options ) {
foreach ( $options['settings'] as $settings ) {
array_push( $template_options, $settings );
}
}
// Prepare `Use On` OR `Exclude From` label.
foreach ( $meta_values as $template_id ) {
foreach ( $template_options as $option ) {
if ( $template_id === $option['id'] ) {
array_push( $data, $option['label'] );
}
}
}
return array_unique( $data );
}
/**
* Processes item taxonomies for inclusion in the theme builder library UI items data.
*
* @since 4.18.0
*
* @param WP_POST $post Unprocessed item.
* @param object $item Currently processing item.
* @param int $index The item's index position.
* @param array[] $item_terms Processed items.
* @param string $taxonomy_name Item name.
* @param string $type Item type.
*
* @return void
*/
function et_theme_builder_library_process_item_taxonomy( $post, $item, $index, &$item_terms, $taxonomy_name, $type ) {
$terms = wp_get_post_terms( $post->ID, $taxonomy_name );
if ( ! $terms ) {
if ( 'category' === $type ) {
$item->category_slug = 'uncategorized';
}
return;
}
foreach ( $terms as $term ) {
$term_name = et_core_intentionally_unescaped( $term->name, 'react_jsx' );
if ( ! isset( $item_terms[ $term->term_id ] ) ) {
$item_terms[ $term->term_id ] = array(
'id' => $term->term_id,
'name' => $term_name,
'slug' => $term->slug,
'items' => array(),
);
}
$item_terms[ $term->term_id ]['items'][] = $index;
if ( 'category' === $type ) {
$item->categories[] = $term_name;
} else {
$item->tags[] = $term_name;
}
$item->{$type . '_ids'}[] = $term->term_id;
if ( ! isset( $item->{$type . '_slug'} ) ) {
$item->{$type . '_slug'} = $term->slug;
}
$id = get_post_meta( $post->ID, "_primary_{$taxonomy_name}", true );
if ( $id ) {
// $id is a string, $term->term_id is an int.
if ( $id === $term->term_id ) {
// This is the primary term (used in the item URL).
$item->{$type . '_slug'} = $term->slug;
}
}
}
}
/**
* Gets the terms list and processes it into desired format.
*
* @since 4.18.0
*
* @param string $term_name Term Name.
*
* @return array $terms_by_id
*/
function et_theme_builder_library_get_processed_terms( $term_name ) {
$terms = get_terms( $term_name, array( 'hide_empty' => false ) );
$terms_by_id = array();
if ( is_wp_error( $terms ) || empty( $terms ) ) {
return array();
}
foreach ( $terms as $term ) {
$term_id = $term->term_id;
$terms_by_id[ $term_id ]['id'] = $term_id;
$terms_by_id[ $term_id ]['name'] = $term->name;
$terms_by_id[ $term_id ]['slug'] = $term->slug;
$terms_by_id[ $term_id ]['count'] = $term->count;
}
return $terms_by_id;
}
/**
* Get item description.
*
* @since 4.18.0
*
* @param integer $post_id Post ID.
* @param boolean $is_default_template Whether it is default template or not.
*
* @return mixed $output
*/
function et_theme_builder_library_get_item_description( $post_id, $is_default_template ) {
if ( $is_default_template ) {
$item_desc = et_core_intentionally_unescaped( ET_Builder_TBItem_Library::__( 'Default Website Template' ), 'react_jsx' );
return '<span class="et-cloud-app-layout-desc-item-global">' . $item_desc . '</span>';
}
$output = '';
$use_on = et_theme_builder_library_get_item_use_or_exclude_data( $post_id, '_et_use_on' );
$exclude_from = et_theme_builder_library_get_item_use_or_exclude_data( $post_id, '_et_exclude_from' );
if ( empty( $use_on ) && empty( $exclude_from ) ) {
return $output;
}
if ( ! empty( $use_on ) ) {
$output .= '
<li class="et-cloud-app-layout-desc__use-on-label">
' . et_core_intentionally_unescaped( ET_Builder_TBItem_Library::__( 'Use On' ), 'react_jsx' ) . '
</li>
<ul class="et-cloud-app-layout-desc__inner_list">
<li>' . join( ',</li><li>', $use_on ) . '</li>
</ul>
';
}
if ( ! empty( $exclude_from ) ) {
$output .= '
<li class="et-cloud-app-layout-desc__exclude-from-label">
' . et_core_intentionally_unescaped( ET_Builder_TBItem_Library::__( 'Exclude From' ), 'react_jsx' ) . '
</li>
<ul class="et-cloud-app-layout-desc__inner_list">
<li>' . join( ',</li><li>', $exclude_from ) . '</li>
</ul>
';
}
return '<ul class="et-cloud-app-layout-desc">' . $output . '</ul>';
}
/**
* Get item description.
*
* @since 4.18.0
*
* @param array $template Payload Template data.
*
* @return string $output
*/
function et_theme_builder_library_get_item_description_from_payload( $template ) {
$_ = et_();
$is_default_template = '1' === $_->array_get( $template, 'default', '0' );
if ( $is_default_template ) {
$item_desc = esc_html( ET_Builder_TBItem_Library::__( 'Default Website Template' ) );
return '<span class="et-cloud-app-layout-desc-item-global">' . $item_desc . '</span>';
}
$output = '';
$use_on = et_theme_builder_library_get_item_use_or_exclude_data( 0, '_et_use_on', $_->array_get( $template, 'use_on', [] ) );
$exclude_from = et_theme_builder_library_get_item_use_or_exclude_data( 0, '_et_exclude_from', $_->array_get( $template, 'exclude_from', [] ) );
if ( empty( $use_on ) && empty( $exclude_from ) ) {
return $output;
}
if ( ! empty( $use_on ) ) {
$output .= '
<li class="et-cloud-app-layout-desc__use-on-label">
' . esc_html( ET_Builder_TBItem_Library::__( 'Use On' ) ) . '
</li>
<ul class="et-cloud-app-layout-desc__inner_list">
<li>' . join( ',</li><li>', $use_on ) . '</li>
</ul>
';
}
if ( ! empty( $exclude_from ) ) {
$output .= '
<li class="et-cloud-app-layout-desc__exclude-from-label">
' . esc_html( ET_Builder_TBItem_Library::__( 'Exclude From' ) ) . '
</li>
<ul class="et-cloud-app-layout-desc__inner_list">
<li>' . join( ',</li><li>', $exclude_from ) . '</li>
</ul>
';
}
return '<ul class="et-cloud-app-layout-desc">' . $output . '</ul>';
}
/**
* Get all terms of an item and merge any newly passed IDs with the list.
*
* @since 4.18.0
*
* @param string $new_terms_list List of new terms.
* @param array $taxonomies Taxonomies.
* @param string $taxonomy_name Taxonomy name.
*
* @return array
*/
function et_theme_builder_library_get_all_item_terms( $new_terms_list, $taxonomies, $taxonomy_name ) {
$new_names_array = explode( ',', $new_terms_list );
foreach ( $new_names_array as $new_name ) {
if ( '' !== $new_name ) {
$new_term = wp_insert_term( $new_name, $taxonomy_name );
if ( ! is_wp_error( $new_term ) ) {
$taxonomies[] = $new_term['term_id'];
} elseif (
! empty( $new_term->error_data ) &&
! empty( $new_term->error_data['term_exists'] )
) {
$taxonomies[] = $new_term->error_data['term_exists'];
}
}
}
return $taxonomies;
}
/**
* Insert the theme builder library item duplication post.
*
* @since 4.18.0
*
* @param string $title Title.
* @param string $content Content.
* @param array $tax_input Taxonomy.
* @param array $meta_input Meta.
*
* @return int ID
*/
function et_theme_builder_library_insert_post( $title, $content, $tax_input = array(), $meta_input = array() ) {
$tb_items = ET_Builder_Post_Type_TBItem::instance();
$item = array(
'post_title' => $title,
'post_content' => $content,
'post_status' => 'publish',
'post_type' => $tb_items->name,
'tax_input' => $tax_input,
'meta_input' => $meta_input,
);
return wp_insert_post( $item );
}
/**
* Get the theme builder library temporary item info.
*
* @since 4.18.0
*
* @param array $template Template.
* @param string $layout_type Layout type.
* @param array $post_content Post content.
* @param array $global_layouts Global layouts.
* @param array $layouts_info Layout info.
* @param bool $is_cloud_item Is cloud item or not.
* @param int $id ID (optional).
*
* @return array
*/
function et_theme_builder_library_get_temp_item_info( $template, $layout_type, $post_content, $global_layouts, $layouts_info, $is_cloud_item, $id = null ) {
if ( $is_cloud_item ) {
$layout_id = (int) $layouts_info[ $layout_type ]['id'];
$is_enabled = (int) filter_var( $layouts_info[ $layout_type ]['enabled'], FILTER_VALIDATE_BOOLEAN );
$is_global = $template[ $layout_type . '_layout_global' ];
$content = $post_content[ $layout_id ]['data'][ $layout_id ];
} else {
$is_enabled = get_post_meta( $id, '_et_' . $layout_type . '_layout_enabled', true );
$is_global = get_post_meta( $id, '_et_' . $layout_type . '_layout_global', true );
$content = $post_content->post_content;
}
if ( $is_global && isset( $global_layouts[ $layout_type ] ) ) {
$layout_id = $global_layouts[ $layout_type ];
} else {
if ( ! current_user_can( 'edit_others_posts' ) ) {
return;
}
$layout_id = wp_insert_post(
array(
'post_content' => $content,
'post_type' => et_theme_builder_get_valid_layout_post_type( $layout_type ),
)
);
}
return array(
'id' => $layout_id,
'enabled' => $is_enabled,
);
}
/**
* Save the theme builder library temporary cloud item.
*
* @since 4.18.0
*
* @param array $template_info Template info.
* @param array $layouts_detail Template layouts.
* @param array $global_layouts Global layouts.
*
* @return array
*/
function et_theme_builder_library_save_temp_cloud_layout_data( $template_info, $layouts_detail, $global_layouts = array() ) {
$template = array();
$layouts_info = et_()->array_get( $template_info, 'layouts', array() );
$layout_types = array( 'header', 'body', 'footer' );
$is_cloud_item = true;
$is_default = et_()->array_get( $template_info, 'default', false );
$is_enabled = et_()->array_get( $template_info, 'enabled', false );
$is_auto_title = et_()->array_get( $template_info, 'autogenerated_title', false );
$template['default'] = (int) filter_var( $is_default, FILTER_VALIDATE_BOOLEAN );
$template['enabled'] = (int) filter_var( $is_enabled, FILTER_VALIDATE_BOOLEAN );
$template['autogenerated_title'] = (int) filter_var( $is_auto_title, FILTER_VALIDATE_BOOLEAN );
foreach ( $layout_types as $layout_type ) {
if ( ! isset( $layouts_info[ $layout_type ] ) ) {
continue;
}
$layout_id = $layouts_info[ $layout_type ]['id'];
if ( empty( $layout_id ) ) {
continue;
}
$is_global = $layouts_detail[ $layout_id ]['theme_builder']['is_global'];
$template[ $layout_type . '_layout_global' ] = (int) filter_var( $is_global, FILTER_VALIDATE_BOOLEAN );
$template['layouts'][ $layout_type ] = et_theme_builder_library_get_temp_item_info(
$template,
$layout_type,
$layouts_detail,
$global_layouts,
$layouts_info,
$is_cloud_item
);
}
return $template;
}
/**
* Save the theme builder library temporary item.
*
* @since 4.18.0
*
* @param integer $id Template ID.
* @param object $content Template content.
* @param array $global_layouts Global Layouts.
*
* @return array
*/
function et_theme_builder_library_save_temp_local_layout_data( $id, $content, $global_layouts = array() ) {
if ( ! current_user_can( 'edit_others_posts' ) ) {
wp_die();
}
$template = array();
$layout_types = array( 'header', 'body', 'footer' );
$is_cloud_item = false;
$template['default'] = get_post_meta( $id, '_et_default', true );
$template['use_on'] = get_post_meta( $id, '_et_use_on' );
$template['exclude_from'] = get_post_meta( $id, '_et_exclude_from' );
foreach ( $layout_types as $layout_type ) {
if ( ! isset( $content->{$layout_type} ) ) {
continue;
}
$template['layouts'][ $layout_type ] = et_theme_builder_library_get_temp_item_info(
$template,
$layout_type,
$content->{$layout_type},
$global_layouts,
$content,
$is_cloud_item,
$id
);
}
return $template;
}
/**
* Remove the theme builder library temporary item.
*
* @since 4.18.0
*
* @param array $data Template Ids.
*
* @return void
*/
function et_theme_builder_library_remove_temp_layout_data( $data ) {
if ( ! current_user_can( 'delete_others_posts' ) ) {
wp_die();
}
if ( isset( $data['layouts']['header'] ) ) {
$post_id = absint( $data['layouts']['header']['id'] );
if ( current_user_can( 'edit_post', $post_id ) && ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE === get_post_type( $post_id ) ) {
wp_delete_post( $post_id, true );
}
}
if ( isset( $data['layouts']['body'] ) ) {
$post_id = absint( $data['layouts']['body']['id'] );
if ( current_user_can( 'edit_post', $post_id ) && ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE === get_post_type( $post_id ) ) {
wp_delete_post( $post_id, true );
}
}
if ( isset( $data['layouts']['footer'] ) ) {
$post_id = absint( $data['layouts']['footer']['id'] );
if ( current_user_can( 'edit_post', $post_id ) && ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE === get_post_type( $post_id ) ) {
wp_delete_post( $post_id, true );
}
}
}
/**
* Get the theme builder library exported content.
*
* @since 4.18.0
*
* @param array $ids Items Id.
*
* @return array
*/
function et_theme_builder_library_get_exported_content( $ids ) {
$context = 'et_theme_builder';
$portability = et_core_portability_load( $context );
$result = array(
'context' => $context,
'templates' => array(),
'layouts' => array(),
);
$result['has_default_template'] = false;
$result['has_global_layouts'] = false;
$global_layouts = array();
foreach ( $ids as $id ) {
$post = get_post( $id );
if ( ! $post ) {
continue;
}
$content = json_decode( $post->post_content );
$is_default = (bool) get_post_meta( $id, '_et_default', true );
$is_enabled = (bool) get_post_meta( $id, '_et_enabled', true );
$is_auto_title = (bool) get_post_meta( $id, '_et_autogenerated_title', true );
$is_global_header = (bool) get_post_meta( $id, '_et_header_layout_global', true );
$is_global_body = (bool) get_post_meta( $id, '_et_body_layout_global', true );
$is_global_footer = (bool) get_post_meta( $id, '_et_footer_layout_global', true );
$result['has_default_template'] = $result['has_default_template'] || $is_default;
$result['has_global_layouts'] = $result['has_global_layouts'] || $is_global_header || $is_global_body || $is_global_footer;
$data = et_theme_builder_library_save_temp_local_layout_data( $id, $content, $global_layouts );
if ( $is_default ) {
$global_layouts['body'] = et_()->array_get( $data, 'layouts.body.id', 0 );
$global_layouts['header'] = et_()->array_get( $data, 'layouts.header.id', 0 );
$global_layouts['footer'] = et_()->array_get( $data, 'layouts.footer.id', 0 );
}
$result['templates'][] = array_merge(
array(
'title' => $post->post_title,
'autogenerated_title' => $is_auto_title,
'enabled' => $is_enabled,
),
$data
);
if ( isset( $content->header ) ) {
$layout_id = $data['layouts']['header']['id'];
$shortcode_object = et_fb_process_shortcode( $content->header->post_content );
$global_colors = $portability->get_theme_builder_library_used_global_colors( $shortcode_object );
$presets_manager = ET_Builder_Global_Presets_Settings::instance();
$global_presets = $presets_manager->get_global_presets();
$layout_data = array( $layout_id => $content->header->post_content );
$result['layouts'][ $layout_id ] = array(
'context' => 'et_builder',
'data' => $layout_data,
'images' => $portability->get_theme_builder_library_images( $layout_data ),
'thumbnails' => $portability->get_theme_builder_library_thumbnail_images( $layout_data ),
'post_type' => et_theme_builder_get_valid_layout_post_type( 'header' ),
'theme_builder' => array(
'is_global' => $is_global_header,
),
'post_meta' => isset( $content->header->post_meta ) ? $content->header->post_meta : array(),
);
if ( ! empty( $global_colors ) ) {
$result['global_colors'][] = $global_colors;
}
if ( ! empty( $global_presets ) ) {
$result['presets'] = $global_presets;
}
}
if ( isset( $content->body ) ) {
$layout_id = $data['layouts']['body']['id'];
$shortcode_object = et_fb_process_shortcode( $content->body->post_content );
$global_colors = $portability->get_theme_builder_library_used_global_colors( $shortcode_object );
$presets_manager = ET_Builder_Global_Presets_Settings::instance();
$global_presets = $presets_manager->get_global_presets();
$layout_data = array( $layout_id => $content->body->post_content );
$result['layouts'][ $layout_id ] = array(
'context' => 'et_builder',
'data' => $layout_data,
'images' => $portability->get_theme_builder_library_images( $layout_data ),
'thumbnails' => $portability->get_theme_builder_library_thumbnail_images( $layout_data ),
'post_type' => et_theme_builder_get_valid_layout_post_type( 'body' ),
'theme_builder' => array(
'is_global' => $is_global_body,
),
'post_meta' => isset( $content->body->post_meta ) ? $content->body->post_meta : array(),
);
if ( ! empty( $global_colors ) ) {
$result['global_colors'][] = $global_colors;
}
if ( ! empty( $global_presets ) ) {
$result['presets'] = $global_presets;
}
}
if ( isset( $content->footer ) ) {
$layout_id = $data['layouts']['footer']['id'];
$shortcode_object = et_fb_process_shortcode( $content->footer->post_content );
$global_colors = $portability->get_theme_builder_library_used_global_colors( $shortcode_object );
$presets_manager = ET_Builder_Global_Presets_Settings::instance();
$global_presets = $presets_manager->get_global_presets();
$layout_data = array( $layout_id => $content->footer->post_content );
$result['layouts'][ $layout_id ] = array(
'context' => 'et_builder',
'data' => $layout_data,
'images' => $portability->get_theme_builder_library_images( $layout_data ),
'thumbnails' => $portability->get_theme_builder_library_thumbnail_images( $layout_data ),
'post_type' => et_theme_builder_get_valid_layout_post_type( 'footer' ),
'theme_builder' => array(
'is_global' => $is_global_footer,
),
'post_meta' => isset( $content->footer->post_meta ) ? $content->footer->post_meta : array(),
);
if ( ! empty( $global_colors ) ) {
$result['global_colors'][] = $global_colors;
}
if ( ! empty( $global_presets ) ) {
$result['presets'] = $global_presets;
}
}
et_theme_builder_library_remove_temp_layout_data( $data );
}
return $result;
}
/**
* Gets Preset Items.
*
* @since 4.18.0
*
* @param int $item_id Item Id.
*
* @return array $items
*/
function et_theme_builder_library_get_set_items_data( $item_id ) {
$items = [];
$maybe_template_ids = get_post_meta( $item_id, '_et_template_id' );
$template_ids = array_map( 'absint', $maybe_template_ids );
foreach ( $template_ids as $template_id ) {
$template_post = get_post( $template_id );
if ( ! $template_post ) {
continue;
}
$item = new stdClass();
$item->id = $template_id;
$item->title = $template_post->post_title;
$items[] = $item;
}
return $items;
}
/**
* Update library taxonomy terms.
*
* @since 4.18.0
*
* @param array $payload Item payload.
* @param string $et_library_taxonomy Taxonomy.
*
* @return array $data
*/
function et_theme_builder_library_update_taxonomy_terms( $payload, $et_library_taxonomy ) {
$et_library_taxonomy = sanitize_text_field( $et_library_taxonomy );
if ( empty( $payload ) || empty( $et_library_taxonomy ) ) {
return false;
}
$new_terms = array();
foreach ( $payload as $single_item ) {
$filter_type = sanitize_text_field( $single_item['filterType'] );
switch ( $single_item['updateType'] ) {
case 'remove':
$term_id = (int) $single_item['id'];
wp_delete_term( $term_id, $et_library_taxonomy );
break;
case 'rename':
$term_id = (int) $single_item['id'];
$new_name = (string) $single_item['newName'];
if ( '' !== $new_name ) {
$updated_term_data = wp_update_term( $term_id, $et_library_taxonomy, array( 'name' => $new_name ) );
if ( ! is_wp_error( $updated_term_data ) ) {
$new_terms[] = array(
'name' => $new_name,
'id' => $updated_term_data['term_id'],
'location' => 'local',
);
}
}
break;
case 'add':
$term_name = (string) $single_item['id'];
$new_term_data = wp_insert_term( $term_name, $et_library_taxonomy );
if ( ! is_wp_error( $new_term_data ) ) {
$new_terms[] = array(
'name' => $term_name,
'id' => $new_term_data['term_id'],
'location' => 'local',
);
}
break;
}
}
return array(
'newFilters' => $new_terms,
'filterType' => $filter_type,
);
}

View File

@@ -0,0 +1,267 @@
<?php
/**
* Get placeholders for WooCommerce module in Theme Builder
*
* @since 4.0.1
* @since 4.0.10 Product placeholders is initialized as TB placeholder product's default props
*
* @return array
*/
function et_theme_builder_wc_placeholders() {
return array(
'title' => esc_html__( 'Product name', 'et_builder' ),
'slug' => 'product-name',
'short_description' => esc_html__( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris bibendum eget dui sed vehicula. Suspendisse potenti. Nam dignissim at elit non lobortis.', 'et_builder' ),
'description' => esc_html__( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris bibendum eget dui sed vehicula. Suspendisse potenti. Nam dignissim at elit non lobortis. Cras sagittis dui diam, a finibus nibh euismod vestibulum. Integer sed blandit felis. Maecenas commodo ante in mi ultricies euismod. Morbi condimentum interdum luctus. Mauris iaculis interdum risus in volutpat. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Praesent cursus odio eget cursus pharetra. Aliquam lacinia lectus a nibh ullamcorper maximus. Quisque at sapien pulvinar, dictum elit a, bibendum massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris non pellentesque urna.', 'et_builder' ),
'status' => 'publish',
'comment_status' => 'open',
);
}
/**
* Force set product's class to ET_Theme_Builder_Woocommerce_Product_Variable_Placeholder in TB's woocommerceComponent
* rendering. This product classname is specifically filled and will returned TB placeholder data
* without retrieving actual value from database
*
* @since 4.0.10
*
* @return string
*/
function et_theme_builder_wc_product_class() {
return 'ET_Theme_Builder_Woocommerce_Product_Variable_Placeholder';
}
/**
* Get review placeholder for WooCommerce module in Theme Builder. This can't be included at
* `et_theme_builder_wc_placeholders()` due to dependability on global $post value and
* `et_theme_builder_wc_placeholders()`'s returned value being cached on static variable
*
* @since 4.0.1
*
* @return object
*/
function et_theme_builder_wc_review_placeholder() {
global $post;
$review = new stdClass();
$review->comment_ID = 1;
$review->comment_author = 'John Doe';
$review->comment_author_email = 'john@doe.com';
$review->comment_date = '2019-10-15 16:13:13';
$review->comment_date_gmt = '2019-10-15 16:13:13';
$review->comment_content = 'Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Cum sociis natoque penatibus et magnis dis parturient montes; nascetur ridiculus mus.';
$review->comment_post_ID = $post->ID;
$review->user_id = null;
return new WP_Comment( $review );
}
/**
* Set global objects needed to manipulate `ETBuilderBackend.currentPage.woocommerceComponents` on
* theme builder into displaying WooCommerce module placeholder (even though TB's CPT is not
* WooCommerce's product CPT)
*
* @since 4.0.1
*
* @param array $conditional_tags evaluate conditional tags when current request is AJAX request
*/
function et_theme_builder_wc_set_global_objects( $conditional_tags = array() ) {
$is_tb = et_()->array_get( $conditional_tags, 'is_tb', false );
$is_use_placeholder = $is_tb || is_et_pb_preview();
// Check if current request is theme builder (direct page / AJAX request)
if ( ! et_builder_tb_enabled() && ! $is_use_placeholder ) {
return;
}
// Global variable that affects WC module rendering
global $product, $post, $tb_original_product, $tb_original_post, $tb_wc_post, $tb_wc_product;
// Making sure correct comment template is loaded on WC tabs' review tab
add_filter( 'comments_template', array( 'ET_Builder_Module_Woocommerce_Tabs', 'comments_template_loader' ), 20 );
// Force display related posts; technically sets all products as related
add_filter( 'woocommerce_product_related_posts_force_display', '__return_true' );
// Make sure review's form is opened
add_filter( 'comments_open', '__return_true' );
// Save original $post for reset later
$tb_original_post = $post;
// Save original $product for reset later
$tb_original_product = $product;
// If modified global existed, use it for efficiency
if ( ! is_null( $tb_wc_post ) && ! is_null( $tb_wc_product ) ) {
$post = $tb_wc_post;
$product = $tb_wc_product;
return;
}
// Get placeholders
$placeholders = et_theme_builder_wc_placeholders();
if ( $is_use_placeholder ) {
$placeholder_src = wc_placeholder_img_src( 'full' );
$placeholder_id = attachment_url_to_postid( $placeholder_src );
if ( absint( $placeholder_id ) > 0 ) {
$placeholders['gallery_image_ids'] = array( $placeholder_id );
}
} else {
$placeholders['gallery_image_ids'] = array();
}
// $post might be null if current request is computed callback (ie. WC gallery)
if ( is_null( $post ) ) {
$post = new stdClass();
}
// Overwrite $post global
$post->post_title = $placeholders['title'];
$post->post_slug = $placeholders['slug'];
$post->post_excerpt = $placeholders['short_description'];
$post->post_content = $placeholders['description'];
$post->post_status = $placeholders['status'];
$post->comment_status = $placeholders['comment_status'];
// Overwrite global $product
$product = new ET_Theme_Builder_Woocommerce_Product_Variable_Placeholder();
// Set current post ID as product's ID. `ET_Theme_Builder_Woocommerce_Product_Variable_Placeholder`
// handles all placeholder related value but product ID need to be manually set to match current
// post's ID. This is especially needed when add-ons is used and accessing get_id() method.
if ( isset( $post->ID ) ) {
$product->set_id( $post->ID );
}
// Save modified global for later use
$tb_wc_post = $post;
$tb_wc_product = $product;
}
/**
* Reset global objects needed to manipulate `ETBuilderBackend.currentPage.woocommerceComponents`
*
* @since 4.0.1
* @since 4.14.5 Add conditional tags parameter to evaluate AJAX request.
*
* @param array $conditional_tags Evaluate conditional tags when current request is AJAX request.
*/
function et_theme_builder_wc_reset_global_objects( $conditional_tags = array() ) {
$is_tb = et_()->array_get( $conditional_tags, 'is_tb', false );
$is_use_placeholder = $is_tb || is_et_pb_preview();
// Check if current request is theme builder (direct page / AJAX request).
if ( ! et_builder_tb_enabled() && ! $is_use_placeholder ) {
return;
}
global $product, $post, $tb_original_product, $tb_original_post;
remove_filter( 'comments_template', array( 'ET_Builder_Module_Woocommerce_Tabs', 'comments_template_loader' ), 20 );
remove_filter( 'woocommerce_product_related_posts_force_display', '__return_true' );
remove_filter( 'comments_open', '__return_true' );
$post = $tb_original_post; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Need override the post with the theme builder post.
$product = $tb_original_product; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Need override the product with the theme builder product.
}
/**
* Modify reviews output on WooCommerce's review and tabs' review module in TB
*
* @since 4.0.1
*
* @param array $comments
*
* @return array
*/
function et_theme_builder_wc_set_review_objects( $comments ) {
// Return early if it isn't theme builder
if ( ! et_builder_tb_enabled() ) {
return $comments;
}
$placeholder = et_theme_builder_wc_review_placeholder();
// Add two placeholder reviews
$comments = array(
$placeholder,
$placeholder,
);
// When comment metadata is modified via `get_comment_metadata` filter, the $comment param
// passed into template functions is int instead of WP_Comment object which triggers
// `get_comment()` which triggers error because there's no real review/comment saved in database
// to fix it, modify cache to short-circuit and prevent full `get_comment()` execution
wp_cache_set( $placeholder->comment_ID, $placeholder, 'comment' );
return $comments;
}
// Modify review output on WooCommerce Tabs module
add_filter( 'comments_array', 'et_theme_builder_wc_set_review_objects' );
// Modify review output on WooCommerce Review module
add_filter( 'the_comments', 'et_theme_builder_wc_set_review_objects' );
/**
* Modify review rating output on WooCommerce review and tabs review module in TB
*
* @since 4.0.1
*
* @param mixed $value
* @param int $object_id
* @param string $meta_key
* @param bool $single
*
* @return mixed
*/
function et_theme_builder_wc_set_review_metadata( $value, $object_id, $meta_key, $single ) {
$is_tb = et_builder_tb_enabled();
// Modify rating metadata
if ( $is_tb && 'rating' === $meta_key ) {
global $product;
return $product->get_average_rating();
}
// Modify verified metadata
if ( $is_tb && 'verified' === $meta_key ) {
return false;
}
return $value;
}
add_filter( 'get_comment_metadata', 'et_theme_builder_wc_set_review_metadata', 10, 4 );
/**
* Filter `get_the_terms()` output for Theme Builder layout usage. `get_the_term()` is used for
* product tags and categories in WC meta module and relies on current post's ID to output product's
* tags and categories. In TB settings, post ID is irrelevant as the current layout can be used in
* various pages. Thus, simply get the first tags and cats then output it for visual preview purpose
*
* @since 4.0.10
*
* @param WP_Term[]|WP_Error $terms Array of attached terms, or WP_Error on failure.
* @param int $post_id Post ID.
* @param string $taxonomy Name of the taxonomy.
*
* @return
*/
function et_theme_builder_wc_terms( $terms, $post_id, $taxonomy ) {
// Only modify product_cat and product_tag taxonomies; This function is only called in TB's
// woocommerceComponent output for current product setting
if ( in_array( $taxonomy, array( 'product_cat', 'product_tag' ) ) && empty( $terms ) ) {
$tags = get_categories( array( 'taxonomy' => $taxonomy ) );
if ( isset( $tags[0] ) ) {
$terms = array( $tags[0] );
}
}
return $terms;
}

View File

@@ -0,0 +1,127 @@
<?php
/**
* Disable language filtering of terms in TB.
*
* @since 4.2
*
* @param string $parent_id
* @param string $child_type
* @param string $child_value
*/
function et_theme_builder_wpml_disable_term_filters( $parent_id, $child_type, $child_value ) {
global $sitepress;
if ( ! $sitepress || 'taxonomy' !== $child_type ) {
return;
}
remove_filter( 'terms_clauses', array( $sitepress, 'terms_clauses' ), 10 );
remove_filter( 'get_terms_args', array( $sitepress, 'get_terms_args_filter' ), 10 );
remove_filter( 'get_term', array( $sitepress, 'get_term_adjust_id' ), 1 );
}
add_action( 'et_theme_builder_before_get_template_setting_child_options', 'et_theme_builder_wpml_disable_term_filters', 10, 3 );
/**
* Enable language filtering of terms in TB.
*
* @since 4.2
*
* @param string $parent_id
* @param string $child_type
* @param string $child_value
*/
function et_theme_builder_wpml_enable_term_filters( $parent_id, $child_type, $child_value ) {
global $sitepress;
if ( ! $sitepress || 'taxonomy' !== $child_type ) {
return;
}
add_filter( 'terms_clauses', array( $sitepress, 'terms_clauses' ), 10, 3 );
add_filter( 'get_terms_args', array( $sitepress, 'get_terms_args_filter' ), 10, 2 );
add_filter( 'get_term', array( $sitepress, 'get_term_adjust_id' ), 1, 1 );
}
add_action( 'et_theme_builder_after_get_template_setting_child_options', 'et_theme_builder_wpml_enable_term_filters', 10, 3 );
/**
* Normalize an object ID to it's base language ID if it is a translation.
*
* @since 4.2
*
* @param integer $id WPML object ID.
* @param string $type Type.
* @param string $subtype Subtype.
*
* @return integer
*/
function et_theme_builder_wpml_normalize_object_id( $id, $type, $subtype ) {
return apply_filters( 'wpml_object_id', $id, $subtype, true );
}
add_filter( 'et_theme_builder_template_setting_filter_validation_id', 'et_theme_builder_wpml_normalize_object_id', 10, 3 );
/**
* Prioritize IDs for the current active language over translated IDs
* when comparing template settings priority.
*
* @since 4.2
*
* @param string $prioritized_setting Prioritized setting.
* @param string $a First translated id.
* @param string $b Second translated id.
* @param ET_Theme_Builder_Request $request
*
* @return string
*/
function et_theme_builder_wpml_prioritize_translated_id( $prioritized_setting, $a, $b, $request ) {
$a_id = '';
$a_id_translated = '';
$b_id = '';
$b_id_translated = '';
$a_matches = array();
$b_matches = array();
// Match singular:post_type:<post_type>:id:<id>
$singular = '/^singular:post_type:([^:]+):id:(\d+)$/i';
// Match singular:post_type:<post_type>:children:id:<id>
$singular_children = '/^singular:post_type:([^:]+):children:id:(\d+)$/i';
// Match singular:taxonomy:<taxonomy>:term:id:<id>
$singular_term = '/^singular:taxonomy:([^:]+):term:id:(\d+)$/i';
// Match archive:taxonomy:<taxonomy>:term:id:<id>
$archive_term = '/^archive:taxonomy:([^:]+):term:id:(\d+)$/i';
if ( preg_match( $singular, $a, $a_matches ) && preg_match( $singular, $b, $b_matches ) ) {
$a_id = (int) $a_matches[2];
$a_id_translated = et_theme_builder_wpml_normalize_object_id( $a_id, 'post', $a_matches[1] );
$b_id = (int) $b_matches[2];
$b_id_translated = et_theme_builder_wpml_normalize_object_id( $b_id, 'post', $b_matches[1] );
} elseif ( preg_match( $singular_children, $a, $a_matches ) && preg_match( $singular_children, $b, $b_matches ) ) {
$a_id = (int) $a_matches[2];
$a_id_translated = et_theme_builder_wpml_normalize_object_id( $a_id, 'post', $a_matches[1] );
$b_id = (int) $b_matches[2];
$b_id_translated = et_theme_builder_wpml_normalize_object_id( $b_id, 'post', $b_matches[1] );
} elseif ( preg_match( $singular_term, $a, $a_matches ) && preg_match( $singular_term, $b, $b_matches ) ) {
$a_id = (int) $a_matches[2];
$a_id_translated = et_theme_builder_wpml_normalize_object_id( $a_id, 'taxonomy', $a_matches[1] );
$b_id = (int) $b_matches[2];
$b_id_translated = et_theme_builder_wpml_normalize_object_id( $b_id, 'taxonomy', $b_matches[1] );
} elseif ( preg_match( $archive_term, $a, $a_matches ) && preg_match( $archive_term, $b, $b_matches ) ) {
$a_id = (int) $a_matches[2];
$a_id_translated = et_theme_builder_wpml_normalize_object_id( $a_id, 'taxonomy', $a_matches[1] );
$b_id = (int) $b_matches[2];
$b_id_translated = et_theme_builder_wpml_normalize_object_id( $b_id, 'taxonomy', $b_matches[1] );
}
if ( $a_id && $a_id_translated && $a_id_translated === $a_id ) {
// $a is an exact match for the current request and not a translated match so we prioritize it.
return $a;
}
if ( $b_id && $b_id_translated && $b_id_translated === $b_id ) {
// $b is an exact match for the current request and not a translated match so we prioritize it.
return $b;
}
// Neither $a nor $b are exact matches so don't prioritize either.
return $prioritized_setting;
}
add_filter( 'et_theme_builder_prioritized_template_setting', 'et_theme_builder_wpml_prioritize_translated_id', 10, 6 );