: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
class ET_Builder_Library {
* @var ET_Builder_Library
private static $_instance;
* @var ET_Core_Data_Utils
protected static $_primary_category_key;
protected static $_standard_post_types = array( 'post', 'page', 'project' );
* @var ET_Builder_Post_Taxonomy_LayoutCategory
public $layout_categories;
* @var ET_Builder_Post_Taxonomy_LayoutPack
* @var ET_Builder_Post_Taxonomy_LayoutType
* @var ET_Builder_Post_Type_Layout
* ET_Builder_Library constructor.
public function __construct() {
$this->_instance_check();
$this->_register_cpt_and_taxonomies();
self::$_ = ET_Core_Data_Utils::instance();
$this->_register_hooks();
$this->_register_ajax_callbacks();
self::$_i18n = require( ET_BUILDER_DIR . '/frontend-builder/i18n/library.php' );
self::$_standard_post_types = self::_standard_post_types();
public static function compare_by_slug( $a, $b ) {
return strcmp( $a->slug, $b->slug );
* Gets a translated string from {@see self::$_i18n}.
* @param string $string The untranslated string.
* @param string $path Optional path for nested strings.
* @return string The translated string if found, the original string otherwise.
public static function __( $string, $path = '' ) {
$path .= $path ? ".{$string}" : $string;
return self::$_->array_get( self::$_i18n, $path, $string );
* Dies if an instance already exists.
protected function _instance_check() {
if ( self::$_instance ) {
et_error( 'Multiple instances are not allowed!' );
* Get the name of a thumbnail image size used in the library UI.
* @param string $type The thumbnail type. Accepts 'thumbnail', 'screenshot'.
protected static function _get_image_size_name( $type ) {
'thumbnail' => 'et-pb-portfolio-image',
'thumbnail_small' => 'et-pb-portfolio-image',
'screenshot' => 'et-pb-portfolio-image-single',
* Filters the names of the registered image sizes to use for layout thumbnails. The
* dynamic portion of the filter name, '$type', refers to the layout image
* type ('thumbnail' or 'screenshot').
* @param string $name The name of the registered image size that should be used.
return apply_filters( "et_builder_layout_{$type}_image_size_name", $name );
* Returns a filtered short name for a layout.
* @param string $long_name
protected function _get_layout_short_name( $long_name, $layout ) {
* Filters the short name for layouts that do not have one.
* @param string $long_name
return apply_filters( 'et_builder_library_layout_short_name', $long_name, $layout );
* Processes layout categories for inclusion in the library UI layouts data.
* @param WP_POST $post Unprocessed layout.
* @param object $layout Currently processing layout.
* @param int $index The layout's index position.
* @param array[] $layout_categories Processed layouts.
protected function _process_layout_categories( $post, $layout, $index, &$layout_categories ) {
if ( ! $terms = wp_get_post_terms( $post->ID, $this->layout_categories->name ) ) {
$layout->category_slug = 'uncategorized';
foreach ( $terms as $category ) {
$category_name = self::__( html_entity_decode( $category->name ), '@categories' );
$category_name = et_core_intentionally_unescaped( $category_name, 'react_jsx' );
if ( ! isset( $layout_categories[ $category->term_id ] ) ) {
$layout_categories[ $category->term_id ] = array(
'id' => $category->term_id,
'name' => $category_name,
'slug' => $category->slug,
$layout_categories[ $category->term_id ]['layouts'][] = $index;
$layout->categories[] = $category_name;
$layout->category_ids[] = $category->term_id;
if ( ! isset( $layout->category_slug ) ) {
$layout->category_slug = $category->slug;
if ( $id = get_post_meta( $post->ID, self::$_primary_category_key, true ) ) {
// $id is a string, $category->term_id is an int.
if ( $id === $category->term_id ) {
// This is the primary category (used in the layout URL)
$layout->category_slug = $category->slug;
* Processes layout packs for inclusion in the library UI layouts data.
* @param WP_POST $post Unprocessed layout.
* @param object $layout Currently processing layout.
* @param int $index The layout's index position.
* @param array[] $layout_packs Processed layouts.
protected function _process_layout_packs( $post, $layout, $index, &$layout_packs ) {
if ( ! $terms = wp_get_post_terms( $post->ID, $this->layout_packs->name ) ) {
$pack = array_shift( $terms );
$pack_name = self::__( html_entity_decode( $pack->name ), '@packs' );
$pack_name = et_core_intentionally_unescaped( $pack_name, 'react_jsx' );
if ( ! isset( $layout_packs[ $pack->term_id ] ) ) {
$layout_packs[ $pack->term_id ] = array(
if ( $layout->is_landing ) {
$layout_packs[ $pack->term_id ]['thumbnail'] = $layout->thumbnail;
$layout_packs[ $pack->term_id ]['screenshot'] = $layout->screenshot;
$layout_packs[ $pack->term_id ]['description'] = et_core_intentionally_unescaped( html_entity_decode( $post->post_excerpt ), 'react_jsx' );
$layout_packs[ $pack->term_id ]['category_slug'] = $layout->category_slug;
$layout_packs[ $pack->term_id ]['landing_index'] = $index;
$layout_packs[ $pack->term_id ]['layouts'][] = $index;
$layout_packs[ $pack->term_id ]['categories'] = $layout->categories;
$layout_packs[ $pack->term_id ]['category_ids'] = $layout->category_ids;
$layout->pack = $pack_name;
$layout->pack_id = $pack->term_id;
* Registers the Library's AJAX callbacks.
protected function _register_ajax_callbacks() {
add_action( 'wp_ajax_et_builder_library_get_layouts_data', array( $this, 'wp_ajax_et_builder_library_get_layouts_data' ) );
add_action( 'wp_ajax_et_builder_library_get_layout', array( $this, 'wp_ajax_et_builder_library_get_layout' ) );
add_action( 'wp_ajax_et_builder_library_update_account', array( $this, 'wp_ajax_et_builder_library_update_account' ) );
* Registers the Library's custom post types and taxonomies.
protected function _register_cpt_and_taxonomies() {
if ( ! $files = glob( ET_BUILDER_DIR . 'post/*/Layout*.php' ) ) {
foreach ( $files as $file ) {
$this->layouts = ET_Builder_Post_Type_Layout::instance();
$this->layout_categories = ET_Builder_Post_Taxonomy_LayoutCategory::instance();
$this->layout_packs = ET_Builder_Post_Taxonomy_LayoutPack::instance();
$this->layout_types = ET_Builder_Post_Taxonomy_LayoutType::instance();
ET_Builder_Post_Taxonomy_LayoutScope::instance();
ET_Builder_Post_Taxonomy_LayoutWidth::instance();
// We manually call register_all() now to ensure the CPT and taxonomies are registered
// at exactly the same point during the request that they were in prior releases.
ET_Builder_Post_Type_Layout::register_all( 'builder' );
self::$_primary_category_key = "_yoast_wpseo_primary_{$this->layout_categories->name}";
* Registers the Library's non-AJAX callbacks.
public function _register_hooks() {
add_action( 'admin_init', 'ET_Builder_Library::update_old_layouts' );
add_action( 'admin_enqueue_scripts', array( $this, 'wp_hook_admin_enqueue_scripts' ), 4 );
* Returns sorted layout category and pack IDs for use in library UI layouts data.
* @param array[] $categories
* @type int[] $categories Layout category ids sorted alphabetically by category name.
* @type int[] $packs Layout pack ids sorted alphabetically by pack name.
protected static function _sort_builder_library_data( $categories, $packs ) {
$categories = array_values( $categories );
$packs = array_values( $packs );
foreach ( array( 'categories', 'packs' ) as $taxonomy ) {
$sorted[ $taxonomy ] = array();
$$taxonomy = self::$_->array_sort_by( $$taxonomy, 'slug' );
foreach ( $$taxonomy as $term ) {
$sorted[ $taxonomy ][] = $term['id'];
public static function _standard_post_types() {
* Filters the Divi Library's Standard Post Types.
* @param string[] $standard_post_types
return apply_filters( 'et_pb_standard_post_types', self::$_standard_post_types );
* Generates layouts data for the builder's library UI.
public function builder_library_layouts_data() {
$layout_categories = array();
$thumbnail = self::_get_image_size_name( 'thumbnail' );
$thumbnail_small = self::_get_image_size_name( 'thumbnail_small' );
$screenshot = self::_get_image_size_name( 'screenshot' );
$extra_layout_post_type = 'layout';
->not()->with_meta( '_et_pb_built_for_post_type', $extra_layout_post_type )
$posts = self::$_->array_sort_by( is_array( $posts ) ? $posts : array( $posts ), 'post_name' );
foreach ( $posts as $post ) {
$layout->date = $post->post_date;
if ( ! $types = wp_get_post_terms( $layout->id, $this->layout_types->name ) ) {
$layout->type = $types[0]->name;
// For the initial release of new library UI, only 'layouts' are needed.
if ( 'layout' !== $layout->type ) {
$title = html_entity_decode( $post->post_title );
if ( ! $short_name = get_post_meta( $post->ID, '_et_builder_library_short_name', true ) ) {
$short_name = $this->_get_layout_short_name( $title, $post );
if ( $short_name !== $title ) {
update_post_meta( $post->ID, '_et_builder_library_short_name', $short_name );
$layout->name = $layout->short_name = '';
// Remove periods since we use dot notation to retrieve translation
str_replace( '.', '', $title );
$layout->name = et_core_intentionally_unescaped( self::__( $title, '@layoutsLong' ), 'react_jsx' );
// Remove periods since we use dot notation to retrieve translation
str_replace( '.', '', $title );
$layout->short_name = et_core_intentionally_unescaped( self::__( $short_name, '@layoutsShort' ), 'react_jsx' );
$layout->slug = $post->post_name;
$layout->url = esc_url( wp_make_link_relative( get_permalink( $post ) ) );
$layout->thumbnail = esc_url( get_the_post_thumbnail_url( $post->ID, $thumbnail ) );
$layout->thumbnail_small = esc_url( get_the_post_thumbnail_url( $post->ID, $thumbnail_small ) );
$layout->screenshot = esc_url( get_the_post_thumbnail_url( $post->ID, $screenshot ) );
$layout->categories = array();
$layout->category_ids = array();
$layout->is_global = $this->layouts->is_global( $layout->id );
$layout->is_landing = ! empty( $post->post_excerpt );
$layout->description = '';
$this->_process_layout_categories( $post, $layout, $index, $layout_categories );
$this->_process_layout_packs( $post, $layout, $index, $layout_packs );
* Filters data for the 'My Saved Layouts' tab.
* @param array[] $saved_layouts_data {
* @type array[] $categories {
* @type int[] $layouts Id's of layouts in category.
* @type string $name Name.
* @type string $slug Slug.
* @type string $category_ids Category ids.
* @type string $category_slug Primary category slug.
* @type string $date Published date.
* @type string $description Description.
* @type int[] $layouts Id's of layouts in pack.
* @type string $name Name.
* @type string $screenshot Screenshot URL.
* @type string $slug Slug.
* @type string $thumbnail Thumbnail URL.
* @type object[] $layouts {
* @type string[] $categories
* @type int[] $category_ids
* @type string $category_slug
* @type string $description
* @type string $screenshot
* @type string $short_name
* @type string $thumbnail
* @type string $thumbnail_small