Passed
Pull Request — master (#123)
by
unknown
11:14
created

Embedded3DViewer::renderDefaultViewer()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 6
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 9
rs 10
1
<?php
2
3
namespace Kitodo\Dlf\Middleware;
4
5
/**
6
 * (c) Kitodo. Key to digital objects e.V. <[email protected]>
7
 *
8
 * This file is part of the Kitodo and TYPO3 projects.
9
 *
10
 * @license GNU General Public License version 3 or later.
11
 * For the full copyright and license information, please read the
12
 * LICENSE.txt file that was distributed with this source code.
13
 */
14
15
use Psr\Http\Message\ResponseInterface;
16
use Psr\Http\Message\ServerRequestInterface;
17
use Psr\Http\Server\MiddlewareInterface;
18
use Psr\Http\Server\RequestHandlerInterface;
19
use Psr\Log\LoggerAwareTrait;
20
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
21
use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader;
22
use TYPO3\CMS\Core\Http\HtmlResponse;
23
use TYPO3\CMS\Core\Resource\Folder;
24
use TYPO3\CMS\Core\Resource\ResourceFactory;
25
use TYPO3\CMS\Core\Resource\StorageRepository;
26
use TYPO3\CMS\Core\Utility\GeneralUtility;
27
use TYPO3\CMS\Core\Utility\PathUtility;
28
use TYPO3\CMS\Frontend\Controller\ErrorController;
29
30
/**
31
 * Middleware for embedding custom 3D Viewer implementation of the 'dlf' extension.
32
 *
33
 * @package TYPO3
34
 * @subpackage dlf
35
 * @access public
36
 */
37
class Embedded3DViewer implements MiddlewareInterface
38
{
39
    use LoggerAwareTrait;
40
41
    const VIEWER_FOLDER = "dlf_3d_viewers";
42
    const VIEWER_CONFIG_YML = "dlf-3d-viewer.yml";
43
    const EXT_KEY = "dlf";
44
45
    /**
46
     * The main method of the middleware.
47
     *
48
     * @access public
49
     *
50
     * @param ServerRequestInterface $request for processing
51
     * @param RequestHandlerInterface $handler for processing
52
     *
53
     * @return ResponseInterface
54
     */
55
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
56
    {
57
        $response = $handler->handle($request);
58
        // parameters are sent by POST --> use getParsedBody() instead of getQueryParams()
59
        $parameters = $request->getQueryParams();
60
        // Return if not this middleware
61
        if (!isset($parameters['middleware']) || ($parameters['middleware'] != 'dlf/embedded3DViewer')) {
62
            return $response;
63
        }
64
65
        if (empty($parameters['model'])) {
66
            return $this->warningResponse('Model url is missing.', $request);
67
        }
68
69
        $modelInfo = PathUtility::pathinfo($parameters['model']);
70
        $modelFormat = $modelInfo["extension"];
71
        if (empty($modelFormat)) {
72
            return $this->warningResponse('Model path "' . $parameters['model'] . '" has no extension format', $request);
73
        }
74
75
        if (empty($parameters['viewer'])) {
76
            // determine viewer from extension configuration
77
            $viewer = $this->getViewerByExtensionConfiguration($modelFormat);
78
        } else {
79
            $viewer = $parameters['viewer'];
80
        }
81
82
        if (empty($viewer)) {
83
            return $this->renderDefaultViewer($parameters['model']);
84
        }
85
86
        /** @var StorageRepository $storageRepository */
87
        $storageRepository = GeneralUtility::makeInstance(StorageRepository::class);
88
        $defaultStorage = $storageRepository->getDefaultStorage();
89
90
        if (!$defaultStorage->hasFolder(self::VIEWER_FOLDER)) {
91
            return $this->errorResponse('Required folder "' . self::VIEWER_FOLDER . '" was not found in the default storage "' . $defaultStorage->getName() . '"', $request);
92
        }
93
94
        $viewerModules = $defaultStorage->getFolder(self::VIEWER_FOLDER);
95
        if (!$viewerModules->hasFolder($viewer)) {
96
            return $this->errorResponse('Viewer folder "' . $viewer . '" was not found under the folder "' . self::VIEWER_FOLDER . '"', $request);
97
        }
98
99
        $viewerFolder = $viewerModules->getSubfolder($viewer);
100
        if (!$viewerFolder->hasFile(self::VIEWER_CONFIG_YML)) {
101
            return $this->errorResponse('Viewer folder "' . $viewer . '" does not contain a file named "' . self::VIEWER_CONFIG_YML . '"', $request);
102
        }
103
104
        /** @var YamlFileLoader $yamlFileLoader */
105
        $yamlFileLoader = GeneralUtility::makeInstance(YamlFileLoader::class);
106
        $viewerConfigPath = $defaultStorage->getName() . "/" . self::VIEWER_FOLDER . "/" . $viewer . "/";
107
        $config = $yamlFileLoader->load($viewerConfigPath . self::VIEWER_CONFIG_YML)["viewer"];
108
109
        if (!isset($config["supportedModelFormats"]) || empty($config["supportedModelFormats"])) {
110
            return $this->errorResponse('Required key "supportedModelFormats" does not exist in the file "' . self::VIEWER_CONFIG_YML . '" of viewer "' . $viewer . '" or has no value', $request);
111
        }
112
113
        if (array_search(strtolower($modelFormat), array_map('strtolower', $config["supportedModelFormats"])) === false) {
114
            return $this->warningResponse('Viewer "' . $viewer . '" does not support the model format "' . $modelFormat . '"', $request);
115
        }
116
117
        $html = $this->getViewerHtml($config, $viewerConfigPath, $viewerFolder, $parameters['model'], $modelInfo);
0 ignored issues
show
Bug introduced by
$modelInfo of type string is incompatible with the type array expected by parameter $modelInfo of Kitodo\Dlf\Middleware\Em...Viewer::getViewerHtml(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

117
        $html = $this->getViewerHtml($config, $viewerConfigPath, $viewerFolder, $parameters['model'], /** @scrutinizer ignore-type */ $modelInfo);
Loading history...
118
        return new HtmlResponse($html);
119
    }
120
121
    /**
122
     * Build the error response.
123
     *
124
     * Logs the given message as error and return internal error response.
125
     *
126
     * @param string $message
127
     * @param ServerRequestInterface $request
128
     * @return ResponseInterface
129
     * @throws \TYPO3\CMS\Core\Error\Http\InternalServerErrorException
130
     */
131
    public function errorResponse(string $message, ServerRequestInterface $request): ResponseInterface
132
    {
133
        /** @var ErrorController $errorController */
134
        $errorController = GeneralUtility::makeInstance(ErrorController::class);
135
        $this->logger->error($message);
0 ignored issues
show
Bug introduced by
The method error() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

135
        $this->logger->/** @scrutinizer ignore-call */ 
136
                       error($message);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
136
        return $errorController->internalErrorAction($request, $message);
137
    }
138
139
    /**
140
     * Build the warning response.
141
     *
142
     * Logs the given message as warning and return page not found response.
143
     *
144
     * @param string $message
145
     * @param ServerRequestInterface $request
146
     * @return ResponseInterface
147
     * @throws \TYPO3\CMS\Core\Error\Http\PageNotFoundException
148
     */
149
    public function warningResponse(string $message, ServerRequestInterface $request): ResponseInterface
150
    {
151
        /** @var ErrorController $errorController */
152
        $errorController = GeneralUtility::makeInstance(ErrorController::class);
153
        $this->logger->warning($message);
154
        return $errorController->pageNotFoundAction($request, $message);
155
    }
156
157
    /**
158
     * Determines the viewer based on the extension configuration and the given model format.
159
     *
160
     * @param $modelFormat string The model format
161
     * @return string The 3D viewer
162
     */
163
    private function getViewerByExtensionConfiguration($modelFormat): string
164
    {
165
        $extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get(self::EXT_KEY, '3dviewer');
166
        $viewerModelFormatMappings = explode(";", $extConf['viewerModelFormatMapping']);
167
        foreach ($viewerModelFormatMappings as $viewerModelFormatMapping) {
168
            $explodedViewerModelMapping = explode(":", $viewerModelFormatMapping);
169
            if (count($explodedViewerModelMapping) == 2) {
170
                $viewer = trim($explodedViewerModelMapping[0]);
171
                $viewerModelFormats = array_map('trim', explode(",", $explodedViewerModelMapping[1]));
172
                if (in_array($modelFormat, $viewerModelFormats)) {
173
                    return $viewer;
174
                }
175
            }
176
        }
177
178
        return $extConf['defaultViewer'] ?? "";
179
    }
180
181
    /**
182
     * @param string $viewerUrl
183
     * @param string $html
184
     * @param string $modelUrl
185
     * @param array $modelInfo
186
     * @return string
187
     */
188
    public function replacePlaceholders(string $viewerUrl, string $html, $modelUrl, array $modelInfo): string
189
    {
190
        $html = str_replace("{{viewerPath}}", $viewerUrl, $html);
191
        $html = str_replace("{{modelUrl}}", $modelUrl, $html);
192
        $html = str_replace("{{modelPath}}", $modelInfo["dirname"], $html);
193
        return str_replace("{{modelResource}}", $modelInfo["basename"], $html);
194
    }
195
196
    /**
197
     * @param $model
198
     * @return HtmlResponse
199
     * @throws \TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException
200
     */
201
    public function renderDefaultViewer($model): HtmlResponse
202
    {
203
        /** @var ResourceFactory $resourceFactory */
204
        $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
205
        $html = $resourceFactory->retrieveFileOrFolderObject('EXT:dlf/Resources/Private/Templates/View3D/Standalone.html')->getContents();
0 ignored issues
show
Bug introduced by
The method getContents() does not exist on TYPO3\CMS\Core\Resource\Folder. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

205
        $html = $resourceFactory->retrieveFileOrFolderObject('EXT:dlf/Resources/Private/Templates/View3D/Standalone.html')->/** @scrutinizer ignore-call */ getContents();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
206
        $file = $resourceFactory->retrieveFileOrFolderObject('EXT:dlf/Resources/Public/JavaScript/3DViewer/model-viewer-3.5.0.min.js');
207
        $html = str_replace('{{modelViewerJS}}', $file->getPublicUrl(), $html);
208
        $html = str_replace("{{modelUrl}}", $model, $html);
209
        return new HtmlResponse($html);
210
    }
211
212
    /**
213
     * @param array $config
214
     * @param string $viewerConfigPath
215
     * @param Folder $viewerFolder
216
     * @param string $modelUrl
217
     * @param array $modelInfo
218
     * @return string
219
     */
220
    public function getViewerHtml(array $config, string $viewerConfigPath, Folder $viewerFolder, string $modelUrl, array $modelInfo): string
221
    {
222
        $htmlFile = "index.html";
223
        if (isset($config["base"]) && !empty($config["base"])) {
224
            $htmlFile = $config["base"];
225
        }
226
227
        $viewerUrl = $viewerConfigPath;
228
        if (isset($config["url"]) && !empty($config["url"])) {
229
            $viewerUrl = rtrim($config["url"]);
230
        }
231
232
        $html = $viewerFolder->getFile($htmlFile)->getContents();
233
        return $this->replacePlaceholders($viewerUrl, $html, $modelUrl, $modelInfo);
234
    }
235
}
236