wp/wp-includes/blocks/template-part.php
changeset 21 48c4eec2b7e6
parent 19 3d72ae0968f4
child 22 8c2e4d02f4ef
equal deleted inserted replaced
20:7b1b88e27a20 21:48c4eec2b7e6
     5  * @package WordPress
     5  * @package WordPress
     6  */
     6  */
     7 
     7 
     8 /**
     8 /**
     9  * Renders the `core/template-part` block on the server.
     9  * Renders the `core/template-part` block on the server.
       
    10  *
       
    11  * @since 5.9.0
       
    12  *
       
    13  * @global WP_Embed $wp_embed WordPress Embed object.
    10  *
    14  *
    11  * @param array $attributes The block attributes.
    15  * @param array $attributes The block attributes.
    12  *
    16  *
    13  * @return string The render.
    17  * @return string The render.
    14  */
    18  */
    16 	static $seen_ids = array();
    20 	static $seen_ids = array();
    17 
    21 
    18 	$template_part_id = null;
    22 	$template_part_id = null;
    19 	$content          = null;
    23 	$content          = null;
    20 	$area             = WP_TEMPLATE_PART_AREA_UNCATEGORIZED;
    24 	$area             = WP_TEMPLATE_PART_AREA_UNCATEGORIZED;
    21 
    25 	$theme            = isset( $attributes['theme'] ) ? $attributes['theme'] : get_stylesheet();
    22 	if (
    26 
    23 		isset( $attributes['slug'] ) &&
    27 	if ( isset( $attributes['slug'] ) && get_stylesheet() === $theme ) {
    24 		isset( $attributes['theme'] ) &&
    28 		$template_part_id    = $theme . '//' . $attributes['slug'];
    25 		wp_get_theme()->get_stylesheet() === $attributes['theme']
       
    26 	) {
       
    27 		$template_part_id    = $attributes['theme'] . '//' . $attributes['slug'];
       
    28 		$template_part_query = new WP_Query(
    29 		$template_part_query = new WP_Query(
    29 			array(
    30 			array(
    30 				'post_type'      => 'wp_template_part',
    31 				'post_type'           => 'wp_template_part',
    31 				'post_status'    => 'publish',
    32 				'post_status'         => 'publish',
    32 				'post_name__in'  => array( $attributes['slug'] ),
    33 				'post_name__in'       => array( $attributes['slug'] ),
    33 				'tax_query'      => array(
    34 				'tax_query'           => array(
    34 					array(
    35 					array(
    35 						'taxonomy' => 'wp_theme',
    36 						'taxonomy' => 'wp_theme',
    36 						'field'    => 'slug',
    37 						'field'    => 'name',
    37 						'terms'    => $attributes['theme'],
    38 						'terms'    => $theme,
    38 					),
    39 					),
    39 				),
    40 				),
    40 				'posts_per_page' => 1,
    41 				'posts_per_page'      => 1,
    41 				'no_found_rows'  => true,
    42 				'no_found_rows'       => true,
       
    43 				'lazy_load_term_meta' => false, // Do not lazy load term meta, as template parts only have one term.
    42 			)
    44 			)
    43 		);
    45 		);
    44 		$template_part_post  = $template_part_query->have_posts() ? $template_part_query->next_post() : null;
    46 		$template_part_post  = $template_part_query->have_posts() ? $template_part_query->next_post() : null;
    45 		if ( $template_part_post ) {
    47 		if ( $template_part_post ) {
    46 			// A published post might already exist if this template part was customized elsewhere
    48 			// A published post might already exist if this template part was customized elsewhere
    47 			// or if it's part of a customized template.
    49 			// or if it's part of a customized template.
    48 			$content    = $template_part_post->post_content;
    50 			$block_template = _build_block_template_result_from_post( $template_part_post );
    49 			$area_terms = get_the_terms( $template_part_post, 'wp_template_part_area' );
    51 			$content        = $block_template->content;
    50 			if ( ! is_wp_error( $area_terms ) && false !== $area_terms ) {
    52 			if ( isset( $block_template->area ) ) {
    51 				$area = $area_terms[0]->name;
    53 				$area = $block_template->area;
    52 			}
    54 			}
    53 			/**
    55 			/**
    54 			 * Fires when a block template part is loaded from a template post stored in the database.
    56 			 * Fires when a block template part is loaded from a template post stored in the database.
    55 			 *
    57 			 *
    56 			 * @since 5.9.0
    58 			 * @since 5.9.0
    60 			 * @param WP_Post $template_part_post The template part post object.
    62 			 * @param WP_Post $template_part_post The template part post object.
    61 			 * @param string  $content            The template part content.
    63 			 * @param string  $content            The template part content.
    62 			 */
    64 			 */
    63 			do_action( 'render_block_core_template_part_post', $template_part_id, $attributes, $template_part_post, $content );
    65 			do_action( 'render_block_core_template_part_post', $template_part_id, $attributes, $template_part_post, $content );
    64 		} else {
    66 		} else {
       
    67 			$template_part_file_path = '';
    65 			// Else, if the template part was provided by the active theme,
    68 			// Else, if the template part was provided by the active theme,
    66 			// render the corresponding file content.
    69 			// render the corresponding file content.
    67 			$parent_theme_folders        = get_block_theme_folders( get_template() );
    70 			if ( 0 === validate_file( $attributes['slug'] ) ) {
    68 			$child_theme_folders         = get_block_theme_folders( get_stylesheet() );
    71 				$block_template = get_block_file_template( $template_part_id, 'wp_template_part' );
    69 			$child_theme_part_file_path  = get_theme_file_path( '/' . $child_theme_folders['wp_template_part'] . '/' . $attributes['slug'] . '.html' );
    72 
    70 			$parent_theme_part_file_path = get_theme_file_path( '/' . $parent_theme_folders['wp_template_part'] . '/' . $attributes['slug'] . '.html' );
    73 				$content = $block_template->content;
    71 			$template_part_file_path     = 0 === validate_file( $attributes['slug'] ) && file_exists( $child_theme_part_file_path ) ? $child_theme_part_file_path : $parent_theme_part_file_path;
    74 				if ( isset( $block_template->area ) ) {
    72 			if ( 0 === validate_file( $attributes['slug'] ) && file_exists( $template_part_file_path ) ) {
    75 					$area = $block_template->area;
    73 				$content = file_get_contents( $template_part_file_path );
    76 				}
    74 				$content = is_string( $content ) && '' !== $content
    77 
    75 						? _inject_theme_attribute_in_block_template_content( $content )
    78 				// Needed for the `render_block_core_template_part_file` and `render_block_core_template_part_none` actions below.
    76 						: '';
    79 				$block_template_file = _get_block_template_file( 'wp_template_part', $attributes['slug'] );
       
    80 				if ( $block_template_file ) {
       
    81 					$template_part_file_path = $block_template_file['path'];
       
    82 				}
    77 			}
    83 			}
    78 
    84 
    79 			if ( '' !== $content && null !== $content ) {
    85 			if ( '' !== $content && null !== $content ) {
    80 				/**
    86 				/**
    81 				 * Fires when a block template part is loaded from a template part in the theme.
    87 				 * Fires when a block template part is loaded from a template part in the theme.
   103 		}
   109 		}
   104 	}
   110 	}
   105 
   111 
   106 	// WP_DEBUG_DISPLAY must only be honored when WP_DEBUG. This precedent
   112 	// WP_DEBUG_DISPLAY must only be honored when WP_DEBUG. This precedent
   107 	// is set in `wp_debug_mode()`.
   113 	// is set in `wp_debug_mode()`.
   108 	$is_debug = defined( 'WP_DEBUG' ) && WP_DEBUG &&
   114 	$is_debug = WP_DEBUG && WP_DEBUG_DISPLAY;
   109 		defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG_DISPLAY;
   115 
   110 
   116 	if ( is_null( $content ) ) {
   111 	if ( is_null( $content ) && $is_debug ) {
   117 		if ( $is_debug && isset( $attributes['slug'] ) ) {
   112 		if ( ! isset( $attributes['slug'] ) ) {
   118 			return sprintf(
   113 			// If there is no slug this is a placeholder and we dont want to return any message.
   119 				/* translators: %s: Template part slug. */
   114 			return;
   120 				__( 'Template part has been deleted or is unavailable: %s' ),
   115 		}
   121 				$attributes['slug']
   116 		return sprintf(
   122 			);
   117 			/* translators: %s: Template part slug. */
   123 		}
   118 			__( 'Template part has been deleted or is unavailable: %s' ),
   124 
   119 			$attributes['slug']
   125 		return '';
   120 		);
       
   121 	}
   126 	}
   122 
   127 
   123 	if ( isset( $seen_ids[ $template_part_id ] ) ) {
   128 	if ( isset( $seen_ids[ $template_part_id ] ) ) {
   124 		return $is_debug ?
   129 		return $is_debug ?
   125 			// translators: Visible only in the front end, this warning takes the place of a faulty block.
   130 			// translators: Visible only in the front end, this warning takes the place of a faulty block.
   126 			__( '[block rendering halted]' ) :
   131 			__( '[block rendering halted]' ) :
   127 			'';
   132 			'';
   128 	}
   133 	}
   129 
   134 
       
   135 	// Look up area definition.
       
   136 	$area_definition = null;
       
   137 	$defined_areas   = get_allowed_block_template_part_areas();
       
   138 	foreach ( $defined_areas as $defined_area ) {
       
   139 		if ( $defined_area['area'] === $area ) {
       
   140 			$area_definition = $defined_area;
       
   141 			break;
       
   142 		}
       
   143 	}
       
   144 
       
   145 	// If $area is not allowed, set it back to the uncategorized default.
       
   146 	if ( ! $area_definition ) {
       
   147 		$area = WP_TEMPLATE_PART_AREA_UNCATEGORIZED;
       
   148 	}
       
   149 
   130 	// Run through the actions that are typically taken on the_content.
   150 	// Run through the actions that are typically taken on the_content.
       
   151 	$content                       = shortcode_unautop( $content );
       
   152 	$content                       = do_shortcode( $content );
   131 	$seen_ids[ $template_part_id ] = true;
   153 	$seen_ids[ $template_part_id ] = true;
   132 	$content                       = do_blocks( $content );
   154 	$content                       = do_blocks( $content );
   133 	unset( $seen_ids[ $template_part_id ] );
   155 	unset( $seen_ids[ $template_part_id ] );
   134 	$content = wptexturize( $content );
   156 	$content = wptexturize( $content );
   135 	$content = convert_smilies( $content );
   157 	$content = convert_smilies( $content );
   136 	$content = shortcode_unautop( $content );
   158 	$content = wp_filter_content_tags( $content, "template_part_{$area}" );
   137 	$content = wp_filter_content_tags( $content );
       
   138 	$content = do_shortcode( $content );
       
   139 
   159 
   140 	// Handle embeds for block template parts.
   160 	// Handle embeds for block template parts.
   141 	global $wp_embed;
   161 	global $wp_embed;
   142 	$content = $wp_embed->autoembed( $content );
   162 	$content = $wp_embed->autoembed( $content );
   143 
   163 
   144 	if ( empty( $attributes['tagName'] ) ) {
   164 	if ( empty( $attributes['tagName'] ) || tag_escape( $attributes['tagName'] ) !== $attributes['tagName'] ) {
   145 		$defined_areas = get_allowed_block_template_part_areas();
   165 		$area_tag = 'div';
   146 		$area_tag      = 'div';
   166 		if ( $area_definition && isset( $area_definition['area_tag'] ) ) {
   147 		foreach ( $defined_areas as $defined_area ) {
   167 			$area_tag = $area_definition['area_tag'];
   148 			if ( $defined_area['area'] === $area && isset( $defined_area['area_tag'] ) ) {
       
   149 				$area_tag = $defined_area['area_tag'];
       
   150 			}
       
   151 		}
   168 		}
   152 		$html_tag = $area_tag;
   169 		$html_tag = $area_tag;
   153 	} else {
   170 	} else {
   154 		$html_tag = esc_attr( $attributes['tagName'] );
   171 		$html_tag = esc_attr( $attributes['tagName'] );
   155 	}
   172 	}
   157 
   174 
   158 	return "<$html_tag $wrapper_attributes>" . str_replace( ']]>', ']]&gt;', $content ) . "</$html_tag>";
   175 	return "<$html_tag $wrapper_attributes>" . str_replace( ']]>', ']]&gt;', $content ) . "</$html_tag>";
   159 }
   176 }
   160 
   177 
   161 /**
   178 /**
   162  * Returns an array of variation objects for the template part block.
   179  * Returns an array of area variation objects for the template part block.
       
   180  *
       
   181  * @since 6.1.0
       
   182  *
       
   183  * @param array $instance_variations The variations for instances.
   163  *
   184  *
   164  * @return array Array containing the block variation objects.
   185  * @return array Array containing the block variation objects.
   165  */
   186  */
   166 function build_template_part_block_variations() {
   187 function build_template_part_block_area_variations( $instance_variations ) {
   167 	$variations    = array();
   188 	$variations    = array();
   168 	$defined_areas = get_allowed_block_template_part_areas();
   189 	$defined_areas = get_allowed_block_template_part_areas();
       
   190 
   169 	foreach ( $defined_areas as $area ) {
   191 	foreach ( $defined_areas as $area ) {
   170 		if ( 'uncategorized' !== $area['area'] ) {
   192 		if ( 'uncategorized' !== $area['area'] ) {
       
   193 			$has_instance_for_area = false;
       
   194 			foreach ( $instance_variations as $variation ) {
       
   195 				if ( $variation['attributes']['area'] === $area['area'] ) {
       
   196 					$has_instance_for_area = true;
       
   197 					break;
       
   198 				}
       
   199 			}
       
   200 
       
   201 			$scope = $has_instance_for_area ? array() : array( 'inserter' );
       
   202 
   171 			$variations[] = array(
   203 			$variations[] = array(
   172 				'name'        => $area['area'],
   204 				'name'        => 'area_' . $area['area'],
   173 				'title'       => $area['label'],
   205 				'title'       => $area['label'],
   174 				'description' => $area['description'],
   206 				'description' => $area['description'],
   175 				'attributes'  => array(
   207 				'attributes'  => array(
   176 					'area' => $area['area'],
   208 					'area' => $area['area'],
   177 				),
   209 				),
   178 				'scope'       => array( 'inserter' ),
   210 				'scope'       => $scope,
   179 				'icon'        => $area['icon'],
   211 				'icon'        => $area['icon'],
   180 			);
   212 			);
   181 		}
   213 		}
   182 	}
   214 	}
   183 	return $variations;
   215 	return $variations;
   184 }
   216 }
   185 
   217 
   186 /**
   218 /**
       
   219  * Returns an array of instance variation objects for the template part block
       
   220  *
       
   221  * @since 6.1.0
       
   222  *
       
   223  * @return array Array containing the block variation objects.
       
   224  */
       
   225 function build_template_part_block_instance_variations() {
       
   226 	// Block themes are unavailable during installation.
       
   227 	if ( wp_installing() ) {
       
   228 		return array();
       
   229 	}
       
   230 
       
   231 	if ( ! current_theme_supports( 'block-templates' ) && ! current_theme_supports( 'block-template-parts' ) ) {
       
   232 		return array();
       
   233 	}
       
   234 
       
   235 	$variations     = array();
       
   236 	$template_parts = get_block_templates(
       
   237 		array(
       
   238 			'post_type' => 'wp_template_part',
       
   239 		),
       
   240 		'wp_template_part'
       
   241 	);
       
   242 
       
   243 	$defined_areas = get_allowed_block_template_part_areas();
       
   244 	$icon_by_area  = array_combine( array_column( $defined_areas, 'area' ), array_column( $defined_areas, 'icon' ) );
       
   245 
       
   246 	foreach ( $template_parts as $template_part ) {
       
   247 		$variations[] = array(
       
   248 			'name'        => 'instance_' . sanitize_title( $template_part->slug ),
       
   249 			'title'       => $template_part->title,
       
   250 			// If there's no description for the template part don't show the
       
   251 			// block description. This is a bit hacky, but prevent the fallback
       
   252 			// by using a non-breaking space so that the value of description
       
   253 			// isn't falsey.
       
   254 			'description' => $template_part->description || '&nbsp;',
       
   255 			'attributes'  => array(
       
   256 				'slug'  => $template_part->slug,
       
   257 				'theme' => $template_part->theme,
       
   258 				'area'  => $template_part->area,
       
   259 			),
       
   260 			'scope'       => array( 'inserter' ),
       
   261 			'icon'        => isset( $icon_by_area[ $template_part->area ] ) ? $icon_by_area[ $template_part->area ] : null,
       
   262 			'example'     => array(
       
   263 				'attributes' => array(
       
   264 					'slug'  => $template_part->slug,
       
   265 					'theme' => $template_part->theme,
       
   266 					'area'  => $template_part->area,
       
   267 				),
       
   268 			),
       
   269 		);
       
   270 	}
       
   271 	return $variations;
       
   272 }
       
   273 
       
   274 /**
       
   275  * Returns an array of all template part block variations.
       
   276  *
       
   277  * @since 5.9.0
       
   278  *
       
   279  * @return array Array containing the block variation objects.
       
   280  */
       
   281 function build_template_part_block_variations() {
       
   282 	$instance_variations = build_template_part_block_instance_variations();
       
   283 	$area_variations     = build_template_part_block_area_variations( $instance_variations );
       
   284 	return array_merge( $area_variations, $instance_variations );
       
   285 }
       
   286 
       
   287 /**
   187  * Registers the `core/template-part` block on the server.
   288  * Registers the `core/template-part` block on the server.
       
   289  *
       
   290  * @since 5.9.0
   188  */
   291  */
   189 function register_block_core_template_part() {
   292 function register_block_core_template_part() {
   190 	register_block_type_from_metadata(
   293 	register_block_type_from_metadata(
   191 		__DIR__ . '/template-part',
   294 		__DIR__ . '/template-part',
   192 		array(
   295 		array(
   193 			'render_callback' => 'render_block_core_template_part',
   296 			'render_callback'    => 'render_block_core_template_part',
   194 			'variations'      => build_template_part_block_variations(),
   297 			'variation_callback' => 'build_template_part_block_variations',
   195 		)
   298 		)
   196 	);
   299 	);
   197 }
   300 }
   198 add_action( 'init', 'register_block_core_template_part' );
   301 add_action( 'init', 'register_block_core_template_part' );