Skip to content
Snippets Groups Projects
Unverified Commit b436c8c9 authored by Alex Pott's avatar Alex Pott
Browse files

Issue #3503190 by phenaproxima, thejimbirch: Allow recipes to contain an...

Issue #3503190 by phenaproxima, thejimbirch: Allow recipes to contain an "extra" property with arbitrary information for specific modules to use

(cherry picked from commit c9212079)
parent 6ada4987
No related branches found
No related tags found
9 merge requests!11887Issue #3520065: The migrate Row class API is incomplete,!11636Draft: Issue #3515643 by macsim: fieldNameExists method is inconsistent,!11515Issue #3480419 by mondrake, smustgrave, catch: Method...,!11380Issue #3490698 by catch, spokje: Bump MINIMUM_STABILITY back to 'stable' when...,!11281Use Drupal Core Leadership terminology in MAINTAINERS.txt,!11239Issue #3507548: Allow workspace changes listing to show all items, without a pager,!11238Fix issue #3051797,!11213Issue #3506743 by tomislav.matokovic: Increasing the color contrast for the navigation block title against the background of the navigation sidebar to at least 4.5:1,!11147Draft: Try to avoid manually setting required cache contexts
Pipeline #418793 canceled
Pipeline: drupal

#418795

    ...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
    use Drupal\Core\DefaultContent\Finder; use Drupal\Core\DefaultContent\Finder;
    use Drupal\Core\Extension\Dependency; use Drupal\Core\Extension\Dependency;
    use Drupal\Core\Extension\ExtensionDiscovery;
    use Drupal\Core\Extension\ModuleExtensionList; use Drupal\Core\Extension\ModuleExtensionList;
    use Drupal\Core\Extension\ThemeExtensionList; use Drupal\Core\Extension\ThemeExtensionList;
    use Drupal\Component\Serialization\Yaml; use Drupal\Component\Serialization\Yaml;
    ...@@ -60,6 +61,8 @@ final class Recipe { ...@@ -60,6 +61,8 @@ final class Recipe {
    * The default content finder. * The default content finder.
    * @param string $path * @param string $path
    * The recipe's path. * The recipe's path.
    * @param array $extra
    * Any extra information to expose to specific modules.
    */ */
    public function __construct( public function __construct(
    public readonly string $name, public readonly string $name,
    ...@@ -71,6 +74,7 @@ public function __construct( ...@@ -71,6 +74,7 @@ public function __construct(
    public readonly InputConfigurator $input, public readonly InputConfigurator $input,
    public readonly Finder $content, public readonly Finder $content,
    public readonly string $path, public readonly string $path,
    private readonly array $extra,
    ) {} ) {}
    /** /**
    ...@@ -90,7 +94,7 @@ public static function createFromDirectory(string $path): static { ...@@ -90,7 +94,7 @@ public static function createFromDirectory(string $path): static {
    $config = new ConfigConfigurator($recipe_data['config'], $path, \Drupal::service('config.storage')); $config = new ConfigConfigurator($recipe_data['config'], $path, \Drupal::service('config.storage'));
    $input = new InputConfigurator($recipe_data['input'] ?? [], $recipes, basename($path), \Drupal::typedDataManager()); $input = new InputConfigurator($recipe_data['input'] ?? [], $recipes, basename($path), \Drupal::typedDataManager());
    $content = new Finder($path . '/content'); $content = new Finder($path . '/content');
    return new static($recipe_data['name'], $recipe_data['description'], $recipe_data['type'], $recipes, $install, $config, $input, $content, $path); return new static($recipe_data['name'], $recipe_data['description'], $recipe_data['type'], $recipes, $install, $config, $input, $content, $path, $recipe_data['extra'] ?? []);
    } }
    /** /**
    ...@@ -296,6 +300,12 @@ private static function parse(string $file): array { ...@@ -296,6 +300,12 @@ private static function parse(string $file): array {
    'content' => new Optional([ 'content' => new Optional([
    new Type('array'), new Type('array'),
    ]), ]),
    'extra' => new Optional([
    new Sequentially([
    new Type('associative_array'),
    new Callback(self::validateKeysAreValidExtensionNames(...)),
    ]),
    ]),
    ]); ]);
    $recipe_data = Yaml::decode($recipe_contents); $recipe_data = Yaml::decode($recipe_contents);
    ...@@ -423,4 +433,40 @@ private static function validateConfigActions(mixed $value, ExecutionContextInte ...@@ -423,4 +433,40 @@ private static function validateConfigActions(mixed $value, ExecutionContextInte
    } }
    } }
    /**
    * Validates that the keys of an array are valid extension names.
    *
    * Note that the keys do not have to be the names of extensions that are
    * installed, or even extensions that exist. They just have to follow the
    * form of a valid extension name.
    *
    * @param array $value
    * The array being validated.
    * @param \Symfony\Component\Validator\Context\ExecutionContextInterface $context
    * The validator execution context.
    */
    private static function validateKeysAreValidExtensionNames(array $value, ExecutionContextInterface $context): void {
    $keys = array_keys($value);
    foreach ($keys as $key) {
    if (!preg_match(ExtensionDiscovery::PHP_FUNCTION_PATTERN, $key)) {
    $context->addViolation('%name is not a valid extension name.', [
    '%name' => $key,
    ]);
    }
    }
    }
    /**
    * Returns extra information to expose to a particular extension.
    *
    * @param string $extension_name
    * The name of a Drupal extension.
    *
    * @return mixed
    * The extra data exposed to the given extension, or NULL if there is none.
    */
    public function getExtra(string $extension_name): mixed {
    return $this->extra[$extension_name] ?? NULL;
    }
    } }
    ...@@ -98,4 +98,19 @@ public function testImplicitlyRequiredModule(): void { ...@@ -98,4 +98,19 @@ public function testImplicitlyRequiredModule(): void {
    $this->assertIsObject($recipe); $this->assertIsObject($recipe);
    } }
    /**
    * Tests getting extra extension-specific info from a recipe.
    *
    * @covers ::getExtra
    */
    public function testExtra(): void {
    $recipe = $this->createRecipe([
    'name' => 'Getting extra info',
    'extra' => [
    'special_sauce' => 'Wasabi',
    ],
    ]);
    $this->assertSame('Wasabi', $recipe->getExtra('special_sauce'));
    }
    } }
    ...@@ -719,6 +719,45 @@ public static function providerRecipeValidation(): iterable { ...@@ -719,6 +719,45 @@ public static function providerRecipeValidation(): iterable {
    default: default:
    source: config source: config
    config: ['system.site', 'mail'] config: ['system.site', 'mail']
    YAML,
    NULL,
    ];
    yield 'extra is present and not an array' => [
    <<<YAML
    name: Bad extra
    extra: 'yes!'
    YAML,
    [
    '[extra]' => ['This value should be of type associative_array.'],
    ],
    ];
    yield 'extra is an indexed array' => [
    <<<YAML
    name: Bad extra
    extra:
    - one
    - two
    YAML,
    [
    '[extra]' => ['This value should be of type associative_array.'],
    ],
    ];
    yield 'invalid key in extra' => [
    <<<YAML
    name: Bad extra
    extra:
    'not a valid extension name': true
    YAML,
    [
    '[extra]' => ['not a valid extension name is not a valid extension name.'],
    ],
    ];
    yield 'valid extra' => [
    <<<YAML
    name: Bad extra
    extra:
    project_browser:
    yes: sir
    YAML, YAML,
    NULL, NULL,
    ]; ];
    ......
    0% Loading or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Please register or to comment