From 02975bd7d29b8291bc908bb1edab7677bd724f16 Mon Sep 17 00:00:00 2001 From: Yash Rode <57207-yash.rode@users.noreply.drupalcode.org> Date: Wed, 21 Aug 2024 16:17:08 +0000 Subject: [PATCH] Issue #3466307 by yash.rode, phenaproxima, chrisfromredfin, prashant.c: Expose the project browser as a render element --- src/Controller/BrowserController.php | 102 +--------- src/Element/ProjectBrowser.php | 192 ++++++++++++++++++ .../ProjectBrowserUiTestJsonApi.php | 2 +- 3 files changed, 196 insertions(+), 100 deletions(-) create mode 100644 src/Element/ProjectBrowser.php diff --git a/src/Controller/BrowserController.php b/src/Controller/BrowserController.php index a17dcbe2f..73941c43b 100644 --- a/src/Controller/BrowserController.php +++ b/src/Controller/BrowserController.php @@ -3,12 +3,6 @@ namespace Drupal\project_browser\Controller; use Drupal\Core\Controller\ControllerBase; -use Drupal\project_browser\DevelopmentStatus; -use Drupal\project_browser\EnabledSourceHandler; -use Drupal\project_browser\InstallReadiness; -use Drupal\project_browser\MaintenanceStatus; -use Drupal\project_browser\SecurityStatus; -use Symfony\Component\DependencyInjection\ContainerInterface; // cspell:ignore ctools @@ -20,21 +14,6 @@ use Symfony\Component\DependencyInjection\ContainerInterface; */ class BrowserController extends ControllerBase { - public function __construct( - private readonly EnabledSourceHandler $enabledSource, - private readonly ?InstallReadiness $installReadiness, - ) {} - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get(EnabledSourceHandler::class), - $container->get(InstallReadiness::class, ContainerInterface::NULL_ON_INVALID_REFERENCE), - ); - } - /** * Builds the browse page and the individual module page. * @@ -55,85 +34,10 @@ class BrowserController extends ControllerBase { * A render array. */ public function browse(?string $source, ?string $id): array { - $current_sources = $this->enabledSource->getCurrentSources(); - $ui_install_enabled = (bool) $this->config('project_browser.admin_settings')->get('allow_ui_install') && is_object($this->installReadiness); - - $package_manager = [ - 'available' => $ui_install_enabled, - 'errors' => [], - 'warnings' => [], - // This is just for testing and can probably be removed in - // https://www.drupal.org/project/project_browser/issues/3457682. - // @see \Drupal\Tests\project_browser\FunctionalJavascript\ProjectBrowserInstallerUiTest::testPackageManagerErrorPreventsDownload() - 'status_checked' => FALSE, - ]; - - if (empty($source) || empty($id)) { - // If Package Manager is available, only run status checks if we're not - // looking at a particular project. - if ($package_manager['available']) { - $package_manager = array_merge($package_manager, $this->installReadiness->validatePackageManager()); - $package_manager['status_checked'] = TRUE; - } - } - - $current_sources_keys = array_keys($current_sources); - // To get common data from single source plugin. - $current_source = reset($current_sources); - - $sort_options = $active_plugins = []; - foreach ($current_sources as $source) { - $sort_options[$source->getPluginId()] = array_values($source->getSortOptions()); - $active_plugins[$source->getPluginId()] = $source->getPluginDefinition()['label']; - } - - return [ - '#theme' => 'project_browser_main_app', - '#attached' => [ - 'library' => [ - 'project_browser/svelte', - ], - 'drupalSettings' => [ - 'project_browser' => [ - 'active_plugins' => $active_plugins, - 'module_path' => $this->moduleHandler()->getModule('project_browser')->getPath(), - 'special_ids' => $this->getSpecialIds(), - 'sort_options' => $sort_options, - 'maintenance_options' => MaintenanceStatus::asOptions(), - 'security_options' => SecurityStatus::asOptions(), - 'development_options' => DevelopmentStatus::asOptions(), - 'default_plugin_id' => $current_source->getPluginId(), - 'current_sources_keys' => $current_sources_keys, - 'package_manager' => $package_manager, - ], - ], - ], - ]; - } - - /** - * Return special IDs for some vocabularies. - * - * This is needed because these two vocabularies have a special term - * in them that shows an icon next to the label, so we need to be - * explicit about these special cases. - * - * @return array - * List of special IDs per vocabulary. - */ - protected function getSpecialIds(): array { - $maintained = MaintenanceStatus::Maintained; - $covered = SecurityStatus::Covered; return [ - 'maintenance_status' => [ - 'id' => $maintained->value, - 'name' => $maintained->label(), - ], - 'security_coverage' => [ - 'id' => $covered->value, - 'name' => $covered->label(), - ], - 'all_values' => MaintenanceStatus::All->value, + '#type' => 'project_browser', + '#source' => $source, + '#id' => $id, ]; } diff --git a/src/Element/ProjectBrowser.php b/src/Element/ProjectBrowser.php new file mode 100644 index 000000000..2646cf1b0 --- /dev/null +++ b/src/Element/ProjectBrowser.php @@ -0,0 +1,192 @@ +<?php + +namespace Drupal\project_browser\Element; + +use Drupal\Component\Utility\DeprecationHelper; +use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\DependencyInjection\DependencySerializationTrait; +use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\Core\Render\Attribute\RenderElement; +use Drupal\Core\Render\Element; +use Drupal\Core\Render\Element\ElementInterface; +use Drupal\Core\Render\Element\RenderElementBase; +use Drupal\project_browser\DevelopmentStatus; +use Drupal\project_browser\EnabledSourceHandler; +use Drupal\project_browser\InstallReadiness; +use Drupal\project_browser\MaintenanceStatus; +use Drupal\project_browser\SecurityStatus; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Provides a render element for the Project Browser. + * + * @RenderElement("project_browser") + */ +#[RenderElement('project_browser')] +final class ProjectBrowser implements ElementInterface, ContainerFactoryPluginInterface { + + use DependencySerializationTrait; + + public function __construct( + private readonly string $pluginId, + private readonly mixed $pluginDefinition, + private readonly EnabledSourceHandler $enabledSourceHandler, + private readonly ?InstallReadiness $installReadiness, + private readonly ModuleHandlerInterface $moduleHandler, + private readonly ConfigFactoryInterface $configFactory, + ) {} + + /** + * {@inheritdoc} + */ + public function getPluginId(): string { + return $this->pluginId; + } + + /** + * {@inheritdoc} + */ + public function getPluginDefinition(): mixed { + return $this->pluginDefinition; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static { + return new static( + $plugin_id, + $plugin_definition, + $container->get(EnabledSourceHandler::class), + $container->get(InstallReadiness::class, ContainerInterface::NULL_ON_INVALID_REFERENCE), + $container->get(ModuleHandlerInterface::class), + $container->get(ConfigFactoryInterface::class), + ); + } + + /** + * {@inheritdoc} + */ + public function getInfo(): array { + return [ + '#theme' => 'project_browser_main_app', + '#attached' => [ + 'library' => [ + 'project_browser/svelte', + ], + 'drupalSettings' => [ + 'project_browser' => [], + ], + ], + '#pre_render' => [ + [$this, 'attachProjectBrowserSettings'], + ], + ]; + } + + /** + * Prepares a render element for Project Browser. + * + * @param array $element + * A render element array. + * + * @return array + * The render element array. + */ + public function attachProjectBrowserSettings(array $element): array { + $element['#attached']['drupalSettings']['project_browser'] = $this->getDrupalSettings( + $element['#source'] ?? NULL, + $element['#id'] ?? NULL + ); + return $element; + } + + /** + * Gets the Drupal settings for the Project Browser. + * + * @param string|null $source + * If viewing a specific project, the ID of its source plugin. + * @param string|null $id + * If viewing a specific project, the project's local ID (as known to the + * source plugin). + * + * @return array + * An array of Drupal settings. + */ + private function getDrupalSettings(?string $source, ?string $id): array { + $current_sources = $this->enabledSourceHandler->getCurrentSources(); + $ui_install_enabled = (bool) $this->configFactory->get('project_browser.admin_settings')->get('allow_ui_install') && is_object($this->installReadiness); + + $package_manager = [ + 'available' => $ui_install_enabled, + 'errors' => [], + 'warnings' => [], + 'status_checked' => FALSE, + ]; + + if (empty($source) || empty($id)) { + if ($package_manager['available']) { + $package_manager = array_merge($package_manager, $this->installReadiness->validatePackageManager()); + $package_manager['status_checked'] = TRUE; + } + } + + $current_sources_keys = array_keys($current_sources); + $current_source = reset($current_sources); + + $sort_options = $active_plugins = []; + foreach ($current_sources as $source) { + $sort_options[$source->getPluginId()] = array_values($source->getSortOptions()); + $active_plugins[$source->getPluginId()] = $source->getPluginDefinition()['label']; + } + + return [ + 'active_plugins' => $active_plugins, + 'module_path' => $this->moduleHandler->getModule('project_browser')->getPath(), + 'special_ids' => static::getSpecialIds(), + 'sort_options' => $sort_options, + 'maintenance_options' => MaintenanceStatus::asOptions(), + 'security_options' => SecurityStatus::asOptions(), + 'development_options' => DevelopmentStatus::asOptions(), + 'default_plugin_id' => $current_source->getPluginId(), + 'current_sources_keys' => $current_sources_keys, + 'package_manager' => $package_manager, + ]; + } + + /** + * Return special IDs for some vocabularies. + * + * @return array + * List of special IDs per vocabulary. + */ + private static function getSpecialIds(): array { + $maintained = MaintenanceStatus::Maintained; + $covered = SecurityStatus::Covered; + return [ + 'maintenance_status' => [ + 'id' => $maintained->value, + 'name' => $maintained->label(), + ], + 'security_coverage' => [ + 'id' => $covered->value, + 'name' => $covered->label(), + ], + 'all_values' => MaintenanceStatus::All->value, + ]; + } + + /** + * {@inheritdoc} + */ + public static function setAttributes(&$element, $class = []): void { + DeprecationHelper::backwardsCompatibleCall( + \Drupal::VERSION, + '10.3', + static fn () => RenderElementBase::setAttributes($element, $class), + static fn () => Element::setAttributes($element, $class) + ); + } + +} diff --git a/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php b/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php index 02343fdea..97371ef55 100644 --- a/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php +++ b/tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php @@ -227,7 +227,7 @@ class ProjectBrowserUiTestJsonApi extends WebDriverTestBase { $this->clickWithWait('#acc38507-ac85-43e6-8f32-beb3febea93f', bypass_wait: TRUE); // Click 'Utility' checkbox. - $this->clickWithWait('#fddb4569-cb89-42f5-8699-182b10234dfa', '557 Results'); + $this->clickWithWait('#fddb4569-cb89-42f5-8699-182b10234dfa', '557 Results', TRUE); $this->assertPagerItems(['1', '2', '3', '4', '5', '…', 'Next', 'Last']); } -- GitLab