Skip to content
Snippets Groups Projects
Commit e71b1d39 authored by Adam G-H's avatar Adam G-H Committed by Chris Wells
Browse files

Issue #3506511: Use attributes for source plugin definitions

parent 5fb1a6a8
No related branches found
No related tags found
1 merge request!729Switch to attributes
Pipeline #423509 failed
Showing
with 100 additions and 162 deletions
enabled_sources: enabled_sources:
- drupalorg_jsonapi - drupalorg_jsonapi
- recipes
allow_ui_install: false allow_ui_install: false
allowed_projects: {} allowed_projects: {}
max_selections: null max_selections: null
...@@ -4,7 +4,9 @@ namespace Drupal\project_browser_devel\Plugin\ProjectBrowserSource; ...@@ -4,7 +4,9 @@ namespace Drupal\project_browser_devel\Plugin\ProjectBrowserSource;
use Drupal\Component\Utility\Random; use Drupal\Component\Utility\Random;
use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url; use Drupal\Core\Url;
use Drupal\project_browser\Attribute\ProjectBrowserSource;
use Drupal\project_browser\Plugin\ProjectBrowserSourceBase; use Drupal\project_browser\Plugin\ProjectBrowserSourceBase;
use Drupal\project_browser\ProjectBrowser\Filter\BooleanFilter; use Drupal\project_browser\ProjectBrowser\Filter\BooleanFilter;
use Drupal\project_browser\ProjectBrowser\Filter\MultipleChoiceFilter; use Drupal\project_browser\ProjectBrowser\Filter\MultipleChoiceFilter;
...@@ -14,18 +16,13 @@ use Symfony\Component\DependencyInjection\ContainerInterface; ...@@ -14,18 +16,13 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
* Random data plugin. Used mostly for testing. * Random data plugin. Used mostly for testing.
*
* To enable this source use the following drush command.
* phpcs:ignore
* drush config:set project_browser.admin_settings enabled_source random_data
*
* @ProjectBrowserSource(
* id = "random_data",
* label = @Translation("Random data"),
* description = @Translation("Gets random project and filters information"),
* local_task = {}
* )
*/ */
#[ProjectBrowserSource(
id: 'random_data',
label: new TranslatableMarkup('Random data'),
description: new TranslatableMarkup('Gets random project and filters information'),
local_task: [],
)]
final class RandomDataPlugin extends ProjectBrowserSourceBase { final class RandomDataPlugin extends ProjectBrowserSourceBase {
/** /**
......
...@@ -2,7 +2,9 @@ ...@@ -2,7 +2,9 @@
namespace Drupal\project_browser_source_example\Plugin\ProjectBrowserSource; namespace Drupal\project_browser_source_example\Plugin\ProjectBrowserSource;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url; use Drupal\Core\Url;
use Drupal\project_browser\Attribute\ProjectBrowserSource;
use Drupal\project_browser\Plugin\ProjectBrowserSourceBase; use Drupal\project_browser\Plugin\ProjectBrowserSourceBase;
use Drupal\project_browser\ProjectBrowser\Project; use Drupal\project_browser\ProjectBrowser\Project;
use Drupal\project_browser\ProjectBrowser\ProjectsResultsPage; use Drupal\project_browser\ProjectBrowser\ProjectsResultsPage;
...@@ -11,13 +13,12 @@ use Symfony\Component\HttpFoundation\RequestStack; ...@@ -11,13 +13,12 @@ use Symfony\Component\HttpFoundation\RequestStack;
/** /**
* Project Browser Source Plugin example code. * Project Browser Source Plugin example code.
*
* @ProjectBrowserSource(
* id = "project_browser_source_example",
* label = @Translation("Example source"),
* description = @Translation("Example source plugin for Project Browser."),
* )
*/ */
#[ProjectBrowserSource(
id: 'project_browser_source_example',
label: new TranslatableMarkup('Example source'),
description: new TranslatableMarkup('Example source plugin for Project Browser.'),
)]
final class ProjectBrowserSourceExample extends ProjectBrowserSourceBase { final class ProjectBrowserSourceExample extends ProjectBrowserSourceBase {
/** /**
......
...@@ -5,24 +5,6 @@ ...@@ -5,24 +5,6 @@
* Contains install and update functions for Project Browser. * Contains install and update functions for Project Browser.
*/ */
use Drupal\Core\Recipe\Recipe;
/**
* Implements hook_install().
*
* Populates the project_browser_projects using a fixture with PHP serialized
* items.
*/
function project_browser_install(): void {
if (class_exists(Recipe::class)) {
$config = \Drupal::configFactory()
->getEditable('project_browser.admin_settings');
$enabled_sources = $config->get('enabled_sources');
$enabled_sources[] = 'recipes';
$config->set('enabled_sources', $enabled_sources)->save();
}
}
/** /**
* Implements hook_update_last_removed(). * Implements hook_update_last_removed().
*/ */
......
...@@ -5,10 +5,8 @@ ...@@ -5,10 +5,8 @@
* Provides hook implementations for the Project Browser module. * Provides hook implementations for the Project Browser module.
*/ */
use Drupal\Core\Recipe\Recipe;
use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url; use Drupal\Core\Url;
use Drupal\project_browser\Plugin\ProjectBrowserSource\Recipes;
/** /**
* Implements hook_help(). * Implements hook_help().
...@@ -43,23 +41,6 @@ function project_browser_theme(): array { ...@@ -43,23 +41,6 @@ function project_browser_theme(): array {
]; ];
} }
/**
* Implements hook_project_browser_source_info_alter().
*/
function project_browser_project_browser_source_info_alter(array &$definitions): void {
if (class_exists(Recipe::class)) {
$definitions['recipes'] = [
'id' => 'recipes',
'label' => t('Recipes'),
'description' => t('Recipes available in the local code base'),
'local_task' => [
'weight' => ($definitions['drupalorg_jsonapi']['local_task']['weight'] ?? 5) + 2,
],
'class' => Recipes::class,
];
}
}
/** /**
* Preprocess function for the project_browser_main_app theme hook. * Preprocess function for the project_browser_main_app theme hook.
* *
......
<?php
namespace Drupal\project_browser\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines a Project Browser source plugin annotation object.
*
* Project Browser sources are used to provide information about
* available projects that can be installed on a Drupal site.
* Typically, these come from Drupal.org, but may also come
* from a private repository, etc.
*
* Plugin Namespace: Plugin\ProjectBrowserSource
*
* For a working example, see:
* \Drupal\project_browser\Plugin\ProjectBrowserSource\MockDrupalDotOrg
*
* @see \Drupal\project_browser\Plugin\ProjectBrowserSourceInterface
* @see \Drupal\project_browser\Plugin\ProjectBrowserSourceManager
* @see plugin_api
*
* @Annotation
*/
class ProjectBrowserSource extends Plugin {
/**
* The plugin ID.
*
* @var string
*/
public $id;
/**
* The human-readable name of the source.
*
* @var \Drupal\Core\Annotation\Translation
* @ingroup plugin_translatable
*/
public $label;
/**
* A short description of the source.
*
* @var \Drupal\Core\Annotation\Translation
* @ingroup plugin_translatable
*/
public $description;
/**
* The local task definition at which this source should be exposed.
*
* If NULL, the source will never be exposed as a local task.
*
* @var array|null
*/
public ?array $local_task = NULL;
}
<?php
declare(strict_types=1);
namespace Drupal\project_browser\Attribute;
use Drupal\Component\Plugin\Attribute\Plugin;
use Drupal\Core\StringTranslation\TranslatableMarkup;
/**
* Defines an attribute to identify Project Browser source plugins.
*/
#[\Attribute(\Attribute::TARGET_CLASS)]
final class ProjectBrowserSource extends Plugin {
/**
* Constructs a ProjectBrowserSource attribute.
*
* @param string $id
* The plugin ID.
* @param \Drupal\Core\StringTranslation\TranslatableMarkup $label
* The plugin's human-readable name.
* @param \Drupal\Core\StringTranslation\TranslatableMarkup $description
* A brief description of the plugin and what it does.
* @param array|null $local_task
* A local task definition at which this source should be exposed in the UI.
* If NULL, the source will not be shown as a local task.
* @param class-string|null $deriver
* The plugin's deriver class, if any.
*/
public function __construct(
string $id,
public TranslatableMarkup $label,
public TranslatableMarkup $description,
public ?array $local_task = NULL,
?string $deriver = NULL,
) {
parent::__construct($id, $deriver);
}
}
...@@ -6,24 +6,25 @@ use Drupal\Core\Cache\CacheBackendInterface; ...@@ -6,24 +6,25 @@ use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\Extension; use Drupal\Core\Extension\Extension;
use Drupal\Core\Extension\ModuleExtensionList; use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Core\Site\Settings; use Drupal\Core\Site\Settings;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url; use Drupal\Core\Url;
use Drupal\project_browser\Attribute\ProjectBrowserSource;
use Drupal\project_browser\Plugin\ProjectBrowserSourceBase; use Drupal\project_browser\Plugin\ProjectBrowserSourceBase;
use Drupal\project_browser\ProjectBrowser\Project; use Drupal\project_browser\ProjectBrowser\Project;
use Drupal\project_browser\ProjectBrowser\ProjectsResultsPage; use Drupal\project_browser\ProjectBrowser\ProjectsResultsPage;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
* The source plugin to get Drupal core projects list. * A source that lists Drupal core modules.
*
* @ProjectBrowserSource(
* id = "drupal_core",
* label = @Translation("Core modules"),
* description = @Translation("Modules included in Drupal core"),
* local_task = {
* "title" = @Translation("Core modules"),
* }
* )
*/ */
#[ProjectBrowserSource(
id: 'drupal_core',
label: new TranslatableMarkup('Core modules'),
description: new TranslatableMarkup('Modules included in Drupal core'),
local_task: [
'title' => new TranslatableMarkup('Core modules'),
],
)]
final class DrupalCore extends ProjectBrowserSourceBase { final class DrupalCore extends ProjectBrowserSourceBase {
public function __construct( public function __construct(
......
...@@ -9,7 +9,9 @@ use Drupal\Core\Cache\CacheBackendInterface; ...@@ -9,7 +9,9 @@ use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ExtensionVersion; use Drupal\Core\Extension\ExtensionVersion;
use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url; use Drupal\Core\Url;
use Drupal\project_browser\Attribute\ProjectBrowserSource;
use Drupal\project_browser\Plugin\ProjectBrowserSourceBase; use Drupal\project_browser\Plugin\ProjectBrowserSourceBase;
use Drupal\project_browser\ProjectBrowser\Filter\BooleanFilter; use Drupal\project_browser\ProjectBrowser\Filter\BooleanFilter;
use Drupal\project_browser\ProjectBrowser\Filter\MultipleChoiceFilter; use Drupal\project_browser\ProjectBrowser\Filter\MultipleChoiceFilter;
...@@ -23,16 +25,15 @@ use Symfony\Component\HttpFoundation\Response; ...@@ -23,16 +25,15 @@ use Symfony\Component\HttpFoundation\Response;
/** /**
* Drupal.org JSON:API endpoint. * Drupal.org JSON:API endpoint.
*
* @ProjectBrowserSource(
* id = "drupalorg_jsonapi",
* label = @Translation("Contrib modules"),
* description = @Translation("Modules on Drupal.org queried via the JSON:API endpoint"),
* local_task = {
* "title" = @Translation("Contrib modules"),
* }
* )
*/ */
#[ProjectBrowserSource(
id: 'drupalorg_jsonapi',
label: new TranslatableMarkup('Contrib modules'),
description: new TranslatableMarkup('Modules on Drupal.org queried via the JSON:API endpoint'),
local_task: [
'title' => new TranslatableMarkup('Contrib modules'),
],
)]
final class DrupalDotOrgJsonApi extends ProjectBrowserSourceBase { final class DrupalDotOrgJsonApi extends ProjectBrowserSourceBase {
use StringTranslationTrait; use StringTranslationTrait;
......
...@@ -13,7 +13,9 @@ use Drupal\Core\Extension\ModuleExtensionList; ...@@ -13,7 +13,9 @@ use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Core\File\FileSystemInterface; use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Recipe\Recipe; use Drupal\Core\Recipe\Recipe;
use Drupal\Core\Site\Settings; use Drupal\Core\Site\Settings;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url; use Drupal\Core\Url;
use Drupal\project_browser\Attribute\ProjectBrowserSource;
use Drupal\project_browser\Plugin\ProjectBrowserSourceBase; use Drupal\project_browser\Plugin\ProjectBrowserSourceBase;
use Drupal\project_browser\ProjectBrowser\Project; use Drupal\project_browser\ProjectBrowser\Project;
use Drupal\project_browser\ProjectBrowser\ProjectsResultsPage; use Drupal\project_browser\ProjectBrowser\ProjectsResultsPage;
...@@ -24,6 +26,14 @@ use Symfony\Component\Finder\Finder; ...@@ -24,6 +26,14 @@ use Symfony\Component\Finder\Finder;
/** /**
* A source plugin that exposes recipes installed locally. * A source plugin that exposes recipes installed locally.
*/ */
#[ProjectBrowserSource(
id: 'recipes',
label: new TranslatableMarkup('Recipes'),
description: new TranslatableMarkup('Recipes available in the local code base'),
local_task: [
'weight' => 2,
]
)]
final class Recipes extends ProjectBrowserSourceBase { final class Recipes extends ProjectBrowserSourceBase {
public function __construct( public function __construct(
......
...@@ -5,6 +5,7 @@ namespace Drupal\project_browser\Plugin; ...@@ -5,6 +5,7 @@ namespace Drupal\project_browser\Plugin;
use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager; use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\project_browser\Attribute\ProjectBrowserSource;
/** /**
* Provides a Project Browser Source Manager. * Provides a Project Browser Source Manager.
...@@ -32,7 +33,7 @@ class ProjectBrowserSourceManager extends DefaultPluginManager { ...@@ -32,7 +33,7 @@ class ProjectBrowserSourceManager extends DefaultPluginManager {
$namespaces, $namespaces,
$module_handler, $module_handler,
'Drupal\project_browser\Plugin\ProjectBrowserSourceInterface', 'Drupal\project_browser\Plugin\ProjectBrowserSourceInterface',
'Drupal\project_browser\Annotation\ProjectBrowserSource', ProjectBrowserSource::class,
); );
$this->alterInfo('project_browser_source_info'); $this->alterInfo('project_browser_source_info');
......
...@@ -9,7 +9,9 @@ use Drupal\Component\Utility\Html; ...@@ -9,7 +9,9 @@ use Drupal\Component\Utility\Html;
use Drupal\Core\Database\Connection; use Drupal\Core\Database\Connection;
use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\State\StateInterface; use Drupal\Core\State\StateInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url; use Drupal\Core\Url;
use Drupal\project_browser\Attribute\ProjectBrowserSource;
use Drupal\project_browser\Plugin\ProjectBrowserSourceBase; use Drupal\project_browser\Plugin\ProjectBrowserSourceBase;
use Drupal\project_browser\ProjectBrowser\Filter\BooleanFilter; use Drupal\project_browser\ProjectBrowser\Filter\BooleanFilter;
use Drupal\project_browser\ProjectBrowser\Filter\MultipleChoiceFilter; use Drupal\project_browser\ProjectBrowser\Filter\MultipleChoiceFilter;
...@@ -22,16 +24,15 @@ use Symfony\Component\DependencyInjection\ContainerInterface; ...@@ -22,16 +24,15 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
* Database driven plugin. * Database driven plugin.
*
* @ProjectBrowserSource(
* id = "project_browser_test_mock",
* label = @Translation("Project Browser Mock Plugin"),
* description = @Translation("Gets project and filters information from a database"),
* local_task = {
* "title" = @Translation("Browse"),
* }
* )
*/ */
#[ProjectBrowserSource(
id: 'project_browser_test_mock',
label: new TranslatableMarkup('Project Browser Mock Plugin'),
description: new TranslatableMarkup('Gets project and filters information from a database'),
local_task: [
'title' => new TranslatableMarkup('Browse'),
],
)]
final class ProjectBrowserTestMock extends ProjectBrowserSourceBase { final class ProjectBrowserTestMock extends ProjectBrowserSourceBase {
/** /**
......
...@@ -5,10 +5,8 @@ declare(strict_types=1); ...@@ -5,10 +5,8 @@ declare(strict_types=1);
namespace Drupal\Tests\project_browser\Kernel; namespace Drupal\Tests\project_browser\Kernel;
use Drupal\Component\FileSystem\FileSystem; use Drupal\Component\FileSystem\FileSystem;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Recipe\Recipe; use Drupal\Core\Recipe\Recipe;
use Drupal\KernelTests\KernelTestBase; use Drupal\KernelTests\KernelTestBase;
use Drupal\project_browser\EnabledSourceHandler;
use Drupal\project_browser\Plugin\ProjectBrowserSourceManager; use Drupal\project_browser\Plugin\ProjectBrowserSourceManager;
use Drupal\project_browser\ProjectType; use Drupal\project_browser\ProjectType;
use Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem; use Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem;
...@@ -44,23 +42,6 @@ class RecipesSourceTest extends KernelTestBase { ...@@ -44,23 +42,6 @@ class RecipesSourceTest extends KernelTestBase {
$this->installConfig('project_browser'); $this->installConfig('project_browser');
} }
/**
* @covers \project_browser_install
* @covers \project_browser_project_browser_source_info_alter
*/
public function testRecipeSourceIsEnabledAtInstallTime(): void {
$this->assertNotContains('recipes', $this->config('project_browser.admin_settings')->get('enabled_sources'));
$this->container->get(ModuleHandlerInterface::class)
->loadInclude('project_browser', 'install');
project_browser_install();
$this->assertContains('recipes', $this->config('project_browser.admin_settings')->get('enabled_sources'));
$enabled_sources = $this->container->get(EnabledSourceHandler::class)
->getCurrentSources();
$this->assertArrayHasKey('recipes', $enabled_sources);
}
/** /**
* Tests that recipes are discovered by the plugin. * Tests that recipes are discovered by the plugin.
*/ */
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment