inner_blocks ) ) { foreach ( $block->inner_blocks as $inner_block ) { if ( 'core/playlist-track' === $inner_block->name ) { $inner_block->context['playlistId'] = $playlist_id; $track_attributes = $inner_block->attributes; $unique_id = $track_attributes['uniqueId'] ?? wp_unique_id( 'playlist-track-' ); $playlist_tracks[] = $unique_id; $inner_block->attributes['uniqueId'] = $unique_id; // Extract track metadata from block attributes. $title = isset( $track_attributes['title'] ) && ! empty( $track_attributes['title'] ) ? $track_attributes['title'] : __( 'Unknown title' ); $artist = $track_attributes['artist'] ?? ''; $album = $track_attributes['album'] ?? ''; $image = $track_attributes['image'] ?? ''; $url = $track_attributes['src'] ?? ''; $aria_label = $title; if ( $title && $artist && $album ) { $aria_label = sprintf( /* translators: %1$s: track title, %2$s artist name, %3$s: album name. */ _x( '%1$s by %2$s from the album %3$s', 'track title, artist name, album name' ), $title, $artist, $album ); } // Data is passed to wp_interactivity_state() which JSON-encodes it, // so we use wp_strip_all_tags() instead of esc_html() to prevent // HTML injection without double-encoding. URLs still use esc_url(). $tracks_data[ $unique_id ] = array( 'url' => esc_url( $url ), 'title' => wp_strip_all_tags( $title ), 'artist' => wp_strip_all_tags( $artist ), 'album' => wp_strip_all_tags( $album ), 'image' => esc_url( $image ), 'ariaLabel' => wp_strip_all_tags( $aria_label ), ); if ( $unique_id === $current_media_id ) { $current_unique_id = $unique_id; } } } } // If there are no tracks but there is a currentTrack set, do not render the block. // This can happen for example if the currentTrack was not deleted correctly // or if the block is manually edited in the code editor mode. if ( empty( $playlist_tracks ) || ! in_array( $current_media_id, $playlist_tracks, true ) ) { return ''; } wp_enqueue_script_module( '@wordpress/block-library/playlist/view' ); // Add the playlist tracks to the global state, // but keep them isolated from other playlists with the help of playlistId. wp_interactivity_state( 'core/playlist', array( 'playlists' => array( $playlist_id => array( 'tracks' => $tracks_data, ), ), ) ); // Add waveform player container with translated button labels. $label_play = esc_attr__( 'Play' ); $label_pause = esc_attr__( 'Pause' ); $html = '
'; // Add the HTML for the current track inside the figure. $figure = null; preg_match( '/]*>/', $content, $figure ); if ( ! empty( $figure[0] ) ) { $content = preg_replace( '/(]*>)/', '$1' . $html, $content, 1 ); } $processor = new WP_HTML_Tag_Processor( $content ); $processor->next_tag( 'figure' ); $processor->set_attribute( 'data-wp-interactive', 'core/playlist' ); // Extract the waveform style from the block style variation class. $waveform_style = 'bars'; if ( ! empty( $attributes['className'] ) && preg_match( '/is-style-([\w-]+)/', $attributes['className'], $matches ) ) { $waveform_style = $matches[1]; } $processor->set_attribute( 'data-wp-context', json_encode( array( 'playlistId' => $playlist_id, 'currentId' => $current_unique_id, 'tracks' => $playlist_tracks, 'waveformStyle' => $waveform_style, ) ) ); return $processor->get_updated_html(); } /** * Registers the `core/playlist` block on server. * * @since 6.9.0 */ function gutenberg_register_block_core_playlist() { register_block_type_from_metadata( __DIR__ . '/playlist', array( 'render_callback' => 'gutenberg_render_block_core_playlist', ) ); } add_action( 'init', 'gutenberg_register_block_core_playlist', 20 );