Customize: Reuse existing non-auto-draft posts and existing auto-draft posts in the customized state with matching slugs when applying starter content.

* Updates `wp_unique_post_slug()` to ignore `auto-draft` posts. Prevents publishing multiple posts that have the same slugs from starter content.
* Fixes fatal error when attempting to save an header_image setting from a non-admin context.
* Fixes substituting attachment symbols in options and theme mods.
* Fixes applying starter content for header images and background images.

Merges [39411] to 4.7 branch.
See #38114.
Fixes #38928 for 4.7.

Built from https://develop.svn.wordpress.org/branches/4.7@39412


git-svn-id: http://core.svn.wordpress.org/branches/4.7@39352 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Weston Ruter 2016-12-02 00:26:34 +00:00
parent 816196aee7
commit 90cd5e5f80
4 changed files with 157 additions and 48 deletions

View File

@ -971,6 +971,32 @@ final class WP_Customize_Manager {
$starter_content_auto_draft_post_ids = array_merge( $starter_content_auto_draft_post_ids, $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' )
);
// Re-use auto-draft starter content posts referenced in the current customized state.
$existing_starter_content_posts = array(); $existing_starter_content_posts = array();
if ( ! empty( $starter_content_auto_draft_post_ids ) ) { if ( ! empty( $starter_content_auto_draft_post_ids ) ) {
$existing_posts_query = new WP_Query( array( $existing_posts_query = new WP_Query( array(
@ -984,50 +1010,32 @@ final class WP_Customize_Manager {
} }
} }
// 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',
'number' => -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. // Attachments are technically posts but handled differently.
if ( ! empty( $attachments ) ) { if ( ! empty( $attachments ) ) {
// Such is The WordPress Way.
require_once( ABSPATH . 'wp-admin/includes/file.php' );
require_once( ABSPATH . 'wp-admin/includes/media.php' );
require_once( ABSPATH . 'wp-admin/includes/image.php' );
$attachment_ids = array(); $attachment_ids = array();
foreach ( $attachments as $symbol => $attachment ) { foreach ( $attachments as $symbol => $attachment ) {
$file_array = array(
// A file is required and URLs to files are not currently allowed. 'name' => $attachment['file_name'],
if ( empty( $attachment['file'] ) || preg_match( '#^https?://$#', $attachment['file'] ) ) { );
continue; $file_path = $attachment['file_path'];
}
$file_array = array();
$file_path = null;
if ( file_exists( $attachment['file'] ) ) {
$file_path = $attachment['file']; // Could be absolute path to file in plugin.
} elseif ( is_child_theme() && file_exists( get_stylesheet_directory() . '/' . $attachment['file'] ) ) {
$file_path = get_stylesheet_directory() . '/' . $attachment['file'];
} elseif ( file_exists( get_template_directory() . '/' . $attachment['file'] ) ) {
$file_path = get_template_directory() . '/' . $attachment['file'];
} else {
continue;
}
$file_array['name'] = basename( $attachment['file'] );
// Skip file types that are not recognized.
$checked_filetype = wp_check_filetype( $file_array['name'] );
if ( empty( $checked_filetype['type'] ) ) {
continue;
}
// Ensure post_name is set since not automatically derived from post_title for new auto-draft posts.
if ( empty( $attachment['post_name'] ) ) {
if ( ! empty( $attachment['post_title'] ) ) {
$attachment['post_name'] = sanitize_title( $attachment['post_title'] );
} else {
$attachment['post_name'] = sanitize_title( preg_replace( '/\.\w+$/', '', $file_array['name'] ) );
}
}
$attachment_id = null; $attachment_id = null;
$attached_file = null; $attached_file = null;
if ( isset( $existing_starter_content_posts[ 'attachment:' . $attachment['post_name'] ] ) ) { if ( isset( $existing_starter_content_posts[ 'attachment:' . $attachment['post_name'] ] ) ) {
@ -1080,14 +1088,14 @@ final class WP_Customize_Manager {
} }
$attachment_ids[ $symbol ] = $attachment_id; $attachment_ids[ $symbol ] = $attachment_id;
$starter_content_auto_draft_post_ids = array_merge( $starter_content_auto_draft_post_ids, array_values( $attachment_ids ) );
} }
$starter_content_auto_draft_post_ids = array_merge( $starter_content_auto_draft_post_ids, array_values( $attachment_ids ) );
} }
// Posts & pages. // Posts & pages.
if ( ! empty( $posts ) ) { if ( ! empty( $posts ) ) {
foreach ( array_keys( $posts ) as $post_symbol ) { foreach ( array_keys( $posts ) as $post_symbol ) {
if ( empty( $posts[ $post_symbol ]['post_type'] ) ) { if ( empty( $posts[ $post_symbol ]['post_type'] ) || empty( $posts[ $post_symbol ]['post_name'] ) ) {
continue; continue;
} }
$post_type = $posts[ $post_symbol ]['post_type']; $post_type = $posts[ $post_symbol ]['post_type'];
@ -1209,8 +1217,14 @@ final class WP_Customize_Manager {
// Options. // Options.
foreach ( $options as $name => $value ) { foreach ( $options as $name => $value ) {
if ( preg_match( '/^{{(?P<symbol>.+)}}$/', $value, $matches ) && isset( $posts[ $matches['symbol'] ] ) ) { if ( preg_match( '/^{{(?P<symbol>.+)}}$/', $value, $matches ) ) {
if ( isset( $posts[ $matches['symbol'] ] ) ) {
$value = $posts[ $matches['symbol'] ]['ID']; $value = $posts[ $matches['symbol'] ]['ID'];
} elseif ( isset( $attachment_ids[ $matches['symbol'] ] ) ) {
$value = $attachment_ids[ $matches['symbol'] ];
} else {
continue;
}
} }
if ( empty( $changeset_data[ $name ] ) || ! empty( $changeset_data[ $name ]['starter_content'] ) ) { if ( empty( $changeset_data[ $name ] ) || ! empty( $changeset_data[ $name ]['starter_content'] ) ) {
@ -1221,8 +1235,31 @@ final class WP_Customize_Manager {
// Theme mods. // Theme mods.
foreach ( $theme_mods as $name => $value ) { foreach ( $theme_mods as $name => $value ) {
if ( preg_match( '/^{{(?P<symbol>.+)}}$/', $value, $matches ) && isset( $posts[ $matches['symbol'] ] ) ) { if ( preg_match( '/^{{(?P<symbol>.+)}}$/', $value, $matches ) ) {
if ( isset( $posts[ $matches['symbol'] ] ) ) {
$value = $posts[ $matches['symbol'] ]['ID']; $value = $posts[ $matches['symbol'] ]['ID'];
} elseif ( isset( $attachment_ids[ $matches['symbol'] ] ) ) {
$value = $attachment_ids[ $matches['symbol'] ];
} else {
continue;
}
}
// 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'] ) ) { if ( empty( $changeset_data[ $name ] ) || ! empty( $changeset_data[ $name ]['starter_content'] ) ) {
@ -1240,6 +1277,69 @@ final class WP_Customize_Manager {
} }
} }
/**
* Prepare starter content attachments.
*
* Ensure that the attachments are valid and that they have slugs and file name/path.
*
* @since 4.7.0
* @access private
*
* @param array $attachments Attachments.
* @return array Prepared attachments.
*/
protected function prepare_starter_content_attachments( $attachments ) {
$prepared_attachments = array();
if ( empty( $attachments ) ) {
return $prepared_attachments;
}
// Such is The WordPress Way.
require_once( ABSPATH . 'wp-admin/includes/file.php' );
require_once( ABSPATH . 'wp-admin/includes/media.php' );
require_once( ABSPATH . 'wp-admin/includes/image.php' );
foreach ( $attachments as $symbol => $attachment ) {
// A file is required and URLs to files are not currently allowed.
if ( empty( $attachment['file'] ) || preg_match( '#^https?://$#', $attachment['file'] ) ) {
continue;
}
$file_path = null;
if ( file_exists( $attachment['file'] ) ) {
$file_path = $attachment['file']; // Could be absolute path to file in plugin.
} elseif ( is_child_theme() && file_exists( get_stylesheet_directory() . '/' . $attachment['file'] ) ) {
$file_path = get_stylesheet_directory() . '/' . $attachment['file'];
} elseif ( file_exists( get_template_directory() . '/' . $attachment['file'] ) ) {
$file_path = get_template_directory() . '/' . $attachment['file'];
} else {
continue;
}
$file_name = basename( $attachment['file'] );
// Skip file types that are not recognized.
$checked_filetype = wp_check_filetype( $file_name );
if ( empty( $checked_filetype['type'] ) ) {
continue;
}
// Ensure post_name is set since not automatically derived from post_title for new auto-draft posts.
if ( empty( $attachment['post_name'] ) ) {
if ( ! empty( $attachment['post_title'] ) ) {
$attachment['post_name'] = sanitize_title( $attachment['post_title'] );
} else {
$attachment['post_name'] = sanitize_title( preg_replace( '/\.\w+$/', '', $file_name ) );
}
}
$attachment['file_name'] = $file_name;
$attachment['file_path'] = $file_path;
$prepared_attachments[ $symbol ] = $attachment;
}
return $prepared_attachments;
}
/** /**
* Save starter content changeset. * Save starter content changeset.
* *

View File

@ -29,6 +29,15 @@ final class WP_Customize_Header_Image_Setting extends WP_Customize_Setting {
public function update( $value ) { public function update( $value ) {
global $custom_image_header; global $custom_image_header;
// If _custom_header_background_just_in_time() fails to initialize $custom_image_header when not is_admin().
if ( empty( $custom_image_header ) ) {
require_once( ABSPATH . 'wp-admin/custom-header.php' );
$args = get_theme_support( 'custom-header' );
$admin_head_callback = isset( $args[0]['admin-head-callback'] ) ? $args[0]['admin-head-callback'] : null;
$admin_preview_callback = isset( $args[0]['admin-preview-callback'] ) ? $args[0]['admin-preview-callback'] : null;
$custom_image_header = new Custom_Image_Header( $admin_head_callback, $admin_preview_callback );
}
// If the value doesn't exist (removed or random), // If the value doesn't exist (removed or random),
// use the header_image value. // use the header_image value.
if ( ! $value ) if ( ! $value )

View File

@ -3673,7 +3673,7 @@ function wp_unique_post_slug( $slug, $post_ID, $post_status, $post_type, $post_p
if ( 'attachment' == $post_type ) { if ( 'attachment' == $post_type ) {
// Attachment slugs must be unique across all types. // Attachment slugs must be unique across all types.
$check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND ID != %d LIMIT 1"; $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_status != 'auto-draft' AND post_name = %s AND ID != %d LIMIT 1";
$post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_ID ) ); $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_ID ) );
/** /**
@ -3701,7 +3701,7 @@ function wp_unique_post_slug( $slug, $post_ID, $post_status, $post_type, $post_p
* Page slugs must be unique within their own trees. Pages are in a separate * Page slugs must be unique within their own trees. Pages are in a separate
* namespace than posts so page slugs are allowed to overlap post slugs. * namespace than posts so page slugs are allowed to overlap post slugs.
*/ */
$check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type IN ( %s, 'attachment' ) AND ID != %d AND post_parent = %d LIMIT 1"; $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_status != 'auto-draft' AND post_name = %s AND post_type IN ( %s, 'attachment' ) AND ID != %d AND post_parent = %d LIMIT 1";
$post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_type, $post_ID, $post_parent ) ); $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_type, $post_ID, $post_parent ) );
/** /**
@ -3725,7 +3725,7 @@ function wp_unique_post_slug( $slug, $post_ID, $post_status, $post_type, $post_p
} }
} else { } else {
// Post slugs must be unique across all posts. // Post slugs must be unique across all posts.
$check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type = %s AND ID != %d LIMIT 1"; $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_status != 'auto-draft' AND post_name = %s AND post_type = %s AND ID != %d LIMIT 1";
$post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_type, $post_ID ) ); $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_type, $post_ID ) );
// Prevent new post slugs that could result in URLs that conflict with date archives. // Prevent new post slugs that could result in URLs that conflict with date archives.

View File

@ -4,7 +4,7 @@
* *
* @global string $wp_version * @global string $wp_version
*/ */
$wp_version = '4.7-RC1-39410'; $wp_version = '4.7-RC1-39412';
/** /**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema. * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.