From 0f5566976cd4b84055fffa699d36b2130163e0b2 Mon Sep 17 00:00:00 2001 From: Markus Weigelt Date: Fri, 28 Feb 2025 09:00:06 +0100 Subject: [PATCH] [FEATURE] 3D viewer selection (#1395) Co-authored-by: Sebastian Meyer --- .../Configuration/UseGroupsConfiguration.php | 15 +++- Classes/Controller/ToolboxController.php | 87 +++++++++++++++++-- Documentation/Developers/Embedded3DViewer.rst | 6 +- Documentation/Plugins/Index.rst | 15 ++++ Resources/Private/Language/de.locallang.xlf | 4 + .../Private/Language/de.locallang_labels.xlf | 4 + Resources/Private/Language/locallang.xlf | 3 + .../Private/Language/locallang_labels.xlf | 3 + Resources/Private/Templates/Toolbox/Main.html | 33 +++++-- ext_conf_template.txt | 2 + 10 files changed, 155 insertions(+), 17 deletions(-) diff --git a/Classes/Configuration/UseGroupsConfiguration.php b/Classes/Configuration/UseGroupsConfiguration.php index 756a92ab9..c083dd166 100644 --- a/Classes/Configuration/UseGroupsConfiguration.php +++ b/Classes/Configuration/UseGroupsConfiguration.php @@ -56,7 +56,8 @@ private function __construct() 'useGroupsDownload', 'useGroupsFulltext', 'useGroupsAudio', - 'useGroupsScore' + 'useGroupsScore', + 'useGroupsModel' ]; foreach ($configKeys as $key) { @@ -130,6 +131,18 @@ public function getImage(): array return $this->getByType('Image'); } + /** + * Get the configuration for 'Model' use groups type. + * + * @access public + * + * @return array + */ + public function getModel(): array + { + return $this->getByType('Model'); + } + /** * Get the configuration for 'Score' use groups type. * diff --git a/Classes/Controller/ToolboxController.php b/Classes/Controller/ToolboxController.php index e36681358..8ac8fa52d 100644 --- a/Classes/Controller/ToolboxController.php +++ b/Classes/Controller/ToolboxController.php @@ -13,9 +13,14 @@ use Kitodo\Dlf\Common\AbstractDocument; use Kitodo\Dlf\Common\Helper; +use Kitodo\Dlf\Middleware\Embedded3dViewer; use Psr\Http\Message\ResponseInterface; +use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader; +use TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException; +use TYPO3\CMS\Core\Resource\StorageRepository; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\MathUtility; +use TYPO3\CMS\Core\Utility\PathUtility; /** * Controller class for plugin 'Toolbox'. @@ -113,6 +118,10 @@ private function renderTools(): void case 'scoretool': $this->renderToolByName('renderScoreTool'); break; + case 'tx_dlf_viewerselectiontool': + case 'viewerselectiontool': + $this->renderToolByName('renderViewerSelectionTool'); + break; default: $this->logger->warning('Incorrect tool configuration: "' . $this->settings['tools'] . '". Tool "' . $tool . '" does not exist.'); } @@ -135,6 +144,30 @@ private function renderToolByName(string $tool): void $this->view->assign($tool, true); } + /** + * Get the URL of the model. + * + * Gets the URL of the model by parameter or from the configured file group of the document. + * + * @access private + * + * @return string + */ + private function getModelUrl(): string + { + $modelUrl = ''; + if (!empty($this->requestData['model'])) { + $modelUrl = $this->requestData['model']; + } elseif (!($this->isDocMissingOrEmpty() || empty($this->useGroupsConfiguration->getModel()))) { + $this->setPage(); + if (isset($this->requestData['page'])) { + $file = $this->getFile($this->requestData['page'], $this->useGroupsConfiguration->getModel()); + $modelUrl = $file['url'] ?? ''; + } + } + return $modelUrl; + } + /** * Get the score file. * @@ -370,19 +403,55 @@ private function renderImageManipulationTool(): void */ private function renderModelDownloadTool(): void { - // TODO: missing fileGrpsModelDownload, should be added to ext config as useGroupsModelDownload - if ( - $this->isDocMissingOrEmpty() - || empty($this->settings['fileGrpsModelDownload']) - ) { - // Quit without doing anything if required variables are not set. + $modelUrl = $this->getModelUrl(); + if ($modelUrl === '') { + $this->logger->debug("Model URL could not be determined"); return; } + $this->view->assign('modelUrl', $modelUrl); + } - $this->setPage(); - if (isset($this->requestData['page'])) { - $this->view->assign('modelDownload', $this->getFile($this->requestData['page'], GeneralUtility::trimExplode(',', $this->settings['fileGrpsModelDownload']))); + /** + * Renders the viewer selection tool + * Renders the viewer selection tool (used in template) + * @SuppressWarnings(PHPMD.UnusedPrivateMethod) + * + * @access private + * + * @return void + * @throws InsufficientFolderAccessPermissionsException + */ + private function renderViewerSelectionTool(): void + { + $model = $this->getModelUrl(); + if (!$model) { + $this->logger->debug("Model URL could not be determined"); + return; + } + + $pathInfo = PathUtility::pathinfo($model); + $modelFormat = strtolower($pathInfo["extension"]); + $viewers = []; + /** @var StorageRepository $storageRepository */ + $storageRepository = GeneralUtility::makeInstance(StorageRepository::class); + $defaultStorage = $storageRepository->getDefaultStorage(); + if ($defaultStorage->hasFolder(Embedded3dViewer::VIEWER_FOLDER)) { + $viewerFolders = $defaultStorage->getFoldersInFolder($defaultStorage->getFolder(Embedded3dViewer::VIEWER_FOLDER)); + if (count($viewerFolders) > 0) { + /** @var YamlFileLoader $yamlFileLoader */ + $yamlFileLoader = GeneralUtility::makeInstance(YamlFileLoader::class); + foreach ($viewerFolders as $viewerFolder) { + if ($viewerFolder->hasFile(Embedded3dViewer::VIEWER_CONFIG_YML)) { + $fileIdentifier = $viewerFolder->getFile(Embedded3dViewer::VIEWER_CONFIG_YML)->getIdentifier(); + $viewerConfig = $yamlFileLoader->load($defaultStorage->getName() . $fileIdentifier)["viewer"]; + if (!empty($viewerConfig["supportedModelFormats"]) && in_array($modelFormat, array_map('strtolower', $viewerConfig["supportedModelFormats"]))) { + $viewers[] = (object) ['id' => $viewerFolder->getName(), 'name' => $viewerConfig["name"] ?? $viewerFolder->getName()]; + } + } + } + $this->view->assign('viewers', $viewers); + } } } diff --git a/Documentation/Developers/Embedded3DViewer.rst b/Documentation/Developers/Embedded3DViewer.rst index fbcfe643b..9cc93f797 100644 --- a/Documentation/Developers/Embedded3DViewer.rst +++ b/Documentation/Developers/Embedded3DViewer.rst @@ -10,6 +10,8 @@ On this page, you will find all the information needed to configure and embed an :local: :depth: 2 +.. _Embedded 3D Viewer Setup: + Setup ======= @@ -27,11 +29,13 @@ Configuration By default, the viewers from the folder ``dlf_3d_viewers`` are all active and can be accessed and tested via URL. -For this, only the parameter ``tx_dlf[viewer]`` with the name of the viewer and the encoded URL to the model via the parameter ``tx_dlf[model]`` need to be passed to the URL under which the plugin ``plugin.tx_dlf_embedded3dViewer`` is rendered. +For this, only the parameter ``tx_dlf[viewer]`` with the encoded subfolder name of the viewer needs to be passed to the URL where the plugin ``plugin.tx_dlf_embedded3dViewer`` is rendered. .. note:: For example in the DFG Viewer, this is the page whose ID is set via the constant ``config.kitodoPageView``. +To render the model, the encoded URL to the METS document should be set using the parameter ``tx_dlf[id]``. Alternatively, it is possible to define the model directly with an encoded URL via the parameter ``tx_dlf[model]``. + Automatic selection of the viewer ------- diff --git a/Documentation/Plugins/Index.rst b/Documentation/Plugins/Index.rst index 1d24f4495..c9385583e 100644 --- a/Documentation/Plugins/Index.rst +++ b/Documentation/Plugins/Index.rst @@ -1018,6 +1018,21 @@ The fulltext is fetched and rendered by JavaScript into the `
+ + + + diff --git a/Resources/Private/Language/de.locallang_labels.xlf b/Resources/Private/Language/de.locallang_labels.xlf index 041560920..78d30071e 100644 --- a/Resources/Private/Language/de.locallang_labels.xlf +++ b/Resources/Private/Language/de.locallang_labels.xlf @@ -701,6 +701,10 @@ Audio Datei Nutzungsgruppen: Komma-getrennte Liste der @USE Attributwerte der Audiodateien nach absteigender Priorität sortiert (Standard ist "AUDIO") Audio file use groups: comma-separated list of @USE attribute values ordered by decreasing priority (default is "AUDIO") + + Modell Datei Nutzungsgruppen: Komma-getrennte Liste der @USE Attributwerte der Modelldateien nach absteigender Priorität sortiert (Standard ist "DEFAULT") + Model file use groups: comma-separated list of @USE attribute values ordered by decreasing priority (default is "DEFAULT") + IIIF-Annotationen mit Motivation "painting" als Volltext behandeln?: Als Volltext behandelte Annotationen werden im Suchid idiert (Standard ist "FALSE") Handle IIIF annotations with motivation "painting" as fulltext?: Handling annotations as fulltexts means they are ided (default is "FALSE") diff --git a/Resources/Private/Language/locallang.xlf b/Resources/Private/Language/locallang.xlf index ab8f3addc..4af360e97 100644 --- a/Resources/Private/Language/locallang.xlf +++ b/Resources/Private/Language/locallang.xlf @@ -269,6 +269,9 @@ + + + diff --git a/Resources/Private/Language/locallang_labels.xlf b/Resources/Private/Language/locallang_labels.xlf index 0ce97bd6e..687ea8d55 100644 --- a/Resources/Private/Language/locallang_labels.xlf +++ b/Resources/Private/Language/locallang_labels.xlf @@ -527,6 +527,9 @@ Audio file use groups: comma-separated list of @USE attribute values ordered by decreasing priority (default is "AUDIO") + + Model file use groups: comma-separated list of @USE attribute values ordered by decreasing priority (default is "DEFAULT") + Handle IIIF annotations with motivation "painting" as fulltext?: Handling annotations as fulltexts means they are ided (default is "FALSE") diff --git a/Resources/Private/Templates/Toolbox/Main.html b/Resources/Private/Templates/Toolbox/Main.html index f8a8b7754..1ba441a08 100644 --- a/Resources/Private/Templates/Toolbox/Main.html +++ b/Resources/Private/Templates/Toolbox/Main.html @@ -75,18 +75,39 @@ - +
  • - - - - - + + + + +
  • + + +
    + + + + + + + + + + + +
  • diff --git a/ext_conf_template.txt b/ext_conf_template.txt index 18a2fa79f..fe890dae1 100644 --- a/ext_conf_template.txt +++ b/ext_conf_template.txt @@ -30,6 +30,8 @@ files.useGroupsDownload = DOWNLOAD files.useGroupsFulltext = FULLTEXT # cat=Files; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.files.useGroupsScore files.useGroupsScore = SCORE +# cat=Files; type=string; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.files.useGroupsModel +files.useGroupsModel = DEFAULT # cat=IIIF; type=boolean; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.iiif.indexAnnotations iiif.indexAnnotations = 0 # cat=IIIF; type=int[1-2000]; label=LLL:EXT:dlf/Resources/Private/Language/locallang_labels.xlf:config.iiif.thumbnailWidth