: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* Export Theme Builder templates in chunks.
* @param integer $id Unique ID to represent this theme builder export process.
* @param integer $step_index
public function export_theme_builder( $id, $step, $steps, $step_index = 0, $chunk = 0 ) {
$result = $this->serialize_theme_builder( $id, $step, $steps, $step_index, $chunk );
if ( false === $result ) {
$temp_file_id = sanitize_file_name( 'et_theme_builder_export_' . $id );
$temp_file = $this->temp_file( $temp_file_id, 'et_core_export' );
if ( $result['ready'] ) {
$this->get_filesystem()->put_contents( $temp_file, wp_json_encode( $result[ 'data' ] ) );
return array_merge( $result, array(
'temp_file' => $temp_file,
'temp_file_id' => $temp_file_id,
* Get whether an array represents a valid Theme Builder export.
public function is_valid_theme_builder_export( $export ) {
$valid_context = isset( $export['context'] ) && $export['context'] === $this->instance->context;
$has_templates = isset( $export['templates'] ) && is_array( $export['templates'] );
$has_layouts = isset( $export['layouts'] ) && is_array( $export['layouts'] );
return $valid_context && $has_templates && $has_layouts;
* Import a single layout in chunks.
* @param string $id Unique ID to represent this layout serialization.
public function import_layout( $id, $layout, $chunk = 0 ) {
$import = $this->validate( $layout );
if ( false === $import ) {
$import['data'] = $this->apply_query( $import['data'], 'set' );
if ( ! isset( $import['context'] ) || ( isset( $import['context'] ) && 'et_builder' !== $import['context'] ) ) {
$result = $this->chunk_images( self::$_->array_get( $import, 'images', array() ), 'upload_images', $id, $chunk );
if ( $result['ready'] ) {
$import['data'] = $this->replace_images_urls( $result['images'], $import['data'] );
$post_type = self::$_->array_get( $import, 'post_type', 'post' );
$post_title = self::$_->array_get( $import, 'post_title', '' );
$post_meta = self::$_->array_get( $import, 'post_meta', array() );
$post_type_object = get_post_type_object( $post_type );
if ( ! $post_type_object || ! current_user_can( $post_type_object->cap->create_posts ) ) {
$content = array_values( $import['data'] );
'post_type' => $post_type,
'post_content' => current_user_can( 'unfiltered_html' ) ? $content : wp_kses_post( $content ),
if ( ! empty( $post_title ) ) {
$args['post_title'] = current_user_can( 'unfiltered_html' ) ? $post_title : wp_kses( $post_title );
$post_id = et_theme_builder_insert_layout( $args );
if ( is_wp_error( $post_id ) ) {
foreach ( $post_meta as $entry ) {
update_post_meta( $post_id, $entry['key'], $entry['value'] );
'ready' => $result['ready'],
'chunks' => $result['chunks'],
* Import Theme Builder templates in chunks.
* @param integer $id Unique ID to represent this theme builder import process.
* @param integer $step_index
public function import_theme_builder( $id, $step, $steps, $step_index = 0, $chunk = 0 ) {
if ( $step_index >= $steps ) {
$layout_id_map = array();
switch ( $step['type'] ) {
$presets = et_()->array_get( $step, 'presets', array() );
$presets_rewrite_map = et_()->array_get( $step, 'presets_rewrite_map', array() );
$import_presets = et_()->array_get( $step, 'import_presets', false );
$layouts = et_()->array_get( $step['data'], 'data', array() );
// Apply any presets to the layouts' shortcodes prior to importing them.
if ( ! empty( $presets ) && ! empty( $layouts ) ) {
foreach ( $layouts as $key => $layout ) {
$shortcode_object = et_fb_process_shortcode( $layout );
$this->rewrite_module_preset_ids( $shortcode_object, $presets, $presets_rewrite_map );
$this->apply_global_presets( $shortcode_object, $presets );
$layouts[ $key ] = et_fb_process_to_shortcode( $shortcode_object, array(), '', false );
$step['data']['data'] = $layouts;
$result = $this->import_layout( $id, $step['data'], $chunk );
if ( false === $result ) {
if ( $result['ready'] ) {
if ( ! isset( $layout_id_map[ $step['id'] ] ) ) {
$layout_id_map[ $step['id'] ] = array();
// Since a single layout can be duplicated multiple times if
// it's global we have to keep an array of duplicated ids.
$layout_id_map[ $step['id'] ][ $step['template_id'] ] = $result['id'];
$chunks = $result['chunks'];
$ready = ( $step_index + 1 >= $steps ) && ( $chunk + 1 >= $chunks );
'layout_id_map' => $layout_id_map,
* Download temporary file.
* @param string $filename
* @param string $temp_file_id
* @param string $temp_file
public function download_file( $filename, $temp_file_id, $temp_file ) {
$this->prevent_failure();
$filename = sanitize_file_name( $filename );
header( 'Content-Description: File Transfer' );
header( "Content-Disposition: attachment; filename=\"{$filename}.json\"" );
header( 'Content-Type: application/json' );
header( 'Pragma: no-cache' );
if ( file_exists( $temp_file ) ) {
echo et_core_esc_previously( $this->get_filesystem()->get_contents( $temp_file ) );
$this->delete_temp_files( 'et_core_export', array( $temp_file_id => $temp_file ) );
public function download_export() {
$this->prevent_failure();
et_core_nonce_verified_previously();
$timestamp = isset( $_GET['timestamp'] ) ? sanitize_text_field( $_GET['timestamp'] ) : null;
$name = isset( $_GET['name'] ) ? sanitize_text_field( rawurldecode( $_GET['name'] ) ) : $this->instance->name;
$filesystem = $this->set_filesystem();
$temp_file = $this->temp_file( sanitize_file_name( $timestamp ), 'et_core_export' );
header( 'Content-Description: File Transfer' );
header( "Content-Disposition: attachment; filename=\"{$name}.json\"" );
header( 'Content-Type: application/json' );
header( 'Pragma: no-cache' );
if ( file_exists( $temp_file ) ) {
echo et_core_esc_previously( $filesystem->get_contents( $temp_file ) );
$this->delete_temp_files( 'et_core_export' );
protected function to_megabytes( $value ) {
$unit = strtoupper( substr( $value, -1 ) );
$amount = intval( substr( $value, 0, -1 ) );
case 'G': return $amount << 10;
case 'M': return $amount;
if ( is_numeric( $unit ) ) {
// Numeric unit is present, assume bytes
return intval( $value ) >> 20;
* Get selected posts data.
protected function export_posts_query() {
et_core_nonce_verified_previously();
'post_type' => $this->instance->target,
// Only include selected posts if set and not empty.
if ( isset( $_POST['selection'] ) ) {
$include = json_decode( stripslashes( $_POST['selection'] ), true );
if ( ! empty( $include ) ) {
$include = array_map( 'intval', array_values( $include ) );
$args['post__in'] = $include;
$get_posts = get_posts( apply_filters( "et_core_portability_export_wp_query_{$this->instance->context}", $args ) );
$taxonomies = get_object_taxonomies( $this->instance->target );
foreach ( $get_posts as $key => $post ) {
$posts[$post->ID] = $post;
$post_meta = (array) get_post_meta( $post->ID );
if ( isset( $post_meta['_edit_lock'] ) ) {
$post_meta['_edit_lock'],
$posts[$post->ID]->post_meta = $post_meta;
$get_terms = (array) wp_get_object_terms( $post->ID, $taxonomies );
// Order terms to make sure children are after the parents.
while ( $term = array_shift( $get_terms ) ) {
if ( 0 === $term->parent || isset( $terms[$term->parent] ) ) {
$terms[$term->term_id] = $term;
// if parent category is also exporting then add the term to the end of the list and process it later
// otherwise add a term as usual
if ( $this->is_parent_term_included( $get_terms, $term->parent ) ) {
$terms[$term->term_id] = $term;
$posts[$post->ID]->terms = array();
foreach ( $terms as $term ) {
$parent_slug = isset( $terms[$term->parent] ) ? $terms[$term->parent]->slug : $this->get_parent_slug( $term->parent, $term->taxonomy );
$parents_data = $this->get_all_parents( $term->parent, $term->taxonomy );
$posts[$post->ID]->terms[$term->term_id] = array(
'taxonomy' => $term->taxonomy,
'parent' => $parent_slug,
'all_parents' => $parents_data,
'description' => $term->description
* Check whether the $parent_id included into the $terms_list.
* @param array $terms_list Array of term objects.
* @param int $parent_id .
protected function is_parent_term_included( $terms_list, $parent_id ) {
$is_parent_found = false;
foreach ( $terms_list as $term => $term_details ) {
if ( $parent_id === $term_details->term_id ) {
* Retrieve the term slug.
* @param int $parent_id .
* @param string $taxonomy .
protected function get_parent_slug( $parent_id, $taxonomy ) {
$term_data = get_term( $parent_id, $taxonomy );
$slug = '' === $term_data->slug ? 0 : $term_data->slug;
* Prepare array of all parents so the correct hierarchy can be restored during the import.
* @param int $parent_id .
* @param string $taxonomy .
protected function get_all_parents( $parent_id, $taxonomy ) {
$parents_data_array = array();
// retrieve data for all parent categories
$parent_term_data = get_term( $parent, $taxonomy );
$parents_data_array[$parent_term_data->slug] = array(
'name' => $parent_term_data->name,
'description' => $parent_term_data->description,
'parent' => 0 !== $parent_term_data->parent ? $this->get_parent_slug( $parent_term_data->parent, $taxonomy ) : 0,
$parent = $parent_term_data->parent;
//reverse order of items, to simplify the restoring process
return array_reverse( $parents_data_array );
* Check if a layout exists in the database already based on both its title and its slug.
* @return int $post_id The post id if it exists, zero otherwise.
protected static function layout_exists( $title, $slug ) {
return (int) $wpdb->get_var( $wpdb->prepare(
"SELECT ID FROM $wpdb->posts WHERE post_title = %s AND post_name = %s",
wp_unslash( sanitize_post_field( 'post_title', $title, 0, 'db' ) ),
wp_unslash( sanitize_post_field( 'post_name', $slug, 0, 'db' ) ),
* @since 4.0.10 Made public.
* @param array $presets - The Global Presets to be imported
public function import_global_presets( $presets ) {
if ( ! is_array( $presets ) ) {
$all_modules = ET_Builder_Element::get_modules();
$module_presets_manager = ET_Builder_Global_Presets_Settings::instance();
$global_presets = $module_presets_manager->get_global_presets();
$presets_to_import = array();
foreach ( $presets as $module_type => $module_presets ) {
$presets_to_import[ $module_type ] = array(
if ( ! isset( $global_presets->$module_type->presets ) ) {
$initial_preset_structure = ET_Builder_Global_Presets_Settings::generate_module_initial_presets_structure( $module_type, $all_modules );
$global_presets->$module_type = $initial_preset_structure;
$local_presets = $global_presets->$module_type->presets;
$local_preset_names = array();
foreach ( $local_presets as $preset ) {
array_push( $local_preset_names, $preset->name );
foreach ( $module_presets['presets'] as $preset_id => $preset ) {
$imported_name = sanitize_text_field( $preset['name'] );
$name = in_array( $imported_name, $local_preset_names )
? $imported_name . ' ' . esc_html__( 'imported', 'et-core' )
$presets_to_import[ $module_type ]['presets'][ $preset_id ] = array(
'created' => time() * 1000,
'updated' => time() * 1000,
'version' => $preset['version'],
'settings' => $preset['settings'],