<?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 Layouts', 'et_builder' ),
			'singular_name'      => esc_html__( 'Header Layout', 'et_builder' ),
			'add_new'            => esc_html__( 'Add New', 'et_builder' ),
			'add_new_item'       => esc_html__( 'Add New Header Layout', 'et_builder' ),
			'edit_item'          => esc_html__( 'Edit Header Layout', 'et_builder' ),
			'new_item'           => esc_html__( 'New Header Layout', 'et_builder' ),
			'all_items'          => esc_html__( 'All Header Layouts', 'et_builder' ),
			'view_item'          => esc_html__( 'View Header Layout', 'et_builder' ),
			'search_items'       => esc_html__( 'Search Header Layouts', '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 Layouts', 'et_builder' ),
			'singular_name'      => esc_html__( 'Body Layout', 'et_builder' ),
			'add_new'            => esc_html__( 'Add New', 'et_builder' ),
			'add_new_item'       => esc_html__( 'Add New Body Layout', 'et_builder' ),
			'edit_item'          => esc_html__( 'Edit Body Layout', 'et_builder' ),
			'new_item'           => esc_html__( 'New Body Layout', 'et_builder' ),
			'all_items'          => esc_html__( 'All Body Layouts', 'et_builder' ),
			'view_item'          => esc_html__( 'View Body Layout', 'et_builder' ),
			'search_items'       => esc_html__( 'Search Body Layouts', '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 Layouts', 'et_builder' ),
			'singular_name'      => esc_html__( 'Footer Layout', 'et_builder' ),
			'add_new'            => esc_html__( 'Add New', 'et_builder' ),
			'add_new_item'       => esc_html__( 'Add New Footer Layout', 'et_builder' ),
			'edit_item'          => esc_html__( 'Edit Footer Layout', 'et_builder' ),
			'new_item'           => esc_html__( 'New Footer Layout', 'et_builder' ),
			'all_items'          => esc_html__( 'All Footer Layouts', 'et_builder' ),
			'view_item'          => esc_html__( 'View Footer Layout', 'et_builder' ),
			'search_items'       => esc_html__( 'Search Footer Layouts', '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 blacklisted from appearing as options when enabling/disabling the builder.
 *
 * @param $post_types
 *
 * @return array
 */
function et_theme_builder_filter_builder_post_type_options_blacklist( $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_blacklist', '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 );
	$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') );
	}

	// 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),
				'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',
		);
	} else if ( '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' );
		} else if ( 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 );

		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      => $template['id'],
					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 );

	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;
}

/**
 * Get the failure modal html for a template that overrides the body but does not display
 * post content (e.g. Post Content module is missing or the body has been disabled).
 *
 * @param string $template_name
 * @param boolean $layout_enabled
 *
 * @return string
 */
function et_theme_builder_get_failure_notification_modal( $template_name, $layout_enabled ) {
	$i18n        = require ET_BUILDER_DIR . 'frontend-builder/i18n/theme-builder.php';
	$error       = $i18n['This post has been assigned a template using the Theme Builder, however, the template being used does not contain a Post Content module.'];
	$description = $i18n['A Post Content module is required in order to add unique content within the Theme Builder template.'];

	if ( ! $layout_enabled ) {
		$error       = $i18n['Oops, it looks like the current Theme Builder Template body layout is disabled.'];
		$description = $i18n['The body layout of a template should not be disabled if you wish to display and edit the content of individual posts using this layout.'];
	}

	$output = sprintf(
		'<div class="et-core-modal-overlay et-theme-builder-no-post-content et-core-active">
		<div class="et-core-modal">
			<div class="et-core-modal-header">
				<h3 class="et-core-modal-title">%1$s</h3>
				<a href="#" class="et-core-modal-close" data-et-core-modal="close"></a>
			</div>

			<div class="et-core-modal-content">
				<p>%2$s</p>
				<p>%3$s</p>
				<p>%4$s</p>
			</div>

			<div class="et_pb_prompt_buttons">
				<br>
				<span class="spinner"></span>
				<a href="%6$s" class="et-core-modal-action">%5$s</a>
			</div>
		</div>
	</div>',
		esc_html__( 'Missing Post Content Module', 'et_builder' ),
		esc_html__( $error, 'et_builder' ),
		esc_html__( $description, 'et_builder' ),
		esc_html( sprintf( __( 'Current template: %1$s', 'et_builder' ), $template_name ) ),
		esc_html__( 'Edit Theme Builder', 'et_builder' ),
		esc_url( admin_url( 'admin.php?page=et_theme_builder' ) )
	);

	return $output;
}

/**
 * Create or update a Theme Builder template.
 *
 * @since 4.0
 *
 * @param integer $theme_builder_id
 * @param array $template
 * @param boolean $allow_default
 *
 * @return integer
 */
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         = 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 ) {
		wp_update_post( array(
			'ID'         => $post_id,
			'post_title' => $title,
		) );
	} else {
		$post_id = wp_insert_post( array(
			'post_type'   => ET_THEME_BUILDER_TEMPLATE_POST_TYPE,
			'post_status' => 'publish',
			'post_title'  => $title,
		) );
	}

	update_post_meta( $post_id, '_et_autogenerated_title', $autogenerated_title ? '1' : '0' );
	update_post_meta( $post_id, '_et_default', $default ? '1' : '0' );
	update_post_meta( $post_id, '_et_enabled', $enabled ? '1' : '0' );
	update_post_meta( $post_id, '_et_header_layout_id', $header_id );
	update_post_meta( $post_id, '_et_header_layout_enabled', $header_enabled ? '1' : '0' );
	update_post_meta( $post_id, '_et_body_layout_id', $body_id );
	update_post_meta( $post_id, '_et_body_layout_enabled', $body_enabled ? '1' : '0' );
	update_post_meta( $post_id, '_et_footer_layout_id', $footer_id );
	update_post_meta( $post_id, '_et_footer_layout_enabled', $footer_enabled ? '1' : '0' );

	delete_post_meta( $post_id, '_et_use_on' );
	foreach ( $use_on as $condition ) {
		add_post_meta( $post_id, '_et_use_on', $condition );
	}

	delete_post_meta( $post_id, '_et_exclude_from' );
	foreach ( $exclude_from as $condition ) {
		add_post_meta( $post_id, '_et_exclude_from', $condition );
	}

	$templates = get_post_meta( $theme_builder_id, '_et_template', false );
	if ( ! in_array( $post_id, $templates, true ) ) {
		add_post_meta( $theme_builder_id, '_et_template', $post_id );
	}

	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
 *
 * @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' );
