Completed
Push — master ( 1c0463...6ee2a7 )
by Rafael
34:09
created

InfoModuleController::documentsDetailsAction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 0
cts 4
cp 0
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 4
crap 2
1
<?php
2
namespace ApacheSolrForTypo3\Solr\Controller\Backend\Search;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2010-2017 dkd Internet Service GmbH <[email protected]>
8
 *  All rights reserved
9
 *
10
 *  This script is part of the TYPO3 project. The TYPO3 project is
11
 *  free software; you can redistribute it and/or modify
12
 *  it under the terms of the GNU General Public License as published by
13
 *  the Free Software Foundation; either version 3 of the License, or
14
 *  (at your option) any later version.
15
 *
16
 *  The GNU General Public License can be found at
17
 *  http://www.gnu.org/copyleft/gpl.html.
18
 *
19
 *  This script is distributed in the hope that it will be useful,
20
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 *  GNU General Public License for more details.
23
 *
24
 *  This copyright notice MUST APPEAR in all copies of the script!
25
 ***************************************************************/
26
27
use ApacheSolrForTypo3\Solr\Api;
28
use ApacheSolrForTypo3\Solr\ConnectionManager;
29
use ApacheSolrForTypo3\Solr\Domain\Search\Statistics\StatisticsRepository;
30
use ApacheSolrForTypo3\Solr\Domain\Search\ApacheSolrDocument\Repository;
31
use ApacheSolrForTypo3\Solr\System\Validator\Path;
32
use TYPO3\CMS\Backend\Template\ModuleTemplate;
33
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
34
use TYPO3\CMS\Core\Messaging\FlashMessage;
35
use TYPO3\CMS\Core\Registry;
36
use TYPO3\CMS\Core\Utility\GeneralUtility;
37
use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException;
38
use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
39
40
/**
41
 * Info Module
42
 */
