WP_Customize_Manager::import_theme_starter_content()publicWP 4.7.0

Imports theme starter content into the customized state.

Method of the class: WP_Customize_Manager{}

No Hooks.

Return

null. Nothing (null).

Usage

$WP_Customize_Manager = new WP_Customize_Manager();
$WP_Customize_Manager->import_theme_starter_content( $starter_content );
$starter_content(array)
Starter content.
Default: get_theme_starter_content()

Changelog

Since 4.7.0 Introduced.

WP_Customize_Manager::import_theme_starter_content() code WP 6.5.2

public function import_theme_starter_content( $starter_content = array() ) {
	if ( empty( $starter_content ) ) {
		$starter_content = get_theme_starter_content();
	}

	$changeset_data = array();
	if ( $this->changeset_post_id() ) {
		/*
		 * Don't re-import starter content into a changeset saved persistently.
		 * This will need to be revisited in the future once theme switching
		 * is allowed with drafted/scheduled changesets, since switching to
		 * another theme could result in more starter content being applied.
		 * However, when doing an explicit save it is currently possible for
		 * nav menus and nav menu items specifically to lose their starter_content
		 * flags, thus resulting in duplicates being created since they fail
		 * to get re-used. See #40146.
		 */
		if ( 'auto-draft' !== get_post_status( $this->changeset_post_id() ) ) {
			return;
		}

		$changeset_data = $this->get_changeset_post_data( $this->changeset_post_id() );
	}

	$sidebars_widgets = isset( $starter_content['widgets'] ) && ! empty( $this->widgets ) ? $starter_content['widgets'] : array();
	$attachments      = isset( $starter_content['attachments'] ) && ! empty( $this->nav_menus ) ? $starter_content['attachments'] : array();
	$posts            = isset( $starter_content['posts'] ) && ! empty( $this->nav_menus ) ? $starter_content['posts'] : array();
	$options          = isset( $starter_content['options'] ) ? $starter_content['options'] : array();
	$nav_menus        = isset( $starter_content['nav_menus'] ) && ! empty( $this->nav_menus ) ? $starter_content['nav_menus'] : array();
	$theme_mods       = isset( $starter_content['theme_mods'] ) ? $starter_content['theme_mods'] : array();

	// Widgets.
	$max_widget_numbers = array();
	foreach ( $sidebars_widgets as $sidebar_id => $widgets ) {
		$sidebar_widget_ids = array();
		foreach ( $widgets as $widget ) {
			list( $id_base, $instance ) = $widget;

			if ( ! isset( $max_widget_numbers[ $id_base ] ) ) {

				// When $settings is an array-like object, get an intrinsic array for use with array_keys().
				$settings = get_option( "widget_{$id_base}", array() );
				if ( $settings instanceof ArrayObject || $settings instanceof ArrayIterator ) {
					$settings = $settings->getArrayCopy();
				}

				unset( $settings['_multiwidget'] );

				// Find the max widget number for this type.
				$widget_numbers = array_keys( $settings );
				if ( count( $widget_numbers ) > 0 ) {
					$widget_numbers[]               = 1;
					$max_widget_numbers[ $id_base ] = max( ...$widget_numbers );
				} else {
					$max_widget_numbers[ $id_base ] = 1;
				}
			}
			$max_widget_numbers[ $id_base ] += 1;

			$widget_id  = sprintf( '%s-%d', $id_base, $max_widget_numbers[ $id_base ] );
			$setting_id = sprintf( 'widget_%s[%d]', $id_base, $max_widget_numbers[ $id_base ] );

			$setting_value = $this->widgets->sanitize_widget_js_instance( $instance );
			if ( empty( $changeset_data[ $setting_id ] ) || ! empty( $changeset_data[ $setting_id ]['starter_content'] ) ) {
				$this->set_post_value( $setting_id, $setting_value );
				$this->pending_starter_content_settings_ids[] = $setting_id;
			}
			$sidebar_widget_ids[] = $widget_id;
		}

		$setting_id = sprintf( 'sidebars_widgets[%s]', $sidebar_id );
		if ( empty( $changeset_data[ $setting_id ] ) || ! empty( $changeset_data[ $setting_id ]['starter_content'] ) ) {
			$this->set_post_value( $setting_id, $sidebar_widget_ids );
			$this->pending_starter_content_settings_ids[] = $setting_id;
		}
	}

	$starter_content_auto_draft_post_ids = array();
	if ( ! empty( $changeset_data['nav_menus_created_posts']['value'] ) ) {
		$starter_content_auto_draft_post_ids = array_merge( $starter_content_auto_draft_post_ids, $changeset_data['nav_menus_created_posts']['value'] );
	}

	// Make an index of all the posts needed and what their slugs are.
	$needed_posts = array();
	$attachments  = $this->prepare_starter_content_attachments( $attachments );
	foreach ( $attachments as $attachment ) {
		$key                  = 'attachment:' . $attachment['post_name'];
		$needed_posts[ $key ] = true;
	}
	foreach ( array_keys( $posts ) as $post_symbol ) {
		if ( empty( $posts[ $post_symbol ]['post_name'] ) && empty( $posts[ $post_symbol ]['post_title'] ) ) {
			unset( $posts[ $post_symbol ] );
			continue;
		}
		if ( empty( $posts[ $post_symbol ]['post_name'] ) ) {
			$posts[ $post_symbol ]['post_name'] = sanitize_title( $posts[ $post_symbol ]['post_title'] );
		}
		if ( empty( $posts[ $post_symbol ]['post_type'] ) ) {
			$posts[ $post_symbol ]['post_type'] = 'post';
		}
		$needed_posts[ $posts[ $post_symbol ]['post_type'] . ':' . $posts[ $post_symbol ]['post_name'] ] = true;
	}
	$all_post_slugs = array_merge(
		wp_list_pluck( $attachments, 'post_name' ),
		wp_list_pluck( $posts, 'post_name' )
	);

	/*
	 * Obtain all post types referenced in starter content to use in query.
	 * This is needed because 'any' will not account for post types not yet registered.
	 */
	$post_types = array_filter( array_merge( array( 'attachment' ), wp_list_pluck( $posts, 'post_type' ) ) );

	// Re-use auto-draft starter content posts referenced in the current customized state.
	$existing_starter_content_posts = array();
	if ( ! empty( $starter_content_auto_draft_post_ids ) ) {
		$existing_posts_query = new WP_Query(
			array(
				'post__in'       => $starter_content_auto_draft_post_ids,
				'post_status'    => 'auto-draft',
				'post_type'      => $post_types,
				'posts_per_page' => -1,
			)
		);
		foreach ( $existing_posts_query->posts as $existing_post ) {
			$post_name = $existing_post->post_name;
			if ( empty( $post_name ) ) {
				$post_name = get_post_meta( $existing_post->ID, '_customize_draft_post_name', true );
			}
			$existing_starter_content_posts[ $existing_post->post_type . ':' . $post_name ] = $existing_post;
		}
	}

	// Re-use non-auto-draft posts.
	if ( ! empty( $all_post_slugs ) ) {
		$existing_posts_query = new WP_Query(
			array(
				'post_name__in'  => $all_post_slugs,
				'post_status'    => array_diff( get_post_stati(), array( 'auto-draft' ) ),
				'post_type'      => 'any',
				'posts_per_page' => -1,
			)
		);
		foreach ( $existing_posts_query->posts as $existing_post ) {
			$key = $existing_post->post_type . ':' . $existing_post->post_name;
			if ( isset( $needed_posts[ $key ] ) && ! isset( $existing_starter_content_posts[ $key ] ) ) {
				$existing_starter_content_posts[ $key ] = $existing_post;
			}
		}
	}

	// Attachments are technically posts but handled differently.
	if ( ! empty( $attachments ) ) {

		$attachment_ids = array();

		foreach ( $attachments as $symbol => $attachment ) {
			$file_array    = array(
				'name' => $attachment['file_name'],
			);
			$file_path     = $attachment['file_path'];
			$attachment_id = null;
			$attached_file = null;
			if ( isset( $existing_starter_content_posts[ 'attachment:' . $attachment['post_name'] ] ) ) {
				$attachment_post = $existing_starter_content_posts[ 'attachment:' . $attachment['post_name'] ];
				$attachment_id   = $attachment_post->ID;
				$attached_file   = get_attached_file( $attachment_id );
				if ( empty( $attached_file ) || ! file_exists( $attached_file ) ) {
					$attachment_id = null;
					$attached_file = null;
				} elseif ( $this->get_stylesheet() !== get_post_meta( $attachment_post->ID, '_starter_content_theme', true ) ) {

					// Re-generate attachment metadata since it was previously generated for a different theme.
					$metadata = wp_generate_attachment_metadata( $attachment_post->ID, $attached_file );
					wp_update_attachment_metadata( $attachment_id, $metadata );
					update_post_meta( $attachment_id, '_starter_content_theme', $this->get_stylesheet() );
				}
			}

			// Insert the attachment auto-draft because it doesn't yet exist or the attached file is gone.
			if ( ! $attachment_id ) {

				// Copy file to temp location so that original file won't get deleted from theme after sideloading.
				$temp_file_name = wp_tempnam( wp_basename( $file_path ) );
				if ( $temp_file_name && copy( $file_path, $temp_file_name ) ) {
					$file_array['tmp_name'] = $temp_file_name;
				}
				if ( empty( $file_array['tmp_name'] ) ) {
					continue;
				}

				$attachment_post_data = array_merge(
					wp_array_slice_assoc( $attachment, array( 'post_title', 'post_content', 'post_excerpt' ) ),
					array(
						'post_status' => 'auto-draft', // So attachment will be garbage collected in a week if changeset is never published.
					)
				);

				$attachment_id = media_handle_sideload( $file_array, 0, null, $attachment_post_data );
				if ( is_wp_error( $attachment_id ) ) {
					continue;
				}
				update_post_meta( $attachment_id, '_starter_content_theme', $this->get_stylesheet() );
				update_post_meta( $attachment_id, '_customize_draft_post_name', $attachment['post_name'] );
			}

			$attachment_ids[ $symbol ] = $attachment_id;
		}
		$starter_content_auto_draft_post_ids = array_merge( $starter_content_auto_draft_post_ids, array_values( $attachment_ids ) );
	}

	// Posts & pages.
	if ( ! empty( $posts ) ) {
		foreach ( array_keys( $posts ) as $post_symbol ) {
			if ( empty( $posts[ $post_symbol ]['post_type'] ) || empty( $posts[ $post_symbol ]['post_name'] ) ) {
				continue;
			}
			$post_type = $posts[ $post_symbol ]['post_type'];
			if ( ! empty( $posts[ $post_symbol ]['post_name'] ) ) {
				$post_name = $posts[ $post_symbol ]['post_name'];
			} elseif ( ! empty( $posts[ $post_symbol ]['post_title'] ) ) {
				$post_name = sanitize_title( $posts[ $post_symbol ]['post_title'] );
			} else {
				continue;
			}

			// Use existing auto-draft post if one already exists with the same type and name.
			if ( isset( $existing_starter_content_posts[ $post_type . ':' . $post_name ] ) ) {
				$posts[ $post_symbol ]['ID'] = $existing_starter_content_posts[ $post_type . ':' . $post_name ]->ID;
				continue;
			}

			// Translate the featured image symbol.
			if ( ! empty( $posts[ $post_symbol ]['thumbnail'] )
				&& preg_match( '/^{{(?P<symbol>.+)}}$/', $posts[ $post_symbol ]['thumbnail'], $matches )
				&& isset( $attachment_ids[ $matches['symbol'] ] ) ) {
				$posts[ $post_symbol ]['meta_input']['_thumbnail_id'] = $attachment_ids[ $matches['symbol'] ];
			}

			if ( ! empty( $posts[ $post_symbol ]['template'] ) ) {
				$posts[ $post_symbol ]['meta_input']['_wp_page_template'] = $posts[ $post_symbol ]['template'];
			}

			$r = $this->nav_menus->insert_auto_draft_post( $posts[ $post_symbol ] );
			if ( $r instanceof WP_Post ) {
				$posts[ $post_symbol ]['ID'] = $r->ID;
			}
		}

		$starter_content_auto_draft_post_ids = array_merge( $starter_content_auto_draft_post_ids, wp_list_pluck( $posts, 'ID' ) );
	}

	// The nav_menus_created_posts setting is why nav_menus component is dependency for adding posts.
	if ( ! empty( $this->nav_menus ) && ! empty( $starter_content_auto_draft_post_ids ) ) {
		$setting_id = 'nav_menus_created_posts';
		$this->set_post_value( $setting_id, array_unique( array_values( $starter_content_auto_draft_post_ids ) ) );
		$this->pending_starter_content_settings_ids[] = $setting_id;
	}

	// Nav menus.
	$placeholder_id              = -1;
	$reused_nav_menu_setting_ids = array();
	foreach ( $nav_menus as $nav_menu_location => $nav_menu ) {

		$nav_menu_term_id    = null;
		$nav_menu_setting_id = null;
		$matches             = array();

		// Look for an existing placeholder menu with starter content to re-use.
		foreach ( $changeset_data as $setting_id => $setting_params ) {
			$can_reuse = (
				! empty( $setting_params['starter_content'] )
				&&
				! in_array( $setting_id, $reused_nav_menu_setting_ids, true )
				&&
				preg_match( '#^nav_menu\[(?P<nav_menu_id>-?\d+)\]$#', $setting_id, $matches )
			);
			if ( $can_reuse ) {
				$nav_menu_term_id              = (int) $matches['nav_menu_id'];
				$nav_menu_setting_id           = $setting_id;
				$reused_nav_menu_setting_ids[] = $setting_id;
				break;
			}
		}

		if ( ! $nav_menu_term_id ) {
			while ( isset( $changeset_data[ sprintf( 'nav_menu[%d]', $placeholder_id ) ] ) ) {
				--$placeholder_id;
			}
			$nav_menu_term_id    = $placeholder_id;
			$nav_menu_setting_id = sprintf( 'nav_menu[%d]', $placeholder_id );
		}

		$this->set_post_value(
			$nav_menu_setting_id,
			array(
				'name' => isset( $nav_menu['name'] ) ? $nav_menu['name'] : $nav_menu_location,
			)
		);
		$this->pending_starter_content_settings_ids[] = $nav_menu_setting_id;

		// @todo Add support for menu_item_parent.
		$position = 0;
		foreach ( $nav_menu['items'] as $nav_menu_item ) {
			$nav_menu_item_setting_id = sprintf( 'nav_menu_item[%d]', $placeholder_id-- );
			if ( ! isset( $nav_menu_item['position'] ) ) {
				$nav_menu_item['position'] = $position++;
			}
			$nav_menu_item['nav_menu_term_id'] = $nav_menu_term_id;

			if ( isset( $nav_menu_item['object_id'] ) ) {
				if ( 'post_type' === $nav_menu_item['type'] && preg_match( '/^{{(?P<symbol>.+)}}$/', $nav_menu_item['object_id'], $matches ) && isset( $posts[ $matches['symbol'] ] ) ) {
					$nav_menu_item['object_id'] = $posts[ $matches['symbol'] ]['ID'];
					if ( empty( $nav_menu_item['title'] ) ) {
						$original_object        = get_post( $nav_menu_item['object_id'] );
						$nav_menu_item['title'] = $original_object->post_title;
					}
				} else {
					continue;
				}
			} else {
				$nav_menu_item['object_id'] = 0;
			}

			if ( empty( $changeset_data[ $nav_menu_item_setting_id ] ) || ! empty( $changeset_data[ $nav_menu_item_setting_id ]['starter_content'] ) ) {
				$this->set_post_value( $nav_menu_item_setting_id, $nav_menu_item );
				$this->pending_starter_content_settings_ids[] = $nav_menu_item_setting_id;
			}
		}

		$setting_id = sprintf( 'nav_menu_locations[%s]', $nav_menu_location );
		if ( empty( $changeset_data[ $setting_id ] ) || ! empty( $changeset_data[ $setting_id ]['starter_content'] ) ) {
			$this->set_post_value( $setting_id, $nav_menu_term_id );
			$this->pending_starter_content_settings_ids[] = $setting_id;
		}
	}

	// Options.
	foreach ( $options as $name => $value ) {

		// Serialize the value to check for post symbols.
		$value = maybe_serialize( $value );

		if ( is_serialized( $value ) ) {
			if ( preg_match( '/s:\d+:"{{(?P<symbol>.+)}}"/', $value, $matches ) ) {
				if ( isset( $posts[ $matches['symbol'] ] ) ) {
					$symbol_match = $posts[ $matches['symbol'] ]['ID'];
				} elseif ( isset( $attachment_ids[ $matches['symbol'] ] ) ) {
					$symbol_match = $attachment_ids[ $matches['symbol'] ];
				}

				// If we have any symbol matches, update the values.
				if ( isset( $symbol_match ) ) {
					// Replace found string matches with post IDs.
					$value = str_replace( $matches[0], "i:{$symbol_match}", $value );
				} else {
					continue;
				}
			}
		} elseif ( preg_match( '/^{{(?P<symbol>.+)}}$/', $value, $matches ) ) {
			if ( isset( $posts[ $matches['symbol'] ] ) ) {
				$value = $posts[ $matches['symbol'] ]['ID'];
			} elseif ( isset( $attachment_ids[ $matches['symbol'] ] ) ) {
				$value = $attachment_ids[ $matches['symbol'] ];
			} else {
				continue;
			}
		}

		// Unserialize values after checking for post symbols, so they can be properly referenced.
		$value = maybe_unserialize( $value );

		if ( empty( $changeset_data[ $name ] ) || ! empty( $changeset_data[ $name ]['starter_content'] ) ) {
			$this->set_post_value( $name, $value );
			$this->pending_starter_content_settings_ids[] = $name;
		}
	}

	// Theme mods.
	foreach ( $theme_mods as $name => $value ) {

		// Serialize the value to check for post symbols.
		$value = maybe_serialize( $value );

		// Check if value was serialized.
		if ( is_serialized( $value ) ) {
			if ( preg_match( '/s:\d+:"{{(?P<symbol>.+)}}"/', $value, $matches ) ) {
				if ( isset( $posts[ $matches['symbol'] ] ) ) {
					$symbol_match = $posts[ $matches['symbol'] ]['ID'];
				} elseif ( isset( $attachment_ids[ $matches['symbol'] ] ) ) {
					$symbol_match = $attachment_ids[ $matches['symbol'] ];
				}

				// If we have any symbol matches, update the values.
				if ( isset( $symbol_match ) ) {
					// Replace found string matches with post IDs.
					$value = str_replace( $matches[0], "i:{$symbol_match}", $value );
				} else {
					continue;
				}
			}
		} elseif ( preg_match( '/^{{(?P<symbol>.+)}}$/', $value, $matches ) ) {
			if ( isset( $posts[ $matches['symbol'] ] ) ) {
				$value = $posts[ $matches['symbol'] ]['ID'];
			} elseif ( isset( $attachment_ids[ $matches['symbol'] ] ) ) {
				$value = $attachment_ids[ $matches['symbol'] ];
			} else {
				continue;
			}
		}

		// Unserialize values after checking for post symbols, so they can be properly referenced.
		$value = maybe_unserialize( $value );

		// Handle header image as special case since setting has a legacy format.
		if ( 'header_image' === $name ) {
			$name     = 'header_image_data';
			$metadata = wp_get_attachment_metadata( $value );
			if ( empty( $metadata ) ) {
				continue;
			}
			$value = array(
				'attachment_id' => $value,
				'url'           => wp_get_attachment_url( $value ),
				'height'        => $metadata['height'],
				'width'         => $metadata['width'],
			);
		} elseif ( 'background_image' === $name ) {
			$value = wp_get_attachment_url( $value );
		}

		if ( empty( $changeset_data[ $name ] ) || ! empty( $changeset_data[ $name ]['starter_content'] ) ) {
			$this->set_post_value( $name, $value );
			$this->pending_starter_content_settings_ids[] = $name;
		}
	}

	if ( ! empty( $this->pending_starter_content_settings_ids ) ) {
		if ( did_action( 'customize_register' ) ) {
			$this->_save_starter_content_changeset();
		} else {
			add_action( 'customize_register', array( $this, '_save_starter_content_changeset' ), 1000 );
		}
	}
}