diff --git a/project_browser.routing.yml b/project_browser.routing.yml index 1529306129146c832815d6a2af46926080d9bdcf..5c15191d0a7ff7c65b3a6d3e180c7251cc90ebf1 100644 --- a/project_browser.routing.yml +++ b/project_browser.routing.yml @@ -19,11 +19,11 @@ project_browser.api_project_get_all: #options: # no_cache: 'TRUE' project_browser.browse: - path: '/admin/modules/browse/{module_name}' + path: '/admin/modules/browse/{id}' defaults: _controller: '\Drupal\project_browser\Controller\BrowserController::browse' _title: 'Browse projects' - module_name: '' + id: null requirements: _permission: 'administer modules' project_browser.settings: diff --git a/src/Controller/BrowserController.php b/src/Controller/BrowserController.php index 7ddf97e6c7d2196ee223bac4a306c5c85794bc32..da1d0fbcd57543857734e36d8e2dbc7b2d1c4acd 100644 --- a/src/Controller/BrowserController.php +++ b/src/Controller/BrowserController.php @@ -63,18 +63,18 @@ class BrowserController extends ControllerBase { * rendered. For example, 'https//drupal-site/admin/modules/browse/ctools' * will display the details for ctools. * - * @param string $module_name - * Module for which the detailed page is built. + * @param string|null $id + * The project ID, if any. * * @return array * A render array. */ - public function browse($module_name) { + public function browse(?string $id = NULL) { $request = $this->requestStack->getCurrentRequest(); $current_sources = $this->enabledSource->getCurrentSources(); $ui_install_enabled = (bool) $this->config('project_browser.admin_settings')->get('allow_ui_install') && (bool) $this->installReadiness; - if (!empty($current_sources['drupalorg_mockapi']) && !$module_name) { + if (array_key_exists('drupalorg_mockapi', $current_sources) && empty($id)) { $this->messenger() ->addStatus($this->t('Project Browser is currently a prototype, and the projects listed may not be up to date with Drupal.org. For the most updated list of projects, visit <a href=":url">:url</a>', [':url' => 'https://www.drupal.org/project/project_module'])) ->addStatus($this->t('Your feedback and input are welcome at <a href=":url">:url</a>', [':url' => 'https://www.drupal.org/project/issues/project_browser'])); diff --git a/src/Controller/ProjectBrowserEndpointController.php b/src/Controller/ProjectBrowserEndpointController.php index 3d0bcfcfd492a494be2a5fd73e1b5a5a7ec4a9e3..e0798b29608c5c1763f45781e470af261cd8bef3 100644 --- a/src/Controller/ProjectBrowserEndpointController.php +++ b/src/Controller/ProjectBrowserEndpointController.php @@ -45,11 +45,30 @@ class ProjectBrowserEndpointController extends ControllerBase { * Typically a project listing. */ public function getAllProjects(Request $request) { + $id = $request->query->get('id'); + if ($id) { + return new JsonResponse($this->enabledSource->getStoredProject($id)); + } + $current_sources = $this->enabledSource->getCurrentSources(); if (!$current_sources) { return new JsonResponse([], Response::HTTP_ACCEPTED); } + $query = $this->buildQuery($request); + return new JsonResponse($this->enabledSource->getProjects($query)); + } + + /** + * Builds the query based on the current request. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The request. + * + * @return array + * See \Drupal\project_browser\EnabledSourceHandler::getProjects(). + */ + private function buildQuery(Request $request): array { // Validate and build query. $query = [ 'page' => (int) $request->query->get('page', 0), @@ -101,7 +120,7 @@ class ProjectBrowserEndpointController extends ControllerBase { $query['tabwise_categories'] = $tabwise_categories; } - return new JsonResponse($this->enabledSource->getProjects($query)); + return $query; } /** diff --git a/src/EnabledSourceHandler.php b/src/EnabledSourceHandler.php index 79779f855449e11ff9bf4d0c24e9380779fbb445..2a56fac2041c0c798a7bf7e49e9522142ccccd55 100644 --- a/src/EnabledSourceHandler.php +++ b/src/EnabledSourceHandler.php @@ -119,8 +119,13 @@ class EnabledSourceHandler implements LoggerAwareInterface, EventSubscriberInter // name, which are unlikely to change. This isn't security-sensitive, // so SHA1 is okay for this purpose. $project->id = sha1($source_id . $project->packageName . $project->machineName); + // Remember the ID of the source plugin that exposed this project, + // since that information might be needed by the front-end. + $project->source = $source_id; + $this->keyValue->setIfNotExists($project->id, $project); - // Add activation data to the project. + // Add activation data to the project. This is volatile and should not + // be changed. $this->getActivationData($project); } // Store each source's results for this query as a set of arguments to @@ -189,10 +194,10 @@ class EnabledSourceHandler implements LoggerAwareInterface, EventSubscriberInter } /** - * Looks up a previously stored project by its UUID. + * Looks up a previously stored project by its ID. * - * @param string $uuid - * The project UUID. See ::getProjects() for where this is set. + * @param string $id + * The project ID. See ::getProjects() for where this is set. * * @return \Drupal\project_browser\ProjectBrowser\Project * The project object, with activation status and commands added. @@ -200,8 +205,8 @@ class EnabledSourceHandler implements LoggerAwareInterface, EventSubscriberInter * @throws \RuntimeException * Thrown if the project is not found in the non-volatile data store. */ - public function getStoredProject(string $uuid): Project { - $project = $this->keyValue->get($uuid) ?? throw new \RuntimeException("Project '$uuid' was not found in non-volatile storage."); + public function getStoredProject(string $id): Project { + $project = $this->keyValue->get($id) ?? throw new \RuntimeException("Project '$id' was not found in non-volatile storage."); $this->getActivationData($project); return $project; } diff --git a/src/ProjectBrowser/Project.php b/src/ProjectBrowser/Project.php index fc8370593af593a262d1f99eb710312f4d4634d0..f861e4ed31755df5f7a33c82e6df931da1597e91 100644 --- a/src/ProjectBrowser/Project.php +++ b/src/ProjectBrowser/Project.php @@ -17,15 +17,30 @@ class Project implements \JsonSerializable { /** * A persistent ID for this project in non-volatile storage. * + * This property is internal and should be ignored by source plugins. + * * @var string * * @see \Drupal\project_browser\EnabledSourceHandler::getProjects() */ public string $id; + /** + * The ID of the source plugin which exposed this project. + * + * This property is internal and should be ignored by source plugins. + * + * @var string + * + * @see \Drupal\project_browser\EnabledSourceHandler::getProjects() + */ + public string $source; + /** * The status of this project in the current site. * + * This property is internal and should be ignored by source plugins. + * * @var \Drupal\project_browser\ActivationStatus */ public ActivationStatus $status; @@ -33,6 +48,8 @@ class Project implements \JsonSerializable { /** * The instructions, if any, to activate this project. * + * This property is internal and should be ignored by source plugins. + * * @var string|\Drupal\Core\Url|null * * @see \Drupal\project_browser\ActivatorInterface::getInstructions() @@ -187,6 +204,7 @@ class Project implements \JsonSerializable { 'selector_id' => $this->getSelectorId(), 'commands' => $commands, 'id' => $this->id, + 'source' => $this->source, ]; } diff --git a/sveltejs/public/build/bundle.js b/sveltejs/public/build/bundle.js index 62ff98b2c5c813145a0aa6005f6d239811e13236..cb9f528c49d6673100aae26249b18ef014e4a50a 100644 Binary files a/sveltejs/public/build/bundle.js and b/sveltejs/public/build/bundle.js differ diff --git a/sveltejs/public/build/bundle.js.map b/sveltejs/public/build/bundle.js.map index f3937ede71587d42c8dff0624832aee1c020be67..aa1574549a7b322482df69a1a5d9b7d3642cb6b2 100644 Binary files a/sveltejs/public/build/bundle.js.map and b/sveltejs/public/build/bundle.js.map differ diff --git a/sveltejs/src/App.svelte b/sveltejs/src/App.svelte index 63d31fa3e58bb83a669a5a67bdce2f319bf8adab..6bcf837daf555fbf980e872527a9a47a1ed27bb3 100644 --- a/sveltejs/src/App.svelte +++ b/sveltejs/src/App.svelte @@ -8,7 +8,7 @@ const matches = window.location.pathname.match( /\/admin\/modules\/browse\/([^/]+)/, ); - const moduleName = matches ? matches[1] : null; + const projectId = matches ? matches[1] : null; let loading = true; let data; @@ -18,20 +18,11 @@ loading = true; const res = await fetch(url); if (res.ok) { - data = await res.json(); - Object.entries(data).forEach((item) => { - const [source, result] = item; - if (result.totalResults !== 0) { - $activeTab = source; - [project] = result.list; - projectExists = true; - } - }); + project = await res.json(); + $activeTab = project.source; + projectExists = true; } loading = false; - if (!projectExists) { - $searchString = moduleName; - } return project; } @@ -42,10 +33,10 @@ } </script> -{#if !moduleName} +{#if !projectId} <ProjectBrowser /> {:else} - {#await load(`${ORIGIN_URL}/drupal-org-proxy/project?machine_name=${moduleName}`)} + {#await load(`${ORIGIN_URL}/drupal-org-proxy/project?id=${projectId}`)} {#if loading} <Loading /> {/if} diff --git a/sveltejs/src/Project/Project.svelte b/sveltejs/src/Project/Project.svelte index 56af1bf24b711195ada1d3b23dc415dc2f948128..bc0fd76b5b78b5b65b92751c90424cdee9ffcad0 100644 --- a/sveltejs/src/Project/Project.svelte +++ b/sveltejs/src/Project/Project.svelte @@ -33,7 +33,7 @@ <a id="{project.project_machine_name}_title" class="pb-project__link" - href="{ORIGIN_URL}/admin/modules/browse/{project.project_machine_name}" + href="{ORIGIN_URL}/admin/modules/browse/{project.id}" rel="noreferrer">{project.title}</a > </h3> diff --git a/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php b/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php index 4064035b2a7be0115cb5b208fc3e35598856bd7f..c230f9781a58d5e8418fc4e3db2b05e170b62855 100644 --- a/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php +++ b/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php @@ -590,7 +590,8 @@ class ProjectBrowserUiTest extends WebDriverTestBase { $this->svelteInitHelper('text', 'Helvetica'); $page->clickLink('Helvetica'); $this->assertTrue($assert_session->waitForText('By Hel Vetica')); - $assert_session->addressEquals('admin/modules/browse/helvetica'); + // cspell:disable-next-line + $assert_session->addressEquals('/admin/modules/browse/' . sha1('drupalorg_mockapidrupal/helveticahelvetica')); $page->clickLink('Back to Browsing'); $assert_session->addressEquals('admin/modules/browse'); }