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

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