[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

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

   1  <?php
   2  /**
   3   * Functions related to registering and parsing blocks.
   4   *
   5   * @package WordPress
   6   * @subpackage Blocks
   7   * @since 5.0.0
   8   */
   9  
  10  /**
  11   * Registers a block type.
  12   *
  13   * @since 5.0.0
  14   *
  15   * @param string|WP_Block_Type $name Block type name including namespace, or alternatively
  16   *                                   a complete WP_Block_Type instance. In case a WP_Block_Type
  17   *                                   is provided, the $args parameter will be ignored.
  18   * @param array                $args {
  19   *     Optional. Array of block type arguments. Accepts any public property of `WP_Block_Type`.
  20   *     Any arguments may be defined, however the ones described below are supported by default.
  21   *     Default empty array.
  22   *
  23   *     @type callable $render_callback Callback used to render blocks of this block type.
  24   * }
  25   * @return WP_Block_Type|false The registered block type on success, or false on failure.
  26   */
  27  function register_block_type( $name, $args = array() ) {
  28      return WP_Block_Type_Registry::get_instance()->register( $name, $args );
  29  }
  30  
  31  /**
  32   * Unregisters a block type.
  33   *
  34   * @since 5.0.0
  35   *
  36   * @param string|WP_Block_Type $name Block type name including namespace, or alternatively
  37   *                                   a complete WP_Block_Type instance.
  38   * @return WP_Block_Type|false The unregistered block type on success, or false on failure.
  39   */
  40  function unregister_block_type( $name ) {
  41      return WP_Block_Type_Registry::get_instance()->unregister( $name );
  42  }
  43  
  44  /**
  45   * Removes the block asset's path prefix if provided.
  46   *
  47   * @since 5.5.0
  48   *
  49   * @param string $asset_handle_or_path Asset handle or prefixed path.
  50   * @return string Path without the prefix or the original value.
  51   */
  52  function remove_block_asset_path_prefix( $asset_handle_or_path ) {
  53      $path_prefix = 'file:';
  54      if ( 0 !== strpos( $asset_handle_or_path, $path_prefix ) ) {
  55          return $asset_handle_or_path;
  56      }
  57      return substr(
  58          $asset_handle_or_path,
  59          strlen( $path_prefix )
  60      );
  61  }
  62  
  63  /**
  64   * Generates the name for an asset based on the name of the block
  65   * and the field name provided.
  66   *
  67   * @since 5.5.0
  68   *
  69   * @param string $block_name Name of the block.
  70   * @param string $field_name Name of the metadata field.
  71   * @return string Generated asset name for the block's field.
  72   */
  73  function generate_block_asset_handle( $block_name, $field_name ) {
  74      if ( 0 === strpos( $block_name, 'core/' ) ) {
  75          $asset_handle = str_replace( 'core/', 'wp-block-', $block_name );
  76          if ( 0 === strpos( $field_name, 'editor' ) ) {
  77              $asset_handle .= '-editor';
  78          }
  79          return $asset_handle;
  80      }
  81  
  82      $field_mappings = array(
  83          'editorScript' => 'editor-script',
  84          'script'       => 'script',
  85          'editorStyle'  => 'editor-style',
  86          'style'        => 'style',
  87      );
  88      return str_replace( '/', '-', $block_name ) .
  89          '-' . $field_mappings[ $field_name ];
  90  }
  91  
  92  /**
  93   * Finds a script handle for the selected block metadata field. It detects
  94   * when a path to file was provided and finds a corresponding asset file
  95   * with details necessary to register the script under automatically
  96   * generated handle name. It returns unprocessed script handle otherwise.
  97   *
  98   * @since 5.5.0
  99   *
 100   * @param array  $metadata   Block metadata.
 101   * @param string $field_name Field name to pick from metadata.
 102   * @return string|false Script handle provided directly or created through
 103   *                      script's registration, or false on failure.
 104   */
 105  function register_block_script_handle( $metadata, $field_name ) {
 106      if ( empty( $metadata[ $field_name ] ) ) {
 107          return false;
 108      }
 109      $script_handle = $metadata[ $field_name ];
 110      $script_path   = remove_block_asset_path_prefix( $metadata[ $field_name ] );
 111      if ( $script_handle === $script_path ) {
 112          return $script_handle;
 113      }
 114  
 115      $script_handle     = generate_block_asset_handle( $metadata['name'], $field_name );
 116      $script_asset_path = realpath(
 117          dirname( $metadata['file'] ) . '/' .
 118          substr_replace( $script_path, '.asset.php', - strlen( '.js' ) )
 119      );
 120      if ( ! file_exists( $script_asset_path ) ) {
 121          $message = sprintf(
 122              /* translators: %1: field name. %2: block name */
 123              __( 'The asset file for the "%1$s" defined in "%2$s" block definition is missing.', 'default' ),
 124              $field_name,
 125              $metadata['name']
 126          );
 127          _doing_it_wrong( __FUNCTION__, $message, '5.5.0' );
 128          return false;
 129      }
 130      $script_asset = require $script_asset_path;
 131      $result       = wp_register_script(
 132          $script_handle,
 133          plugins_url( $script_path, $metadata['file'] ),
 134          $script_asset['dependencies'],
 135          $script_asset['version']
 136      );
 137      if ( ! $result ) {
 138          return false;
 139      }
 140  
 141      if ( ! empty( $metadata['textdomain'] ) ) {
 142          wp_set_script_translations( $script_handle, $metadata['textdomain'] );
 143      }
 144  
 145      return $script_handle;
 146  }
 147  
 148  /**
 149   * Finds a style handle for the block metadata field. It detects when a path
 150   * to file was provided and registers the style under automatically
 151   * generated handle name. It returns unprocessed style handle otherwise.
 152   *
 153   * @since 5.5.0
 154   *
 155   * @param array  $metadata Block metadata.
 156   * @param string $field_name Field name to pick from metadata.
 157   * @return string|false Style handle provided directly or created through
 158   *                      style's registration, or false on failure.
 159   */
 160  function register_block_style_handle( $metadata, $field_name ) {
 161      if ( empty( $metadata[ $field_name ] ) ) {
 162          return false;
 163      }
 164      $style_handle = $metadata[ $field_name ];
 165      $style_path   = remove_block_asset_path_prefix( $metadata[ $field_name ] );
 166      if ( $style_handle === $style_path ) {
 167          return $style_handle;
 168      }
 169  
 170      $style_handle = generate_block_asset_handle( $metadata['name'], $field_name );
 171      $block_dir    = dirname( $metadata['file'] );
 172      $style_file   = realpath( "$block_dir/$style_path" );
 173      $result       = wp_register_style(
 174          $style_handle,
 175          plugins_url( $style_path, $metadata['file'] ),
 176          array(),
 177          filemtime( $style_file )
 178      );
 179      if ( file_exists( str_replace( '.css', '-rtl.css', $style_file ) ) ) {
 180          wp_style_add_data( $style_handle, 'rtl', 'replace' );
 181      }
 182  
 183      return $result ? $style_handle : false;
 184  }
 185  
 186  /**
 187   * Registers a block type from metadata stored in the `block.json` file.
 188   *
 189   * @since 5.5.0
 190   *
 191   * @param string $file_or_folder Path to the JSON file with metadata definition for
 192   *                               the block or path to the folder where the `block.json` file is located.
 193   * @param array  $args {
 194   *     Optional. Array of block type arguments. Accepts any public property of `WP_Block_Type`.
 195   *     Any arguments may be defined, however the ones described below are supported by default.
 196   *     Default empty array.
 197   *
 198   *     @type callable $render_callback Callback used to render blocks of this block type.
 199   * }
 200   * @return WP_Block_Type|false The registered block type on success, or false on failure.
 201   */
 202  function register_block_type_from_metadata( $file_or_folder, $args = array() ) {
 203      $filename      = 'block.json';
 204      $metadata_file = ( substr( $file_or_folder, -strlen( $filename ) ) !== $filename ) ?
 205          trailingslashit( $file_or_folder ) . $filename :
 206          $file_or_folder;
 207      if ( ! file_exists( $metadata_file ) ) {
 208          return false;
 209      }
 210  
 211      $metadata = json_decode( file_get_contents( $metadata_file ), true );
 212      if ( ! is_array( $metadata ) || empty( $metadata['name'] ) ) {
 213          return false;
 214      }
 215      $metadata['file'] = $metadata_file;
 216  
 217      /**
 218       * Filters the metadata provided for registering a block type.
 219       *
 220       * @since 5.7.0
 221       *
 222       * @param array $metadata Metadata for registering a block type.
 223       */
 224      $metadata = apply_filters( 'block_type_metadata', $metadata );
 225  
 226      $settings          = array();
 227      $property_mappings = array(
 228          'title'           => 'title',
 229          'category'        => 'category',
 230          'parent'          => 'parent',
 231          'icon'            => 'icon',
 232          'description'     => 'description',
 233          'keywords'        => 'keywords',
 234          'attributes'      => 'attributes',
 235          'providesContext' => 'provides_context',
 236          'usesContext'     => 'uses_context',
 237          'supports'        => 'supports',
 238          'styles'          => 'styles',
 239          'example'         => 'example',
 240          'apiVersion'      => 'api_version',
 241      );
 242  
 243      foreach ( $property_mappings as $key => $mapped_key ) {
 244          if ( isset( $metadata[ $key ] ) ) {
 245              $value = $metadata[ $key ];
 246              if ( empty( $metadata['textdomain'] ) ) {
 247                  $settings[ $mapped_key ] = $value;
 248                  continue;
 249              }
 250              $textdomain = $metadata['textdomain'];
 251              switch ( $key ) {
 252                  case 'title':
 253                  case 'description':
 254                      // phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralContext,WordPress.WP.I18n.NonSingularStringLiteralDomain
 255                      $settings[ $mapped_key ] = translate_with_gettext_context( $value, sprintf( 'block %s', $key ), $textdomain );
 256                      break;
 257                  case 'keywords':
 258                      $settings[ $mapped_key ] = array();
 259                      if ( ! is_array( $value ) ) {
 260                          continue 2;
 261                      }
 262  
 263                      foreach ( $value as $keyword ) {
 264                          // phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain
 265                          $settings[ $mapped_key ][] = translate_with_gettext_context( $keyword, 'block keyword', $textdomain );
 266                      }
 267  
 268                      break;
 269                  case 'styles':
 270                      $settings[ $mapped_key ] = array();
 271                      if ( ! is_array( $value ) ) {
 272                          continue 2;
 273                      }
 274  
 275                      foreach ( $value as $style ) {
 276                          if ( ! empty( $style['label'] ) ) {
 277                              // phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain
 278                              $style['label'] = translate_with_gettext_context( $style['label'], 'block style label', $textdomain );
 279                          }
 280                          $settings[ $mapped_key ][] = $style;
 281                      }
 282  
 283                      break;
 284                  default:
 285                      $settings[ $mapped_key ] = $value;
 286              }
 287          }
 288      }
 289  
 290      if ( ! empty( $metadata['editorScript'] ) ) {
 291          $settings['editor_script'] = register_block_script_handle(
 292              $metadata,
 293              'editorScript'
 294          );
 295      }
 296  
 297      if ( ! empty( $metadata['script'] ) ) {
 298          $settings['script'] = register_block_script_handle(
 299              $metadata,
 300              'script'
 301          );
 302      }
 303  
 304      if ( ! empty( $metadata['editorStyle'] ) ) {
 305          $settings['editor_style'] = register_block_style_handle(
 306              $metadata,
 307              'editorStyle'
 308          );
 309      }
 310  
 311      if ( ! empty( $metadata['style'] ) ) {
 312          $settings['style'] = register_block_style_handle(
 313              $metadata,
 314              'style'
 315          );
 316      }
 317  
 318      /**
 319       * Filters the settings determined from the block type metadata.
 320       *
 321       * @since 5.7.0
 322       *
 323       * @param array $settings Array of determined settings for registering a block type.
 324       * @param array $metadata Metadata provided for registering a block type.
 325       */
 326      $settings = apply_filters(
 327          'block_type_metadata_settings',
 328          array_merge(
 329              $settings,
 330              $args
 331          ),
 332          $metadata
 333      );
 334  
 335      return register_block_type(
 336          $metadata['name'],
 337          $settings
 338      );
 339  }
 340  
 341  /**
 342   * Determine whether a post or content string has blocks.
 343   *
 344   * This test optimizes for performance rather than strict accuracy, detecting
 345   * the pattern of a block but not validating its structure. For strict accuracy,
 346   * you should use the block parser on post content.
 347   *
 348   * @since 5.0.0
 349   *
 350   * @see parse_blocks()
 351   *
 352   * @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object. Defaults to global $post.
 353   * @return bool Whether the post has blocks.
 354   */
 355  function has_blocks( $post = null ) {
 356      if ( ! is_string( $post ) ) {
 357          $wp_post = get_post( $post );
 358          if ( $wp_post instanceof WP_Post ) {
 359              $post = $wp_post->post_content;
 360          }
 361      }
 362  
 363      return false !== strpos( (string) $post, '<!-- wp:' );
 364  }
 365  
 366  /**
 367   * Determine whether a $post or a string contains a specific block type.
 368   *
 369   * This test optimizes for performance rather than strict accuracy, detecting
 370   * the block type exists but not validating its structure. For strict accuracy,
 371   * you should use the block parser on post content.
 372   *
 373   * @since 5.0.0
 374   *
 375   * @see parse_blocks()
 376   *
 377   * @param string                  $block_name Full Block type to look for.
 378   * @param int|string|WP_Post|null $post Optional. Post content, post ID, or post object. Defaults to global $post.
 379   * @return bool Whether the post content contains the specified block.
 380   */
 381  function has_block( $block_name, $post = null ) {
 382      if ( ! has_blocks( $post ) ) {
 383          return false;
 384      }
 385  
 386      if ( ! is_string( $post ) ) {
 387          $wp_post = get_post( $post );
 388          if ( $wp_post instanceof WP_Post ) {
 389              $post = $wp_post->post_content;
 390          }
 391      }
 392  
 393      /*
 394       * Normalize block name to include namespace, if provided as non-namespaced.
 395       * This matches behavior for WordPress 5.0.0 - 5.3.0 in matching blocks by
 396       * their serialized names.
 397       */
 398      if ( false === strpos( $block_name, '/' ) ) {
 399          $block_name = 'core/' . $block_name;
 400      }
 401  
 402      // Test for existence of block by its fully qualified name.
 403      $has_block = false !== strpos( $post, '<!-- wp:' . $block_name . ' ' );
 404  
 405      if ( ! $has_block ) {
 406          /*
 407           * If the given block name would serialize to a different name, test for
 408           * existence by the serialized form.
 409           */
 410          $serialized_block_name = strip_core_block_namespace( $block_name );
 411          if ( $serialized_block_name !== $block_name ) {
 412              $has_block = false !== strpos( $post, '<!-- wp:' . $serialized_block_name . ' ' );
 413          }
 414      }
 415  
 416      return $has_block;
 417  }
 418  
 419  /**
 420   * Returns an array of the names of all registered dynamic block types.
 421   *
 422   * @since 5.0.0
 423   *
 424   * @return string[] Array of dynamic block names.
 425   */
 426  function get_dynamic_block_names() {
 427      $dynamic_block_names = array();
 428  
 429      $block_types = WP_Block_Type_Registry::get_instance()->get_all_registered();
 430      foreach ( $block_types as $block_type ) {
 431          if ( $block_type->is_dynamic() ) {
 432              $dynamic_block_names[] = $block_type->name;
 433          }
 434      }
 435  
 436      return $dynamic_block_names;
 437  }
 438  
 439  /**
 440   * Given an array of attributes, returns a string in the serialized attributes
 441   * format prepared for post content.
 442   *
 443   * The serialized result is a JSON-encoded string, with unicode escape sequence
 444   * substitution for characters which might otherwise interfere with embedding
 445   * the result in an HTML comment.
 446   *
 447   * @since 5.3.1
 448   *
 449   * @param array $block_attributes Attributes object.
 450   * @return string Serialized attributes.
 451   */
 452  function serialize_block_attributes( $block_attributes ) {
 453      $encoded_attributes = json_encode( $block_attributes );
 454      $encoded_attributes = preg_replace( '/--/', '\\u002d\\u002d', $encoded_attributes );
 455      $encoded_attributes = preg_replace( '/</', '\\u003c', $encoded_attributes );
 456      $encoded_attributes = preg_replace( '/>/', '\\u003e', $encoded_attributes );
 457      $encoded_attributes = preg_replace( '/&/', '\\u0026', $encoded_attributes );
 458      // Regex: /\\"/
 459      $encoded_attributes = preg_replace( '/\\\\"/', '\\u0022', $encoded_attributes );
 460  
 461      return $encoded_attributes;
 462  }
 463  
 464  /**
 465   * Returns the block name to use for serialization. This will remove the default
 466   * "core/" namespace from a block name.
 467   *
 468   * @since 5.3.1
 469   *
 470   * @param string $block_name Original block name.
 471   * @return string Block name to use for serialization.
 472   */
 473  function strip_core_block_namespace( $block_name = null ) {
 474      if ( is_string( $block_name ) && 0 === strpos( $block_name, 'core/' ) ) {
 475          return substr( $block_name, 5 );
 476      }
 477  
 478      return $block_name;
 479  }
 480  
 481  /**
 482   * Returns the content of a block, including comment delimiters.
 483   *
 484   * @since 5.3.1
 485   *
 486   * @param string|null $block_name       Block name. Null if the block name is unknown,
 487   *                                      e.g. Classic blocks have their name set to null.
 488   * @param array       $block_attributes Block attributes.
 489   * @param string      $block_content    Block save content.
 490   * @return string Comment-delimited block content.
 491   */
 492  function get_comment_delimited_block_content( $block_name, $block_attributes, $block_content ) {
 493      if ( is_null( $block_name ) ) {
 494          return $block_content;
 495      }
 496  
 497      $serialized_block_name = strip_core_block_namespace( $block_name );
 498      $serialized_attributes = empty( $block_attributes ) ? '' : serialize_block_attributes( $block_attributes ) . ' ';
 499  
 500      if ( empty( $block_content ) ) {
 501          return sprintf( '<!-- wp:%s %s/-->', $serialized_block_name, $serialized_attributes );
 502      }
 503  
 504      return sprintf(
 505          '<!-- wp:%s %s-->%s<!-- /wp:%s -->',
 506          $serialized_block_name,
 507          $serialized_attributes,
 508          $block_content,
 509          $serialized_block_name
 510      );
 511  }
 512  
 513  /**
 514   * Returns the content of a block, including comment delimiters, serializing all
 515   * attributes from the given parsed block.
 516   *
 517   * This should be used when preparing a block to be saved to post content.
 518   * Prefer `render_block` when preparing a block for display. Unlike
 519   * `render_block`, this does not evaluate a block's `render_callback`, and will
 520   * instead preserve the markup as parsed.
 521   *
 522   * @since 5.3.1
 523   *
 524   * @param WP_Block_Parser_Block $block A single parsed block object.
 525   * @return string String of rendered HTML.
 526   */
 527  function serialize_block( $block ) {
 528      $block_content = '';
 529  
 530      $index = 0;
 531      foreach ( $block['innerContent'] as $chunk ) {
 532          $block_content .= is_string( $chunk ) ? $chunk : serialize_block( $block['innerBlocks'][ $index++ ] );
 533      }
 534  
 535      if ( ! is_array( $block['attrs'] ) ) {
 536          $block['attrs'] = array();
 537      }
 538  
 539      return get_comment_delimited_block_content(
 540          $block['blockName'],
 541          $block['attrs'],
 542          $block_content
 543      );
 544  }
 545  
 546  /**
 547   * Returns a joined string of the aggregate serialization of the given parsed
 548   * blocks.
 549   *
 550   * @since 5.3.1
 551   *
 552   * @param WP_Block_Parser_Block[] $blocks Parsed block objects.
 553   * @return string String of rendered HTML.
 554   */
 555  function serialize_blocks( $blocks ) {
 556      return implode( '', array_map( 'serialize_block', $blocks ) );
 557  }
 558  
 559  /**
 560   * Filters and sanitizes block content to remove non-allowable HTML from
 561   * parsed block attribute values.
 562   *
 563   * @since 5.3.1
 564   *
 565   * @param string         $text              Text that may contain block content.
 566   * @param array[]|string $allowed_html      An array of allowed HTML elements
 567   *                                          and attributes, or a context name
 568   *                                          such as 'post'.
 569   * @param string[]       $allowed_protocols Array of allowed URL protocols.
 570   * @return string The filtered and sanitized content result.
 571   */
 572  function filter_block_content( $text, $allowed_html = 'post', $allowed_protocols = array() ) {
 573      $result = '';
 574  
 575      $blocks = parse_blocks( $text );
 576      foreach ( $blocks as $block ) {
 577          $block   = filter_block_kses( $block, $allowed_html, $allowed_protocols );
 578          $result .= serialize_block( $block );
 579      }
 580  
 581      return $result;
 582  }
 583  
 584  /**
 585   * Filters and sanitizes a parsed block to remove non-allowable HTML from block
 586   * attribute values.
 587   *
 588   * @since 5.3.1
 589   *
 590   * @param WP_Block_Parser_Block $block             The parsed block object.
 591   * @param array[]|string        $allowed_html      An array of allowed HTML
 592   *                                                 elements and attributes, or a
 593   *                                                 context name such as 'post'.
 594   * @param string[]              $allowed_protocols Allowed URL protocols.
 595   * @return array The filtered and sanitized block object result.
 596   */
 597  function filter_block_kses( $block, $allowed_html, $allowed_protocols = array() ) {
 598      $block['attrs'] = filter_block_kses_value( $block['attrs'], $allowed_html, $allowed_protocols );
 599  
 600      if ( is_array( $block['innerBlocks'] ) ) {
 601          foreach ( $block['innerBlocks'] as $i => $inner_block ) {
 602              $block['innerBlocks'][ $i ] = filter_block_kses( $inner_block, $allowed_html, $allowed_protocols );
 603          }
 604      }
 605  
 606      return $block;
 607  }
 608  
 609  /**
 610   * Filters and sanitizes a parsed block attribute value to remove non-allowable
 611   * HTML.
 612   *
 613   * @since 5.3.1
 614   *
 615   * @param string[]|string $value             The attribute value to filter.
 616   * @param array[]|string  $allowed_html      An array of allowed HTML elements
 617   *                                           and attributes, or a context name
 618   *                                           such as 'post'.
 619   * @param string[]        $allowed_protocols Array of allowed URL protocols.
 620   * @return string[]|string The filtered and sanitized result.
 621   */
 622  function filter_block_kses_value( $value, $allowed_html, $allowed_protocols = array() ) {
 623      if ( is_array( $value ) ) {
 624          foreach ( $value as $key => $inner_value ) {
 625              $filtered_key   = filter_block_kses_value( $key, $allowed_html, $allowed_protocols );
 626              $filtered_value = filter_block_kses_value( $inner_value, $allowed_html, $allowed_protocols );
 627  
 628              if ( $filtered_key !== $key ) {
 629                  unset( $value[ $key ] );
 630              }
 631  
 632              $value[ $filtered_key ] = $filtered_value;
 633          }
 634      } elseif ( is_string( $value ) ) {
 635          return wp_kses( $value, $allowed_html, $allowed_protocols );
 636      }
 637  
 638      return $value;
 639  }
 640  
 641  /**
 642   * Parses blocks out of a content string, and renders those appropriate for the excerpt.
 643   *
 644   * As the excerpt should be a small string of text relevant to the full post content,
 645   * this function renders the blocks that are most likely to contain such text.
 646   *
 647   * @since 5.0.0
 648   *
 649   * @param string $content The content to parse.
 650   * @return string The parsed and filtered content.
 651   */
 652  function excerpt_remove_blocks( $content ) {
 653      $allowed_inner_blocks = array(
 654          // Classic blocks have their blockName set to null.
 655          null,
 656          'core/freeform',
 657          'core/heading',
 658          'core/html',
 659          'core/list',
 660          'core/media-text',
 661          'core/paragraph',
 662          'core/preformatted',
 663          'core/pullquote',
 664          'core/quote',
 665          'core/table',
 666          'core/verse',
 667      );
 668  
 669      $allowed_blocks = array_merge( $allowed_inner_blocks, array( 'core/columns' ) );
 670  
 671      /**
 672       * Filters the list of blocks that can contribute to the excerpt.
 673       *
 674       * If a dynamic block is added to this list, it must not generate another
 675       * excerpt, as this will cause an infinite loop to occur.
 676       *
 677       * @since 5.0.0
 678       *
 679       * @param array $allowed_blocks The list of allowed blocks.
 680       */
 681      $allowed_blocks = apply_filters( 'excerpt_allowed_blocks', $allowed_blocks );
 682      $blocks         = parse_blocks( $content );
 683      $output         = '';
 684  
 685      foreach ( $blocks as $block ) {
 686          if ( in_array( $block['blockName'], $allowed_blocks, true ) ) {
 687              if ( ! empty( $block['innerBlocks'] ) ) {
 688                  if ( 'core/columns' === $block['blockName'] ) {
 689                      $output .= _excerpt_render_inner_columns_blocks( $block, $allowed_inner_blocks );
 690                      continue;
 691                  }
 692  
 693                  // Skip the block if it has disallowed or nested inner blocks.
 694                  foreach ( $block['innerBlocks'] as $inner_block ) {
 695                      if (
 696                          ! in_array( $inner_block['blockName'], $allowed_inner_blocks, true ) ||
 697                          ! empty( $inner_block['innerBlocks'] )
 698                      ) {
 699                          continue 2;
 700                      }
 701                  }
 702              }
 703  
 704              $output .= render_block( $block );
 705          }
 706      }
 707  
 708      return $output;
 709  }
 710  
 711  /**
 712   * Render inner blocks from the `core/columns` block for generating an excerpt.
 713   *
 714   * @since 5.2.0
 715   * @access private
 716   *
 717   * @param array $columns        The parsed columns block.
 718   * @param array $allowed_blocks The list of allowed inner blocks.
 719   * @return string The rendered inner blocks.
 720   */
 721  function _excerpt_render_inner_columns_blocks( $columns, $allowed_blocks ) {
 722      $output = '';
 723  
 724      foreach ( $columns['innerBlocks'] as $column ) {
 725          foreach ( $column['innerBlocks'] as $inner_block ) {
 726              if ( in_array( $inner_block['blockName'], $allowed_blocks, true ) && empty( $inner_block['innerBlocks'] ) ) {
 727                  $output .= render_block( $inner_block );
 728              }
 729          }
 730      }
 731  
 732      return $output;
 733  }
 734  
 735  /**
 736   * Renders a single block into a HTML string.
 737   *
 738   * @since 5.0.0
 739   *
 740   * @global WP_Post  $post     The post to edit.
 741   * @global WP_Query $wp_query WordPress Query object.
 742   *
 743   * @param array $parsed_block A single parsed block object.
 744   * @return string String of rendered HTML.
 745   */
 746  function render_block( $parsed_block ) {
 747      global $post, $wp_query;
 748  
 749      /**
 750       * Allows render_block() to be short-circuited, by returning a non-null value.
 751       *
 752       * @since 5.1.0
 753       *
 754       * @param string|null $pre_render   The pre-rendered content. Default null.
 755       * @param array       $parsed_block The block being rendered.
 756       */
 757      $pre_render = apply_filters( 'pre_render_block', null, $parsed_block );
 758      if ( ! is_null( $pre_render ) ) {
 759          return $pre_render;
 760      }
 761  
 762      $source_block = $parsed_block;
 763  
 764      /**
 765       * Filters the block being rendered in render_block(), before it's processed.
 766       *
 767       * @since 5.1.0
 768       *
 769       * @param array $parsed_block The block being rendered.
 770       * @param array $source_block An un-modified copy of $parsed_block, as it appeared in the source content.
 771       */
 772      $parsed_block = apply_filters( 'render_block_data', $parsed_block, $source_block );
 773  
 774      $context = array();
 775  
 776      if ( $post instanceof WP_Post ) {
 777          $context['postId'] = $post->ID;
 778  
 779          /*
 780           * The `postType` context is largely unnecessary server-side, since the ID
 781           * is usually sufficient on its own. That being said, since a block's
 782           * manifest is expected to be shared between the server and the client,
 783           * it should be included to consistently fulfill the expectation.
 784           */
 785          $context['postType'] = $post->post_type;
 786      }
 787  
 788      if ( $wp_query instanceof WP_Query && isset( $wp_query->tax_query->queried_terms['category'] ) ) {
 789          $context['query'] = array( 'categoryIds' => array() );
 790          foreach ( $wp_query->tax_query->queried_terms['category']['terms'] as $category_slug_or_id ) {
 791              $context['query']['categoryIds'][] = 'slug' === $wp_query->tax_query->queried_terms['category']['field'] ? get_cat_ID( $category_slug_or_id ) : $category_slug_or_id;
 792          }
 793      }
 794  
 795      /**
 796       * Filters the default context provided to a rendered block.
 797       *
 798       * @since 5.5.0
 799       *
 800       * @param array $context      Default context.
 801       * @param array $parsed_block Block being rendered, filtered by `render_block_data`.
 802       */
 803      $context = apply_filters( 'render_block_context', $context, $parsed_block );
 804  
 805      $block = new WP_Block( $parsed_block, $context );
 806  
 807      return $block->render();
 808  }
 809  
 810  /**
 811   * Parses blocks out of a content string.
 812   *
 813   * @since 5.0.0
 814   *
 815   * @param string $content Post content.
 816   * @return array[] Array of parsed block objects.
 817   */
 818  function parse_blocks( $content ) {
 819      /**
 820       * Filter to allow plugins to replace the server-side block parser
 821       *
 822       * @since 5.0.0
 823       *
 824       * @param string $parser_class Name of block parser class.
 825       */
 826      $parser_class = apply_filters( 'block_parser_class', 'WP_Block_Parser' );
 827  
 828      $parser = new $parser_class();
 829      return $parser->parse( $content );
 830  }
 831  
 832  /**
 833   * Parses dynamic blocks out of `post_content` and re-renders them.
 834   *
 835   * @since 5.0.0
 836   *
 837   * @param string $content Post content.
 838   * @return string Updated post content.
 839   */
 840  function do_blocks( $content ) {
 841      $blocks = parse_blocks( $content );
 842      $output = '';
 843  
 844      foreach ( $blocks as $block ) {
 845          $output .= render_block( $block );
 846      }
 847  
 848      // If there are blocks in this content, we shouldn't run wpautop() on it later.
 849      $priority = has_filter( 'the_content', 'wpautop' );
 850      if ( false !== $priority && doing_filter( 'the_content' ) && has_blocks( $content ) ) {
 851          remove_filter( 'the_content', 'wpautop', $priority );
 852          add_filter( 'the_content', '_restore_wpautop_hook', $priority + 1 );
 853      }
 854  
 855      return $output;
 856  }
 857  
 858  /**
 859   * If do_blocks() needs to remove wpautop() from the `the_content` filter, this re-adds it afterwards,
 860   * for subsequent `the_content` usage.
 861   *
 862   * @access private
 863   *
 864   * @since 5.0.0
 865   *
 866   * @param string $content The post content running through this filter.
 867   * @return string The unmodified content.
 868   */
 869  function _restore_wpautop_hook( $content ) {
 870      $current_priority = has_filter( 'the_content', '_restore_wpautop_hook' );
 871  
 872      add_filter( 'the_content', 'wpautop', $current_priority - 1 );
 873      remove_filter( 'the_content', '_restore_wpautop_hook', $current_priority );
 874  
 875      return $content;
 876  }
 877  
 878  /**
 879   * Returns the current version of the block format that the content string is using.
 880   *
 881   * If the string doesn't contain blocks, it returns 0.
 882   *
 883   * @since 5.0.0
 884   *
 885   * @param string $content Content to test.
 886   * @return int The block format version is 1 if the content contains one or more blocks, 0 otherwise.
 887   */
 888  function block_version( $content ) {
 889      return has_blocks( $content ) ? 1 : 0;
 890  }
 891  
 892  /**
 893   * Registers a new block style.
 894   *
 895   * @since 5.3.0
 896   *
 897   * @param string $block_name       Block type name including namespace.
 898   * @param array  $style_properties Array containing the properties of the style name,
 899   *                                 label, style (name of the stylesheet to be enqueued),
 900   *                                 inline_style (string containing the CSS to be added).
 901   * @return bool True if the block style was registered with success and false otherwise.
 902   */
 903  function register_block_style( $block_name, $style_properties ) {
 904      return WP_Block_Styles_Registry::get_instance()->register( $block_name, $style_properties );
 905  }
 906  
 907  /**
 908   * Unregisters a block style.
 909   *
 910   * @since 5.3.0
 911   *
 912   * @param string $block_name       Block type name including namespace.
 913   * @param array  $block_style_name Block style name.
 914   * @return bool True if the block style was unregistered with success and false otherwise.
 915   */
 916  function unregister_block_style( $block_name, $block_style_name ) {
 917      return WP_Block_Styles_Registry::get_instance()->unregister( $block_name, $block_style_name );
 918  }


Generated : Tue Jan 26 08:20:01 2021 Cross-referenced by PHPXref