: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* This file defines Builder Layouts and Layout Parts
* Themify_Builder_Layouts class register post type for Layouts and Layout Parts
* Custom metabox, shortcode, and load layout / layout part.
* @package Themify_Builder
* @subpackage Themify_Builder/classes
if (!class_exists('Themify_Builder_Layouts',false)) {
* The Builder Layouts class.
* This class register post type for Layouts and Layout Parts
* Custom metabox, shortcode, and load layout / layout part.
* @package Themify_Builder
* @subpackage Themify_Builder/classes
class Themify_Builder_Layouts {
* Post Type Layout Object.
const LAYOUT_SLUG = 'tbuilder_layout';
* Post Type Layout Part Object.
* @var string $layout_part_slug .
const LAYOUT_PART_SLUG = 'tbuilder_layout_part';
* Store registered layout / part post types.
* @var array $post_types .
private static $post_types = array();
* Holds a list of layout provider instances
private static $provider_instances = array();
public static function init() {
add_filter('themify_post_types', array(__CLASS__, 'extend_post_types'));
add_filter('themify_builder_post_types_support', array(__CLASS__, 'add_builder_support'));
add_action('add_meta_boxes_tbuilder_layout_part', array(__CLASS__, 'custom_meta_boxes'));
add_action('add_meta_boxes_tbuilder_layout', array(__CLASS__, 'custom_meta_boxes'));
// add_action('wp_ajax_set_layout_action', array(__CLASS__, 'set_layout_action'), 10);
add_action('wp_ajax_tb_save_custom_layout', array(__CLASS__, 'save_custom_layout_ajaxify'), 10);
add_action('wp_ajax_tb_get_save_custom_layout', array(__CLASS__, 'get_custom_layout_ajaxify'), 10);
add_filter('post_row_actions', array(__CLASS__, 'row_actions'));
add_filter('page_row_actions', array(__CLASS__, 'row_actions'));
add_filter('bulk_actions-edit-tbuilder_layout_part', array(__CLASS__, 'row_bulk_actions'));
add_filter('bulk_actions-edit-tbuilder_layout', array(__CLASS__, 'row_bulk_actions'));
add_filter('handle_bulk_actions-edit-tbuilder_layout_part', array(__CLASS__, 'export_row_bulk'), 10, 3);
add_filter('handle_bulk_actions-edit-tbuilder_layout', array(__CLASS__, 'export_row_bulk'), 10, 3);
add_action('admin_init', array(__CLASS__, 'duplicate_action'));
add_action('admin_init', array(__CLASS__, 'export_row'));
// Ajax hook for Layout and Layout Parts import file.
add_action('wp_ajax_tb_bulk_import', array(__CLASS__, 'row_bulk_import'));
add_action('admin_enqueue_scripts', array(__CLASS__, 'admin_enqueue'));
add_filter('template_include', array(__CLASS__, 'template_singular_layout'));
add_shortcode('themify_layout_part', array(__CLASS__, 'layout_part_shortcode'));
* Registers providers for layouts in Builder
private static function register_providers() {
$providers = apply_filters('themify_builder_layout_providers', array(
'Themify_Builder_Layouts_Provider_Custom'
foreach ($providers as $provider) {
if (class_exists($provider,false)) {
$instance = new $provider();
self::$provider_instances[$instance->get_id()] = $instance;
* Get a single layout provider instance
public static function get_provider($id) {
return isset(self::$provider_instances[$id]) ? self::$provider_instances[$id] : false;
private static function register_layout_post_type() {
'post_type_name' => self::LAYOUT_SLUG,
'singular' => __('Layout', 'themify'),
'plural' => __('Layouts', 'themify')
'supports' => array('title', 'thumbnail'),
'exclude_from_search' => true,
'show_in_nav_menus' => false,
private static function register_layout_part_post_type() {
'post_type_name' => self::LAYOUT_PART_SLUG,
'singular' => __('Layout Part', 'themify'),
'plural' => __('Layout Parts', 'themify'),
'slug' => 'tbuilder-layout-part'
'supports' => array('title', 'thumbnail'),
'exclude_from_search' => true,
'show_in_nav_menus' => false,
'show_in_admin_bar' => true,
* Register Layout and Layout Part Custom Post Type
private static function register_layout() {
if (!class_exists('CPT',false)) {
include THEMIFY_DIR . '/CPT.php';
// create a template custom post type
$layout = self::register_layout_post_type();
// define the columns to appear on the admin edit screen
'cb' => '<input type="checkbox" />',
'title' => __('Title', 'themify'),
'thumbnail' => __('Thumbnail', 'themify'),
'author' => __('Author', 'themify'),
'date' => __('Date', 'themify')
// populate the thumbnail column
$layout->populate_column('thumbnail', array(__CLASS__, 'populate_column_layout_thumbnail'));
// use "pages" icon for post type
$layout->menu_icon('dashicons-admin-page');
// create a template custom post type
$layout_part = self::register_layout_part_post_type();
// define the columns to appear on the admin edit screen
$layout_part->columns(array(
'cb' => '<input type="checkbox" />',
'title' => __('Title', 'themify'),
'shortcode' => __('Shortcode', 'themify'),
'author' => __('Author', 'themify'),
'date' => __('Date', 'themify')
// populate the thumbnail column
$layout_part->populate_column('shortcode', array(__CLASS__, 'populate_column_layout_part_shortcode'));
// use "pages" icon for post type
$layout_part->menu_icon('dashicons-screenoptions');
self::set_post_type_var($layout->post_type_name);
self::set_post_type_var($layout_part->post_type_name);
add_post_type_support($layout->post_type_name, 'revisions');
add_post_type_support($layout_part->post_type_name, 'revisions');
self::register_providers();
* Set the post type variable.
public static function set_post_type_var(string $name) {
self::$post_types[] = $name;
* Custom column thumbnail.
public static function populate_column_layout_thumbnail($column, $post) {
echo get_the_post_thumbnail($post->ID, 'thumbnail');
* Custom column for shortcode.
public static function populate_column_layout_part_shortcode($column, $post) {
'<input readonly size="30" type="text" onclick="this.select();" value="' . esc_attr(sprintf('[themify_layout_part id="%d"]', $post->ID)) . '">',
'<input readonly size="30" type="text" onclick="this.select();" value="' . esc_attr(sprintf('[themify_layout_part slug="%s"]', $post->post_name)) . '">';
* Includes this custom post to array of cpts managed by Themify
public static function extend_post_types(array $types):array {
$types[]=self::LAYOUT_SLUG;
$types[]=self::LAYOUT_PART_SLUG;
* Add meta boxes to layout and/or layout part screens.
public static function custom_meta_boxes($post) {
add_meta_box('layout-part-info', __('Using this Layout Part', 'themify'), array(__CLASS__, 'layout_part_info'), self::LAYOUT_PART_SLUG, 'side', 'default');
* Displays information about this layout part.
public static function layout_part_info() {
$layout_part = get_post();
echo '<div>', __('To display this Layout Part, insert this shortcode:', 'themify'), '<br/>
<input type="text" readonly="readonly" class="widefat" onclick="this.select()" value="' . esc_attr('[themify_layout_part id="' . $layout_part->ID . '"]') . '" />';
if (!empty($layout_part->post_name)) {
echo '<input type="text" readonly="readonly" class="widefat" onclick="this.select()" value="' . esc_attr('[themify_layout_part slug="' . $layout_part->post_name . '"]') . '" />';
* Custom layout for Template / Template Part Builder Editor.
public static function template_singular_layout(?string $original_template):?string{
if (is_singular(array(self::LAYOUT_SLUG, self::LAYOUT_PART_SLUG))) {
$templatefilename = 'template-builder-editor.php';
$return_template = locate_template(
trailingslashit('themify-builder/templates') . $templatefilename
$return_template = THEMIFY_BUILDER_TEMPLATES_DIR . '/' . $templatefilename;
return $original_template;
public static function set_layout_action() {
check_ajax_referer('tf_nonce', 'nonce');
if ( ! empty( $_POST['bid'] ) && current_user_can( 'edit_post', $_POST['bid'] ) ) {
$mode = !empty($_POST['mode']) ? 'themify_builder_layout_appended' : 'themify_builder_layout_loaded';
do_action($mode, array('template_slug' => '', 'current_builder_id' => (int) $_POST['bid'], 'layout_group' => '', 'builder_data' => ''));
public static function layout_part_shortcode(array $atts):string {
if ( ! empty( $atts['id'] ) ) {
} else if ( ! empty( $atts['slug'] ) ) {
$post = get_page_by_path( $atts['slug'], OBJECT, self::LAYOUT_PART_SLUG );
$id = themify_maybe_translate_object_id( $id );
if ($id == Themify_Builder::$builder_active_id && Themify_Builder_Model::is_front_builder_activate()) {
static $isDone = false; //return only for first element
return Themify_Builder::render($id);
// infinite-loop prevention
if (isset($stack[$id])) {
$message = sprintf(__('Layout Part %s is in an infinite loop.', 'themify'), $id);
return "<!-- {$message} -->";
$builder_data = ThemifyBuilder_Data_Manager::get_data($id);
// Check For page break module
if (!Themify_Builder::$frontedit_active) {
$module_list = Themify_Builder::get_builder_modules_list($id);
foreach ($module_list as $module) {
if (isset($module['mod_name']) && 'page-break' === $module['mod_name']) {
$template_args = array();
$pb_result = Themify_Builder::load_current_inner_page_content($builder_data, $page_breaks);
$builder_data = $pb_result['builder_data'];
$template_args['pb_pagination'] = $pb_result['pagination'];
if (!empty($builder_data)) {
$template_args['builder_output'] = $builder_data;
$template_args['builder_id'] = $id;
$template_args['l_p'] = true;
if (Themify_Builder::$frontedit_active === false) {
$isActive = isset($_POST['action']) && $_POST['action'] === 'tb_render_element_shortcode';
Themify_Builder::$frontedit_active = $isActive;
$output = Themify_Builder_Component_Module::retrieve_template('builder-layout-part-output.php', $template_args, THEMIFY_BUILDER_TEMPLATES_DIR, '', false);
Themify_Builder::$frontedit_active = false;
if (!themify_is_ajax()) {
Themify_Builder::get_builder_stylesheet($output);
public static function save_custom_layout_ajaxify() {
check_ajax_referer('tf_nonce', 'nonce');
'msg' => __('Something went wrong', 'themify')
if (!empty($_POST['postid']) && current_user_can( 'edit_posts' )) {
$template = get_post((int) $_POST['postid']);
$title = !empty($_POST['layout_title_field']) ? sanitize_text_field($_POST['layout_title_field']) : $template->post_title . ' Layout';
$builder_data = ThemifyBuilder_Data_Manager::get_data($template->ID);
if (!empty($builder_data)) {
$new_id = wp_insert_post(array(
'post_status' => current_user_can( 'publish_posts' ) ? 'publish' : 'draft',
'post_type' => self::LAYOUT_SLUG,
'post_author' => $template->post_author,
ThemifyBuilder_Data_Manager::save_data($builder_data, $new_id);
// Set image as Featured Image
if (!empty($_POST['layout_img_field_id'])) {
set_post_thumbnail($new_id, (int) $_POST['layout_img_field_id']);
$response['status'] = 'success';
public static function get_custom_layout_ajaxify() {
check_ajax_referer('tf_nonce', 'nonce');
if ( ! current_user_can( 'edit_posts' ) ) {
$slug = !empty($_POST['slug']) ? sanitize_text_field($_POST['slug']) : '';
'post_type' => self::LAYOUT_SLUG,
'post_status' => 'publish',
'cache_results' => false,
$template = get_posts($args);
$layouts = ThemifyBuilder_Data_Manager::get_data($template[0]->ID);
wp_send_json_error(__('Requested layout not found.', 'themify'));
$layouts = self::get_saved_layouts();
* Get a list of "custom" layouts, each post from the "tbuilder_layout" post type
* is a Custom layout, this returns a list of them all
public static function get_saved_layouts(int $limit = -1) {
$posts = new WP_Query(array(
'post_type' => self::LAYOUT_SLUG,
'post_status' => 'publish',
'posts_per_page' => $limit,
'ignore_sticky_posts' => true,
'update_post_term_cache' => false,
'update_post_meta_cache' => false,
while ($posts->have_posts()) {
$url = get_the_post_thumbnail_url($post, 'thumbnail');
'title' => get_the_title(),
'slug' => $post->post_name,
'thumbnail' => !empty($url) ? $url : THEMIFY_BUILDER_URI . '/img/image-placeholder.png',
'url' => get_permalink( $post->ID )
* Add custom link actions in post / page rows