: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
// Merge existing Global Presets with imported ones
foreach ( $presets_to_import as $module_type => $module_presets ) {
foreach ( $module_presets['presets'] as $preset_id => $preset ) {
$global_presets->$module_type->presets->$preset_id = (object) array();
$global_presets->$module_type->presets->$preset_id->name = sanitize_text_field( $preset['name'] );
$global_presets->$module_type->presets->$preset_id->created = $preset['created'];
$global_presets->$module_type->presets->$preset_id->updated = $preset['updated'];
$global_presets->$module_type->presets->$preset_id->version = $preset['version'];
$global_presets->$module_type->presets->$preset_id->settings = (object) array();
foreach ( $preset['settings'] as $setting_name => $value ) {
$setting_name_sanitized = sanitize_text_field( $setting_name );
$value_sanitized = sanitize_text_field( $value );
$global_presets->$module_type->presets->$preset_id->settings->$setting_name_sanitized = $value_sanitized;
et_update_option( ET_Builder_Global_Presets_Settings::GLOBAL_PRESETS_OPTION, $global_presets );
$global_presets_history = ET_Builder_Global_Presets_History::instance();
$global_presets_history->add_global_history_record( $global_presets );
* @param array $posts Array of data formatted by the portability exporter.
protected function import_posts( $posts ) {
* Filters the array of builder layouts to import. Returning an empty value will
* short-circuit the import process.
$posts = apply_filters( 'et_core_portability_import_posts', $posts );
foreach ( $posts as $post ) {
if ( isset( $post['post_status'] ) && 'auto-draft' === $post['post_status'] ) {
$fields_validatation = array(
'post_title' => 'sanitize_text_field',
'post_type' => 'sanitize_text_field',
if ( ! $post = $this->validate( $post, $fields_validatation ) ) {
$layout_exists = self::layout_exists( $post['post_title'], $post['post_name'] );
if ( $layout_exists && get_post_type( $layout_exists ) === $post['post_type'] ) {
// Make sure the post is published.
if ( 'publish' !== get_post_status( $layout_exists ) ) {
'ID' => intval( $layout_exists ),
'post_status' => 'publish',
$post['import_id'] = $post['ID'];
$post['post_author'] = (int) get_current_user_id();
// Insert or update post.
$post_id = wp_insert_post( $post, true );
if ( ! $post_id || is_wp_error( $post_id ) ) {
if ( isset( $post['terms'] ) && is_array( $post['terms'] ) ) {
$processed_terms = array();
foreach ( $post['terms'] as $term ) {
$fields_validatation = array(
'name' => 'sanitize_text_field',
'slug' => 'sanitize_title',
'taxonomy' => 'sanitize_title',
'parent' => 'sanitize_title',
'description' => 'wp_kses_post',
if ( ! $term = $this->validate( $term, $fields_validatation ) ) {
if ( empty( $term['parent'] ) ) {
if ( isset( $term['all_parents'] ) && ! empty( $term['all_parents'] ) ) {
$this->restore_parent_categories( $term['all_parents'], $term['taxonomy'] );
$parent = term_exists( $term['parent'], $term['taxonomy'] );
if ( is_array( $parent ) ){
$parent = $parent['term_id'];
if ( ! $insert = term_exists( $term['slug'], $term['taxonomy'] ) ) {
$insert = wp_insert_term( $term['name'], $term['taxonomy'], array(
'description' => $term['description'],
'parent' => intval( $parent ),
if ( is_array( $insert ) && ! is_wp_error( $insert ) ) {
$processed_terms[$term['taxonomy']][] = $term['slug'];
foreach ( $processed_terms as $taxonomy => $ids ) {
wp_set_object_terms( $post_id, $ids, $taxonomy );
// Insert or update post meta.
if ( isset( $post['post_meta'] ) && is_array( $post['post_meta'] ) ) {
foreach ( $post['post_meta'] as $meta_key => $meta ) {
$meta_key = sanitize_text_field( $meta_key );
if ( count( $meta ) < 2 ) {
$meta = wp_kses_post( $meta[0] );
$meta = array_map( 'wp_kses_post', $meta );
update_post_meta( $post_id, $meta_key, $meta );
* Restore the categories hierarchy in library.
* @param array $parents_array Array of parent categories data.
* @param string $taxonomy
protected function restore_parent_categories( $parents_array, $taxonomy ) {
foreach( $parents_array as $slug => $category_data ) {
$current_category = term_exists( $slug, $taxonomy );
if ( ! is_array( $current_category ) ) {
$parent_id = 0 !== $category_data['parent'] ? term_exists( $category_data['parent'], $taxonomy ) : 0;
wp_insert_term( $category_data['name'], $taxonomy, array(
'description' => $category_data['description'],
'parent' => is_array( $parent_id ) ? $parent_id['term_id'] : $parent_id,
} else if ( ( ! isset( $current_category['parent'] ) || 0 === $current_category['parent'] ) && 0 !== $category_data['parent'] ) {
$parent_id = 0 !== $category_data['parent'] ? term_exists( $category_data['parent'], $taxonomy ) : 0;
wp_update_term( $current_category['term_id'], $taxonomy, array( 'parent' => is_array( $parent_id ) ? $parent_id['term_id'] : $parent_id ) );
* Generates UUIDs for the presets to avoid collisions.
* @param array $global_presets - The Global Presets to be imported
* @return array - The list of module types for which preset ids have been changed
public function prepare_to_import_layout_presets( &$global_presets ) {
$preset_rewrite_map = array();
$initial_preset_id = ET_Builder_Global_Presets_Settings::MODULE_INITIAL_PRESET_ID;
foreach ( $global_presets as $component_type => &$component_presets ) {
$preset_rewrite_map[ $component_type ] = array();
foreach ( $component_presets['presets'] as $preset_id => $preset ) {
$new_id = ET_Core_Data_Utils::uuid_v4();
$component_presets['presets'][ $new_id ] = $preset;
$preset_rewrite_map[ $component_type ][ $preset_id ] = $new_id;
unset( $component_presets['presets'][ $preset_id ] );
if ( $component_presets['default'] === $initial_preset_id && ! isset( $preset_rewrite_map[ $component_type ][ $initial_preset_id ] ) ) {
$new_id = ET_Core_Data_Utils::uuid_v4();
$component_presets['default'] = $new_id;
if ( isset( $component_presets['presets'][ $initial_preset_id ] ) ) {
$component_presets['presets'][ $new_id ] = $component_presets['presets'][ $initial_preset_id ];
unset( $component_presets['presets'][ $initial_preset_id ] );
$preset_rewrite_map[ $component_type ][ $initial_preset_id ] = $new_id;
$component_presets['default'] = $preset_rewrite_map[ $component_type ][ $component_presets['default'] ];
return $preset_rewrite_map;
* Injects the given Global Presets settings into the imported layout
* @param array $shortcode_object - The multidimensional array representing a page/module structure
* @param array $global_presets - The Global Presets to be imported
* @param array $preset_rewrite_map - The list of module types for which preset ids have been changed
protected function rewrite_module_preset_ids( &$shortcode_object, $global_presets, $preset_rewrite_map ) {
$global_presets_manager = ET_Builder_Global_Presets_Settings::instance();
$module_preset_attribute = ET_Builder_Global_Presets_Settings::MODULE_PRESET_ATTRIBUTE;
foreach ( $shortcode_object as &$module ) {
$module_type = $global_presets_manager->maybe_convert_module_type( $module['type'], $module['attrs'] );
$module_preset_id = et_()->array_get( $module, "attrs.{$module_preset_attribute}", 'default' );
if ( $module_preset_id === 'default' ) {
$module['attrs'][ $module_preset_attribute ] = et_()->array_get( $global_presets, "{$module_type}.default", 'default' );
if ( isset( $preset_rewrite_map[ $module_type ][ $module_preset_id ] ) ) {
$module['attrs'][ $module_preset_attribute ] = $preset_rewrite_map[ $module_type ][ $module_preset_id ];
$module['attrs'][ $module_preset_attribute ] = et_()->array_get( $global_presets, "{$module_type}.default", 'default' );
if ( is_array( $module['content'] ) ) {
$this->rewrite_module_preset_ids( $module['content'], $global_presets, $preset_rewrite_map );
* Injects the given Global Presets settings into the imported layout
* @param array $shortcode_object - The multidimensional array representing a page/module structure
* @param array $global_presets - The Global Presets to be applied
protected function apply_global_presets( &$shortcode_object, $global_presets ) {
$global_presets_manager = ET_Builder_Global_Presets_Settings::instance();
$module_preset_attribute = ET_Builder_Global_Presets_Settings::MODULE_PRESET_ATTRIBUTE;
foreach ( $shortcode_object as &$module ) {
$module_type = $global_presets_manager->maybe_convert_module_type( $module['type'], $module['attrs'] );
if ( isset( $global_presets[ $module_type ] ) ) {
$default_preset_id = et_()->array_get( $global_presets, "{$module_type}.default", null );
$module_preset_id = et_()->array_get( $module, "attrs.{$module_preset_attribute}", $default_preset_id );
if ( $module_preset_id === 'default' ) {
$module_preset_id = $default_preset_id;
if ( isset( $global_presets[ $module_type ]['presets'][ $module_preset_id ] ) ) {
$module['attrs'] = array_merge( $global_presets[ $module_type ]['presets'][ $module_preset_id ]['settings'], $module['attrs'] );
if ( isset( $global_presets[ $module_type ]['presets'][ $default_preset_id ]['settings'] ) ) {
$module['attrs'] = array_merge( $global_presets[ $module_type ]['presets'][ $default_preset_id ]['settings'], $module['attrs'] );
if ( is_array( $module['content'] ) ) {
$this->apply_global_presets( $module['content'], $global_presets );
* Restrict data according the argument registered.
* @param array $data Array of data the query is applied on.
* @param string $method Whether data should be set or reset. Accepts 'set' or 'unset' which is
* should be used when treating existing data in the db.
protected function apply_query( $data, $method ) {
$operator = ( $method === 'set' ) ? true : false;
foreach ( $data as $id => $value ) {
if ( ! empty( $this->instance->exclude ) && isset( $this->instance->exclude[$id] ) === $operator ) {
if ( ! empty( $this->instance->include ) && isset( $this->instance->include[$id] ) === ! $operator ) {
* Serialize images in chunks.
* @param string $method Method applied on images.
* @param string $id Unique ID to use for temporary files.
protected function chunk_images( $images, $method, $id, $chunk = 0 ) {
* Filters whether or not images in the file being imported should be paginated.
* @param bool $paginate_images Default `true`.
$paginate_images = apply_filters( 'et_core_portability_paginate_images', true );
if ( $paginate_images && count( $images ) > $images_per_chunk ) {
$chunks = ceil( count( $images ) / $images_per_chunk );
$slice = $images_per_chunk * $chunk;
$images = array_slice( $images, $slice, $images_per_chunk );
$images = $this->$method( $images );
$filesystem = $this->get_filesystem();
$temp_file_id = sanitize_file_name( "images_{$id}" );
$temp_file = $this->temp_file( $temp_file_id, 'et_core_export' );
$temp_images = json_decode( $filesystem->get_contents( $temp_file ), true );
if ( is_array( $temp_images ) ) {
$images = array_merge( $temp_images, $images );
if ( $chunk + 1 < $chunks ) {
$filesystem->put_contents( $temp_file, wp_json_encode( (array) $images ) );
$this->delete_temp_files( 'et_core_export', array( $temp_file_id => $temp_file ) );
$images = $this->$method( $images );
'ready' => $chunk + 1 >= $chunks,
* Paginate images processing.
* @param string $method Method applied on images.
* @param int $timestamp Timestamp used to store data upon pagination.
* @internal param array $data Array of images.
protected function maybe_paginate_images( $images, $method, $timestamp ) {
et_core_nonce_verified_previously();
$page = isset( $_POST['page'] ) ? (int) $_POST['page'] : 1;
$result = $this->chunk_images( $images, $method, $timestamp, max( $page - 1, 0 ) );
if ( ! $result['ready'] ) {
'total_pages' => $result['chunks'],
'timestamp' => $timestamp,
return $result['images'];
* Get all images in the data given.
* @param array $data Array of data.
* @param bool $force Set whether the value should be added by force. Usually used for image ids.
protected function get_data_images( $data, $force = false ) {
foreach ( $basenames as $basename ) {
$images_src[] = $basename;
foreach ( $suffixes as $suffix ) {
$images_src[] = $basename . $suffix;
foreach ( $data as $value ) {
if ( is_array( $value ) || is_object( $value ) ) {
$images = array_merge( $images, $this->get_data_images( (array) $value ) );
// Extract images from html or shortcodes.
if ( preg_match_all( '/(' . implode( '|', $images_src ) . ')="(?P<src>\w+[^"]*)"/i', $value, $matches ) ) {
foreach ( array_unique( $matches['src'] ) as $key => $src ) {
$images = array_merge( $images, $this->get_data_images( array( $key => $src ) ) );
// Extract images from shortcodes gallery.
if ( preg_match_all( '/gallery_ids="(?P<ids>\w+[^"]*)"/i', $value, $matches ) ) {
$explode = explode( ',', str_replace( ' ', '', $matches['ids'][0] ) );
foreach ( $explode as $image_id ) {
$images = array_merge( $images, $this->get_data_images( array( (int) $image_id ), true ) );
if ( preg_match( '/^.+?\.(jpg|jpeg|jpe|png|gif)/', $value, $match ) || $force ) {
$basename = basename( $value );
if ( isset( $images[$value] ) ) {
$images[$value] = $value;
* Get the attachment post id for the given url.
* @param string $url The url of an attachment file.
protected function _get_attachment_id_by_url( $url ) {
// Remove any thumbnail size suffix from the filename and use that as a fallback.
$fallback_url = preg_replace( '/-\d+x\d+(\.[^.]+)$/i', '$1', $url );