Verified Commit 95f7025b authored by Dave Long's avatar Dave Long
Browse files

fix: #3463592 Require label and category properties for layout plugins

By: godotislate
By: dcam
By: smustgrave
parent cbf36cb7
Loading
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -100,6 +100,9 @@ public function __construct(
    // @see \Drupal\Core\Layout\LayoutDefinition::$additional
    // @see \Drupal\Core\Layout\LayoutDefinition::get()
    $this->additional = $additional;
    if ($this->label === NULL && $this->deriver === NULL) {
      @trigger_error('A layout plugin not having at least one of the label or deriver properties is deprecated in drupal:11.4.0 and having at least one of these properties will be enforced in drupal:12.0.0. See https://www.drupal.org/node/3464076', E_USER_DEPRECATED);
    }
  }

  /**
+17 −4
Original line number Diff line number Diff line
@@ -94,17 +94,30 @@ public function processDefinition(&$definition, $plugin_id) {
      throw new InvalidPluginDefinitionException($plugin_id, sprintf('The "%s" layout definition must extend %s', $plugin_id, LayoutDefinition::class));
    }

    // Add the module or theme path to the 'path'.
    if (empty($definition->getLabel())) {
      @trigger_error('A layout plugin not having a label is deprecated in drupal:11.4.0 and having a label will be enforced in drupal:12.0.0. See https://www.drupal.org/node/3464076', E_USER_DEPRECATED);
    }

    // Ensure that every plugin has a category.
    $provider = $definition->getProvider();
    if ($this->moduleHandler->moduleExists($provider)) {
      $base_path = $this->moduleHandler->getModule($provider)->getPath();
      $extension = $this->moduleHandler->getModule($provider);
    }
    elseif ($this->themeHandler->themeExists($provider)) {
      $base_path = $this->themeHandler->getTheme($provider)->getPath();
      $extension = $this->themeHandler->getTheme($provider);
    }
    else {
      $base_path = '';
      $extension = NULL;
    }
    if (empty($definition->getCategory())) {
      // Default to the human-readable name if the provider is a module or
      // theme; otherwise the provider machine name is used.
      $category = $extension ? $extension->getName() : $provider;
      $definition->setCategory($category);
    }

    // Add the module or theme path to the 'path'.
    $base_path = $extension ? $extension->getPath() : '';

    $path = $definition->getPath();
    $path = !empty($path) ? $base_path . '/' . $path : $base_path;
+29 −0
Original line number Diff line number Diff line
<?php

declare(strict_types=1);

namespace Drupal\Tests\Core\Layout;

use Drupal\Core\Layout\Attribute\Layout;
use Drupal\Tests\UnitTestCase;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\IgnoreDeprecations;

/**
 * Tests the Layout attribute.
 */
#[CoversClass(Layout::class)]
#[Group('Layout')]
#[IgnoreDeprecations]
class LayoutAttributeTest extends UnitTestCase {

  /**
   * Test deprecating plugins without a label or category.
   */
  public function testDeprecatedMissingProperties(): void {
    $this->expectDeprecation('A layout plugin not having at least one of the label or deriver properties is deprecated in drupal:11.4.0 and having at least one of these properties will be enforced in drupal:12.0.0. See https://www.drupal.org/node/3464076');
    new Layout('layout_without_label');
  }

}
+45 −0
Original line number Diff line number Diff line
@@ -285,6 +285,50 @@ public function testProcessDefinition(): void {
    $this->layoutPluginManager->getDefinitions();
  }

  /**
   * Tests ::processDefinition() with a layout that doesn't have a label.
   *
   * @legacy-covers ::processDefinition
   */
  public function testProcessDefinitionWithMissingLayoutLabel(): void {
    $this->expectDeprecation('A layout plugin not having a label is deprecated in drupal:11.4.0 and having a label will be enforced in drupal:12.0.0. See https://www.drupal.org/node/3464076');
    $module_a_label_less_layout = <<<'EOS'
module_a_label_less_layout:
  description: A layout that doesn't have a label.
EOS;
    vfsStream::create([
      'modules' => [
        'module_a' => [
          'module_a.layouts.yml' => $module_a_label_less_layout,
        ],
      ],
    ]);
    $this->layoutPluginManager->getDefinitions();
  }

  /**
   * Tests ::processDefinition() with a layout that doesn't have a category.
   *
   * @legacy-covers ::processDefinition
   */
  public function testProcessDefinitionWithMissingLayoutCategory(): void {
    $module_a_category_less_layout = <<<'EOS'
module_a_category_less_layout:
  label: Category-less Layout
  description: A layout that doesn't have a category.
EOS;
    $file_name = 'module_a.layouts.yml';
    vfsStream::create([
      'modules' => [
        'module_a' => [
          $file_name => $module_a_category_less_layout,
        ],
      ],
    ]);
    $definitions = $this->layoutPluginManager->getDefinitions();
    $this->assertEquals($file_name, $definitions['module_a_category_less_layout']->getCategory());
  }

  /**
   * Tests get theme implementations.
   *
@@ -541,6 +585,7 @@ public function getDerivativeDefinitions($base_plugin_definition) {
      $this->derivatives['invalid_provider'] = new LayoutDefinition([
        'id' => 'invalid_provider',
        'provider' => 'invalid_provider',
        'label' => 'invalid_provider',
      ]);
      $this->derivatives['invalid_provider']->setClass(LayoutInterface::class);
    }