[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/blocks/ -> gallery.php (source)

   1  <?php
   2  /**
   3   * Server-side rendering of the `core/gallery` block.
   4   *
   5   * @package WordPress
   6   */
   7  
   8  /**
   9   * Handles backwards compatibility for Gallery Blocks,
  10   * whose images feature a `data-id` attribute.
  11   *
  12   * Now that the Gallery Block contains inner Image Blocks,
  13   * we add a custom `data-id` attribute before rendering the gallery
  14   * so that the Image Block can pick it up in its render_callback.
  15   *
  16   * @since 5.9.0
  17   *
  18   * @param array $parsed_block The block being rendered.
  19   * @return array The migrated block object.
  20   */
  21  function block_core_gallery_data_id_backcompatibility( $parsed_block ) {
  22      if ( 'core/gallery' === $parsed_block['blockName'] ) {
  23          foreach ( $parsed_block['innerBlocks'] as $key => $inner_block ) {
  24              if ( 'core/image' === $inner_block['blockName'] ) {
  25                  if ( ! isset( $parsed_block['innerBlocks'][ $key ]['attrs']['data-id'] ) && isset( $inner_block['attrs']['id'] ) ) {
  26                      $parsed_block['innerBlocks'][ $key ]['attrs']['data-id'] = esc_attr( $inner_block['attrs']['id'] );
  27                  }
  28              }
  29          }
  30      }
  31  
  32      return $parsed_block;
  33  }
  34  
  35  add_filter( 'render_block_data', 'block_core_gallery_data_id_backcompatibility' );
  36  
  37  /**
  38   * Adds a unique ID to the gallery block context.
  39   *
  40   * @since 7.0.0
  41   *
  42   * @param array $context      Default context.
  43   * @param array $parsed_block Block being rendered, filtered by render_block_data.
  44   * @return array Filtered context.
  45   */
  46  function block_core_gallery_render_context( $context, $parsed_block ) {
  47      if ( 'core/gallery' === $parsed_block['blockName'] ) {
  48          $context['galleryId'] = uniqid();
  49      }
  50      return $context;
  51  }
  52  
  53  add_filter( 'render_block_context', 'block_core_gallery_render_context', 10, 2 );
  54  
  55  /**
  56   * Renders the `core/gallery` block on the server.
  57   *
  58   * @since 6.0.0
  59   *
  60   * @param array  $attributes Attributes of the block being rendered.
  61   * @param string $content    Content of the block being rendered.
  62   * @param array  $block      The block instance being rendered.
  63   * @return string The content of the block being rendered.
  64   */
  65  function block_core_gallery_render( $attributes, $content, $block ) {
  66      // Adds a style tag for the --wp--style--unstable-gallery-gap var.
  67      // The Gallery block needs to recalculate Image block width based on
  68      // the current gap setting in order to maintain the number of flex columns
  69      // so a css var is added to allow this.
  70  
  71      $gap = $attributes['style']['spacing']['blockGap'] ?? null;
  72      // Skip if gap value contains unsupported characters.
  73      // Regex for CSS value borrowed from `safecss_filter_attr`, and used here
  74      // because we only want to match against the value, not the CSS attribute.
  75      if ( is_array( $gap ) ) {
  76          foreach ( $gap as $key => $value ) {
  77              // Make sure $value is a string to avoid PHP 8.1 deprecation error in preg_match() when the value is null.
  78              $value = is_string( $value ) ? $value : '';
  79              $value = $value && preg_match( '%[\\\(&=}]|/\*%', $value ) ? null : $value;
  80  
  81              // Get spacing CSS variable from preset value if provided.
  82              if ( is_string( $value ) && str_contains( $value, 'var:preset|spacing|' ) ) {
  83                  $index_to_splice = strrpos( $value, '|' ) + 1;
  84                  $slug            = _wp_to_kebab_case( substr( $value, $index_to_splice ) );
  85                  $value           = "var(--wp--preset--spacing--$slug)";
  86              }
  87  
  88              $gap[ $key ] = $value;
  89          }
  90      } else {
  91          // Make sure $gap is a string to avoid PHP 8.1 deprecation error in preg_match() when the value is null.
  92          $gap = is_string( $gap ) ? $gap : '';
  93          $gap = $gap && preg_match( '%[\\\(&=}]|/\*%', $gap ) ? null : $gap;
  94  
  95          // Get spacing CSS variable from preset value if provided.
  96          if ( is_string( $gap ) && str_contains( $gap, 'var:preset|spacing|' ) ) {
  97              $index_to_splice = strrpos( $gap, '|' ) + 1;
  98              $slug            = _wp_to_kebab_case( substr( $gap, $index_to_splice ) );
  99              $gap             = "var(--wp--preset--spacing--$slug)";
 100          }
 101      }
 102  
 103      $unique_gallery_classname = wp_unique_id( 'wp-block-gallery-' );
 104      $processed_content        = new WP_HTML_Tag_Processor( $content );
 105      $processed_content->next_tag();
 106      $processed_content->add_class( $unique_gallery_classname );
 107  
 108      // --gallery-block--gutter-size is deprecated. --wp--style--gallery-gap-default should be used by themes that want to set a default
 109      // gap on the gallery.
 110      $fallback_gap = 'var( --wp--style--gallery-gap-default, var( --gallery-block--gutter-size, var( --wp--style--block-gap, 0.5em ) ) )';
 111      $gap_value    = $gap ? $gap : $fallback_gap;
 112      $gap_column   = $gap_value;
 113  
 114      if ( is_array( $gap_value ) ) {
 115          $gap_row    = $gap_value['top'] ?? $fallback_gap;
 116          $gap_column = $gap_value['left'] ?? $fallback_gap;
 117          $gap_value  = $gap_row === $gap_column ? $gap_row : $gap_row . ' ' . $gap_column;
 118      }
 119  
 120      // The unstable gallery gap calculation requires a real value (such as `0px`) and not `0`.
 121      if ( '0' === $gap_column ) {
 122          $gap_column = '0px';
 123      }
 124  
 125      // Set the CSS variable to the column value, and the `gap` property to the combined gap value.
 126      $gallery_styles = array(
 127          array(
 128              'selector'     => ".wp-block-gallery.{$unique_gallery_classname}",
 129              'declarations' => array(
 130                  '--wp--style--unstable-gallery-gap' => $gap_column,
 131                  'gap'                               => $gap_value,
 132              ),
 133          ),
 134      );
 135  
 136      wp_style_engine_get_stylesheet_from_css_rules(
 137          $gallery_styles,
 138          array( 'context' => 'block-supports' )
 139      );
 140  
 141      // The WP_HTML_Tag_Processor class calls get_updated_html() internally
 142      // when the instance is treated as a string, but here we explicitly
 143      // convert it to a string.
 144      $updated_content = $processed_content->get_updated_html();
 145  
 146      /*
 147       * Randomize the order of image blocks. Ideally we should shuffle
 148       * the `$parsed_block['innerBlocks']` via the `render_block_data` hook.
 149       * However, this hook doesn't apply inner block updates when blocks are
 150       * nested.
 151       * @todo In the future, if this hook supports updating innerBlocks in
 152       * nested blocks, it should be refactored.
 153       *
 154       * @see: https://github.com/WordPress/gutenberg/pull/58733
 155       */
 156      if ( ! empty( $attributes['randomOrder'] ) ) {
 157          // This pattern matches figure elements with the `wp-block-image`
 158          // class to avoid the gallery's wrapping `figure` element and
 159          // extract images only.
 160          $pattern = '/<figure[^>]*\bwp-block-image\b[^>]*>.*?<\/figure>/s';
 161  
 162          preg_match_all( $pattern, $updated_content, $matches );
 163          if ( $matches ) {
 164              $image_blocks = $matches[0];
 165              shuffle( $image_blocks );
 166  
 167              $i               = 0;
 168              $updated_content = preg_replace_callback(
 169                  $pattern,
 170                  static function () use ( $image_blocks, &$i ) {
 171                      return $image_blocks[ $i++ ];
 172                  },
 173                  $updated_content
 174              );
 175          }
 176      }
 177  
 178      // Gets all image IDs from the state that match this gallery's ID.
 179      $state      = wp_interactivity_state( 'core/image' );
 180      $gallery_id = $block->context['galleryId'] ?? null;
 181      $image_ids  = array();
 182  
 183      // Extracts image IDs from state metadata that match the current gallery ID.
 184      if ( isset( $gallery_id ) && isset( $state['metadata'] ) ) {
 185          foreach ( $state['metadata'] as $image_id => $metadata ) {
 186              if ( isset( $metadata['galleryId'] ) && $metadata['galleryId'] === $gallery_id ) {
 187                  $image_ids[] = $image_id;
 188              }
 189          }
 190      }
 191  
 192      // If there are image IDs associated with this gallery, set interactivity
 193      // attributes and order metadata for lightbox navigation.
 194      if ( ! empty( $image_ids ) ) {
 195          $total          = count( $image_ids );
 196          $lightbox_index = 0;
 197          $processor      = new WP_HTML_Tag_Processor( $updated_content );
 198          $processor->next_tag();
 199          $processor->set_attribute( 'data-wp-interactive', 'core/gallery' );
 200          $processor->set_attribute(
 201              'data-wp-context',
 202              wp_json_encode(
 203                  array( 'galleryId' => $gallery_id ),
 204                  JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
 205              )
 206          );
 207          while ( $processor->next_tag( 'figure' ) ) {
 208              $wp_key = $processor->get_attribute( 'data-wp-key' );
 209              if ( $wp_key && isset( $state['metadata'][ $wp_key ] ) ) {
 210                  $alt = $state['metadata'][ $wp_key ]['alt'];
 211                  wp_interactivity_state(
 212                      'core/image',
 213                      array(
 214                          'metadata' => array(
 215                              $wp_key => array(
 216                                  'customAriaLabel'        => empty( $alt )
 217                                      /* translators: %1$s: current image index, %2$s: total number of images */
 218                                      ? sprintf( __( 'Enlarged image %1$s of %2$s' ), $lightbox_index + 1, $total )
 219                                      /* translators: %1$s: current image index, %2$s: total number of images, %3$s: Image alt text */
 220                                      : sprintf( __( 'Enlarged image %1$s of %2$s: %3$s' ), $lightbox_index + 1, $total, $alt ),
 221                                  /* translators: %1$s: current image index, %2$s: total number of images */
 222                                  'triggerButtonAriaLabel' => sprintf( __( 'Enlarge %1$s of %2$s' ), $lightbox_index + 1, $total ),
 223                                  'order'                  => $lightbox_index,
 224                              ),
 225                          ),
 226                      )
 227                  );
 228                  ++$lightbox_index;
 229              }
 230          }
 231          return $processor->get_updated_html();
 232      }
 233  
 234      return $updated_content;
 235  }
 236  
 237  /**
 238   * Registers the `core/gallery` block on server.
 239   *
 240   * @since 5.9.0
 241   */
 242  function register_block_core_gallery() {
 243      register_block_type_from_metadata(
 244          __DIR__ . '/gallery',
 245          array(
 246              'render_callback' => 'block_core_gallery_render',
 247          )
 248      );
 249  }
 250  
 251  add_action( 'init', 'register_block_core_gallery' );


Generated : Thu Jun 18 08:20:10 2026 Cross-referenced by PHPXref