diff --git a/islandora.module b/islandora.module index 22b2ad8a..2e2af9d6 100644 --- a/islandora.module +++ b/islandora.module @@ -18,6 +18,7 @@ use Drupal\Component\Plugin\Exception\PluginNotFoundException; use Drupal\Core\Entity\Display\EntityViewDisplayInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Site\Settings; use Drupal\Core\Url; use Drupal\islandora\Form\IslandoraSettingsForm; use Drupal\islandora\GeminiLookup; @@ -466,27 +467,53 @@ function islandora_entity_view(array &$build, EntityInterface $entity, EntityVie // Ensure the entity matches the route. if ($entity === $route_match_item) { if ($display->getComponent('field_gemini_uri')) { - $gemini = \Drupal::service('islandora.gemini.lookup'); - if ($gemini instanceof GeminiLookup) { - $fedora_uri = $gemini->lookup($entity); - if (!is_null($fedora_uri)) { - $build['field_gemini_uri'] = [ - '#type' => 'container', - '#attributes' => [ - 'id' => 'field-gemini-uri', - ], - 'internal_label' => [ - '#type' => 'item', - '#title' => t('Fedora URI'), - 'internal_uri' => [ - '#type' => 'link', - '#title' => t("@url", ['@url' => $fedora_uri]), - '#url' => Url::fromUri($fedora_uri), - ], - ], - ]; + $mapper = \Drupal::service('islandora.entity_mapper'); + $flysystem_config = Settings::get('flysystem'); + $fedora_root = $flysystem_config['fedora']['config']['root']; + $fedora_root = rtrim($fedora_root, '/'); + + if ($entity->getEntityTypeId() == 'media') { + // Check if the source file is in Fedora or not. + $media_source_service = \Drupal::service('islandora.media_source_service'); + $source_file = $media_source_service->getSourceFile($entity); + $uri = $source_file->getFileUri(); + $scheme = \Drupal::service('file_system')->uriScheme($uri); + $flysystem_config = Settings::get('flysystem'); + + // Use the file's path if it's in fedora. Otherwise do the UUID -> pair tree thang. + if (isset($flysystem_config[$scheme]) && $flysystem_config[$scheme]['driver'] == 'fedora') { + $parts = parse_url($uri); + $path = $parts['host'] . $parts['path']; } + else { + $path = $mapper->getFedoraPath($source_file->uuid()); + } + $path = trim($path, '/'); + $fedora_uri = "$fedora_root/$path/fcr:metadata"; + } + else { + // All non-media entities do the UUID -> pair tree thang. + $path = $mapper->getFedoraPath($entity->uuid()); + $path = trim($path, '/'); + $fedora_uri = "$fedora_root/$path"; } + + // Stuff the fedora url into the pseudo field. + $build['field_gemini_uri'] = [ + '#type' => 'container', + '#attributes' => [ + 'id' => 'field-gemini-uri', + ], + 'internal_label' => [ + '#type' => 'item', + '#title' => t('Fedora URI'), + 'internal_uri' => [ + '#type' => 'link', + '#title' => t("@url", ['@url' => $fedora_uri]), + '#url' => Url::fromUri($fedora_uri), + ], + ], + ]; } } } diff --git a/islandora.services.yml b/islandora.services.yml index 0112f41a..39ebbab0 100644 --- a/islandora.services.yml +++ b/islandora.services.yml @@ -52,10 +52,5 @@ services: islandora.utils: class: Drupal\islandora\IslandoraUtils arguments: ['@entity_type.manager', '@entity_field.manager', '@context.manager', '@flysystem_factory', '@language_manager'] - islandora.gemini.client: - class: Islandora\Crayfish\Commons\Client\GeminiClient - factory: ['Drupal\islandora\GeminiClientFactory', create] - arguments: ['@config.factory', '@logger.channel.islandora'] - islandora.gemini.lookup: - class: Drupal\islandora\GeminiLookup - arguments: ['@islandora.gemini.client', '@jwt.authentication.jwt', '@islandora.media_source_service', '@http_client', '@logger.channel.islandora'] + islandora.entity_mapper: + class: Islandora\Crayfish\Commons\EntityMapper\EntityMapper diff --git a/src/Form/IslandoraSettingsForm.php b/src/Form/IslandoraSettingsForm.php index 6bb868d2..ec9fdfa8 100644 --- a/src/Form/IslandoraSettingsForm.php +++ b/src/Form/IslandoraSettingsForm.php @@ -25,7 +25,6 @@ class IslandoraSettingsForm extends ConfigFormBase { const BROKER_USER = 'broker_user'; const BROKER_PASSWORD = 'broker_password'; const JWT_EXPIRY = 'jwt_expiry'; - const GEMINI_URL = 'gemini_url'; const GEMINI_PSEUDO = 'gemini_pseudo_bundles'; const FEDORA_URL = 'fedora_url'; const TIME_INTERVALS = [ @@ -148,12 +147,6 @@ class IslandoraSettingsForm extends ConfigFormBase { ), ]; - $form[self::GEMINI_URL] = [ - '#type' => 'textfield', - '#title' => $this->t('Gemini URL'), - '#default_value' => $config->get(self::GEMINI_URL), - ]; - $flysystem_config = Settings::get('flysystem'); if ($flysystem_config != NULL) { $fedora_url = $flysystem_config['fedora']['config']['root']; @@ -269,45 +262,6 @@ class IslandoraSettingsForm extends ConfigFormBase { ); } } - - // Needed for the elseif below. - $pseudo_types = array_filter($form_state->getValue(self::GEMINI_PSEUDO)); - - // Validate Gemini URL by validating the URL. - $geminiUrlValue = trim($form_state->getValue(self::GEMINI_URL)); - if (!empty($geminiUrlValue)) { - try { - $geminiUrl = Url::fromUri($geminiUrlValue); - $client = GeminiClient::create($geminiUrlValue, $this->logger('islandora')); - $client->findByUri('http://example.org'); - } - // Uri is invalid. - catch (\InvalidArgumentException $e) { - $form_state->setErrorByName( - self::GEMINI_URL, - $this->t( - 'Cannot parse URL @url', - ['@url' => $geminiUrlValue] - ) - ); - } - // Uri is not available. - catch (ConnectException $e) { - $form_state->setErrorByName( - self::GEMINI_URL, - $this->t( - 'Cannot connect to URL @url', - ['@url' => $geminiUrlValue] - ) - ); - } - } - elseif (count($pseudo_types) > 0) { - $form_state->setErrorByName( - self::GEMINI_URL, - $this->t('Must enter Gemini URL before selecting bundles to display a pseudo field on.') - ); - } } /** @@ -337,7 +291,6 @@ class IslandoraSettingsForm extends ConfigFormBase { $config ->set(self::BROKER_URL, $form_state->getValue(self::BROKER_URL)) ->set(self::JWT_EXPIRY, $form_state->getValue(self::JWT_EXPIRY)) - ->set(self::GEMINI_URL, $form_state->getValue(self::GEMINI_URL)) ->set(self::GEMINI_PSEUDO, $pseudo_types) ->save(); diff --git a/src/GeminiClientFactory.php b/src/GeminiClientFactory.php deleted file mode 100644 index 5a8ccd5b..00000000 --- a/src/GeminiClientFactory.php +++ /dev/null @@ -1,47 +0,0 @@ -get(IslandoraSettingsForm::CONFIG_NAME); - $geminiUrl = $settings->get(IslandoraSettingsForm::GEMINI_URL); - - // Only attempt if there is one. - if (!empty($geminiUrl)) { - return GeminiClient::create($geminiUrl, $logger); - } - else { - $logger->notice("Attempted to create Gemini client without a Gemini URL defined."); - throw new PreconditionFailedHttpException("Unable to instantiate GeminiClient, missing Gemini URI in Islandora setting."); - } - } - -} diff --git a/src/GeminiLookup.php b/src/GeminiLookup.php deleted file mode 100644 index a712ddf3..00000000 --- a/src/GeminiLookup.php +++ /dev/null @@ -1,168 +0,0 @@ -geminiClient = $client; - $this->jwtProvider = $jwt_auth; - $this->mediaSource = $media_source; - $this->guzzle = $guzzle; - $this->logger = $logger; - } - - /** - * Lookup this entity's URI in the Gemini db and return the other URI. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity to look for. - * - * @return string|null - * Return the URI or null - */ - public function lookup(EntityInterface $entity) { - // Exit early if the entity hasn't been saved yet. - if ($entity->id() == NULL) { - return NULL; - } - - $is_media = $entity->getEntityTypeId() == 'media'; - - // Use the entity's uuid unless it's a media, - // use its file's uuid instead. - if ($is_media) { - try { - $file = $this->mediaSource->getSourceFile($entity); - $uuid = $file->uuid(); - } - // If the media has no source file, exit early. - catch (NotFoundHttpException $e) { - return NULL; - } - } - else { - $uuid = $entity->uuid(); - } - - // Look it up in Gemini. - $token = "Bearer " . $this->jwtProvider->generateToken(); - $urls = $this->geminiClient->getUrls($uuid, $token); - - // Exit early if there's no results from Gemini. - if (empty($urls)) { - return NULL; - } - - // If it's not a media, just return the url from Gemini;. - if (!$is_media) { - return $urls['fedora']; - } - - // If it's a media, perform a HEAD request against - // the file in Fedora and get its 'describedy' link header. - try { - $head = $this->guzzle->head( - $urls['fedora'], - ['allow_redirects' => FALSE, 'headers' => ['Authorization' => $token]] - ); - // phpcs:disable - if (class_exists(\GuzzleHttp\Psr7\Header::class)) { - $links = \GuzzleHttp\Psr7\Header::parse($head->getHeader('Link')); - } - else { - $links = \GuzzleHttp\Psr7\parse_header($head->getHeader('Link')); - } - //phpcs:enable - foreach ($links as $link) { - if ($link['rel'] == 'describedby') { - return trim($link[0], '<>'); - } - } - } - catch (RequestException $e) { - $this->logger->warning( - "Error performing Gemini lookup for media. Fedora HEAD to @url returned @status => @message", - [ - '@url' => $urls['fedora'], - '@status' => $e->getCode(), - '@message' => $e->getMessage, - ] - ); - return NULL; - } - - // Return null if no link header is found. - return NULL; - } - -} diff --git a/tests/src/Kernel/GeminiLookupTest.php b/tests/src/Kernel/GeminiLookupTest.php deleted file mode 100644 index e15f4303..00000000 --- a/tests/src/Kernel/GeminiLookupTest.php +++ /dev/null @@ -1,293 +0,0 @@ -prophesize(JwtAuth::class); - $this->jwtAuth = $prophecy->reveal(); - - $prophecy = $this->prophesize(LoggerInterface::class); - $this->logger = $prophecy->reveal(); - - $prophecy = $this->prophesize(MediaSourceService::class); - $this->mediaSource = $prophecy->reveal(); - - $prophecy = $this->prophesize(GeminiClient::class); - $this->geminiClient = $prophecy->reveal(); - - $prophecy = $this->prophesize(Client::class); - $this->guzzle = $prophecy->reveal(); - - // Mock up an entity to use (node in this case). - $prophecy = $this->prophesize(EntityInterface::class); - $prophecy->id()->willReturn(1); - $prophecy->getEntityTypeId()->willReturn('node'); - $prophecy->uuid()->willReturn('abc123'); - $this->entity = $prophecy->reveal(); - - // Mock up a media to use. - $prophecy = $this->prophesize(MediaInterface::class); - $prophecy->id()->willReturn(1); - $prophecy->getEntityTypeId()->willReturn('media'); - $prophecy->uuid()->willReturn('abc123'); - $this->media = $prophecy->reveal(); - } - - /** - * Mocks up a gemini client that fails its lookup. - */ - private function mockGeminiClientForFail() { - $prophecy = $this->prophesize(GeminiClient::class); - $prophecy->getUrls(Argument::any(), Argument::any()) - ->willReturn([]); - $this->geminiClient = $prophecy->reveal(); - } - - /** - * Mocks up a gemini client that finds a fedora url. - */ - private function mockGeminiClientForSuccess() { - $prophecy = $this->prophesize(GeminiClient::class); - $prophecy->getUrls(Argument::any(), Argument::any()) - ->willReturn([ - 'drupal' => '', - 'fedora' => 'http://localhost:8080/fcrepo/rest/abc123', - ]); - $this->geminiClient = $prophecy->reveal(); - } - - /** - * Mocks up a media source service that finds the source file for a media. - */ - private function mockMediaSourceForSuccess() { - $prophecy = $this->prophesize(FileInterface::class); - $prophecy->uuid()->willReturn('abc123'); - $file = $prophecy->reveal(); - - $prophecy = $this->prophesize(MediaSourceService::class); - $prophecy->getSourceFile(Argument::any()) - ->willReturn($file); - $this->mediaSource = $prophecy->reveal(); - } - - /** - * Make the gemini lookup out of class variables. - */ - private function createGeminiLookup() { - return new GeminiLookup( - $this->geminiClient, - $this->jwtAuth, - $this->mediaSource, - $this->guzzle, - $this->logger - ); - } - - /** - * @covers ::lookup - * @covers ::__construct - */ - public function testEntityNotSaved() { - // Mock an entity that returns a null id. - // That means it's not saved in the db yet. - $prophecy = $this->prophesize(EntityInterface::class); - $prophecy->id()->willReturn(NULL); - $this->entity = $prophecy->reveal(); - - $gemini_lookup = $this->createGeminiLookup(); - - $this->assertEquals( - NULL, - $gemini_lookup->lookup($this->entity) - ); - } - - /** - * @covers ::lookup - * @covers ::__construct - */ - public function testEntityNotFound() { - $this->mockGeminiClientForFail(); - - $gemini_lookup = $this->createGeminiLookup(); - - $this->assertEquals( - NULL, - $gemini_lookup->lookup($this->entity) - ); - } - - /** - * @covers ::lookup - * @covers ::__construct - */ - public function testEntityFound() { - $this->mockGeminiClientForSuccess(); - - $gemini_lookup = $this->createGeminiLookup(); - - $this->assertEquals( - 'http://localhost:8080/fcrepo/rest/abc123', - $gemini_lookup->lookup($this->entity) - ); - } - - /** - * @covers ::lookup - * @covers ::__construct - */ - public function testMediaHasNoSourceFile() { - // Mock a media source service that fails to find - // the source file for a media. - $prophecy = $this->prophesize(MediaSourceService::class); - $prophecy->getSourceFile(Argument::any()) - ->willThrow(new NotFoundHttpException("Media has no source")); - $this->mediaSource = $prophecy->reveal(); - - $gemini_lookup = $this->createGeminiLookup(); - - $this->assertEquals( - NULL, - $gemini_lookup->lookup($this->media) - ); - } - - /** - * @covers ::lookup - * @covers ::__construct - */ - public function testMediaNotFound() { - $this->mockMediaSourceForSuccess(); - $this->mockGeminiClientForFail(); - - $gemini_lookup = $this->createGeminiLookup(); - - $this->assertEquals( - NULL, - $gemini_lookup->lookup($this->media) - ); - } - - /** - * @covers ::lookup - * @covers ::__construct - */ - public function testFileFoundButNoDescribedby() { - $this->mockMediaSourceForSuccess(); - $this->mockGeminiClientForSuccess(); - - // Mock up a guzzle client that does not return - // the describedby header. - $prophecy = $this->prophesize(Client::class); - $prophecy->head(Argument::any(), Argument::any()) - ->willReturn(new Response(200, [])); - $this->guzzle = $prophecy->reveal(); - - $gemini_lookup = $this->createGeminiLookup(); - - $this->assertEquals( - NULL, - $gemini_lookup->lookup($this->media) - ); - } - - /** - * @covers ::lookup - * @covers ::__construct - */ - public function testMediaFound() { - $this->mockMediaSourceForSuccess(); - $this->mockGeminiClientForSuccess(); - - // Mock up a guzzle client that returns - // the describedby header. - $prophecy = $this->prophesize(Client::class); - $prophecy->head(Argument::any(), Argument::any()) - ->willReturn(new Response(200, ['Link' => '; rel="describedby"'])); - $this->guzzle = $prophecy->reveal(); - - $gemini_lookup = $this->createGeminiLookup(); - - $this->assertEquals( - 'http://localhost:8080/fcrepo/rest/abc123/fcr:metadata', - $gemini_lookup->lookup($this->media) - ); - } - -}