Passed
Push — typo3v9 ( 2404ee...b9b5fa )
by Tomas Norre
05:51
created

CrawlerInitialization   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 103
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 41
c 1
b 0
f 0
dl 0
loc 103
ccs 0
cts 54
cp 0
rs 10
wmc 13

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A isRequestHashMatchingQueueRecord() 0 3 2
A process() 0 50 5
A runPollSuccessHooks() 0 9 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace AOE\Crawler\Middleware;
6
7
/*
8
 * (c) 2020 AOE GmbH <[email protected]>
9
 *
10
 * This file is part of the TYPO3 Crawler Extension.
11
 *
12
 * It is free software; you can redistribute it and/or modify it under
13
 * the terms of the GNU General Public License, either version 2
14
 * of the License, or any later version.
15
 *
16
 * For the full copyright and license information, please read the
17
 * LICENSE.txt file that was distributed with this source code.
18
 *
19
 * The TYPO3 project - inspiring people to share!
20
 */
21
22
use AOE\Crawler\Domain\Repository\QueueRepository;
23
use Psr\Http\Message\ResponseInterface;
24
use Psr\Http\Message\ServerRequestInterface;
25
use Psr\Http\Server\MiddlewareInterface;
26
use Psr\Http\Server\RequestHandlerInterface;
27
use TYPO3\CMS\Core\Context\Context;
28
use TYPO3\CMS\Core\Http\Response;
29
use TYPO3\CMS\Core\Utility\GeneralUtility;
30
use TYPO3\CMS\Extbase\Object\ObjectManager;
31
use TYPO3\CMS\Frontend\Controller\ErrorController;
32
33
/**
34
 * Evaluates HTTP headers and checks if Crawler should register itself.
35
 * Needs to be run after TSFE initialization AND Frontend User Authentication.
36
 *
37
 * Once done, the queue is fetched, and then the frontend request runs through.
38
 *
39
 * Finally, at the very end, if the crawler is still running, output the data and replace the response.
40
 */
41
class CrawlerInitialization implements MiddlewareInterface
42
{
43
    /**
44
     * @var string
45
     */
46
    protected $headerName = 'X-T3CRAWLER';
47
48
    /**
49
     * @var Context
50
     */
51
    protected $context;
52
53
    /**
54
     * @var QueueRepository
55
     */
56
    protected $queueRepository;
57
58
    /**
59
     * @param Context|null $context
60
     */
61
    public function __construct(?Context $context = null)
62
    {
63
        $this->context = $context ?? GeneralUtility::makeInstance(Context::class);
64
        $this->queueRepository = GeneralUtility::makeInstance(ObjectManager::class)->get(QueueRepository::class);
65
    }
66
67
    /**
68
     * @param ServerRequestInterface $request
69
     * @param RequestHandlerInterface $handler
70
     * @return ResponseInterface
71
     * @throws \TYPO3\CMS\Core\Context\Exception\AspectNotFoundException
72
     * @throws \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException
73
     */
74
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
75
    {
76
        $crawlerInformation = $request->getHeaderLine($this->headerName) ?? null;
77
        if (empty($crawlerInformation)) {
78
            return $handler->handle($request);
79
        }
80
81
        // Authenticate crawler request:
82
        //@todo: ask service to exclude current call for special reasons: for example no relevance because the language version is not affected
83
        [$queueId, $hash] = explode(':', $crawlerInformation);
84
        $queueRec = $this->queueRepository->findByQueueId($queueId);
85
86
        // If a crawler record was found and hash was matching, set it up
87
        if (!$this->isRequestHashMatchingQueueRecord($queueRec, $hash)) {
88
            return GeneralUtility::makeInstance(ErrorController::class)->unavailableAction($request, 'No crawler entry found');
89
        }
90
91
        $queueParameters = unserialize($queueRec['parameters']);
92
        $GLOBALS['TSFE']->applicationData['tx_crawler']['running'] = true;
93
        $GLOBALS['TSFE']->applicationData['tx_crawler']['parameters'] = $queueParameters;
94
        $GLOBALS['TSFE']->applicationData['tx_crawler']['log'] = [];
95
        $request = $request->withAttribute('tx_crawler', $queueParameters);
96
97
        // Now ensure to set the proper user groups
98
        $grList = $queueParameters['feUserGroupList'];
99
        if ($grList) {
100
            if (!is_array($GLOBALS['TSFE']->fe_user->user)) {
101
                $GLOBALS['TSFE']->fe_user->user = [];
102
            }
103
            $GLOBALS['TSFE']->fe_user->user['usergroup'] = $grList;
104
            $GLOBALS['TSFE']->applicationData['tx_crawler']['log'][] = 'User Groups: ' . $grList;
105
            // @todo: set the frontend user aspect again.
106
        }
107
108
        // Execute the frontend request as is
109
        $handler->handle($request);
110
111
        $GLOBALS['TSFE']->applicationData['tx_crawler']['vars'] = [
112
            'id' => $GLOBALS['TSFE']->id,
113
            'gr_list' => implode(',', $this->context->getAspect('frontend.user')->getGroupIds()),
0 ignored issues
show
Bug introduced by
The method getGroupIds() does not exist on TYPO3\CMS\Core\Context\AspectInterface. It seems like you code against a sub-type of TYPO3\CMS\Core\Context\AspectInterface such as TYPO3\CMS\Core\Context\UserAspect. ( Ignorable by Annotation )

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

113
            'gr_list' => implode(',', $this->context->getAspect('frontend.user')->/** @scrutinizer ignore-call */ getGroupIds()),
Loading history...
114
            'no_cache' => $GLOBALS['TSFE']->no_cache,
115
        ];
116
117
        $this->runPollSuccessHooks();
118
119
        // Output log data for crawler (serialized content):
120
        $content = serialize($GLOBALS['TSFE']->applicationData['tx_crawler']);
121
        $response = new Response();
122
        $response->getBody()->write($content);
123
        return $response;
124
    }
125
126
    protected function isRequestHashMatchingQueueRecord(?array $queueRec, string $hash): bool
127
    {
128
        return is_array($queueRec) && $hash === md5($queueRec['qid'] . '|' . $queueRec['set_id'] . '|' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']);
129
    }
130
131
    /**
132
     * Required because some extensions (staticpub) might never be requested to run due to some Core side effects
133
     * and since this is considered as error the crawler should handle it properly
134
     */
135
    protected function runPollSuccessHooks(): void
136
    {
137
        if (!is_array($GLOBALS['TSFE']->applicationData['tx_crawler']['content']['parameters']['procInstructions'])) {
138
            return;
139
        }
140
        foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['crawler']['pollSuccess'] ?? [] as $pollable) {
141
            if (in_array($pollable, $GLOBALS['TSFE']->applicationData['tx_crawler']['content']['parameters']['procInstructions'])) {
142
                if (empty($GLOBALS['TSFE']->applicationData['tx_crawler']['success'][$pollable])) {
143
                    $GLOBALS['TSFE']->applicationData['tx_crawler']['errorlog'][] = 'Error: Pollable extension (' . $pollable . ') did not complete successfully.';
144
                }
145
            }
146
        }
147
    }
148
}
149