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
03:03
created

SearchController::addCurrentCollection()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 13
c 2
b 0
f 0
dl 0
loc 22
rs 8.8333
cc 7
nc 5
nop 0
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\Helper;
15
use Kitodo\Dlf\Common\Indexer;
16
use Kitodo\Dlf\Common\Solr;
17
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
18
use TYPO3\CMS\Core\Utility\GeneralUtility;
19
use Kitodo\Dlf\Domain\Repository\CollectionRepository;
20
use Kitodo\Dlf\Domain\Repository\MetadataRepository;
21
22
class SearchController extends AbstractController
23
{
24
    /**
25
     * @var CollectionRepository
26
     */
27
    protected $collectionRepository;
28
29
    /**
30
     * @param CollectionRepository $collectionRepository
31
     */
32
    public function injectCollectionRepository(CollectionRepository $collectionRepository)
33
    {
34
        $this->collectionRepository = $collectionRepository;
35
    }
36
37
    /**
38
     * @var MetadataRepository
39
     */
40
    protected $metadataRepository;
41
42
    /**
43
     * @param MetadataRepository $metadataRepository
44
     */
45
    public function injectMetadataRepository(MetadataRepository $metadataRepository)
46
    {
47
        $this->metadataRepository = $metadataRepository;
48
    }
49
50
    /**
51
     * @var array $this->searchParams: The current search parameter
52
     * @access protected
53
     */
54
    protected $searchParams;
55
56
    /**
57
     * Search Action
58
     *
59
     * @return void
60
     */
61
    public function searchAction()
62
    {
63
        // if search was triggered, get search parameters from POST variables
64
        $this->searchParams = $this->getParametersSafely('searchParameter');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getParametersSafely('searchParameter') can also be of type string. However, the property $searchParams is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
65
66
        // output is done by main action
67
        $this->forward('main', null, null, ['searchParameter' => $this->searchParams]);
68
    }
69
70
    /**
71
     * Main action
72
     *
73
     * This shows the search form and optional the facets and extended search form.
74
     *
75
     * @return void
76
     */
77
    public function mainAction()
78
    {
79
        $listViewSearch = false;
80
        // Quit without doing anything if required variables are not set.
81
        if (empty($this->settings['solrcore'])) {
82
            $this->logger->warning('Incomplete plugin configuration');
83
            return;
84
        }
85
86
        // if search was triggered, get search parameters from POST variables
87
        $this->searchParams = $this->getParametersSafely('searchParameter');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getParametersSafely('searchParameter') can also be of type string. However, the property $searchParams is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
88
        // if search was triggered by the ListView plugin, get the parameters from GET variables
89
        $listRequestData = GeneralUtility::_GPmerged('tx_dlf_listview');
90
91
        if (isset($listRequestData['searchParameter']) && is_array($listRequestData['searchParameter'])) {
92
            $this->searchParams = array_merge($this->searchParams ? : [], $listRequestData['searchParameter']);
0 ignored issues
show
Bug introduced by
It seems like $this->searchParams ?: array() can also be of type string; however, parameter $arrays of array_merge() does only seem to accept array, 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

92
            $this->searchParams = array_merge(/** @scrutinizer ignore-type */ $this->searchParams ? : [], $listRequestData['searchParameter']);
Loading history...
93
            $listViewSearch = true;
94
        }
95
96
        // Pagination of Results: Pass the currentPage to the fluid template to calculate current index of search result.
97
        $widgetPage = $this->getParametersSafely('@widget_0');
98
        if (empty($widgetPage)) {
99
            $widgetPage = ['currentPage' => 1];
100
        }
101
102
        // If a targetPid is given, the results will be shown by ListView on the target page.
103
        if (!empty($this->settings['targetPid']) && !empty($this->searchParams) && !$listViewSearch) {
104
            $this->redirect('main', 'ListView', null,
105
                [
106
                    'searchParameter' => $this->searchParams,
107
                    'widgetPage' => $widgetPage
108
                ], $this->settings['targetPid']
109
            );
110
        }
111
112
        // If no search has been executed, no variables habe to be prepared. An empty form will be shown.
113
        if (is_array($this->searchParams) && !empty($this->searchParams)) {
114
            // get all sortable metadata records
115
            $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

115
            /** @scrutinizer ignore-call */ 
116
            $sortableMetadata = $this->metadataRepository->findByIsSortable(true);
Loading history...
116
117
            // get all metadata records to be shown in results
118
            $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

118
            /** @scrutinizer ignore-call */ 
119
            $listedMetadata = $this->metadataRepository->findByIsListed(true);
Loading history...
119
120
            $solrResults = [];
121
            // Do not execute the Solr search if used together with ListView plugin.
122
            if (!$listViewSearch) {
123
                $solrResults = $this->documentRepository->findSolrByCollection('', $this->settings, $this->searchParams, $listedMetadata);
0 ignored issues
show
Bug introduced by
'' of type string is incompatible with the type Kitodo\Dlf\Domain\Model\Collection expected by parameter $collection 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

123
                $solrResults = $this->documentRepository->findSolrByCollection(/** @scrutinizer ignore-type */ '', $this->settings, $this->searchParams, $listedMetadata);
Loading history...
124
            }
125
126
            $documents = $solrResults['documents'] ? : [];
127
            $this->view->assign('documents', $documents);
128
            $rawResults = $solrResults['solrResults']['documents'] ? : [];
129
            $this->view->assign('numResults', count($rawResults));
130
            $this->view->assign('widgetPage', $widgetPage);
131
            $this->view->assign('lastSearch', $this->searchParams);
132
            $this->view->assign('listedMetadata', $listedMetadata);
133
            $this->view->assign('sortableMetadata', $sortableMetadata);
134
135
            // Add the facets menu
136
            $this->addFacetsMenu();
137
138
            // Get additional fields for extended search.
139
            $this->addExtendedSearch();
140
        }
141
142
        // Add the current document if present to fluid. This way, we can limit further searches to this document.
143
        if (isset($this->requestData['id'])) {
144
            $currentDocument = $this->documentRepository->findByUid($this->requestData['id']);
145
            $this->view->assign('currentDocument', $currentDocument);
146
        }
147
148
    }
149
150
    /**
151
     * Adds the facets menu to the search form
152
     *
153
     * @access protected
154
     *
155
     * @return string HTML output of facets menu
156
     */
157
    protected function addFacetsMenu()
158
    {
159
        // Check for typoscript configuration to prevent fatal error.
160
        if (empty($this->settings['facetsConf'])) {
161
            $this->logger->warning('Incomplete plugin configuration');
162
            return '';
163
        }
164
        // Quit without doing anything if no facets are selected.
165
        if (empty($this->settings['facets']) && empty($this->settings['facetCollections'])) {
166
            return '';
167
        }
168
169
        // Get facets from plugin configuration.
170
        $facets = [];
171
        foreach (GeneralUtility::trimExplode(',', $this->settings['facets'], true) as $facet) {
172
            $facets[$facet . '_faceting'] = Helper::translate($facet, 'tx_dlf_metadata', $this->settings['storagePid']);
173
        }
174
175
        $this->view->assign('facetsMenu', $this->makeFacetsMenuArray($facets));
176
    }
177
178
    /**
179
     * This builds a menu array for HMENU
180
     *
181
     * @access public
182
     *
183
     * @param string $content: The PlugIn content
184
     * @param array $conf: The PlugIn configuration
185
     *
186
     * @return array HMENU array
187
     */
188
    public function makeFacetsMenuArray($facets)
189
    {
190
        $menuArray = [];
191
        // Set default value for facet search.
192
        $search = [
193
            'query' => '*',
194
            'params' => [
195
                'component' => [
196
                    'facetset' => [
197
                        'facet' => []
198
                    ]
199
                ]
200
            ]
201
        ];
202
203
        // Set needed parameters for facet search.
204
        if (empty($search['params']['filterquery'])) {
205
            $search['params']['filterquery'] = [];
206
        }
207
208
        $fields = Solr::getFields();
209
210
        // Set search query.
211
        $searchParams = $this->searchParams;
212
        if (
213
            (!empty($searchParams['fulltext']))
214
            || preg_match('/' . $fields['fulltext'] . ':\((.*)\)/', trim($searchParams['query']), $matches)
215
        ) {
216
            // If the query already is a fulltext query e.g using the facets
217
            $searchParams['query'] = empty($matches[1]) ? $searchParams['query'] : $matches[1];
218
            // Search in fulltext field if applicable. Query must not be empty!
219
            if (!empty($this->searchParams['query'])) {
220
                $search['query'] = $fields['fulltext'] . ':(' . Solr::escapeQuery(trim($searchParams['query'])) . ')';
221
            }
222
        } else {
223
            // Retain given search field if valid.
224
            if (!empty($searchParams['query'])) {
225
                $search['query'] = Solr::escapeQueryKeepField(trim($earchParams['query']), $this->settings['storagePid']);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $earchParams does not exist. Did you maybe mean $searchParams?
Loading history...
226
            }
227
        }
228
229
        if (isset($this->searchParams['fq']) && is_array($this->searchParams['fq'])) {
230
            foreach($this->searchParams['fq'] as $fq) {
231
                $search['params']['filterquery'][]['query'] = $fq;
232
            }
233
        }
234
        // Get applicable facets.
235
        $solr = Solr::getInstance($this->settings['solrcore']);
236
        if (!$solr->ready) {
237
            $this->logger->error('Apache Solr not available');
238
            return [];
239
        }
240
241
        foreach (array_keys($facets) as $field) {
242
            $search['params']['component']['facetset']['facet'][] = [
243
                'type' => 'field',
244
                'key' => $field,
245
                'field' => $field,
246
                'limit' => $this->settings['limitFacets'],
247
                'sort' => isset($this->settings['sortingFacets']) ? $this->settings['sortingFacets'] : 'count'
248
            ];
249
        }
250
251
        // Set additional query parameters.
252
        $search['params']['start'] = 0;
253
        $search['params']['rows'] = 0;
254
        // Set query.
255
        $search['params']['query'] = $search['query'];
256
        // Perform search.
257
        $selectQuery = $solr->service->createSelect($search['params']);
258
        $results = $solr->service->select($selectQuery);
259
        $facet = $results->getFacetSet();
260
261
        $facetCollectionArray = [];
262
263
        // replace everything expect numbers and comma
264
        $facetCollections = preg_replace('/[^0-9,]/', '', $this->settings['facetCollections']);
265
266
        if (!empty($facetCollections)) {
267
            $collections = $this->collectionRepository->findCollectionsBySettings(['collections' => $facetCollections]);
268
269
            /** @var Collection $collection */
270
            foreach ($collections as $collection) {
271
                $facetCollectionArray[] = $collection->getIndexName();
272
            }
273
        }
274
275
        // Process results.
276
        if ($facet) {
277
            foreach ($facet as $field => $values) {
278
                $entryArray = [];
279
                $entryArray['title'] = htmlspecialchars($facets[$field]);
280
                $entryArray['count'] = 0;
281
                $entryArray['_OVERRIDE_HREF'] = '';
282
                $entryArray['doNotLinkIt'] = 1;
283
                $entryArray['ITEM_STATE'] = 'NO';
284
                // Count number of facet values.
285
                $i = 0;
286
                foreach ($values as $value => $count) {
287
                    if ($count > 0) {
288
                        // check if facet collection configuration exists
289
                        if (!empty($this->settings['facetCollections'])) {
290
                            if ($field == "collection_faceting" && !in_array($value, $facetCollectionArray)) {
291
                                continue;
292
                            }
293
                        }
294
                        $entryArray['count']++;
295
                        if ($entryArray['ITEM_STATE'] == 'NO') {
296
                            $entryArray['ITEM_STATE'] = 'IFSUB';
297
                        }
298
                        $entryArray['_SUB_MENU'][] = $this->getFacetsMenuEntry($field, $value, $count, $search, $entryArray['ITEM_STATE']);
299
                        if (++$i == $this->settings['limit']) {
300
                            break;
301
                        }
302
                    } else {
303
                        break;
304
                    }
305
                }
306
                $menuArray[] = $entryArray;
307
            }
308
        }
309
        return $menuArray;
310
    }
311
312
    /**
313
     * Creates an array for a HMENU entry of a facet value.
314
     *
315
     * @access protected
316
     *
317
     * @param string $field: The facet's index_name
318
     * @param string $value: The facet's value
319
     * @param int $count: Number of hits for this facet
320
     * @param array $search: The parameters of the current search query
321
     * @param string &$state: The state of the parent item
322
     *
323
     * @return array The array for the facet's menu entry
324
     */
325
    protected function getFacetsMenuEntry($field, $value, $count, $search, &$state)
326
    {
327
        $entryArray = [];
328
        // Translate value.
329
        if ($field == 'owner_faceting') {
330
            // Translate name of holding library.
331
            $entryArray['title'] = htmlspecialchars(Helper::translate($value, 'tx_dlf_libraries', $this->settings['storagePid']));
332
        } elseif ($field == 'type_faceting') {
333
            // Translate document type.
334
            $entryArray['title'] = htmlspecialchars(Helper::translate($value, 'tx_dlf_structures', $this->settings['storagePid']));
335
        } elseif ($field == 'collection_faceting') {
336
            // Translate name of collection.
337
            $entryArray['title'] = htmlspecialchars(Helper::translate($value, 'tx_dlf_collections', $this->settings['storagePid']));
338
        } elseif ($field == 'language_faceting') {
339
            // Translate ISO 639 language code.
340
            $entryArray['title'] = htmlspecialchars(Helper::getLanguageName($value));
341
        } else {
342
            $entryArray['title'] = htmlspecialchars($value);
343
        }
344
        $entryArray['count'] = $count;
345
        $entryArray['doNotLinkIt'] = 0;
346
        // Check if facet is already selected.
347
        $queryColumn = array_column($search['params']['filterquery'], 'query');
348
        $index = array_search($field . ':("' . Solr::escapeQuery($value) . '")', $queryColumn);
349
        if ($index !== false) {
350
            // Facet is selected, thus remove it from filter.
351
            unset($queryColumn[$index]);
352
            $queryColumn = array_values($queryColumn);
353
            $entryArray['ITEM_STATE'] = 'CUR';
354
            $state = 'ACTIFSUB';
355
            // Reset facets
356
            if ($this->settings['resetFacets']) {
357
                $entryArray['resetFacet'] = true;
358
                $entryArray['queryColumn'] = $queryColumn;
359
            }
360
        } else {
361
            // Facet is not selected, thus add it to filter.
362
            $queryColumn[] = $field . ':("' . Solr::escapeQuery($value) . '")';
363
            $entryArray['ITEM_STATE'] = 'NO';
364
        }
365
        $entryArray['queryColumn'] = $queryColumn;
366
367
        return $entryArray;
368
    }
369
370
    /**
371
     * Returns the extended search form and adds the JS files necessary for extended search.
372
     *
373
     * @access protected
374
     *
375
     * @return string The extended search form or an empty string
376
     */
377
    protected function addExtendedSearch()
378
    {
379
        // Quit without doing anything if no fields for extended search are selected.
380
        if (
381
            empty($this->settings['extendedSlotCount'])
382
            || empty($this->settings['extendedFields'])
383
        ) {
384
            return '';
385
        }
386
        // Get operator options.
387
        $operatorOptions = [];
388
        foreach (['AND', 'OR', 'NOT'] as $operator) {
389
            $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

389
            $operatorOptions[$operator] = htmlspecialchars(/** @scrutinizer ignore-type */ LocalizationUtility::translate('search.' . $operator, 'dlf'));
Loading history...
390
        }
391
        // Get field selector options.
392
        $fieldSelectorOptions = [];
393
        $searchFields = GeneralUtility::trimExplode(',', $this->settings['extendedFields'], true);
394
        foreach ($searchFields as $searchField) {
395
            $fieldSelectorOptions[$searchField] = Helper::translate($searchField, 'tx_dlf_metadata', $this->settings['storagePid']);
396
        }
397
        $slotCountArray = [];
398
        for ($i = 0; $i < $this->settings['extendedSlotCount']; $i++) {
399
            $slotCountArray[] = $i;
400
        }
401
402
        $this->view->assign('extendedSlotCount', $slotCountArray);
403
        $this->view->assign('extendedFields', $this->settings['extendedFields']);
404
        $this->view->assign('operators', $operatorOptions);
405
        $this->view->assign('searchFields', $fieldSelectorOptions);
406
    }
407
}
408