Skip to content

Commit

Permalink
959 configurable width height caching (#1029)
Browse files Browse the repository at this point in the history
* Refactor IIIF, create IIIF Info Service.

* Islandora IIIF: Add action to retrieve image attributes from IIIF server.

* Islandora IIIF: Add auth headers to IIIF Info request.

* Islandora IIIF: Change media action to node.

* Islandora IIIF: Update README.

* Finish rebase.

* Islandora IIIF: Address PHPCS errors.

* Islandora IIIF: Address PHPCS errors.

* islandora_iiif: Fix Authorization header syntax.

* 959-iiif-width-height-caching Islandora IIIF: Add search endpoint config to manifest.

* 959-iiif-width-height-caching Add handler for ServerException in Islandora IIIF manifest generator.

* 959-iiif-width-height-caching Add option to skip retrieveing TIFF and JP2 dimensions from IIIF server.

* Checking for width/height on media first when generating IIIF manifest

* 959-iiif-width-height-caching Add IIIF service function to get downlaod link with given dimensions.

* Fix redundant function from rebase.

* Rebase resolve conflict.

* WIP: Make Get dimensions from File image action target fields configurable.

* WIP

* WIP use configured height and width fields.

* Extract TIFF and JP2 dimensions action: Make width and height field configurable.

* Islandora IIIF: Make media height and width fields configurable.

* Islandora IIIF: Make media width and height fields configurable in manifest plugin.

* Islandora IIIF: Add config schema update hook.

* Islandora IIIF: Fix warning on Views display plugin page.

* Remove outdated action.

* Islandora IIIF: Update the README.

* Islandora IIIF: Update PHPDoc description of IIIF Dimensions complex action.

* Islandora IIIF: Add instructions for setting up a context reaction for the IIIF Dimensions actions.

* Update modules/islandora_iiif/config/schema/islandora_iiif.schema.yml

Co-authored-by: Adam <607975+adam-vessey@users.noreply.github.com>

* Islandora IIIF: Address PHPCS errors.

* Islandora IIIF: Remove unused schema entry.

* Islandora IIIF: Address PHPCS errors.

* Islandora IIIF: Address PHPCS errors.

* Islandora IIIF: Fix PHPCS errors.

* Islandora IIIF: Fix PHPCS warnings.

* Islandora IIIF: Address code review comment.

* Update modules/islandora_iiif/src/Plugin/views/style/IIIFManifest.php

Co-authored-by: Adam <607975+adam-vessey@users.noreply.github.com>

* Update modules/islandora_iiif/src/Plugin/views/style/IIIFManifest.php

Co-authored-by: Adam <607975+adam-vessey@users.noreply.github.com>

* Islandora IIIF: Address PHPCS errors.

---------

Co-authored-by: dannylamb <dlamb@islandora.ca>
Co-authored-by: Adam <607975+adam-vessey@users.noreply.github.com>
  • Loading branch information
3 people authored Jul 11, 2024
1 parent 60f4925 commit 7bae08f
Show file tree
Hide file tree
Showing 6 changed files with 691 additions and 47 deletions.
45 changes: 40 additions & 5 deletions modules/islandora_iiif/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

## Introduction

Provides IIIF manifests using views.
Provides [IIIF manifests](https://iiif.io) using views.

## Requirements

Expand All @@ -20,7 +20,7 @@ For a full digital repository solution, see our [installation documentation](htt
To download/enable just this module, use the following from the command line:

```bash
$ composer require islandora/islandora
$ composer require drupal/islandora
$ drush en islandora_core_feature
$ drush mim islandora_tags
$ drush en islandora_iiif
Expand All @@ -38,18 +38,53 @@ This module implements a Views Style plugin. It provides the following settings:

1. Tile Source: A field that was added to the views list of fields with the image to be served. This should be a File or Image type field on a Media.
2. Structured Text field: This lets you specify a file field where OCR text with positional data, e.g., hOCR can be found.
3. Structured Text Term: If your Islandora Object has a separate media with hOCR, point to it with this setting.
4. File Width and Height fields: If you want to use TIFF or JP2 files directly, set the fields where the files' width and height can be found with these settings.

### Media Attributes from IIIF Action

The module also provides an action that lets a site owner populate a TIFF or JP2 image's width and
height attributes into fields so the IIIF server is not bogged down trying to generate a manifest if
it doesn't have them.

It is a complex action that must be configured. Go to
Admin -> Actions UI -> Actions, choose
"Add image dimensions retrieved from the IIIF server" from the Create Action drop-down
And on the next screen, choose the width
and height fields that the action should
populate.

To use it, either:
- Add it as a derivative reaction to a node with an Original FIle as its child, or
- Use it as a batch action, such as on a Paged Content object's list of child pages.

### Setting up the action as a Context Reaction

These instructions assume a standard Islandora Starter Site, so if you have different field names or Contexts, adjust accordingly.

1. Go to admin/config/system/actions
2. Choose "Add image dimensions retrieved from the IIIF server" from the Create Complex Action drop-down and then click Create.
3. Enter Original File for the Source Media Use term.
4. Choose media -- file -- Width and Media -- File -- Height for the corresponding configuration values and click Save.
5. Go to admin/structure/context, and click Duplicate oon the Page Derivatives row.
6. Name the new context something like "Retrieve Page Dimensionss". and edit it.
7. This is the tricky bit, delete 'Original File' from the 'Media has term with URI' field and replace it with Service File. The explanation for this is that to retrieve a file from the IIIF server, it must be part of an Islandora Media that has been fully created and saved and given a URL. This hasn't happened yet when Original File derivatives are being created, so we need to hang our action onto a derivative that is created after the original one.
8. Under Reactions, deselect the existing actions and select "Add i mage dimensions from IIIF sserver" and click Save and continue.
9. Go back to your Paged Content object and add another child with a File media, to which you should upload another TIFF or JP2 file.
10. Without going to the Original File Book Manifestt, make sure that a service file has been generated, then click Edit on the Original File media.
11. Ensure that the Width and Height fields are populated with thee correct values from the file.


## Documentation

Official documentation is available on the [Islandora 8 documentation site](https://islandora.github.io/documentation/).
Official documentation is available on the [Islandora 2 documentation site](https://islandora.github.io/documentation/).

## Development

If you would like to contribute, please get involved by attending our weekly [Tech Call](https://github.com/Islandora/documentation/wiki). We love to hear from you!

If you would like to contribute code to the project, you need to be covered by an Islandora Foundation [Contributor License Agreement](http://islandora.ca/sites/default/files/islandora_cla.pdf) or [Corporate Contributor License Agreement](http://islandora.ca/sites/default/files/islandora_ccla.pdf). Please see the [Contributors](http://islandora.ca/resources/contributors) pages on Islandora.ca for more information.

We recommend using the [islandora-playbook](https://github.com/Islandora-Devops/islandora-playbook) to get started.

## License

[GPLv2](http://www.gnu.org/licenses/gpl-2.0.txt)
41 changes: 41 additions & 0 deletions modules/islandora_iiif/islandora_iiif.install
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

/**
* @file
* Install/update hook implementations.
*/

/**
* Update config schema.
*/
function islandora_iiif_update_92002(&$sandbox) {

/**
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
$config_factory = \Drupal::service('config.factory');
$all_configs = $config_factory->listAll();
$views_configs = array_values(array_filter($all_configs, function ($config) {
return str_starts_with($config, 'views.view.');
}));

foreach ($views_configs as $views_config_name) {
$needs_save = FALSE;
$view_config = $config_factory->getEditable($views_config_name);
$displays = $view_config->get('display');
foreach ($displays as $display_name => $display) {
if ($display['display_plugin'] == 'rest_export'
&& $display['display_options']['style']['type'] == 'iiif_manifest'
&&!empty($display['display_options']['style']['options']['iiif_ocr_file_field'])) {

$display['display_options']['style']['options']['advanced']['iiif_ocr_file_field'] = $display['display_options']['style']['options']['iiif_ocr_file_field'];
unset($display['display_options']['style']['options']['iiif_ocr_file_field']);
$view_config->set('display.' . $display_name . '.display_options.style.options', $display['display_options']['style']['options']);
$needs_save = TRUE;
}
}
if ($needs_save) {
$view_config->save();
}
}
}
4 changes: 4 additions & 0 deletions modules/islandora_iiif/islandora_iiif.services.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
services:
islandora_iiif:
class: Drupal\islandora_iiif\IiifInfo
arguments: ['@config.factory', '@http_client', '@logger.channel.islandora', '@jwt.authentication.jwt']
151 changes: 151 additions & 0 deletions modules/islandora_iiif/src/IiifInfo.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
<?php

namespace Drupal\islandora_iiif;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\file\FileInterface;
use Drupal\jwt\Authentication\Provider\JwtAuth;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Exception\ServerException;

/**
* Get IIIF related info for a given File or Image entity.
*/
class IiifInfo {

/**
* The config factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;


/**
* The HTTP client.
*
* @var \GuzzleHttp\Client
*/
protected $httpClient;

/**
* This module's config.
*
* @var \Drupal\Core\Config\ImmutableConfig
*/
protected $iiifConfig;

/**
* JWT Auth provider service.
*
* @var \Drupal\jwt\Authentication\Provider\JwtAuth
*/
protected $jwtAuth;

/**
* The logger.
*
* @var \Drupal\Core\Logger\LoggerChannelInterface
*/
protected $logger;

/**
* Constructs an IiifInfo object.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory.
* @param \Guzzle\Http\Client $http_client
* The HTTP Client.
* @param \Drupal\Core\Logger\LoggerChannelInterface $channel
* Logger channel.
* @param \Drupal\jwt\Authentication\Provider\JwtAuth $jwt_auth
* The JWT auth provider.
*/
public function __construct(ConfigFactoryInterface $config_factory, Client $http_client, LoggerChannelInterface $channel, JwtAuth $jwt_auth) {
$this->configFactory = $config_factory;

$this->iiifConfig = $this->configFactory->get('islandora_iiif.settings');
$this->httpClient = $http_client;
$this->logger = $channel;
$this->jwtAuth = $jwt_auth;
}

/**
* The IIIF base URL for an image.
*
* Visiting this URL will resolve to the info.json for the image.
*
* @return string
* The absolute URL on the IIIF server.
*/
public function baseUrl($image) {

if ($this->iiifConfig->get('use_relative_paths')) {
$file_url = ltrim($image->createFileUrl(TRUE), '/');
}
else {
$file_url = $image->createFileUrl(FALSE);
}

$iiif_address = $this->iiifConfig->get('iiif_server');
$iiif_url = rtrim($iiif_address, '/') . '/' . urlencode($file_url);

return $iiif_url;
}

/**
* Retrieve an image's original dimensions via the IIIF server.
*
* @param \Drupal\File\FileInterface $file
* The image file.
*
* @return array|false
* The image dimensions in an array as [$width, $height]
*/
public function getImageDimensions(FileInterface $file) {
$iiif_url = $this->baseUrl($file);
try {
$info_json = $this->httpClient->request('get', $iiif_url, [
'headers' => [
'Authorization' => 'Bearer ' . $this->jwtAuth->generateToken(),
],
])->getBody();
$resource = json_decode($info_json, TRUE);
$width = $resource['width'];
$height = $resource['height'];
if (is_numeric($width) && is_numeric($height)) {
return [intval($width), intval($height)];
}
}
catch (ClientException | ConnectException | RequestException | ServerException $e) {
$this->logger->info("Error getting image file dimensions from IIIF server: " . $e->getMessage());
}
return FALSE;
}

/**
* The IIIF base URL for an image.
*
* Visiting this URL resolves to the image resized to the maximum dimensions.
*
* @param \Drupal\file\FileInterface $image
* The image entity.
* @param int $width
* The maximum width of the image to be returned. 0 for no constraint.
* @param int $height
* The maxim um height of the image to be returned. 0 for no contraint.
*
* @return string
* The IIIF URl to retrieve the full image with the given max dimensions.
*/
public function getImageWithMaxDimensions(FileInterface $image, $width = 0, $height = 0) {
$base_url = $this->baseUrl($image);
return $base_url . "/full/!$width,$height/0/default.jpg";

}

}
Loading

0 comments on commit 7bae08f

Please sign in to comment.