This repository has been archived on 2022-06-23. You can view files and clone it, but cannot push or open issues or pull requests.
divi/includes/builder/frontend-builder/theme-builder/theme-builder.php

1803 lines
55 KiB
PHP

<?php
if ( ! defined( 'ET_THEME_BUILDER_DIR' ) ) {
define( 'ET_THEME_BUILDER_DIR', ET_BUILDER_DIR . 'frontend-builder/theme-builder/' );
}
if ( ! defined( 'ET_THEME_BUILDER_THEME_BUILDER_POST_TYPE' ) ) {
define( 'ET_THEME_BUILDER_THEME_BUILDER_POST_TYPE', 'et_theme_builder' );
}
if ( ! defined( 'ET_THEME_BUILDER_TEMPLATE_POST_TYPE' ) ) {
define( 'ET_THEME_BUILDER_TEMPLATE_POST_TYPE', 'et_template' );
}
if ( ! defined( 'ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE' ) ) {
define( 'ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE', 'et_header_layout' );
}
if ( ! defined( 'ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE' ) ) {
define( 'ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE', 'et_body_layout' );
}
if ( ! defined( 'ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE' ) ) {
define( 'ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE', 'et_footer_layout' );
}
if ( ! defined( 'ET_THEME_BUILDER_SETTING_SEPARATOR' ) ) {
// Must be a single character.
define( 'ET_THEME_BUILDER_SETTING_SEPARATOR', ':' );
}
if ( ! defined( 'ET_THEME_BUILDER_DYNAMIC_CONTENT_REGEX' ) ) {
define( 'ET_THEME_BUILDER_DYNAMIC_CONTENT_REGEX', '/@ET-DC@(.*?)@/' );
}
require_once ET_THEME_BUILDER_DIR . 'ThemeBuilderApiErrors.php';
require_once ET_THEME_BUILDER_DIR . 'ThemeBuilderRequest.php';
require_once ET_THEME_BUILDER_DIR . 'template-setting-validations.php';
require_once ET_THEME_BUILDER_DIR . 'api.php';
require_once ET_THEME_BUILDER_DIR . 'admin.php';
require_once ET_THEME_BUILDER_DIR . 'frontend.php';
require_once ET_THEME_BUILDER_DIR . 'dynamic-content.php';
// Conditional Includes.
if ( et_is_woocommerce_plugin_active() ) {
require_once ET_THEME_BUILDER_DIR . 'woocommerce.php';
require_once ET_THEME_BUILDER_DIR . 'WoocommerceProductVariationPlaceholder.php';
require_once ET_THEME_BUILDER_DIR . 'WoocommerceProductVariablePlaceholder.php';
require_once ET_THEME_BUILDER_DIR . 'WoocommerceProductVariablePlaceholderDataStoreCPT.php';
}
if ( et_core_is_wpml_plugin_active() ) {
require_once ET_THEME_BUILDER_DIR . 'wpml.php';
}
/**
* Register all relevant Theme Builder entities such as post types.
*
* @since 4.0
*
* @return void
*/
function et_theme_builder_register_entities() {
$publicly_queryable = isset( $_GET['et_fb'] ) && '1' === $_GET['et_fb'] && et_pb_is_allowed( 'use_visual_builder' );
register_post_type(
ET_THEME_BUILDER_THEME_BUILDER_POST_TYPE,
array(
'labels' => array(
'name' => esc_html__( 'Theme Builders', 'et_builder' ),
'singular_name' => esc_html__( 'Theme Builder', 'et_builder' ),
'add_new' => esc_html__( 'Add New', 'et_builder' ),
'add_new_item' => esc_html__( 'Add New Theme Builder', 'et_builder' ),
'edit_item' => esc_html__( 'Edit Theme Builder', 'et_builder' ),
'new_item' => esc_html__( 'New Theme Builder', 'et_builder' ),
'all_items' => esc_html__( 'All Theme Builders', 'et_builder' ),
'view_item' => esc_html__( 'View Theme Builder', 'et_builder' ),
'search_items' => esc_html__( 'Search Theme Builders', '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' => '',
),
'can_export' => false,
'capability_type' => 'post',
'has_archive' => false,
'hierarchical' => false,
'map_meta_cap' => true,
'public' => false,
'publicly_queryable' => false,
'query_var' => false,
'show_ui' => false,
'show_in_rest' => false,
'rewrite' => true,
'supports' => array( 'title', 'author' ),
)
);
register_post_type(
ET_THEME_BUILDER_TEMPLATE_POST_TYPE,
array(
'labels' => array(
'name' => esc_html__( 'Templates', 'et_builder' ),
'singular_name' => esc_html__( 'Template', 'et_builder' ),
'add_new' => esc_html__( 'Add New', 'et_builder' ),
'add_new_item' => esc_html__( 'Add New Template', 'et_builder' ),
'edit_item' => esc_html__( 'Edit Template', 'et_builder' ),
'new_item' => esc_html__( 'New Template', 'et_builder' ),
'all_items' => esc_html__( 'All Templates', 'et_builder' ),
'view_item' => esc_html__( 'View Template', 'et_builder' ),
'search_items' => esc_html__( 'Search Templates', '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' => '',
),
'can_export' => true,
'capability_type' => 'post',
'has_archive' => false,
'hierarchical' => false,
'map_meta_cap' => true,
'public' => false,
'publicly_queryable' => $publicly_queryable,
'query_var' => false,
'show_ui' => false,
'show_in_rest' => false,
'rewrite' => true,
'supports' => array( 'title', 'author' ),
)
);
register_post_type(
ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE,
array(
'labels' => array(
'name' => esc_html__( 'Header Templates', 'et_builder' ),
'singular_name' => esc_html__( 'Header Template', 'et_builder' ),
'add_new' => esc_html__( 'Add New', 'et_builder' ),
'add_new_item' => esc_html__( 'Add New Header Template', 'et_builder' ),
'edit_item' => esc_html__( 'Edit Header Template', 'et_builder' ),
'new_item' => esc_html__( 'New Header Template', 'et_builder' ),
'all_items' => esc_html__( 'All Header Templates', 'et_builder' ),
'view_item' => esc_html__( 'View Header Template', 'et_builder' ),
'search_items' => esc_html__( 'Search Header Templates', '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' => '',
),
'can_export' => true,
'capability_type' => 'post',
'has_archive' => false,
'hierarchical' => false,
'map_meta_cap' => true,
'public' => false,
'publicly_queryable' => $publicly_queryable,
'query_var' => false,
'show_ui' => false,
'show_in_rest' => false,
'rewrite' => true,
'supports' => array( 'title', 'editor', 'author', 'revisions', 'comments' ),
)
);
register_post_type(
ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE,
array(
'labels' => array(
'name' => esc_html__( 'Body Templates', 'et_builder' ),
'singular_name' => esc_html__( 'Body Template', 'et_builder' ),
'add_new' => esc_html__( 'Add New', 'et_builder' ),
'add_new_item' => esc_html__( 'Add New Body Template', 'et_builder' ),
'edit_item' => esc_html__( 'Edit Body Template', 'et_builder' ),
'new_item' => esc_html__( 'New Body Template', 'et_builder' ),
'all_items' => esc_html__( 'All Body Templates', 'et_builder' ),
'view_item' => esc_html__( 'View Body Template', 'et_builder' ),
'search_items' => esc_html__( 'Search Body Templates', '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' => '',
),
'can_export' => true,
'capability_type' => 'post',
'has_archive' => false,
'hierarchical' => false,
'map_meta_cap' => true,
'public' => false,
'publicly_queryable' => $publicly_queryable,
'query_var' => false,
'show_ui' => false,
'show_in_rest' => false,
'rewrite' => true,
'supports' => array( 'title', 'editor', 'author', 'revisions', 'comments' ),
)
);
register_post_type(
ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE,
array(
'labels' => array(
'name' => esc_html__( 'Footer Templates', 'et_builder' ),
'singular_name' => esc_html__( 'Footer Template', 'et_builder' ),
'add_new' => esc_html__( 'Add New', 'et_builder' ),
'add_new_item' => esc_html__( 'Add New Footer Template', 'et_builder' ),
'edit_item' => esc_html__( 'Edit Footer Template', 'et_builder' ),
'new_item' => esc_html__( 'New Footer Template', 'et_builder' ),
'all_items' => esc_html__( 'All Footer Templates', 'et_builder' ),
'view_item' => esc_html__( 'View Footer Template', 'et_builder' ),
'search_items' => esc_html__( 'Search Footer Templates', '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' => '',
),
'can_export' => true,
'capability_type' => 'post',
'has_archive' => false,
'hierarchical' => false,
'map_meta_cap' => true,
'public' => false,
'publicly_queryable' => $publicly_queryable,
'query_var' => false,
'show_ui' => false,
'show_in_rest' => false,
'rewrite' => true,
'supports' => array( 'title', 'editor', 'author', 'revisions', 'comments' ),
)
);
}
add_action( 'init', 'et_theme_builder_register_entities', 11 );
/**
* Get array of post types that can be layouts within templates.
*
* @since 4.0
*
* @return string[]
*/
function et_theme_builder_get_layout_post_types() {
return array(
ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE,
ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE,
ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE,
);
}
/**
* Convert 'header', 'body', 'footer' to the appropriate layout post type name.
*
* @since 4.0
*
* @param string $layout_type
*
* @return string
*/
function et_theme_builder_get_valid_layout_post_type( $layout_type ) {
$map = array(
'header' => ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE,
'body' => ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE,
'footer' => ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE,
);
if ( ! isset( $map[ $layout_type ] ) ) {
return '';
}
return $map[ $layout_type ];
}
/**
* Get whether post type is a Theme Builder layout type.
*
* @since 4.0
*
* @param string $post_type
*
* @return boolean
*/
function et_theme_builder_is_layout_post_type( $post_type ) {
return in_array( $post_type, et_theme_builder_get_layout_post_types(), true );
}
/**
* Get list of post content module slugs.
*
* @since 4.0
*
* @return string[]
*/
function et_theme_builder_get_post_content_modules() {
return array( 'et_pb_post_content', 'et_pb_fullwidth_post_content' );
}
/**
* Filter post types with builder support by default.
*
* @since 4.0
*
* @param $post_types
*
* @return array
*/
function et_theme_builder_filter_builder_default_post_types( $post_types ) {
return array_merge( $post_types, et_theme_builder_get_layout_post_types() );
}
add_filter( 'et_builder_default_post_types', 'et_theme_builder_filter_builder_default_post_types' );
add_filter( 'et_library_builder_post_types', 'et_theme_builder_filter_builder_default_post_types' );
/**
* Filter post types which should be blocklisted from appearing as options when enabling/disabling the builder.
*
* @param $post_types
*
* @return array
*/
function et_theme_builder_filter_builder_post_type_options_blocklist( $post_types ) {
return array_merge(
$post_types,
et_theme_builder_get_layout_post_types(),
array( ET_THEME_BUILDER_TEMPLATE_POST_TYPE )
);
}
add_filter( 'et_builder_post_type_options_blocklist', 'et_theme_builder_filter_builder_default_post_types' );
/**
* Filter builder status for template area posts.
*
* @since 4.0
*
* @param $enabled
* @param $post_id
*
* @return bool
*/
function et_theme_builder_filter_enable_builder_for_post_types( $enabled, $post_id ) {
$post_type = get_post_type( $post_id );
if ( et_theme_builder_is_layout_post_type( $post_type ) ) {
$enabled = true;
}
return $enabled;
}
add_filter( 'et_builder_fb_enabled_for_post', 'et_theme_builder_filter_enable_builder_for_post_types', 10, 2 );
/**
* Get the theme builder post.
*
* @since 4.0
*
* @param boolean $live Get the live version or the draft one.
* @param boolean $create Create the post if it does not exist.
*
* @return integer
*/
function et_theme_builder_get_theme_builder_post_id( $live, $create = true ) {
$status = $live ? 'publish' : 'auto-draft';
$query = new WP_Query(
array(
'post_type' => ET_THEME_BUILDER_THEME_BUILDER_POST_TYPE,
'post_status' => $status,
'posts_per_page' => 1,
'orderby' => 'date',
'order' => 'desc',
'fields' => 'ids',
'no_found_rows' => true,
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
)
);
if ( ! empty( $query->posts ) ) {
return $query->posts[0];
}
if ( ! $create ) {
return 0;
}
$post_id = wp_insert_post(
array(
'post_type' => ET_THEME_BUILDER_THEME_BUILDER_POST_TYPE,
'post_status' => $status,
'post_title' => 'Theme Builder',
)
);
return $post_id;
}
/**
* Get the theme builder post's template IDs.
*
* @since 4.0
*
* @param boolean $live Get the live version or the draft one.
*
* @return integer[]
*/
function et_theme_builder_get_theme_builder_template_ids( $live ) {
$post_id = et_theme_builder_get_theme_builder_post_id( $live, false );
// Try to get the template Ids from the backup.
// that maybe stored during saving templates process.
// @see et_theme_builder_api_save function.
$template_ids = get_option( 'et_tb_templates_backup_' . $post_id, false );
// If there is no backup available, then query the post meta.
if ( false === $template_ids ) {
$template_ids = get_post_meta( $post_id, '_et_template', false );
}
$template_ids = is_array( $template_ids ) ? $template_ids : array();
$template_ids = array_map( 'intval', $template_ids );
return $template_ids;
}
/**
* Get the theme builder post's templates.
*
* @since 4.0
*
* @param boolean $live Get the live version or the draft one.
*
* @return array
*/
function et_theme_builder_get_theme_builder_templates( $live ) {
return array_filter(
array_map(
'et_theme_builder_get_template',
et_theme_builder_get_theme_builder_template_ids( $live )
)
);
}
/**
* Get a template.
* Returns an empty array if the template is not found.
*
* @since 4.0
*
* @param integer $template_id
*
* @return array
*/
function et_theme_builder_get_template( $template_id ) {
$post = get_post( $template_id );
if ( null === $post || ET_THEME_BUILDER_TEMPLATE_POST_TYPE !== $post->post_type ) {
return array();
}
$autogenerated_title = '1' === get_post_meta( $template_id, '_et_autogenerated_title', true );
$header_id = (int) get_post_meta( $post->ID, '_et_header_layout_id', true );
$header_enabled = get_post_meta( $post->ID, '_et_header_layout_enabled', true ) === '1';
$body_id = (int) get_post_meta( $post->ID, '_et_body_layout_id', true );
$body_enabled = get_post_meta( $post->ID, '_et_body_layout_enabled', true ) === '1';
$footer_id = (int) get_post_meta( $post->ID, '_et_footer_layout_id', true );
$footer_enabled = get_post_meta( $post->ID, '_et_footer_layout_enabled', true ) === '1';
$use_on = get_post_meta( $post->ID, '_et_use_on', false );
$exclude_from = get_post_meta( $post->ID, '_et_exclude_from', false );
return array(
'id' => $post->ID,
'default' => get_post_meta( $post->ID, '_et_default', true ) === '1',
'enabled' => get_post_meta( $post->ID, '_et_enabled', true ) === '1',
'title' => $autogenerated_title ? '' : $post->post_title,
'layouts' => array(
'header' => array(
'id' => $header_id,
'enabled' => $header_enabled,
'override' => 0 !== $header_id || false === $header_enabled,
),
'body' => array(
'id' => $body_id,
'enabled' => $body_enabled,
'override' => 0 !== $body_id || false === $body_enabled,
),
'footer' => array(
'id' => $footer_id,
'enabled' => $footer_enabled,
'override' => 0 !== $footer_id || false === $footer_enabled,
),
),
'use_on' => is_array( $use_on ) ? $use_on : array(),
'exclude_from' => is_array( $exclude_from ) ? $exclude_from : array(),
);
}
/**
* Trash the theme builder draft and any unused theme builder templates and layouts.
*
* @since 4.0
*
* @return void
*/
function et_theme_builder_trash_draft_and_unused_posts() {
$mark_meta_key = '_et_theme_builder_marked_as_unused';
$live_id = et_theme_builder_get_theme_builder_post_id( true, false );
$draft_id = et_theme_builder_get_theme_builder_post_id( false, false );
if ( $draft_id > 0 ) {
wp_trash_post( $draft_id );
}
$used_templates = get_post_meta( $live_id, '_et_template', false );
$used_templates = is_array( $used_templates ) ? array_map( 'intval', $used_templates ) : array();
$used_posts = array();
foreach ( $used_templates as $template_id ) {
$used_posts[] = $template_id;
$used_posts[] = (int) get_post_meta( $template_id, '_et_header_layout_id', true );
$used_posts[] = (int) get_post_meta( $template_id, '_et_body_layout_id', true );
$used_posts[] = (int) get_post_meta( $template_id, '_et_footer_layout_id', true );
}
$used_posts = array_filter( $used_posts );
// Unmark all used posts.
foreach ( $used_posts as $post_id ) {
delete_post_meta( $post_id, $mark_meta_key );
}
// Mark unreferenced layouts for trashing.
$posts_to_mark = new WP_Query(
array(
'post_type' => array(
ET_THEME_BUILDER_TEMPLATE_POST_TYPE,
ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE,
ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE,
ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE,
),
'post__not_in' => $used_posts,
'posts_per_page' => -1,
'fields' => 'ids',
'meta_query' => array(
array(
'key' => $mark_meta_key,
'compare' => 'NOT EXISTS',
'value' => 'https://core.trac.wordpress.org/ticket/23268',
),
),
'no_found_rows' => true,
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
)
);
foreach ( $posts_to_mark->posts as $post_id ) {
update_post_meta( $post_id, $mark_meta_key, date( 'Y-m-d H:i:s' ) ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date -- No need to use gmdate.
}
// Trash any posts marked more than 7 days ago.
// We only trash up to 50 posts at a time in order to avoid performance issues.
// Any leftover posts will be cleaned up eventually whenever this is called again.
$posts_to_trash = new WP_Query(
array(
'post_type' => array(
ET_THEME_BUILDER_TEMPLATE_POST_TYPE,
ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE,
ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE,
ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE,
),
'posts_per_page' => 50,
'fields' => 'ids',
'meta_query' => array(
array(
'key' => $mark_meta_key,
'compare' => '<',
'value' => date( 'Y-m-d H:i:s', time() - 60 * 60 * 24 * 7 ), // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date -- No need to use gmdate.
'type' => 'DATE',
),
),
'no_found_rows' => true,
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
)
);
foreach ( $posts_to_trash->posts as $post_id ) {
wp_trash_post( $post_id );
}
}
/**
* Get the template settings options for a given post type.
*
* @since 4.0
*
* @param string $post_type_name
*
* @return array
*/
function et_theme_builder_get_template_settings_options_for_post_type( $post_type_name ) {
$post_type = get_post_type_object( $post_type_name );
if ( null === $post_type ) {
return array();
}
$post_type_plural = ucwords( $post_type->labels->name );
$taxonomies = get_object_taxonomies( $post_type_name, 'objects' );
$group = array(
'label' => et_core_intentionally_unescaped( $post_type_plural, 'react_jsx' ),
'settings' => array(
array(
'id' => implode(
ET_THEME_BUILDER_SETTING_SEPARATOR,
array( 'singular', 'post_type', $post_type_name, 'all' )
),
// Translators: %1$s: Post type plural name.
'label' => et_core_intentionally_unescaped( sprintf( __( 'All %1$s', 'et_builder' ), $post_type_plural ), 'react_jsx' ),
'priority' => 70,
'validate' => 'et_theme_builder_template_setting_validate_singular_post_type_all',
),
),
);
if ( 'page' === $post_type_name ) {
$group['settings'][] = array(
'id' => 'homepage',
'label' => et_core_intentionally_unescaped( __( 'Homepage', 'et_builder' ), 'react_jsx' ),
'priority' => 110,
'validate' => 'et_theme_builder_template_setting_validate_homepage',
);
} elseif ( 'post' === $post_type_name || $post_type->has_archive ) {
$group['settings'][] = array(
'id' => implode(
ET_THEME_BUILDER_SETTING_SEPARATOR,
array( 'archive', 'post_type', $post_type_name )
),
'label' => 'post' === $post_type_name
? et_core_intentionally_unescaped( __( 'Blog', 'et_builder' ), 'react_jsx' )
// Translators: %1$s: Post type plural name.
: et_core_intentionally_unescaped( sprintf( __( '%1$s Archive Page', 'et_builder' ), $post_type_plural ), 'react_jsx' ),
'title' => trim( str_replace( home_url(), '', get_post_type_archive_link( $post_type_name ) ), '/' ),
'priority' => 60,
'validate' => 'et_theme_builder_template_setting_validate_archive_post_type',
);
}
foreach ( $taxonomies as $taxonomy ) {
/**
* Filters whether the given taxonomy should be used to generate the following template settings:
* - Posts with Specific %
*
* @since 4.3.3
*
* @param boolean $show
*/
$show = apply_filters( 'et_theme_builder_template_settings_options_posts_with_specific_term', $taxonomy->show_ui );
if ( ! $show ) {
continue;
}
$taxonomy_plural = ucwords( $taxonomy->labels->name );
$use_short_plural = in_array(
$taxonomy->name,
array(
'project_category',
'project_tag',
'product_cat',
'product_tag',
),
true
);
// Translators: %1$s: Post type plural name; %2$s: Taxonomy plural name.
$label = et_core_intentionally_unescaped(
sprintf(
__( '%1$s with Specific %2$s', 'et_builder' ),
$post_type_plural,
$use_short_plural ? esc_html__( 'Tags', 'et_builder' ) : $taxonomy_plural
),
'react_jsx'
);
if ( in_array( $taxonomy->name, array( 'category', 'project_category', 'product_cat' ), true ) ) {
// Translators: %1$s: Post type plural name; %2$s: Taxonomy plural name.
$label = et_core_intentionally_unescaped(
sprintf(
__( '%1$s in Specific %2$s', 'et_builder' ),
$post_type_plural,
$use_short_plural ? esc_html__( 'Categories', 'et_builder' ) : $taxonomy_plural
),
'react_jsx'
);
}
$group['settings'][] = array(
'id' => implode(
ET_THEME_BUILDER_SETTING_SEPARATOR,
array( 'singular', 'taxonomy', $taxonomy->name, 'term', 'id', '' )
),
// Translators: %1$s: Post type plural name; %2$s: Taxonomy plural name.
'label' => $label,
'priority' => 80,
'validate' => 'et_theme_builder_template_setting_validate_singular_taxonomy_term_id',
'options' => array(
'label' => $taxonomy_plural,
'type' => 'taxonomy',
'value' => $taxonomy->name,
),
);
}
$group['settings'][] = array(
'id' => implode(
ET_THEME_BUILDER_SETTING_SEPARATOR,
array( 'singular', 'post_type', $post_type_name, 'id', '' )
),
// Translators: %1$s: Post type plural name.
'label' => et_core_intentionally_unescaped( sprintf( __( 'Specific %1$s', 'et_builder' ), $post_type_plural ), 'react_jsx' ),
'priority' => 100,
'validate' => 'et_theme_builder_template_setting_validate_singular_post_type_id',
'options' => array(
'label' => $post_type_plural,
'type' => 'post_type',
'value' => $post_type_name,
),
);
if ( is_post_type_hierarchical( $post_type_name ) ) {
$group['settings'][] = array(
'id' => implode(
ET_THEME_BUILDER_SETTING_SEPARATOR,
array( 'singular', 'post_type', $post_type_name, 'children', 'id', '' )
),
// Translators: %1$s: Post type plural name.
'label' => et_core_intentionally_unescaped( sprintf( __( 'Children of Specific %1$s', 'et_builder' ), $post_type_plural ), 'react_jsx' ),
'priority' => 90,
'validate' => 'et_theme_builder_template_setting_validate_singular_post_type_children_id',
'options' => array(
'label' => $post_type_plural,
'type' => 'post_type',
'value' => $post_type_name,
),
);
}
return $group;
}
/**
* Get the template settings options for all archive pages.
*
* @since 4.0
*
* @return array
*/
function et_theme_builder_get_template_settings_options_for_archive_pages() {
$taxonomies = get_taxonomies(
array(
'public' => true,
'show_ui' => true,
'_builtin' => false,
),
'objects'
);
ksort( $taxonomies );
$taxonomies = array_merge(
array(
'category' => get_taxonomy( 'category' ),
'post_tag' => get_taxonomy( 'post_tag' ),
),
$taxonomies
);
$group = array(
'label' => et_core_intentionally_unescaped( __( 'Archive Pages', 'et_builder' ), 'react_jsx' ),
'settings' => array(
array(
'id' => implode(
ET_THEME_BUILDER_SETTING_SEPARATOR,
array( 'archive', 'all' )
),
'label' => et_core_intentionally_unescaped( __( 'All Archive Pages', 'et_builder' ), 'react_jsx' ),
'priority' => 30,
'validate' => 'et_theme_builder_template_setting_validate_archive_all',
),
),
);
foreach ( $taxonomies as $taxonomy ) {
/**
* Filters whether the given taxonomy should be used to generate the following template settings:
* - All % Pages
* - Specific % Pages
*
* @since 4.3.3
*
* @param boolean $show
*/
$show = apply_filters( 'et_theme_builder_template_settings_options_term_pages', $taxonomy->public && $taxonomy->show_ui );
if ( ! $show ) {
continue;
}
$taxonomy_plural = ucwords( $taxonomy->labels->name );
$taxonomy_name = $taxonomy_plural;
if ( 'product_cat' === $taxonomy->name ) {
// WooCommerce registers Product Categories with a singular name of Category instead of Product Category...
$taxonomy_name = __( 'Product Category', 'et_builder' );
} elseif ( false !== strpos( $taxonomy->name, 'cat' ) ) {
// Use singular for Category.
$taxonomy_name = ucwords( $taxonomy->labels->singular_name );
}
$group['settings'][] = array(
'id' => implode(
ET_THEME_BUILDER_SETTING_SEPARATOR,
array( 'archive', 'taxonomy', $taxonomy->name, 'all' )
),
// Translators: %1$s: Taxonomy name.
'label' => et_core_intentionally_unescaped( sprintf( __( 'All %1$s Pages', 'et_builder' ), $taxonomy_name ), 'react_jsx' ),
'priority' => 70,
'validate' => 'et_theme_builder_template_setting_validate_archive_taxonomy_all',
);
$group['settings'][] = array(
'id' => implode(
ET_THEME_BUILDER_SETTING_SEPARATOR,
array( 'archive', 'taxonomy', $taxonomy->name, 'term', 'id', '' )
),
// Translators: %1$s: Taxonomy name.
'label' => et_core_intentionally_unescaped( sprintf( __( 'Specific %1$s Pages', 'et_builder' ), $taxonomy_name ), 'react_jsx' ),
'priority' => 75,
'validate' => 'et_theme_builder_template_setting_validate_archive_taxonomy_term_id',
'options' => array(
'label' => $taxonomy_plural,
'type' => 'taxonomy',
'value' => $taxonomy->name,
),
);
}
$group['settings'][] = array(
'id' => implode(
ET_THEME_BUILDER_SETTING_SEPARATOR,
array( 'archive', 'user', 'all' )
),
'label' => et_core_intentionally_unescaped( __( 'All Author Pages', 'et_builder' ), 'react_jsx' ),
'priority' => 50,
'validate' => 'et_theme_builder_template_setting_validate_archive_user_all',
);
$group['settings'][] = array(
'id' => implode(
ET_THEME_BUILDER_SETTING_SEPARATOR,
array( 'archive', 'user', 'id', '' )
),
'label' => et_core_intentionally_unescaped( __( 'Specific Author Page', 'et_builder' ), 'react_jsx' ),
'priority' => 55,
'validate' => 'et_theme_builder_template_setting_validate_archive_user_id',
'options' => array(
'label' => et_core_intentionally_unescaped( __( 'Users', 'et_builder' ), 'react_jsx' ),
'type' => 'user',
'value' => '',
),
);
$group['settings'][] = array(
'id' => implode(
ET_THEME_BUILDER_SETTING_SEPARATOR,
array( 'archive', 'user', 'role', '' )
),
'label' => et_core_intentionally_unescaped( __( 'Specific Author Page By Role', 'et_builder' ), 'react_jsx' ),
'priority' => 53,
'validate' => 'et_theme_builder_template_setting_validate_archive_user_role',
'options' => array(
'label' => et_core_intentionally_unescaped( __( 'Roles', 'et_builder' ), 'react_jsx' ),
'type' => 'user_role',
'value' => '',
),
);
$group['settings'][] = array(
'id' => implode(
ET_THEME_BUILDER_SETTING_SEPARATOR,
array( 'archive', 'date', 'all' )
),
'label' => et_core_intentionally_unescaped( __( 'All Date Pages', 'et_builder' ), 'react_jsx' ),
'priority' => 40,
'validate' => 'et_theme_builder_template_setting_validate_archive_date_all',
);
$_utils = ET_Core_Data_Utils::instance();
// Order settings alphabetically by label.
$group['settings'] = $_utils->array_sort_by( $group['settings'], 'label' );
return $group;
}
/**
* Get array of template setting options.
* Settings that have children should have a trailing ET_THEME_BUILDER_SETTING_SEPARATOR in their id.
* Settings that have children should have their id be unique even without the trailing ET_THEME_BUILDER_SETTING_SEPARATOR.
*
* @since 4.0
*
* @return array
*/
function et_theme_builder_get_template_settings_options() {
$post_types = get_post_types(
array(
'public' => true,
'_builtin' => false,
)
);
sort( $post_types );
$options = array(
'page' => et_theme_builder_get_template_settings_options_for_post_type( 'page' ),
'post' => et_theme_builder_get_template_settings_options_for_post_type( 'post' ),
'archive' => et_theme_builder_get_template_settings_options_for_archive_pages(),
);
foreach ( $post_types as $post_type_name ) {
$options[ $post_type_name ] = et_theme_builder_get_template_settings_options_for_post_type( $post_type_name );
}
$options['other'] = array(
'label' => et_core_intentionally_unescaped( __( 'Other', 'et_builder' ), 'react_jsx' ),
'settings' => array(
array(
'id' => 'search',
'label' => et_core_intentionally_unescaped( __( 'Search Results', 'et_builder' ), 'react_jsx' ),
'priority' => 1,
'validate' => 'et_theme_builder_template_setting_validate_search',
),
array(
'id' => '404',
'label' => et_core_intentionally_unescaped( __( '404 Page', 'et_builder' ), 'react_jsx' ),
'priority' => 1,
'validate' => 'et_theme_builder_template_setting_validate_404',
),
),
);
/**
* Filters available template settings options.
*
* @since 4.0
*
* @param array
*/
$options = apply_filters( 'et_theme_builder_template_settings_options', $options );
return $options;
}
/**
* Get flat array of template setting options from the current live and draft theme builder posts.
*
* @since 4.0
*
* @return array[]
*/
function et_theme_builder_get_template_settings_options_for_preloading() {
$templates = array_unique(
array_merge(
et_theme_builder_get_theme_builder_template_ids( true ),
et_theme_builder_get_theme_builder_template_ids( false )
)
);
$setting_ids = array();
foreach ( $templates as $template_id ) {
$use_on = get_post_meta( $template_id, '_et_use_on', false );
$exclude_from = get_post_meta( $template_id, '_et_exclude_from', false );
if ( ! is_array( $use_on ) ) {
$use_on = array();
}
if ( ! is_array( $exclude_from ) ) {
$exclude_from = array();
}
$setting_ids = array_merge( $setting_ids, $use_on, $exclude_from );
}
return et_theme_builder_load_template_setting_options( array_unique( $setting_ids ) );
}
/**
* Sanitize an array of use_on/exclude_from conditions stripping out invalid ones.
*
* @since 4.0
*
* @param string[] $setting_ids
*
* @return string[]
*/
function et_theme_builder_load_template_setting_options( $setting_ids ) {
$flat_parent_settings = et_theme_builder_get_flat_template_settings_options();
$groups = array();
foreach ( $setting_ids as $setting_id ) {
$parent_id = explode( ET_THEME_BUILDER_SETTING_SEPARATOR, $setting_id );
$entity_id = implode( '', array_slice( $parent_id, -1 ) );
$parent_id = array_slice( $parent_id, 0, -1 );
$parent_id = implode( ET_THEME_BUILDER_SETTING_SEPARATOR, $parent_id ) . ET_THEME_BUILDER_SETTING_SEPARATOR;
if ( ! isset( $flat_parent_settings[ $parent_id ] ) ) {
// Top-level, invalid or unknown setting.
continue;
}
if ( ! isset( $groups[ $parent_id ] ) ) {
$groups[ $parent_id ] = array(
'parent' => $flat_parent_settings[ $parent_id ],
'settings' => array(),
);
}
$groups[ $parent_id ]['settings'][ $setting_id ] = $entity_id;
}
$settings = array();
foreach ( $groups as $parent_id => $group ) {
$settings = array_merge(
$settings,
et_theme_builder_get_template_setting_child_options( $group['parent'], $group['settings'] )
);
}
return $settings;
}
/**
* Get a flat array of template setting options.
*
* @since 4.0
*
* @return array
*/
function et_theme_builder_get_flat_template_settings_options() {
$settings = et_theme_builder_get_template_settings_options();
$flat = array();
foreach ( $settings as $group ) {
foreach ( $group['settings'] as $setting ) {
$flat[ $setting['id'] ] = $setting;
}
}
return $flat;
}
function et_theme_builder_get_template_setting_child_options( $parent, $include = array(), $search = '', $page = 1, $per_page = 30 ) {
$include = array_map( 'intval', $include );
if ( ! empty( $include ) ) {
$search = '';
$page = 1;
$per_page = -1;
}
$page = $page >= 1 ? $page : 1;
$values = array();
/**
* Fires before loading child options from the database.
*
* @since 4.2
*
* @param string $parent_id
* @param string $child_type
* @param string $child_value
*/
do_action( 'et_theme_builder_before_get_template_setting_child_options', $parent['id'], $parent['options']['type'], $parent['options']['value'] );
switch ( $parent['options']['type'] ) {
case 'post_type':
$posts = get_posts(
array(
'post_type' => $parent['options']['value'],
'post_status' => 'any',
'post__in' => $include,
's' => $search,
'posts_per_page' => $per_page,
'paged' => $page,
)
);
foreach ( $posts as $post ) {
$id = $parent['id'] . $post->ID;
$values[ $id ] = array(
'id' => $id,
'parent' => $parent['id'],
'label' => et_core_intentionally_unescaped( $post->post_title, 'react_jsx' ),
'title' => et_core_intentionally_unescaped( $post->post_name, 'react_jsx' ),
'priority' => $parent['priority'],
'validate' => $parent['validate'],
);
}
break;
case 'taxonomy':
$terms = get_terms(
array(
'taxonomy' => $parent['options']['value'],
'hide_empty' => false,
'include' => $include,
'search' => $search,
'number' => -1 === $per_page ? false : $per_page,
'offset' => -1 !== $per_page ? ( $page - 1 ) * $per_page : 0,
)
);
foreach ( $terms as $term ) {
$id = $parent['id'] . $term->term_id;
$values[ $id ] = array(
'id' => $id,
'parent' => $parent['id'],
'label' => et_core_intentionally_unescaped( $term->name, 'react_jsx' ),
'title' => et_core_intentionally_unescaped( $term->slug, 'react_jsx' ),
'priority' => $parent['priority'],
'validate' => $parent['validate'],
);
}
break;
case 'user':
$users = get_users(
array(
'include' => $include,
'search' => $search,
'number' => $per_page,
'paged' => $page,
)
);
foreach ( $users as $user ) {
$id = $parent['id'] . $user->ID;
$values[ $id ] = array(
'id' => $id,
'parent' => $parent['id'],
'label' => et_core_intentionally_unescaped( $user->display_name, 'react_jsx' ),
'title' => et_core_intentionally_unescaped( $user->user_login, 'react_jsx' ),
'priority' => $parent['priority'],
'validate' => $parent['validate'],
);
}
break;
case 'user_role':
$roles = wp_roles()->get_names();
foreach ( $roles as $role => $label ) {
$id = $parent['id'] . $role;
$values[ $id ] = array(
'id' => $id,
'parent' => $parent['id'],
'label' => et_core_intentionally_unescaped( $label, 'react_jsx' ),
'title' => et_core_intentionally_unescaped( $role, 'react_jsx' ),
'priority' => $parent['priority'],
'validate' => $parent['validate'],
);
}
break;
}
/**
* Fires after loading child options from the database.
*
* @since 4.2
*
* @param string $parent_id
* @param string $child_type
* @param string $child_value
*/
do_action( 'et_theme_builder_after_get_template_setting_child_options', $parent['id'], $parent['options']['type'], $parent['options']['value'] );
return $values;
}
/**
* Get the template and its layouts, if any, for the given request.
*
* @since 4.0
*
* @param ET_Theme_Builder_Request $request Request to check against. Defaults to the current one.
* @param bool $cache Cache the result or not, regardless of whether any layouts should be loaded.
* @param bool $load_from_cache Load the cached result for the given post ID, if available.
*
* @return array Array of layouts or an empty array if no layouts should be loaded.
*/
function et_theme_builder_get_template_layouts( $request = null, $cache = true, $load_from_cache = true ) {
static $store = array();
if ( null === $request ) {
if ( is_embed() ) {
// Ignore TB templates when displaying posts intended for embedding.
return array();
}
if ( is_et_pb_preview() ) {
// Ignore TB templates when previewing.
return array();
}
$request = ET_Theme_Builder_Request::from_current();
}
if ( null === $request || ET_GB_Block_Layout::is_layout_block_preview() ) {
return array();
}
$cache_key = "{$request->get_type()}:{$request->get_subtype()}:{$request->get_id()}";
if ( $load_from_cache && isset( $store[ $cache_key ] ) ) {
return $store[ $cache_key ];
}
$post_type = ET_Theme_Builder_Request::TYPE_SINGULAR === $request->get_type() ? $request->get_subtype() : '';
$layouts = array();
if ( et_theme_builder_is_layout_post_type( $post_type ) ) {
// We are currently editing a layout in the VB.
$layouts = array_replace(
array(
ET_THEME_BUILDER_TEMPLATE_POST_TYPE => 0,
ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE => array(
'id' => 0,
'enabled' => false,
'override' => true,
),
ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE => array(
'id' => 0,
'enabled' => false,
'override' => true,
),
ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE => array(
'id' => 0,
'enabled' => false,
'override' => true,
),
),
array(
$post_type => array(
'id' => $request->get_id(),
'enabled' => true,
'override' => true,
),
)
);
} else {
// We are currently displaying a template in the FE.
$templates = et_theme_builder_get_theme_builder_templates( true );
$settings = et_theme_builder_get_flat_template_settings_options();
$template = $request->get_template( $templates, $settings );
$is_singular = is_singular();
if ( ! empty( $template ) ) {
$is_default = $template['default'];
$override_header = $template['layouts']['header']['override'];
$override_body = $template['layouts']['body']['override'];
$override_footer = $template['layouts']['footer']['override'];
// The Default Website Template has a special case - it should not take over if
// it does not override any areas otherwise it will take over ALL site pages.
if ( ! $is_default || $override_header || $override_body || $override_footer ) {
$layouts = array(
ET_THEME_BUILDER_TEMPLATE_POST_TYPE => $is_singular ? $template['id'] : false,
ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE => $template['layouts']['header'],
ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE => $template['layouts']['body'],
ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE => $template['layouts']['footer'],
);
}
}
}
/**
* Filter template layouts.
*
* @since 4.0
*
* @param array $layouts
*/
$layouts = apply_filters( 'et_theme_builder_template_layouts', $layouts );
// Add AB Subjects array.
foreach ( $layouts as $key => $layout ) {
if ( is_array( $layout ) && $layout['override'] ) {
$layouts[ $key ]['et_pb_ab_subjects'] = et_pb_ab_get_subjects( $layout['id'] );
}
}
if ( $cache ) {
$store[ $cache_key ] = $layouts;
}
return $layouts;
}
/**
* Get whether TB overrides the specified layout for the current request.
*
* @since 4.0.6
*
* @param string $layout Layout post type.
*
* @return boolean
*/
function et_theme_builder_overrides_layout( $layout ) {
$layouts = et_theme_builder_get_template_layouts();
return ! empty( $layouts ) && $layouts[ $layout ]['override'];
}
/**
* Get whether the specified layout will properly render the real post content.
*
* @since 4.0
*
* @param array $layout
*
* @return boolean
*/
function et_theme_builder_layout_has_post_content( $layout ) {
if ( ! $layout['override'] ) {
// The layout does not override the content so post content will render.
return true;
}
if ( $layout['enabled'] ) {
$content = get_post_field( 'post_content', $layout['id'] );
$modules = et_theme_builder_get_post_content_modules();
foreach ( $modules as $module ) {
if ( has_shortcode( $content, $module ) ) {
return true;
}
}
}
return false;
}
/**
* Create or update a Theme Builder template.
*
* @since 4.0
*
* @param integer $theme_builder_id Theme builder ID.
* @param array $template Template.
* @param boolean $allow_default Allow default.
*
* @return (integer|false) Return false on failure.
*/
function et_theme_builder_store_template( $theme_builder_id, $template, $allow_default ) {
$_ = et_();
$raw_post_id = $_->array_get( $template, 'id', 0 );
$post_id = is_numeric( $raw_post_id ) ? (int) $raw_post_id : 0;
$title = sanitize_text_field( $_->array_get( $template, 'title', '' ) );
$default = $allow_default && '1' === $_->array_get( $template, 'default', '1' );
$enabled = '1' === $_->array_get( $template, 'enabled', '1' );
$header_id = (int) $_->array_get( $template, 'layouts.header.id', 0 );
$header_enabled = (bool) $_->array_get( $template, 'layouts.header.enabled', true );
$body_id = (int) $_->array_get( $template, 'layouts.body.id', 0 );
$body_enabled = (bool) $_->array_get( $template, 'layouts.body.enabled', true );
$footer_id = (int) $_->array_get( $template, 'layouts.footer.id', 0 );
$footer_enabled = (bool) $_->array_get( $template, 'layouts.footer.enabled', 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() ) );
$exists = $post_id > 0 && ET_THEME_BUILDER_TEMPLATE_POST_TYPE === get_post_type( $post_id ) && 'publish' === get_post_status( $post_id );
$autogenerated_title = '1' === $_->array_get( $template, 'autogenerated_title', '1' );
if ( ET_THEME_BUILDER_HEADER_LAYOUT_POST_TYPE !== get_post_type( $header_id ) || 'publish' !== get_post_status( $header_id ) ) {
$header_id = 0;
}
if ( ET_THEME_BUILDER_BODY_LAYOUT_POST_TYPE !== get_post_type( $body_id ) || 'publish' !== get_post_status( $body_id ) ) {
$body_id = 0;
}
if ( ET_THEME_BUILDER_FOOTER_LAYOUT_POST_TYPE !== get_post_type( $footer_id ) || 'publish' !== get_post_status( $footer_id ) ) {
$footer_id = 0;
}
if ( $exists ) {
// Preform update only when needed.
if ( get_post_field( 'post_title', $post_id, 'raw' ) !== $title ) {
wp_update_post(
array(
'ID' => $post_id,
'post_title' => $title,
)
);
}
// Update layout title for each template.
et_theme_builder_update_layout_title( $template );
} else {
$post_id = wp_insert_post(
array(
'post_type' => ET_THEME_BUILDER_TEMPLATE_POST_TYPE,
'post_status' => 'publish',
'post_title' => $title,
)
);
}
if ( 0 === $post_id || is_wp_error( $post_id ) ) {
return false;
}
$metas = array(
'_et_autogenerated_title' => $autogenerated_title ? '1' : '0',
'_et_default' => $default ? '1' : '0',
'_et_enabled' => $enabled ? '1' : '0',
'_et_header_layout_id' => $header_id,
'_et_header_layout_enabled' => $header_enabled ? '1' : '0',
'_et_body_layout_id' => $body_id,
'_et_body_layout_enabled' => $body_enabled ? '1' : '0',
'_et_footer_layout_id' => $footer_id,
'_et_footer_layout_enabled' => $footer_enabled ? '1' : '0',
);
foreach ( $metas as $key => $value ) {
if ( strval( $value ) === strval( get_post_meta( $post_id, $key, true ) ) ) {
continue;
}
update_post_meta( $post_id, $key, $value );
}
// Handle _et_use_on meta.
delete_post_meta( $post_id, '_et_use_on' );
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 );
}
}
// Handle _et_exclude_from meta.
delete_post_meta( $post_id, '_et_exclude_from' );
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 );
}
}
return $post_id;
}
/**
* Sanitize a Theme Builder template.
*
* @since 4.0
*
* @param array $template
*
* @return array
*/
function et_theme_builder_sanitize_template( $template ) {
$_ = et_();
$autogenerated_title = $_->array_get( $template, 'autogenerated_title', '0' );
$default = $_->array_get( $template, 'default', '0' );
$enabled = $_->array_get( $template, 'enabled', '0' );
$use_on = $_->array_get( $template, 'use_on', array() );
$exclude_from = $_->array_get( $template, 'exclude_from', array() );
$header_enabled = $_->array_get( $template, 'layouts.header.enabled', '1' );
$body_enabled = $_->array_get( $template, 'layouts.body.enabled', '1' );
$footer_enabled = $_->array_get( $template, 'layouts.footer.enabled', '1' );
$sanitized = array(
'title' => sanitize_text_field( $_->array_get( $template, 'title', '' ) ),
'autogenerated_title' => true === $autogenerated_title || '1' === $autogenerated_title,
'default' => true === $default || '1' === $default,
'enabled' => true === $enabled || '1' === $enabled,
'use_on' => array_map( 'sanitize_text_field', $use_on ),
'exclude_from' => array_map( 'sanitize_text_field', $exclude_from ),
'layouts' => array(
'header' => array(
'id' => (int) $_->array_get( $template, 'layouts.header.id', '0' ),
'enabled' => true === $header_enabled || '1' === $header_enabled,
),
'body' => array(
'id' => (int) $_->array_get( $template, 'layouts.body.id', '0' ),
'enabled' => true === $body_enabled || '1' === $body_enabled,
),
'footer' => array(
'id' => (int) $_->array_get( $template, 'layouts.footer.id', '0' ),
'enabled' => true === $footer_enabled || '1' === $footer_enabled,
),
),
);
return $sanitized;
}
/**
* Insert a Theme Builder layout post.
*
* @since 4.0
*
* @param array $options
*
* @return integer|WP_Error
*/
function et_theme_builder_insert_layout( $options ) {
$post_id = wp_insert_post(
array_merge(
array(
'post_status' => 'publish',
'post_title' => 'Theme Builder Layout',
),
$options
),
true
);
if ( is_wp_error( $post_id ) ) {
return $post_id;
}
wp_set_object_terms( $post_id, 'layout', 'layout_type', true );
et_builder_enable_for_post( $post_id );
return $post_id;
}
/**
* Overrides cache post_type so that TB custom post types and 'page' share the same files.
*
* @since 4.0
*
* @param string $post_type
*
* @return string.
*/
function et_theme_builder_cache_post_type( $post_type ) {
if ( et_theme_builder_is_layout_post_type( $post_type ) ) {
// Use a generic name for all Theme Builder post type modules
// as they are identical for most practical reasons.
$post_type = 'page';
}
return $post_type;
}
add_filter( 'et_builder_cache_post_type', 'et_theme_builder_cache_post_type' );
/**
* Decorate a page resource slug based on the current request and TB.
*
* @since 4.0.7
*
* @param integer|string $post_id
* @param string $resource_slug Resource slug.
*
* @return string
*/
function et_theme_builder_decorate_page_resource_slug( $post_id, $resource_slug ) {
if ( ! is_numeric( $post_id ) || ! is_singular() ) {
return $resource_slug;
}
$post_type = get_post_type( (int) $post_id );
if ( et_theme_builder_is_layout_post_type( $post_type ) ) {
$resource_slug .= '-tb-for-' . ET_Post_Stack::get_main_post_id();
} else {
$layout_types = et_theme_builder_get_layout_post_types();
$layouts = et_theme_builder_get_template_layouts();
foreach ( $layout_types as $type ) {
if ( ! isset( $layouts[ $type ] ) || ! $layouts[ $type ]['override'] ) {
continue;
}
$resource_slug .= '-tb-' . $layouts[ $type ]['id'];
}
}
return $resource_slug;
}
add_filter( 'et_builder_cache_post_type', 'et_theme_builder_cache_post_type' );
/**
* Clear cache of 3P caching plugins partially on the posts or all of them.
*
* @since 4.5.0
*
* @param string|array $post_ids 'all' or array of post IDs.
*
* @return void
*/
function et_theme_builder_clear_wp_cache( $post_ids = 'all' ) {
if ( ! et_pb_detect_cache_plugins() ) {
return;
}
if ( empty( $post_ids ) ) {
return;
}
if ( 'all' === $post_ids ) {
et_core_clear_wp_cache();
} elseif ( is_array( $post_ids ) ) {
foreach ( $post_ids as $post_id ) {
et_core_clear_wp_cache( $post_id );
}
}
}
/**
* Clear cache of 3P caching plugins fully or partially after TB layouts saved.
*
* Clear all the cache when the template updated is:
* - Default template
* - Used on archive, 404, or all posts
* - Non static homepage
*
* @since 4.5.0
*
* @param int $layout_id
*
* @return void
*/
function et_theme_builder_clear_wp_post_cache( $layout_id = '' ) {
$layout_type = get_post_type( $layout_id );
if ( ! et_theme_builder_is_layout_post_type( $layout_type ) ) {
return;
}
if ( ! et_pb_detect_cache_plugins() ) {
return;
}
// Get template of current TB layout.
$template = new WP_Query(
array(
'post_type' => ET_THEME_BUILDER_TEMPLATE_POST_TYPE,
'post_status' => 'publish',
'posts_per_page' => 1,
'fields' => 'ids',
'no_found_rows' => true,
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
'meta_query' => array(
'relation' => 'AND',
array(
'key' => '_et_enabled',
'value' => '1',
'compare' => '=',
),
array(
'key' => "_{$layout_type}_id",
'value' => $layout_id,
'compare' => '=',
),
array(
'key' => "_{$layout_type}_enabled",
'value' => '1',
'compare' => '=',
),
array(
'key' => '_et_theme_builder_marked_as_unused',
'compare' => 'NOT EXISTS',
),
),
)
);
if ( ! $template->have_posts() ) {
return;
}
$_ = et_();
$template_id = $_->array_get( $template->posts, '0' );
$template_use_on = get_post_meta( $template_id, '_et_use_on', false );
$is_template_default = '1' === get_post_meta( $template_id, '_et_default', true );
// Unassigned Template - False or empty _et_use_on means it's unassigned.
if ( empty( $template_use_on ) ) {
// Clear All - If the template is 'default' because it's enabled globally.
if ( $is_template_default ) {
et_theme_builder_clear_wp_cache( 'all' );
}
return;
}
$target_post_ids = array();
foreach ( $template_use_on as $location ) {
$location_pieces = explode( ':', $location );
$location_first = $_->array_get( $location_pieces, '0' );
$location_last = end( $location_pieces );
if ( in_array( $location_first, array( 'archive', '404' ) ) || 'all' === $location_last ) {
// Path: archive:user:id:{user_id}, singular:post_type:{post_type_slug}:all,
// archive:taxonomy:{taxonomy_name}:all, etc.
// Clear All - If the template is being used on 'archive:' or ':all' posts.
$target_post_ids = 'all';
break;
} elseif ( 'homepage' === $location_first ) {
// Path: homepage
$homepage_id = (int) get_option( 'page_on_front' );
$target_post_ids[] = $homepage_id;
if ( ! $homepage_id ) {
// Clear All - If the homepage is non static page.
$target_post_ids = 'all';
break;
}
} elseif ( 'singular' === $location_first ) {
$singular_type = $_->array_get( $location_pieces, '3' );
if ( 'id' === $singular_type ) {
// Path: singular:post_type:{post_type_slug}:id:{post_id}
$target_post_ids[] = (int) $_->array_get( $location_pieces, '4' );
} elseif ( 'children' === $singular_type ) {
// Path: singular:post_type:{post_type_slug}:children:id:{post_id}
$parent_id = (int) $_->array_get( $location_pieces, '5' );
$children_ids = get_children(
array(
'posts_per_page' => -1,
'post_parent' => $parent_id,
'fields' => 'ids',
)
);
$target_post_ids = array_merge( $target_post_ids, $children_ids );
} elseif ( 'term' === $singular_type ) {
// Path: singular:taxonomy:{taxonomy_name}:term:id:{term_id}
$taxonomy = $_->array_get( $location_pieces, '2' );
$taxonomy_object = get_taxonomy( $taxonomy );
$taxonomy_type = ! empty( $taxonomy_object->object_type ) ? $_->array_get( $taxonomy_object->object_type, '0' ) : 'post';
$term_id = (int) $_->array_get( $location_pieces, '5' );
$posts_ids = get_posts(
array(
'posts_per_page' => -1,
'fields' => 'ids',
'post_type' => $taxonomy_type,
'tax_query' => array(
array(
'taxonomy' => $taxonomy,
'field' => 'term_id',
'terms' => $term_id,
),
),
)
);
$target_post_ids = array_merge( $target_post_ids, $posts_ids );
}
} elseif ( 'woocommerce' === $location_first && et_is_woocommerce_plugin_active() && function_exists( 'wc_get_page_id' ) ) {
// Path: woocommerce:my_account, woocommerce:cart, etc.
$woocommerce_page = str_replace( '_', '', $_->array_get( $location_pieces, '1' ) );
$woocommerce_page_id = wc_get_page_id( $woocommerce_page );
if ( $woocommerce_page_id ) {
$target_post_ids[] = $woocommerce_page_id;
}
}
}
// Remove duplicate posts.
if ( is_array( $target_post_ids ) ) {
$target_post_ids = array_unique( $target_post_ids );
}
et_theme_builder_clear_wp_cache( $target_post_ids );
}
add_action( 'et_save_post', 'et_theme_builder_clear_wp_post_cache' );
/**
*
* Update layout title for each template
*
* @param array $template Theme Builder Template.
*/
function et_theme_builder_update_layout_title( $template ) {
if ( empty( $template['layouts'] ) ) {
return;
}
foreach ( $template['layouts'] as $layout ) {
$layout_id = (int) $layout['id'];
if ( ! $layout_id ) {
continue;
}
wp_update_post(
array(
'ID' => $layout_id,
'post_title' => sanitize_text_field( $template['title'] ),
)
);
}
}