diff --git a/module/DigLib/src/DigLib/Controller/VuDLController.php b/module/DigLib/src/DigLib/Controller/VuDLController.php index 9951fde..05212cb 100644 --- a/module/DigLib/src/DigLib/Controller/VuDLController.php +++ b/module/DigLib/src/DigLib/Controller/VuDLController.php @@ -846,16 +846,16 @@ public function passthroughAction() protected function getIdsFromManifest($data) { $ids = []; - if (isset($data['sequences'][0]['canvases'])) { - foreach ($data['sequences'][0]['canvases'] as $canvas) { - preg_match( - '/vudl:[0-9]+/', - $canvas['rendering'][0]['@id'] ?? '', - $matches - ); - if (isset($matches[0])) { - $ids[] = $matches[0]; - } + + foreach ($data['items'] as $canvas) { + preg_match( + '/vudl:[0-9]+/', + $canvas['rendering'][0]['id'] ?? '', + $matches + ); + + if (isset($matches[0])) { + $ids[] = $matches[0]; } } return $ids; diff --git a/module/DigLib/src/DigLib/IIIF/ManifestGenerator.php b/module/DigLib/src/DigLib/IIIF/ManifestGenerator.php index 5dec073..d0dc5fa 100644 --- a/module/DigLib/src/DigLib/IIIF/ManifestGenerator.php +++ b/module/DigLib/src/DigLib/IIIF/ManifestGenerator.php @@ -50,7 +50,7 @@ */ class ManifestGenerator { - public const IIIF_CONTEXT = 'http://iiif.io/api/presentation/2/context.json'; + public const IIIF_CONTEXT = 'http://iiif.io/api/presentation/3/context.json'; /** * Server URL helper. @@ -201,9 +201,10 @@ protected function extractManifestMetadata($id, $details) foreach ($details as $key => $current) { if (!in_array($key, $skip)) { $value = (array)$current['value']; + $formatValues = $this->formatManifestMetadataValues($key, $value); $retVal[$key] = [ - 'label' => $current['title'], - 'value' => $this->formatManifestMetadataValues($key, $value), + 'label' => ['en' => [$current['title']]], + 'value' => ['en' => [$formatValues]], ]; } } @@ -219,16 +220,16 @@ protected function extractManifestMetadata($id, $details) // Add some useful links to the bottom: $recordUrl = $this->getUri('record', ['id' => $id]); $persistUrl = $this->getUri('vudl-record', ['id' => $id]); - $sortedRetVal[] = [ - 'label' => 'About', - 'value' => '' + $value = '' . 'More Details
' . 'Permanent Link' - . '
', + . '
'; + $sortedRetVal[] = [ + 'label' => ['en' => ['About']], + 'value' => ['en' => [$value]], ]; - return $sortedRetVal; } @@ -298,111 +299,158 @@ protected function getImageDetailsForCanvas($raw, $imageServerBase) } /** - * Build JSON data for a single canvas. + * Build JSON data for a non-image canvas. * - * @param string $id Record ID - * @param int $i Position of canvas in overall array (used for canvas - * ID generation) - * @param array $raw Raw data to format into canvas - * @param string $type Type of list ('image' or 'audio') + * @param string $id Record ID + * @param int $i Position of canvas in overall array (used for canvas ID generation) + * @param array $raw Raw data to format into canvas + * @param string $type Type of list ('image' or 'audio') + * @param array $list List to check + * @param array $outline Outline data * * @return array */ - protected function getSingleCanvas($id, $i, $raw, $type) + protected function getNonImageCanvas($id, $i, $raw, $type, $list, $outline) { $canvasUrl = $this->getUri( 'vudl-record-canvas', ['id' => $id, 'canvas' => 'p' . $i] ); - $canvasType = 'sc:Canvas'; - if ($type === 'image') { - $imageServerBase = $this->config->Images->serverUrl ?? false; - if (!$imageServerBase) { - throw new \Exception('Must set image server base URL.'); - } - [$width, $height, $imageUrl, $mimeType] - = $this->getImageDetailsForCanvas($raw, $imageServerBase); - $content = [ - 'height' => $height, - 'width' => $width, - 'images' => [ - [ - '@type' => 'oa:Annotation', - 'motivation' => 'sc:painting', - 'resource' => [ - '@id' => $imageUrl, - '@type' => 'dctypes:Image', - 'format' => $mimeType, - 'service' => [ - '@context' => 'http://iiif.io/api/image/2/context.json', - '@id' => $imageServerBase . urlencode($raw['id']), - 'profile' => 'http://iiif.io/api/image/2/level1.json', + + if ($type == 'audio') { + $preferredType = 'Sound'; + $preferredRendering = 'audio/mp3'; + } elseif ($type == 'video') { + $preferredType = 'MovingImage'; + $preferredRendering = 'video/mp4'; + } else { + // Format as a generic download: + $preferredType = 'foaf:Document'; + $preferredRendering = 'application/pdf'; + } + $url = $this->getUri( + 'files', + ['type' => 'MASTER', 'id' => $raw['id']] + ); + $description = isset($raw['sizebytes_str']) + ? ($raw['sizebytes_str'] / 1024) . 'k' + : ''; + $content = [ + 'height' => 600, + 'width' => 800, + 'items' => [ + [ + 'id' => $canvasUrl . '#content', + 'type' => 'AnnotationPage', + 'items' => [ + [ + 'id' => $canvasUrl . '#item' . $i, + 'type' => 'Annotation', + 'motivation' => 'painting', + 'body' => [ + 'id' => $url, + 'type' => $preferredType, + 'format' => $preferredRendering ?? '', + 'label' => ['en' => [$raw['label']]], + 'description' => $description, ], - 'height' => $height, - 'width' => $width, + 'target' => $canvasUrl, + 'thumbnail' => $this->getThumbnail($raw, $type), ], - 'on' => $canvasUrl, ], ], - ]; - } else { - // Format as a generic download: - $url = $this->getUri( - 'files', - ['type' => 'MASTER', 'id' => $raw['id']] - ); - $description = isset($raw['sizebytes_str']) - ? ($raw['sizebytes_str'] / 1024) . 'k' - : ''; - $content = [ - 'height' => 600, - 'width' => 800, - 'content' => [ - [ - '@id' => $canvasUrl . '#content', - '@type' => 'sc:AnnotationPage', - 'items' => [ - [ - '@id' => $canvasUrl . '#item' . $i, - '@type' => 'sc:Annotation', - 'motivation' => 'painting', - 'body' => [ - '@id' => $url, - '@type' => 'sc:Document', - 'format' => $raw['mimetype'] ?? '', - 'label' => $raw['label'], - 'description' => $description, + ], + ]; + + return [ + 'type' => 'Canvas', + 'id' => $canvasUrl, + 'label' => ['en' => [$raw['label'] ]] ?? '-', + 'rendering' => $this->getSequenceRenderingData($outline, $list, $raw), + 'thumbnail' => $this->getThumbnail($raw, $type), + ] + $content; + } + + /** + * Build JSON data for an image canvas. + * + * @param string $id Record ID + * @param int $i Position of canvas in overall array (used for canvas ID generation) + * @param array $raw Raw data to format into canvas + * @param string $type Type of list ('image' or 'audio') + * @param array $list List to check + * @param array $outline Outline data + * + * @return array + */ + protected function getImageCanvas($id, $i, $raw, $type, $list, $outline) + { + $canvasUrl = $this->getUri( + 'vudl-record-canvas', + ['id' => $id, 'canvas' => 'p' . $i] + ); + + $imageServerBase = $this->config->Images->serverUrl ?? false; + if (!$imageServerBase) { + throw new \Exception('Must set image server base URL.'); + } + [$width, $height, $imageUrl, $mimeType] + = $this->getImageDetailsForCanvas($raw, $imageServerBase); + $content = [ + 'height' => $height, + 'width' => $width, + 'items' => [ + [ + 'id' => $this->getUri('record', ['id' => $id]), + 'type' => 'AnnotationPage', + 'items' => [ + [ + 'id' => $imageUrl, + 'type' => 'Annotation', + 'motivation' => 'painting', + 'body' => [ + 'id' => $imageServerBase . urlencode($raw['id']), + 'type' => 'Image', + 'format' => $mimeType, + 'service' => [ + [ + 'id' => $imageServerBase . urlencode($raw['id']), + 'type' => 'ImageService3', + 'profile' => 'level1', + ], ], - 'target' => $canvasUrl, ], + 'height' => $height, + 'width' => $width, + 'target' => $canvasUrl, + 'thumbnail' => $this->getThumbnail($raw, $type), ], ], ], - ]; - } + ], + ]; + return [ - '@type' => $canvasType, - '@id' => $canvasUrl, - 'label' => $raw['label'] ?? '-', - 'rendering' => $this->getCanvasRenderingData($raw), + 'type' => 'Canvas', + 'id' => $canvasUrl, + 'label' => ['en' => [$raw['label'] ]] ?? '-', + 'rendering' => $this->getSequenceRenderingData($outline, $list, $raw), + 'thumbnail' => $this->getThumbnail($raw, $type), ] + $content; } /** - * Build JSON data for a single canvas. + * Find and arrange thumbnail data for canvas. * - * @param string $id Record ID - * @param int $i Position of canvas in overall array (used for canvas - * ID generation) * @param string $raw Raw data to format into canvas * @param string $type Type of list ('image' or 'audio') * * @return array */ - protected function getSingleElement($id, $i, $raw, $type) + protected function getThumbnail($raw, $type) { if ($type == 'audio') { - $elementType = 'dctypes:Sound'; + $elementType = 'Sound'; $preferredRendering = 'audio/mp3'; } elseif ($type == 'video') { $elementType = 'dctypes:MovingImage'; @@ -420,11 +468,11 @@ protected function getSingleElement($id, $i, $raw, $type) } } $bestRendering = $foundMatch ? $i : 0; - $id = $renderings[$bestRendering]['@id'] . '#element'; + $id = $renderings[$bestRendering]['id'] . '#element'; $format = $renderings[$bestRendering]['format']; - $extras = []; + if (in_array('THUMBNAIL', $raw['datastreams'])) { - $extras['thumbnail'] = $this->getUri( + $getThumb = $this->getUri( 'files', ['type' => 'THUMBNAIL', 'id' => $raw['id']] ); @@ -436,16 +484,17 @@ protected function getSingleElement($id, $i, $raw, $type) } else { $thumbFilename = 'default.png'; } - $extras['thumbnail'] = $this->getUri('home') + $getThumb = $this->getUri('home') . 'themes/vudiglib/images/vudl/' . $thumbFilename; } + return [ - '@id' => $id, - 'format' => $format, - '@type' => $elementType, - 'label' => $raw['label'] ?? '-', - 'rendering' => $renderings, - ] + $extras; + [ + 'id' => $getThumb, + 'type' => 'Image', + 'format' => 'image/png', + ], + ]; } /** @@ -605,50 +654,6 @@ protected function getListForCanvas($outline) return current($outline['lists']) ?: []; } - /** - * Build JSON canvas data. - * - * @param string $id Record ID - * @param array $list List data - * - * @return array - */ - protected function getAllCanvasData($id, $list) - { - $retVal = []; - foreach ($list as $i => $current) { - $retVal[] = $this->getSingleCanvas( - $id, - $i, - $current, - $this->getListType($list) - ); - } - return $retVal; - } - - /** - * Build JSON element data. - * - * @param string $id Record ID - * @param array $list List data - * - * @return array - */ - protected function getAllElementData($id, $list) - { - $retVal = []; - foreach ($list as $i => $current) { - $retVal[] = $this->getSingleElement( - $id, - $i, - $current, - $this->getListType($list) - ); - } - return $retVal; - } - /** * Get the array representing a single rendering. * @@ -661,9 +666,10 @@ protected function getAllElementData($id, $list) protected function getSingleRendering($url, $mime, $label = '') { return [ - '@id' => $url, + 'type' => 'rendering', + 'id' => $url, 'format' => $mime, - 'label' => empty($label) ? "Download as {$mime}" : $label, + 'label' => ['en' => [empty($label) ? "Download as {$mime}" : $label]], ]; } @@ -756,13 +762,13 @@ protected function getMasterRenderings($list) * * @param array $outline Outline data. * @param array $canvasList List chosen as primary canvas list for manifest + * @param array $raw Raw data. * * @return array */ - protected function getSequenceRenderingData($outline, $canvasList) + protected function getSequenceRenderingData($outline, $canvasList, $raw) { $renderings = []; - $hasMixedList = $hasImageList = false; foreach ($outline['lists'] as $list) { if ($this->isAudioList($list) && $this->isAudioList($canvasList)) { $filtered = $this->filterAudioList($list); @@ -771,7 +777,6 @@ protected function getSequenceRenderingData($outline, $canvasList) $renderings, $this->getMasterRenderings($filtered['other']) ); - $hasMixedList = true; } } elseif ($this->isPdfList($list) && $this->isPdfList($canvasList)) { $filtered = $this->filterPdfList($list); @@ -780,34 +785,17 @@ protected function getSequenceRenderingData($outline, $canvasList) $renderings, $this->getMasterRenderings($filtered['other']) ); - $hasMixedList = true; } } elseif (!$this->isImageList($list)) { $renderings = array_merge( $renderings, $this->getMasterRenderings($list) ); - } else { - $hasImageList = true; } } - // If we don't have an image list, we don't need alternate renderings: - return ($hasImageList || $hasMixedList) ? $renderings : []; - } - - /** - * Get a string label for a list. - * - * @param array $list List - * - * @return string - */ - protected function getLabelForList($list) - { - if ($this->isImageList($list)) { - return 'Pages'; - } - return 'Contents'; + $canvasRenderings = $this->getCanvasRenderingData($raw); + $retVal = array_merge($canvasRenderings, $renderings); + return $retVal; } /** @@ -825,18 +813,18 @@ protected function getPlaceholderCanvas($id) ); $placeholderImage = 'http://digital.library.villanova.edu/placeholder.jpg'; return [ - '@type' => 'sc:Canvas', - '@id' => $canvasUrl, - 'label' => 'Placeholder image', + 'type' => 'Canvas', + 'id' => $canvasUrl, + 'label' => ['en' => ['Placeholder image']], 'height' => 600, 'width' => 600, 'images' => [ [ - '@type' => 'oa:Annotation', - 'motivation' => 'sc:painting', - 'resource' => [ - '@id' => $placeholderImage, - '@type' => 'dcTypes:Image', + 'type' => 'AnnotationPage', + 'motivation' => 'painting', + 'items' => [ + 'id' => $placeholderImage, + 'type' => 'dcTypes:Image', 'height' => 600, 'width' => 600, ], @@ -860,67 +848,50 @@ protected function getPlaceholderSequence($id) . 'for non-IIIF content (e.g., audio, video) and is unfortunately ' . 'incompatible with IIIF viewers.'; return [ - '@type' => 'sc:Sequence', - 'label' => $label, + 'type' => 'item', + 'label' => ['en' => [$label]], 'compatibilityHint' => 'displayIfContentUnsupported', 'canvases' => [ $this->getPlaceholderCanvas($id) ], ]; } /** - * Build JSON sequence data. + * Build JSON items data. * * @param string $id Record ID * @param array $outline Outline data * * @return array */ - protected function getSequenceData($id, $outline) + protected function getItems($id, $outline) { $list = $this->getListForCanvas($outline); - $listType = $this->getListType($list); - if ($listType === 'image') { - $type = 'sc:Sequence'; - $sequenceKey = 'sequences'; - $extras = [ - 'viewingDirection' => 'left-to-right', - 'viewingHint' => 'paged', - 'canvases' => $this->getAllCanvasData($id, $list), - ]; - } elseif ($listType !== 'unknown') { - $type = 'ixif:MediaSequence'; - $sequenceKey = 'mediaSequences'; - $extras = [ - 'elements' => $this->getAllElementData($id, $list), - ]; - } else { - $type = 'sc:Sequence'; - $sequenceKey = 'sequences'; - $extras = [ - 'canvases' => $this->getAllCanvasData($id, $list), - ]; - } - $globalRenderings = $this->getSequenceRenderingData($outline, $list); + $type = $this->getListType($list); - $retVal = [ - '@type' => $type, - 'label' => $this->getLabelForList($list), - ] + $extras; - - // Handle global renderings differently for image lists vs. IxIF lists: - if ($this->isImageList($list)) { - $retVal['rendering'] = $globalRenderings; - } elseif (isset($retVal['elements'])) { - foreach ($retVal['elements'] as $i => $element) { - $retVal['elements'][$i]['rendering'] - = array_merge($element['rendering'], $globalRenderings); + $items = []; + foreach ($list as $i => $current) { + if ($type === 'image') { + $items[] = $this->getImageCanvas( + $id, + $i, + $current, + $type, + $list, + $outline + ); + } else { + $items[] = $this->getNonImageCanvas( + $id, + $i, + $current, + $type, + $list, + $outline + ); } } - $finalArray = [ $sequenceKey => [ $retVal ] ]; - if ($sequenceKey != 'sequences') { - $finalArray['sequences'] = $this->getPlaceholderSequence($id); - } - return $finalArray; + + return $items; } /** @@ -943,52 +914,6 @@ protected function getManifestContext($outline) } } - /** - * Format parent IDs into URIs for 'within' section. - * - * @param array $ids IDs to reformat - * - * @return array - */ - protected function getParentData($ids) - { - $uris = []; - foreach ($ids as $id) { - $uris[] = $this->getUri('collection', ['id' => $id, 'tab' => 'IIIF']); - } - return count($uris) == 1 ? $uris[0] : $uris; - } - - /** - * Get data to build a IIIF canvas. - * - * @param string $id Record ID - * @param int $canvas Canvas index to look up - * @param array $outline Outline data - * - * @return array - */ - public function getCanvasData($id, $canvas, $outline) - { - $base = ['@context' => self::IIIF_CONTEXT]; - - // Special case: placeholder canvas: - if ($canvas === 'Placeholder') { - return $base + $this->getPlaceholderCanvas($id); - } - - // Standard case: - $list = $this->getListForCanvas($outline); - return isset($list[$canvas]) - ? $base + $this->getSingleCanvas( - $id, - $canvas, - $list[$canvas], - $this->getListType($list) - ) - : []; - } - /** * Get "related link" information pointing to record view page. * @@ -999,8 +924,10 @@ public function getCanvasData($id, $canvas, $outline) protected function getRelated($id) { return [ - '@id' => $this->getUri('vudl-record', compact('id')), + 'id' => $this->getUri('vudl-record', compact('id')), + 'type' => 'Text', 'format' => 'text/html', + 'label' => ['en' => ['More Details']], ]; } @@ -1081,14 +1008,15 @@ protected function getRequiredStatement($license) ? '' . htmlspecialchars($licenseData['alt']) . '' : htmlspecialchars($licenseData['text']); - return [ - 'label' => 'ATTRIBUTION', - 'value' => 'Digital Library@Villanova University' + $value = 'Digital Library@Villanova University' . '

Disclaimers:
' . $this->getDisclaimers() . '

' . 'License:
' . '' . $linkContent . '' - . '
', + . '
'; + return [ + 'label' => ['en' => ['ATTRIBUTION']], + 'value' => ['en' => [$value]], ]; } @@ -1108,21 +1036,27 @@ public function getManifestData($id, $outline, $parents = []) [$license] = $this->connector->getCopyright($id, $this->getLicenses()); return [ '@context' => $this->getManifestContext($outline), - '@type' => 'sc:Manifest', - '@id' => $uri, - - 'label' => $details['title']['value'] ?? 'Untitled', + 'type' => 'Manifest', + 'id' => $uri, + 'label' => [ + 'en' => [ + $details['title']['value'] ?? 'Untitled', + ], + ], 'metadata' => $this->extractManifestMetadata($id, $details), - 'description' => isset($details['description']['value']) + 'summary' => ['en' => [ + isset($details['description']['value']) ? '

' . str_replace( ['' : '', + ], + ], 'requiredStatement' => $this->getRequiredStatement($license), - 'related' => $this->getRelated($id), - 'within' => $this->getParentData($parents), - ] + $this->getSequenceData($id, $outline); + 'seeAlso' => [$this->getRelated($id)], + 'items' => $this->getItems($id, $outline), + ]; } } diff --git a/themes/vudiglib/js/vudl/uv-setup-v4.js b/themes/vudiglib/js/vudl/uv-setup-v4.js index 2a3dc9c..5c672e8 100644 --- a/themes/vudiglib/js/vudl/uv-setup-v4.js +++ b/themes/vudiglib/js/vudl/uv-setup-v4.js @@ -96,7 +96,7 @@ function setupUV4(configUri, phpData, uvOptions) { function getRenderings(json) { if (json && typeof json === "object") { // Fix for multi-pdf - if (json["@type"] === "foaf:Document") { + if (json["type"] === "foaf:Document") { return []; } // Match? @@ -128,7 +128,7 @@ function setupUV4(configUri, phpData, uvOptions) { for (const render of renderings) { transcripts.push({ label: render.label, - href: render["@id"], + href: render["id"], hint: transcriptTypes[render.format], }); } diff --git a/themes/vudiglib/templates/vudl/viewer.phtml b/themes/vudiglib/templates/vudl/viewer.phtml index 078346b..d428902 100644 --- a/themes/vudiglib/templates/vudl/viewer.phtml +++ b/themes/vudiglib/templates/vudl/viewer.phtml @@ -62,10 +62,12 @@

metadata as $data): ?> - - - - + + + + + +