43
class InfoModuleController extends AbstractModuleController
44
{
45
    /**
46
     * @var ConnectionManager
47
     */
48
    protected $solrConnectionManager;
49
50
    /**
51
     * @var Repository
52
     */
53
    protected $apacheSolrDocumentRepository;
54
55
    /**
56
     * Initializes the controller before invoking an action method.
57
     */
58
    protected function initializeAction()
59
    {
60
        parent::initializeAction();
61
        $this->solrConnectionManager = GeneralUtility::makeInstance(ConnectionManager::class);
62
        $this->apacheSolrDocumentRepository = GeneralUtility::makeInstance(Repository::class);
63
64
    }
65
    /**
66
     * Set up the doc header properly here
67
     *
68
     * @param ViewInterface $view
69
     * @return void
70
     */
71
    protected function initializeView(ViewInterface $view)
72
    {
73
        parent::initializeView($view);
74
75
        /* @var ModuleTemplate $module */ // holds the state of chosen tab
76
        $module = GeneralUtility::makeInstance(ModuleTemplate::class);
77
        $coreOptimizationTabs = $module->getDynamicTabMenu([], 'coreOptimization');
78
        $this->view->assign('tabs', $coreOptimizationTabs);
79
    }
80
81
    /**
82
     * Index action, shows an overview of the state of the Solr index
83
     *
84
     * @return void
85
     */
86
    public function indexAction()
87
    {
88
        if ($this->selectedSite === null) {
89
            $this->view->assign('can_not_proceed', true);
90
            return;
91
        }
92
93
        $this->collectConnectionInfos();
94
        $this->collectStatistics();
95
        $this->collectIndexFieldsInfo();
96
        $this->collectIndexInspectorInfo();
97
    }
98
99
    /**
100
     * @param string $type
101
     * @param int $uid
102
     * @param int $pageId
103
     * @param int $languageUid
104
     * @return void
105
     */
106
    public function documentsDetailsAction(string $type, int $uid, int $pageId, int $languageUid)
107
    {
108
        $documents = $this->apacheSolrDocumentRepository->findByTypeAndPidAndUidAndLanguageId($type, $uid, $pageId, $languageUid);
109
        $this->view->assign('documents', $documents);
110
    }
111
112
    /**
113
     * Checks whether the configured Solr server can be reached and provides a
114
     * flash message according to the result of the check.
115
     *
116
     * @return void
117
     */
118
    protected function collectConnectionInfos()
119
    {
120
        $connectedHosts = [];
121
        $missingHosts = [];
122
        $invalidPaths = [];
123
124
        /* @var Path $path */
125
        $path = GeneralUtility::makeInstance(Path::class);
126
        $connections = $this->solrConnectionManager->getConnectionsBySite($this->selectedSite);
127
128
        if (empty($connections)) {
129
            $this->view->assign('can_not_proceed', true);
130
            return;
131
        }
132
133
        foreach ($connections as $connection) {
134
            $coreAdmin = $connection->getAdminService();
135
            $coreUrl = $coreAdmin->getScheme() . '://' . $coreAdmin->getHost() . ':' . $coreAdmin->getPort() . $coreAdmin->getPath();
136
137
            if ($coreAdmin->ping()) {
138
                $connectedHosts[] = $coreUrl;
139
            } else {
140
                $missingHosts[] = $coreUrl;
141
            }
142
143
            if (!$path->isValidSolrPath($coreAdmin->getPath())) {
144
                $invalidPaths[] = $coreAdmin->getPath();
145
            }
146
        }
147
148
        $this->view->assignMultiple([
149
            'site' => $this->selectedSite,
150
            'apiKey' => Api::getApiKey(),
151
            'connectedHosts' => $connectedHosts,
152
            'missingHosts' => $missingHosts,
153
            'invalidPaths' => $invalidPaths
154
        ]);
155
    }
156
157
    /**
158
     * Index action, shows an overview of the state of the Solr index
159
     *
160
     * @return void
161
     */
162
    protected function collectStatistics()
163
    {
164
        // TODO make time frame user adjustable, for now it's last 30 days
165
166
        $siteRootPageId = $this->selectedSite->getRootPageId();
167
        /* @var StatisticsRepository $statisticsRepository */
168
        $statisticsRepository = GeneralUtility::makeInstance(StatisticsRepository::class);
169
170
        // @TODO: Do we want Typoscript constants to restrict the results?
171
        $this->view->assign(
172
            'top_search_phrases',
173
            $statisticsRepository->getTopKeyWordsWithHits($siteRootPageId, 30, 5)
174
        );
175
        $this->view->assign(
176
            'top_search_phrases_without_hits',
177
            $statisticsRepository->getTopKeyWordsWithoutHits($siteRootPageId, 30, 5)
178
        );
179
        $this->view->assign(
180
            'search_phrases_statistics',
181
            $statisticsRepository->getSearchStatistics($siteRootPageId, 30, 100)
182
        );
183
184
        $labels = [];
185
        $data = [];
186
        $chartData = $statisticsRepository->getQueriesOverTime($siteRootPageId, 30, 86400);
187
        foreach ($chartData as $bucket) {
188
            $labels[] = strftime('%x', $bucket['timestamp']);
189
            $data[] = (int)$bucket['numQueries'];
190
        }
191
192
        $this->view->assign('queriesChartLabels', json_encode($labels));
193
        $this->view->assign('queriesChartData', json_encode($data));
194
    }
195
196
    /**
197
     * Gets Luke meta data for the currently selected core and provides a list
198
     * of that data.
199
     *
200
     * @return void
201
     */
202
    protected function collectIndexFieldsInfo()
203
    {
204
        $indexFieldsInfoByCorePaths = [];
205
206
        $solrCoreConnections = $this->solrConnectionManager->getConnectionsBySite($this->selectedSite);
207
        foreach ($solrCoreConnections as $solrCoreConnection) {
208
            $coreAdmin = $solrCoreConnection->getAdminService();
209
210
            $indexFieldsInfo = [
211
                'corePath' => $coreAdmin->getPath()
212
            ];
213
            if ($coreAdmin->ping()) {
214
                $lukeData = $coreAdmin->getLukeMetaData();
215
216
                /* @var Registry $registry */
217
                $registry = GeneralUtility::makeInstance(Registry::class);
218
                $limit = $registry->get('tx_solr', 'luke.limit', 20000);
219
                $limitNote = '';
220
221
                if (isset($lukeData->index->numDocs) && $lukeData->index->numDocs > $limit) {
222
                    $limitNote = '<em>Too many terms</em>';
223
                } elseif (isset($lukeData->index->numDocs)) {
224
                    $limitNote = 'Nothing indexed';
225
                    // below limit, so we can get more data
226
                    // Note: we use 2 since 1 fails on Ubuntu Hardy.
227
                    $lukeData = $coreAdmin->getLukeMetaData(2);
228
                }
229
230
                $fields = $this->getFields($lukeData, $limitNote);
231
                $coreMetrics = $this->getCoreMetrics($lukeData, $fields);
232
233
                $indexFieldsInfo['noError'] = 'OK';
234
                $indexFieldsInfo['fields'] = $fields;
235
                $indexFieldsInfo['coreMetrics'] = $coreMetrics;
236
            } else {
237
                $indexFieldsInfo['noError'] = null;
238
239
                $this->addFlashMessage(
240
                    '',
241
                    'Unable to contact Apache Solr server: ' . $this->selectedSite->getLabel() . ' ' . $coreAdmin->getPath(),
242
                    FlashMessage::ERROR
243
                );
244
            }
245
            $indexFieldsInfoByCorePaths[$coreAdmin->getPath()] = $indexFieldsInfo;
246
        }
247
        $this->view->assign('indexFieldsInfoByCorePaths', $indexFieldsInfoByCorePaths);
248
    }
249
250
    /**
251
     * Retrieves the information for the index inspector.
252
     *
253
     * @return void
254
     */
255
    protected function collectIndexInspectorInfo()
256
    {
257
        $solrCoreConnections = $this->solrConnectionManager->getConnectionsBySite($this->selectedSite);
258
        $documentsByCoreAndType = [];
259
        foreach ($solrCoreConnections as $languageId => $solrCoreConnection) {
260
            $coreAdmin = $solrCoreConnection->getAdminService();
261
            $documents = $this->apacheSolrDocumentRepository->findByPageIdAndByLanguageId($this->requestedPageUID, $languageId);
262
263
            $documentsByType = [];
264
            foreach ($documents as $document) {
265
                $documentsByType[$document->type][] = $document;
266
            }
267
268
            $documentsByCoreAndType[$languageId]['core'] = $coreAdmin;
269
            $documentsByCoreAndType[$languageId]['documents'] = $documentsByType;
270
        }
271
272
        $this->view->assignMultiple([
273
            'pageId' => $this->requestedPageUID,
274
            'indexInspectorDocumentsByLanguageAndType' => $documentsByCoreAndType
275
        ]);
276
    }
277
278
    /**
279
     * Gets field metrics.
280
     *
281
     * @param \Apache_Solr_Response $lukeData Luke index data
282
     * @param string $limitNote Note to display if there are too many documents in the index to show number of terms for a field
283
     *
284
     * @return array An array of field metrics
285
     */
286
    protected function getFields(\Apache_Solr_Response $lukeData, $limitNote)
287
    {
288
        $rows = [];
289
290
        $fields = (array)$lukeData->fields;
291
        foreach ($fields as $name => $field) {
292
            $rows[$name] = [
293
                'name' => $name,
294
                'type' => $field->type,
295
                'docs' => isset($field->docs) ? $field->docs : 0,
296
                'terms' => isset($field->distinct) ? $field->distinct : $limitNote
297
            ];
298
        }
299
        ksort($rows);
300
301
        return $rows;
302
    }
303
304
    /**
305
     * Gets general core metrics.
306
     *
307
     * @param \Apache_Solr_Response $lukeData Luke index data
308
     * @param array $fields Fields metrics
309
     *
310
     * @return array An array of core metrics
311
     */
312
    protected function getCoreMetrics(\Apache_Solr_Response $lukeData, array $fields)
313
    {
314
        $coreMetrics = [
315
            'numberOfDocuments' => $lukeData->index->numDocs,
316
            'numberOfDeletedDocuments' => $lukeData->index->deletedDocs,
317
            'numberOfTerms' => $lukeData->index->numTerms,
318
            'numberOfFields' => count($fields)
319
        ];
320
321
        return $coreMetrics;
322
    }
323
}
324