[ Index ]

PHP Cross Reference of WordPress

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


Generated: Fri Apr 23 01:00:05 2021 Cross-referenced by PHPXref 0.7.1