Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Pull Request — dev-extbase-fluid (#754)
by Alexander
02:28
created

SearchController   F

Complexity

Total Complexity 61

Size/Duplication

Total Lines 417
Duplicated Lines 0 %

Importance

Changes 24
Bugs 0 Features 0
Metric Value
eloc 182
c 24
b 0
f 0
dl 0
loc 417
rs 3.52
wmc 61

10 Methods

Rating   Name   Duplication   Size   Complexity  
A injectMetadataRepository() 0 3 1
A injectCollectionRepository() 0 3 1
B mainAction() 0 46 6
B getFacetsMenuEntry() 0 56 7
B addCurrentCollection() 0 22 7
D makeFacetsMenuArray() 0 102 18
A addExtendedSearch() 0 29 6
B addCurrentDocument() 0 28 9
A searchAction() 0 4 1
A addFacetsMenu() 0 19 5

How to fix   Complexity   

Complex Class

Complex classes like SearchController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SearchController, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * (c) Kitodo. Key to digital objects e.V. <[email protected]>
4
 *
5
 * This file is part of the Kitodo and TYPO3 projects.
6
 *
7
 * @license GNU General Public License version 3 or later.
8
 * For the full copyright and license information, please read the
9
 * LICENSE.txt file that was distributed with this source code.
10
 */
11
12
namespace Kitodo\Dlf\Controller;
13
14
use Kitodo\Dlf\Common\Doc;
15
use Kitodo\Dlf\Common\DocumentList;
16
use Kitodo\Dlf\Common\Helper;
17
use Kitodo\Dlf\Common\Indexer;
18
use Kitodo\Dlf\Common\Solr;
19
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
20
use TYPO3\CMS\Core\Utility\GeneralUtility;
21
use Kitodo\Dlf\Domain\Repository\CollectionRepository;
22
use Kitodo\Dlf\Domain\Repository\MetadataRepository;
23
24
class SearchController extends AbstractController
25
{
26
    /**
27
     * @var CollectionRepository
28
     */
29
    protected $collectionRepository;
30
31
    /**
32
     * @param CollectionRepository $collectionRepository
33
     */
34
    public function injectCollectionRepository(CollectionRepository $collectionRepository)
35
    {
36
        $this->collectionRepository = $collectionRepository;
37
    }
38
39
    /**
40
     * @var MetadataRepository
41
     */
42
    protected $metadataRepository;
43
44
    /**
45
     * @param MetadataRepository $metadataRepository
46
     */
47
    public function injectMetadataRepository(MetadataRepository $metadataRepository)
48
    {
49
        $this->metadataRepository = $metadataRepository;
50
    }
51
52
    /**
53
     * Search Action
54
     *
55
     * @return void
56
     */
57
    public function searchAction()
58
    {
59
        // if search was triggered, get search parameters from POST variables
60
        $searchParams = $this->getParametersSafely('searchParameter');
0 ignored issues
show
Unused Code introduced by
The assignment to $searchParams is dead and can be removed.
Loading history...
61
62
        // output is done by main action
63
//        $this->forward('main', null, null, ['searchParameter' => $searchParams]);
64
//        $this->forward('main', 'ListView', null, ['searchParameter' => $searchParams]);
65
    }
66
67
    /**
68
     * Main action
69
     *
70
     * @return void
71
     */
72
    public function mainAction()
73
    {
74
        // Quit without doing anything if required variables are not set.
75
        if (empty($this->settings['solrcore'])) {
76
            $this->logger->warning('Incomplete plugin configuration');
77
            return;
78
        }
79
80
        // if search was triggered, get search parameters from POST variables
81
        $searchParams = $this->getParametersSafely('searchParameter');
82
83
        // get all sortable metadata records
84
        $sortableMetadata = $this->metadataRepository->findByIsSortable(true);
0 ignored issues
show
Bug introduced by
The method findByIsSortable() does not exist on Kitodo\Dlf\Domain\Repository\MetadataRepository. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

84
        /** @scrutinizer ignore-call */ 
85
        $sortableMetadata = $this->metadataRepository->findByIsSortable(true);
Loading history...
85
86
        // get all metadata records to be shown in results
87
        $listedMetadata = $this->metadataRepository->findByIsListed(true);
0 ignored issues
show
Bug introduced by
The method findByIsListed() does not exist on Kitodo\Dlf\Domain\Repository\MetadataRepository. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

87
        /** @scrutinizer ignore-call */ 
88
        $listedMetadata = $this->metadataRepository->findByIsListed(true);
Loading history...
88
89
        // get results from search
90
        // find all documents from Solr
91
        $solrResults = [];
92
        if (is_array($searchParams) && !empty($searchParams)) {
93
            $solrResults = $this->documentRepository->findSolrByCollection('', $this->settings, $searchParams, $listedMetadata);
0 ignored issues
show
Bug introduced by
'' of type string is incompatible with the type TYPO3\CMS\Extbase\Persistence\Generic\QueryResult expected by parameter $collections of Kitodo\Dlf\Domain\Reposi...:findSolrByCollection(). ( Ignorable by Annotation )

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

93
            $solrResults = $this->documentRepository->findSolrByCollection(/** @scrutinizer ignore-type */ '', $this->settings, $searchParams, $listedMetadata);
Loading history...
94
            $this->forward('main', 'ListView', null, ['searchParameter' => $searchParams]);
95
        }
96
97
        // Pagination of Results: Pass the currentPage to the fluid template to calculate current index of search result.
98
        $widgetPage = $this->getParametersSafely('@widget_0');
99
        if (empty($widgetPage)) {
100
            $widgetPage = ['currentPage' => 1];
101
        }
102
103
        $documents = $solrResults['documents'] ? : [];
104
        //$this->view->assign('metadata', $sortableMetadata);
105
        $this->view->assign('documents', $documents);
106
        $this->view->assign('widgetPage', $widgetPage);
107
        $this->view->assign('lastSearch', $searchParams);
108
109
        $this->view->assign('listedMetadata', $listedMetadata);
110
        $this->view->assign('sortableMetadata', $sortableMetadata);
111
112
        // ABTODO: facets and extended search might fail
113
        // Add the facets menu
114
        $this->addFacetsMenu();
115
116
        // Get additional fields for extended search.
117
        $this->addExtendedSearch();
118
    }
119
120
    /**
121
     * Adds the current document's UID or parent ID to the search form
122
     *
123
     * @access protected
124
     *
125
     * @return string HTML input fields with current document's UID
126
     */
127
    protected function addCurrentDocument()
128
    {
129
        // Load current list object.
130
        $list = GeneralUtility::makeInstance(DocumentList::class);
131
        // Load current document.
132
        if (
133
            !empty($this->requestData['id'])
134
            && \TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($this->requestData['id'])
135
        ) {
136
            $this->loadDocument($this->requestData);
137
            // Get document's UID
138
            if ($this->document) {
139
                $this->view->assign('DOCUMENT_ID', $this->document->getUid());
140
            }
141
        } elseif (!empty($list->metadata['options']['params']['filterquery'])) {
142
            // Get document's UID from search metadata.
143
            // The string may be e.g. "{!join from=uid to=partof}uid:{!join from=uid to=partof}uid:2" OR {!join from=uid to=partof}uid:2 OR uid:2"
144
            // or "collection_faceting:("Some Collection Title")"
145
            foreach ($list->metadata['options']['params']['filterquery'] as $facet) {
146
                if (($lastUidPos = strrpos($facet['query'], 'uid:')) !== false) {
147
                    $facetKeyVal = explode(':', substr($facet['query'], $lastUidPos));
148
                    if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($facetKeyVal[1])) {
149
                        $documentId = (int) $facetKeyVal[1];
150
                    }
151
                }
152
            }
153
            if (!empty($documentId)) {
154
                $this->view->assign('DOCUMENT_ID', $documentId);
155
            }
156
        }
157
    }
158
159
160
    /**
161
     * Adds the current collection's UID to the search form
162
     *
163
     * @access protected
164
     *
165
     * @return string HTML input fields with current document's UID and parent ID
166
     */
167
    protected function addCurrentCollection()
168
    {
169
        // Load current collection.
170
        $list = GeneralUtility::makeInstance(DocumentList::class);
171
        if (
172
            !empty($list->metadata['options']['source'])
173
            && $list->metadata['options']['source'] == 'collection'
174
        ) {
175
            $this->view->assign('COLLECTION_ID', $list->metadata['options']['select']);
176
            // Get collection's UID.
177
        } elseif (!empty($list->metadata['options']['params']['filterquery'])) {
178
            // Get collection's UID from search metadata.
179
            foreach ($list->metadata['options']['params']['filterquery'] as $facet) {
180
                $facetKeyVal = explode(':', $facet['query'], 2);
181
                if (
182
                    $facetKeyVal[0] == 'collection_faceting'
183
                    && !strpos($facetKeyVal[1], '" OR "')
184
                ) {
185
                    $collectionId = Helper::getUidFromIndexName(trim($facetKeyVal[1], '(")'), 'tx_dlf_collections');
186
                }
187
            }
188
            $this->view->assign('COLLECTION_ID', $collectionId);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $collectionId does not seem to be defined for all execution paths leading up to this point.
Loading history...
189
        }
190
    }
191
192
    /**
193
     * Adds the facets menu to the search form
194
     *
195
     * @access protected
196
     *
197
     * @return string HTML output of facets menu
198
     */
199
    protected function addFacetsMenu()
200
    {
201
        // Check for typoscript configuration to prevent fatal error.
202
        if (empty($this->settings['facetsConf'])) {
203
            $this->logger->warning('Incomplete plugin configuration');
204
            return '';
205
        }
206
        // Quit without doing anything if no facets are selected.
207
        if (empty($this->settings['facets']) && empty($this->settings['facetCollections'])) {
208
            return '';
209
        }
210
211
        // Get facets from plugin configuration.
212
        $facets = [];
213
        foreach (GeneralUtility::trimExplode(',', $this->settings['facets'], true) as $facet) {
214
            $facets[$facet . '_faceting'] = Helper::translate($facet, 'tx_dlf_metadata', $this->settings['storagePid']);
215
        }
216
217
        $this->view->assign('facetsMenu', $this->makeFacetsMenuArray($facets));
218
    }
219
220
    /**
221
     * This builds a menu array for HMENU
222
     *
223
     * @access public
224
     *
225
     * @param string $content: The PlugIn content
226
     * @param array $conf: The PlugIn configuration
227
     *
228
     * @return array HMENU array
229
     */
230
    public function makeFacetsMenuArray($facets)
231
    {
232
        $menuArray = [];
233
        // Set default value for facet search.
234
        $search = [
235
            'query' => '*',
236
            'params' => [
237
                'component' => [
238
                    'facetset' => [
239
                        'facet' => []
240
                    ]
241
                ]
242
            ]
243
        ];
244
        // Extract query and filter from last search.
245
        $list = GeneralUtility::makeInstance(DocumentList::class);
246
        if (!empty($list->metadata['options']['source'])) {
247
            if ($list->metadata['options']['source'] == 'search') {
248
                $search['query'] = $list->metadata['options']['select'];
249
            }
250
            $search['params'] = $list->metadata['options']['params'];
251
        }
252
        // Get applicable facets.
253
        $solr = Solr::getInstance($this->settings['solrcore']);
254
        if (!$solr->ready) {
255
            $this->logger->error('Apache Solr not available');
256
            return [];
257
        }
258
        // Set needed parameters for facet search.
259
        if (empty($search['params']['filterquery'])) {
260
            $search['params']['filterquery'] = [];
261
        }
262
263
        foreach (array_keys($facets) as $field) {
264
            $search['params']['component']['facetset']['facet'][] = [
265
                'type' => 'field',
266
                'key' => $field,
267
                'field' => $field,
268
                'limit' => $this->settings['limitFacets'],
269
                'sort' => isset($this->settings['sortingFacets']) ? $this->settings['sortingFacets'] : 'count'
270
            ];
271
        }
272
273
        // Set additional query parameters.
274
        $search['params']['start'] = 0;
275
        $search['params']['rows'] = 0;
276
        // Set query.
277
        $search['params']['query'] = $search['query'];
278
        // Perform search.
279
        $selectQuery = $solr->service->createSelect($search['params']);
280
        $results = $solr->service->select($selectQuery);
281
        $facet = $results->getFacetSet();
282
283
        $facetCollectionArray = [];
284
285
        // replace everything expect numbers and comma
286
        $facetCollections = preg_replace('/[^0-9,]/', '', $this->settings['facetCollections']);
287
288
        if (!empty($facetCollections)) {
289
            $collections = $this->collectionRepository->findCollectionsBySettings(['collections' => $facetCollections]);
290
291
            /** @var Collection $collection */
292
            foreach ($collections as $collection) {
293
                $facetCollectionArray[] = $collection->getIndexName();
294
            }
295
        }
296
297
        // Process results.
298
        if ($facet) {
299
            foreach ($facet as $field => $values) {
300
                $entryArray = [];
301
                $entryArray['title'] = htmlspecialchars($facets[$field]);
302
                $entryArray['count'] = 0;
303
                $entryArray['_OVERRIDE_HREF'] = '';
304
                $entryArray['doNotLinkIt'] = 1;
305
                $entryArray['ITEM_STATE'] = 'NO';
306
                // Count number of facet values.
307
                $i = 0;
308
                foreach ($values as $value => $count) {
309
                    if ($count > 0) {
310
                        // check if facet collection configuration exists
311
                        if (!empty($this->settings['facetCollections'])) {
312
                            if ($field == "collection_faceting" && !in_array($value, $facetCollectionArray)) {
313
                                continue;
314
                            }
315
                        }
316
                        $entryArray['count']++;
317
                        if ($entryArray['ITEM_STATE'] == 'NO') {
318
                            $entryArray['ITEM_STATE'] = 'IFSUB';
319
                        }
320
                        $entryArray['_SUB_MENU'][] = $this->getFacetsMenuEntry($field, $value, $count, $search, $entryArray['ITEM_STATE']);
321
                        if (++$i == $this->settings['limit']) {
322
                            break;
323
                        }
324
                    } else {
325
                        break;
326
                    }
327
                }
328
                $menuArray[] = $entryArray;
329
            }
330
        }
331
        return $menuArray;
332
    }
333
334
    /**
335
     * Creates an array for a HMENU entry of a facet value.
336
     *
337
     * @access protected
338
     *
339
     * @param string $field: The facet's index_name
340
     * @param string $value: The facet's value
341
     * @param int $count: Number of hits for this facet
342
     * @param array $search: The parameters of the current search query
343
     * @param string &$state: The state of the parent item
344
     *
345
     * @return array The array for the facet's menu entry
346
     */
347
    protected function getFacetsMenuEntry($field, $value, $count, $search, &$state)
348
    {
349
        $entryArray = [];
350
        // Translate value.
351
        if ($field == 'owner_faceting') {
352
            // Translate name of holding library.
353
            $entryArray['title'] = htmlspecialchars(Helper::translate($value, 'tx_dlf_libraries', $this->settings['storagePid']));
354
        } elseif ($field == 'type_faceting') {
355
            // Translate document type.
356
            $entryArray['title'] = htmlspecialchars(Helper::translate($value, 'tx_dlf_structures', $this->settings['storagePid']));
357
        } elseif ($field == 'collection_faceting') {
358
            // Translate name of collection.
359
            $entryArray['title'] = htmlspecialchars(Helper::translate($value, 'tx_dlf_collections', $this->settings['storagePid']));
360
        } elseif ($field == 'language_faceting') {
361
            // Translate ISO 639 language code.
362
            $entryArray['title'] = htmlspecialchars(Helper::getLanguageName($value));
363
        } else {
364
            $entryArray['title'] = htmlspecialchars($value);
365
        }
366
        $entryArray['count'] = $count;
367
        $entryArray['doNotLinkIt'] = 0;
368
        // Check if facet is already selected.
369
        $queryColumn = array_column($search['params']['filterquery'], 'query');
370
        $index = array_search($field . ':("' . Solr::escapeQuery($value) . '")', $queryColumn);
371
        if ($index !== false) {
372
            // Facet is selected, thus remove it from filter.
373
            unset($queryColumn[$index]);
374
            $queryColumn = array_values($queryColumn);
375
            $entryArray['ITEM_STATE'] = 'CUR';
376
            $state = 'ACTIFSUB';
377
            //Reset facets
378
            if ($this->settings['resetFacets']) {
379
                //remove ($count) for selected facet in template
380
                $entryArray['count'] = false;
381
                //build link to delete selected facet
382
                $uri = $this->uriBuilder->reset()
383
                    ->setTargetPageUid($GLOBALS['TSFE']->id)
384
                    ->setArguments(['tx_dlf' => ['query' => $search['query'], 'fq' => $queryColumn], 'tx_dlf_search' => ['action' => 'search']])
385
                    ->setAddQueryString(true)
386
                    ->build();
387
                $entryArray['_OVERRIDE_HREF'] = $uri;
388
                $entryArray['title'] = sprintf(LocalizationUtility::translate('search.resetFacet', 'dlf'), $entryArray['title']);
0 ignored issues
show
Bug introduced by
It seems like TYPO3\CMS\Extbase\Utilit...rch.resetFacet', 'dlf') can also be of type null; however, parameter $format of sprintf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

388
                $entryArray['title'] = sprintf(/** @scrutinizer ignore-type */ LocalizationUtility::translate('search.resetFacet', 'dlf'), $entryArray['title']);
Loading history...
389
            }
390
        } else {
391
            // Facet is not selected, thus add it to filter.
392
            $queryColumn[] = $field . ':("' . Solr::escapeQuery($value) . '")';
393
            $entryArray['ITEM_STATE'] = 'NO';
394
        }
395
        $uri = $this->uriBuilder->reset()
396
            ->setTargetPageUid($GLOBALS['TSFE']->id)
397
            ->setArguments(['tx_dlf' => ['query' => $search['query'], 'fq' => $queryColumn], 'tx_dlf_search' => ['action' => 'search']])
398
            ->setArgumentPrefix('tx_dlf')
399
            ->build();
400
        $entryArray['_OVERRIDE_HREF'] = $uri;
401
402
        return $entryArray;
403
    }
404
405
    /**
406
     * Returns the extended search form and adds the JS files necessary for extended search.
407
     *
408
     * @access protected
409
     *
410
     * @return string The extended search form or an empty string
411
     */
412
    protected function addExtendedSearch()
413
    {
414
        // Quit without doing anything if no fields for extended search are selected.
415
        if (
416
            empty($this->settings['extendedSlotCount'])
417
            || empty($this->settings['extendedFields'])
418
        ) {
419
            return '';
420
        }
421
        // Get operator options.
422
        $operatorOptions = [];
423
        foreach (['AND', 'OR', 'NOT'] as $operator) {
424
            $operatorOptions[$operator] = htmlspecialchars(LocalizationUtility::translate('search.' . $operator, 'dlf'));
0 ignored issues
show
Bug introduced by
It seems like TYPO3\CMS\Extbase\Utilit...h.' . $operator, 'dlf') can also be of type null; however, parameter $string of htmlspecialchars() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

424
            $operatorOptions[$operator] = htmlspecialchars(/** @scrutinizer ignore-type */ LocalizationUtility::translate('search.' . $operator, 'dlf'));
Loading history...
425
        }
426
        // Get field selector options.
427
        $fieldSelectorOptions = [];
428
        $searchFields = GeneralUtility::trimExplode(',', $this->settings['extendedFields'], true);
429
        foreach ($searchFields as $searchField) {
430
            $fieldSelectorOptions[$searchField] = Helper::translate($searchField, 'tx_dlf_metadata', $this->settings['storagePid']);
431
        }
432
        $slotCountArray = [];
433
        for ($i = 0; $i < $this->settings['extendedSlotCount']; $i++) {
434
            $slotCountArray[] = $i;
435
        }
436
437
        $this->view->assign('extendedSlotCount', $slotCountArray);
438
        $this->view->assign('extendedFields', $this->settings['extendedFields']);
439
        $this->view->assign('operators', $operatorOptions);
440
        $this->view->assign('searchFields', $fieldSelectorOptions);
441
    }
442
}
443