[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-content/themes/ipsum/inc/ -> abilities.php (source)

   1  <?php
   2  /**
   3   * The design guide ability, which hands agents that work on the site through
   4   * the Abilities API what Ipsum is built with and how it is meant to be used.
   5   *
   6   * @package Ipsum
   7   * @since Ipsum 1.0
   8   */
   9  
  10  if ( ! function_exists( 'ipsum_register_abilities' ) ) :
  11      /**
  12       * Registers the design guide ability, so agents that work on the site
  13       * through the Abilities API, rather than this repository, can learn how
  14       * Ipsum is meant to be built with.
  15       *
  16       * It needs `edit_theme_options`, the capability behind the Site Editor and
  17       * Global Styles, since the guide is for whoever builds the site's design.
  18       *
  19       * @since Ipsum 1.0
  20       *
  21       * @return void
  22       */
  23  	function ipsum_register_abilities() {
  24          $output_schema = ipsum_get_design_guide_schema();
  25  
  26          wp_register_ability(
  27              'ipsum/get-design-guide',
  28              array(
  29                  'label'                 => __( 'Get Design Guide', 'ipsum' ),
  30                  'description'           => __( 'Returns the design guide of the Ipsum theme, read from the site as it is now: the color palette and what each color is for, whether it is light or dark, fonts, font sizes, spacing, layout widths, style variations, block styles, patterns, and the templates and template parts with the pattern that fills each. Read it before building pages, templates or styles. Pass fields to get only some sections.', 'ipsum' ),
  31                  'category'              => 'site',
  32                  'input_schema'          => array(
  33                      'type'                 => 'object',
  34                      'properties'           => array(
  35                          'fields' => array(
  36                              'type'        => 'array',
  37                              'items'       => array(
  38                                  'type' => 'string',
  39                                  'enum' => array_keys( $output_schema['properties'] ),
  40                              ),
  41                              'description' => __( 'Optional. Limits the guide to these sections. All sections are returned when it is omitted.', 'ipsum' ),
  42                          ),
  43                      ),
  44                      'additionalProperties' => false,
  45                      'default'              => array(),
  46                  ),
  47                  'output_schema'         => $output_schema,
  48                  'execute_callback'      => 'ipsum_get_design_guide',
  49                  'required_capabilities' => array( 'edit_theme_options' ),
  50                  'meta'                  => array(
  51                      'annotations' => array(
  52                          'readonly'    => true,
  53                          'destructive' => false,
  54                          'idempotent'  => true,
  55                      ),
  56                      'public'      => true,
  57                  ),
  58              )
  59          );
  60      }
  61  endif;
  62  add_action( 'wp_abilities_api_init', 'ipsum_register_abilities' );
  63  
  64  if ( ! function_exists( 'ipsum_get_color_roles' ) ) :
  65      /**
  66       * Returns what each palette color is for, keyed by its slug.
  67       *
  68       * Every color variation redefines the same slugs, so the roles hold under
  69       * whichever variation is active.
  70       *
  71       * @since Ipsum 1.0
  72       *
  73       * @return array<string, string> Role descriptions, keyed by palette slug.
  74       */
  75  	function ipsum_get_color_roles() {
  76          return array(
  77              'base'     => __( 'The page background.', 'ipsum' ),
  78              'contrast' => __( 'Body text, headings, links and buttons, on Base. It can be translucent, so on any other background use Strong Contrast.', 'ipsum' ),
  79              'accent-1' => __( 'An accent for small highlights. The theme uses it only for the arrival bar. Links and buttons use Contrast, not this. Use it sparingly.', 'ipsum' ),
  80              'accent-3' => __( 'The strongest contrast with the background, not an accent: hover states, and the background of inverted sections such as the navigation overlay, with Base for the text.', 'ipsum' ),
  81              'accent-5' => __( 'A subtle, translucent wash behind quotes, tags and table headers. It works on any background; keep Contrast text on it.', 'ipsum' ),
  82              'accent-6' => __( 'Faint, translucent lines, such as borders, list markers and table rules. Not for text.', 'ipsum' ),
  83          );
  84      }
  85  endif;
  86  
  87  if ( ! function_exists( 'ipsum_get_design_guide_schema' ) ) :
  88      /**
  89       * Returns the JSON Schema of the design guide, with one property per section.
  90       *
  91       * @since Ipsum 1.0
  92       *
  93       * @return array The output schema of the design guide ability.
  94       */
  95  	function ipsum_get_design_guide_schema() {
  96          $string      = array( 'type' => 'string' );
  97          $nullable    = array( 'type' => array( 'string', 'null' ) );
  98          $strings     = array(
  99              'type'  => 'array',
 100              'items' => $string,
 101          );
 102          $scheme      = array(
 103              'type' => 'string',
 104              'enum' => array( 'light', 'dark' ),
 105          );
 106          $object_list = static function ( $description, $properties ) {
 107              return array(
 108                  'type'        => 'array',
 109                  'description' => $description,
 110                  'items'       => array(
 111                      'type'       => 'object',
 112                      'properties' => $properties,
 113                  ),
 114              );
 115          };
 116          $preset_list = static function ( $description, $value_key, $value_schema ) use ( $object_list, $string ) {
 117              return $object_list(
 118                  $description,
 119                  array(
 120                      'slug'     => $string,
 121                      'name'     => $string,
 122                      $value_key => $value_schema,
 123                  )
 124              );
 125          };
 126  
 127          $colors = $preset_list( __( 'The color palette, with what each color is for. Every style variation redefines the same slugs. Colors added on the site have no role.', 'ipsum' ), 'color', $string );
 128  
 129          $colors['items']['properties']['role'] = $string;
 130  
 131          return array(
 132              'type'                 => 'object',
 133              'additionalProperties' => false,
 134              'properties'           => array(
 135                  'theme'             => array(
 136                      'type'        => 'object',
 137                      'description' => __( 'The active theme. Its stylesheet is the one to use in REST routes such as /wp/v2/global-styles/themes/{stylesheet}.', 'ipsum' ),
 138                      'properties'  => array(
 139                          'name'       => $string,
 140                          'stylesheet' => $string,
 141                      ),
 142                  ),
 143                  'colorScheme'       => array_merge(
 144                      $scheme,
 145                      array( 'description' => __( 'Whether the active palette is light or dark.', 'ipsum' ) )
 146                  ),
 147                  'colors'            => $colors,
 148                  'gradients'         => $preset_list( __( 'The gradient presets.', 'ipsum' ), 'gradient', $string ),
 149                  'duotone'           => $preset_list( __( 'The duotone filter presets.', 'ipsum' ), 'colors', $strings ),
 150                  'typography'        => array(
 151                      'type'        => 'object',
 152                      'description' => __( 'The font families of body text and headings, by slug, and the font family and font size presets.', 'ipsum' ),
 153                      'properties'  => array(
 154                          'bodyFont'     => $nullable,
 155                          'headingFont'  => $nullable,
 156                          'blockFonts'   => array(
 157                              'type'                 => 'object',
 158                              'description'          => __( 'The blocks set in another font than body text, such as buttons, navigation and post meta, grouped by font family slug.', 'ipsum' ),
 159                              'additionalProperties' => $strings,
 160                          ),
 161                          'fontFamilies' => $preset_list( __( 'The font family presets.', 'ipsum' ), 'fontFamily', $string ),
 162                          'fontSizes'    => $preset_list( __( 'The font size presets, smallest first.', 'ipsum' ), 'size', $string ),
 163                      ),
 164                  ),
 165                  'spacing'           => $preset_list( __( 'The spacing scale, smallest first.', 'ipsum' ), 'size', $string ),
 166                  'layout'            => array(
 167                      'type'        => 'object',
 168                      'description' => __( 'The width of the content, and of wide-aligned blocks.', 'ipsum' ),
 169                      'properties'  => array(
 170                          'contentSize' => $nullable,
 171                          'wideSize'    => $nullable,
 172                      ),
 173                  ),
 174                  'styleVariations'   => $object_list(
 175                      __( 'The style variations. Each one lists only what it changes: colorScheme and colors for the palette, bodyFont and headingFont for the fonts, and blocks for the style properties it sets on each block. Their full data is at /wp/v2/global-styles/themes/{stylesheet}/variations.', 'ipsum' ),
 176                      array(
 177                          'title'       => $string,
 178                          'colorScheme' => $scheme,
 179                          'colors'      => array(
 180                              'type'                 => 'object',
 181                              'additionalProperties' => $string,
 182                          ),
 183                          'bodyFont'    => $nullable,
 184                          'headingFont' => $nullable,
 185                          'blocks'      => array(
 186                              'type'                 => 'object',
 187                              'additionalProperties' => $strings,
 188                          ),
 189                      )
 190                  ),
 191                  'blockStyles'       => $object_list(
 192                      __( 'The block styles registered on this site, beyond the ones blocks define themselves. Add className to a block of one of the blockTypes to apply it.', 'ipsum' ),
 193                      array(
 194                          'name'       => $string,
 195                          'label'      => $string,
 196                          'className'  => $string,
 197                          'blockTypes' => $strings,
 198                      )
 199                  ),
 200                  'patterns'          => $object_list(
 201                      __( 'The patterns of the theme, without their markup, which is at /wp/v2/block-patterns/patterns. Hidden patterns fill templates and template parts and are left out of the inserter.', 'ipsum' ),
 202                      array(
 203                          'slug'          => $string,
 204                          'title'         => $string,
 205                          'description'   => $string,
 206                          'categories'    => $strings,
 207                          'blockTypes'    => $strings,
 208                          'templateTypes' => $strings,
 209                          'postTypes'     => $strings,
 210                          'hidden'        => array( 'type' => 'boolean' ),
 211                      )
 212                  ),
 213                  'templates'         => $object_list(
 214                      __( 'The templates, with the pattern that fills each. The pattern is null when a template holds its own markup, as it does once it is customized.', 'ipsum' ),
 215                      array(
 216                          'slug'        => $string,
 217                          'title'       => $string,
 218                          'description' => $string,
 219                          'pattern'     => $nullable,
 220                          'customized'  => array( 'type' => 'boolean' ),
 221                          'postTypes'   => $strings,
 222                      )
 223                  ),
 224                  'templateParts'     => $object_list(
 225                      __( 'The template parts, with their area and the pattern that fills each.', 'ipsum' ),
 226                      array(
 227                          'slug'       => $string,
 228                          'title'      => $string,
 229                          'area'       => $string,
 230                          'pattern'    => $nullable,
 231                          'customized' => array( 'type' => 'boolean' ),
 232                      )
 233                  ),
 234                  'templatePartAreas' => $object_list(
 235                      __( 'The areas a template part can belong to.', 'ipsum' ),
 236                      array(
 237                          'area'        => $string,
 238                          'label'       => $string,
 239                          'description' => $string,
 240                      )
 241                  ),
 242                  'guidelines'        => array_merge(
 243                      $strings,
 244                      array( 'description' => __( 'How the theme is meant to be built with.', 'ipsum' ) )
 245                  ),
 246              ),
 247          );
 248      }
 249  endif;
 250  
 251  if ( ! function_exists( 'ipsum_get_design_guide_presets' ) ) :
 252      /**
 253       * Lists the theme's presets of one kind, followed by any the site adds.
 254       *
 255       * @since Ipsum 1.0
 256       *
 257       * @param array    $presets The presets from the global settings, keyed by origin.
 258       * @param string[] $keys    The preset properties to keep.
 259       * @return array[] The presets, with only the given properties.
 260       */
 261  	function ipsum_get_design_guide_presets( $presets, $keys ) {
 262          $items = array();
 263          foreach ( array( 'theme', 'custom' ) as $origin ) {
 264              foreach ( $presets[ $origin ] ?? array() as $preset ) {
 265                  $item = array();
 266                  foreach ( $keys as $key ) {
 267                      if ( isset( $preset[ $key ] ) ) {
 268                          $item[ $key ] = $preset[ $key ];
 269                      }
 270                  }
 271                  $items[] = $item;
 272              }
 273          }
 274          return $items;
 275      }
 276  endif;
 277  
 278  if ( ! function_exists( 'ipsum_get_design_guide_font_slug' ) ) :
 279      /**
 280       * Returns the slug of the font family preset a style refers to.
 281       *
 282       * @since Ipsum 1.0
 283       *
 284       * @param mixed $font_family A fontFamily style value.
 285       * @return string|null The preset slug, the value itself when it is not a
 286       *                     preset, or null when there is no value.
 287       */
 288  	function ipsum_get_design_guide_font_slug( $font_family ) {
 289          if ( ! is_string( $font_family ) || '' === $font_family ) {
 290              return null;
 291          }
 292          if ( preg_match( '/^var(?::preset\|font-family\||\(--wp--preset--font-family--)([a-z0-9-]+)\)?$/', $font_family, $matches ) ) {
 293              return $matches[1];
 294          }
 295          return $font_family;
 296      }
 297  endif;
 298  
 299  if ( ! function_exists( 'ipsum_get_design_guide_pattern_slug' ) ) :
 300      /**
 301       * Returns the pattern a template or template part delegates to.
 302       *
 303       * @since Ipsum 1.0
 304       *
 305       * @param string $content The template or template part markup.
 306       * @return string|null The pattern slug, or null when the markup is not a
 307       *                     single Pattern block.
 308       */
 309  	function ipsum_get_design_guide_pattern_slug( $content ) {
 310          $blocks = array_values(
 311              array_filter(
 312                  parse_blocks( (string) $content ),
 313                  static function ( $block ) {
 314                      return null !== $block['blockName'];
 315                  }
 316              )
 317          );
 318          if ( 1 === count( $blocks ) && 'core/pattern' === $blocks[0]['blockName'] && ! empty( $blocks[0]['attrs']['slug'] ) ) {
 319              return $blocks[0]['attrs']['slug'];
 320          }
 321          return null;
 322      }
 323  endif;
 324  
 325  if ( ! function_exists( 'ipsum_get_design_guide' ) ) :
 326      /**
 327       * Builds the design guide from the site as it is, so it follows the active
 328       * style variation and any change made in Global Styles or the Site Editor.
 329       *
 330       * @since Ipsum 1.0
 331       *
 332       * @param array $input {
 333       *     The ability input.
 334       *
 335       *     @type string[] $fields Optional. The sections to return. Default all of them.
 336       * }
 337       * @return array The design guide.
 338       */
 339  	function ipsum_get_design_guide( $input = array() ) {
 340          $sections = array_keys( ipsum_get_design_guide_schema()['properties'] );
 341          if ( ! empty( $input['fields'] ) ) {
 342              $sections = array_intersect( $sections, $input['fields'] );
 343          }
 344          $sections = array_flip( $sections );
 345  
 346          // Resolving the merged data also registers the block styles in styles/blocks.
 347          $settings = wp_get_global_settings();
 348          $styles   = wp_get_global_styles();
 349          $guide    = array();
 350  
 351          if ( isset( $sections['theme'] ) ) {
 352              $guide['theme'] = array(
 353                  'name'       => wp_get_theme()->get( 'Name' ),
 354                  'stylesheet' => get_stylesheet(),
 355              );
 356          }
 357  
 358          if ( isset( $sections['colorScheme'] ) ) {
 359              // The audio player's CSS also reads a palette that sets no scheme as light.
 360              $guide['colorScheme'] = 'dark' === ( $settings['custom']['controlScheme'] ?? '' ) ? 'dark' : 'light';
 361          }
 362  
 363          if ( isset( $sections['colors'] ) ) {
 364              $roles           = ipsum_get_color_roles();
 365              $guide['colors'] = array();
 366              foreach ( ipsum_get_design_guide_presets( $settings['color']['palette'] ?? array(), array( 'slug', 'name', 'color' ) ) as $color ) {
 367                  if ( isset( $color['slug'], $roles[ $color['slug'] ] ) ) {
 368                      $color['role'] = $roles[ $color['slug'] ];
 369                  }
 370                  $guide['colors'][] = $color;
 371              }
 372          }
 373  
 374          if ( isset( $sections['gradients'] ) ) {
 375              $guide['gradients'] = ipsum_get_design_guide_presets( $settings['color']['gradients'] ?? array(), array( 'slug', 'name', 'gradient' ) );
 376          }
 377  
 378          if ( isset( $sections['duotone'] ) ) {
 379              $guide['duotone'] = ipsum_get_design_guide_presets( $settings['color']['duotone'] ?? array(), array( 'slug', 'name', 'colors' ) );
 380          }
 381  
 382          if ( isset( $sections['typography'] ) ) {
 383              $body_font   = ipsum_get_design_guide_font_slug( $styles['typography']['fontFamily'] ?? '' );
 384              $block_fonts = array();
 385              foreach ( $styles['blocks'] ?? array() as $block_type => $block_styles ) {
 386                  $block_font = ipsum_get_design_guide_font_slug( $block_styles['typography']['fontFamily'] ?? '' );
 387                  if ( null !== $block_font && $block_font !== $body_font ) {
 388                      $block_fonts[ $block_font ][] = $block_type;
 389                  }
 390              }
 391              $guide['typography'] = array(
 392                  'bodyFont'     => $body_font,
 393                  'headingFont'  => ipsum_get_design_guide_font_slug( $styles['elements']['heading']['typography']['fontFamily'] ?? '' ) ?? $body_font,
 394                  'blockFonts'   => $block_fonts,
 395                  'fontFamilies' => ipsum_get_design_guide_presets( $settings['typography']['fontFamilies'] ?? array(), array( 'slug', 'name', 'fontFamily' ) ),
 396                  'fontSizes'    => ipsum_get_design_guide_presets( $settings['typography']['fontSizes'] ?? array(), array( 'slug', 'name', 'size' ) ),
 397              );
 398          }
 399  
 400          if ( isset( $sections['spacing'] ) ) {
 401              $guide['spacing'] = ipsum_get_design_guide_presets( $settings['spacing']['spacingSizes'] ?? array(), array( 'slug', 'name', 'size' ) );
 402          }
 403  
 404          if ( isset( $sections['layout'] ) ) {
 405              $guide['layout'] = array(
 406                  'contentSize' => $settings['layout']['contentSize'] ?? null,
 407                  'wideSize'    => $settings['layout']['wideSize'] ?? null,
 408              );
 409          }
 410  
 411          if ( isset( $sections['styleVariations'] ) ) {
 412              $guide['styleVariations'] = array();
 413              foreach ( WP_Theme_JSON_Resolver::get_style_variations() as $variation ) {
 414                  $variation_settings = $variation['settings'] ?? array();
 415                  $variation_styles   = $variation['styles'] ?? array();
 416                  $item               = array( 'title' => $variation['title'] );
 417  
 418                  if ( isset( $variation_settings['custom']['controlScheme'] ) ) {
 419                      $item['colorScheme'] = 'dark' === $variation_settings['custom']['controlScheme'] ? 'dark' : 'light';
 420                  }
 421                  if ( ! empty( $variation_settings['color']['palette'] ) ) {
 422                      $item['colors'] = array_column( ipsum_get_design_guide_presets( $variation_settings['color']['palette'], array( 'slug', 'color' ) ), 'color', 'slug' );
 423                  }
 424                  if ( ! empty( $variation_styles['typography']['fontFamily'] ) ) {
 425                      $item['bodyFont'] = ipsum_get_design_guide_font_slug( $variation_styles['typography']['fontFamily'] );
 426                  }
 427                  if ( ! empty( $variation_styles['elements']['heading']['typography']['fontFamily'] ) ) {
 428                      $item['headingFont'] = ipsum_get_design_guide_font_slug( $variation_styles['elements']['heading']['typography']['fontFamily'] );
 429                  }
 430                  if ( ! empty( $variation_styles['blocks'] ) ) {
 431                      $item['blocks'] = array_map( 'array_keys', $variation_styles['blocks'] );
 432                  }
 433                  $guide['styleVariations'][] = $item;
 434              }
 435          }
 436  
 437          if ( isset( $sections['blockStyles'] ) ) {
 438              $block_styles = array();
 439              foreach ( WP_Block_Styles_Registry::get_instance()->get_all_registered() as $block_type => $block_type_styles ) {
 440                  foreach ( $block_type_styles as $name => $style ) {
 441                      if ( ! isset( $block_styles[ $name ] ) ) {
 442                          $block_styles[ $name ] = array(
 443                              'name'       => $name,
 444                              'label'      => $style['label'] ?? $name,
 445                              'className'  => 'is-style-' . $name,
 446                              'blockTypes' => array(),
 447                          );
 448                      }
 449                      $block_styles[ $name ]['blockTypes'][] = $block_type;
 450                  }
 451              }
 452              $guide['blockStyles'] = array_values( $block_styles );
 453          }
 454  
 455          if ( isset( $sections['patterns'] ) ) {
 456              $theme_pattern_dirs = array(
 457                  wp_normalize_path( get_stylesheet_directory() . '/patterns' ),
 458                  wp_normalize_path( get_template_directory() . '/patterns' ),
 459              );
 460              $guide['patterns']  = array();
 461              foreach ( WP_Block_Patterns_Registry::get_instance()->get_all_registered() as $pattern ) {
 462                  if ( empty( $pattern['filePath'] ) || ! in_array( wp_normalize_path( dirname( $pattern['filePath'] ) ), $theme_pattern_dirs, true ) ) {
 463                      continue;
 464                  }
 465                  $item = array(
 466                      'slug'        => $pattern['name'],
 467                      'title'       => $pattern['title'],
 468                      'description' => $pattern['description'] ?? '',
 469                      'categories'  => $pattern['categories'] ?? array(),
 470                  );
 471                  foreach ( array( 'blockTypes', 'templateTypes', 'postTypes' ) as $key ) {
 472                      if ( ! empty( $pattern[ $key ] ) ) {
 473                          $item[ $key ] = $pattern[ $key ];
 474                      }
 475                  }
 476                  $item['hidden']      = isset( $pattern['inserter'] ) && ! $pattern['inserter'];
 477                  $guide['patterns'][] = $item;
 478              }
 479          }
 480  
 481          foreach ( array(
 482              'templates'     => 'wp_template',
 483              'templateParts' => 'wp_template_part',
 484          ) as $section => $template_type ) {
 485              if ( ! isset( $sections[ $section ] ) ) {
 486                  continue;
 487              }
 488              $guide[ $section ] = array();
 489              foreach ( get_block_templates( array(), $template_type ) as $template ) {
 490                  $item = array(
 491                      'slug'  => $template->slug,
 492                      'title' => $template->title,
 493                  );
 494                  if ( 'wp_template' === $template_type ) {
 495                      $item['description'] = (string) $template->description;
 496                  } else {
 497                      $item['area'] = $template->area;
 498                  }
 499                  $item['pattern']    = ipsum_get_design_guide_pattern_slug( $template->content );
 500                  $item['customized'] = 'custom' === $template->source;
 501                  if ( ! empty( $template->post_types ) ) {
 502                      $item['postTypes'] = $template->post_types;
 503                  }
 504                  $guide[ $section ][] = $item;
 505              }
 506          }
 507  
 508          if ( isset( $sections['templatePartAreas'] ) ) {
 509              $guide['templatePartAreas'] = array();
 510              foreach ( get_allowed_block_template_part_areas() as $area ) {
 511                  $guide['templatePartAreas'][] = array(
 512                      'area'        => $area['area'],
 513                      'label'       => $area['label'],
 514                      'description' => $area['description'],
 515                  );
 516              }
 517          }
 518  
 519          if ( isset( $sections['guidelines'] ) ) {
 520              $guide['guidelines'] = array(
 521                  __( 'Style with the presets in this guide, by slug, rather than with literal values. Every style variation redefines the colors, so a literal color keeps the look of one variation under all the others.', 'ipsum' ),
 522                  __( 'Each template and template part holds a single Pattern block, and that pattern holds the markup. To change one on this site, save new markup to the template or template part itself.', 'ipsum' ),
 523                  __( 'Hidden patterns are there to fill templates and template parts, not to be inserted on their own.', 'ipsum' ),
 524              );
 525          }
 526  
 527          return $guide;
 528      }
 529  endif;


Generated : Wed Nov 4 08:20:38 2026 Cross-referenced by PHPXref