Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
97.28% covered (success)
97.28%
143 / 147
72.73% covered (warning)
72.73%
8 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
Search
97.28% covered (success)
97.28%
143 / 147
72.73% covered (warning)
72.73%
8 / 11
50
0.00% covered (danger)
0.00%
0 / 1
 readResponse
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
1 / 1
2
 readSearchParameters
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
1 / 1
12
 readProcessSearchResults
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
5
 shouldRunProcessSearch
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
4
 hasStructuredSearchFilters
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 buildProcessSearchParameters
88.24% covered (warning)
88.24%
15 / 17
0.00% covered (danger)
0.00%
0 / 1
6.06
 readLogSearchResults
100.00% covered (success)
100.00%
23 / 23
100.00% covered (success)
100.00%
1 / 1
5
 filterProcessListForUserRights
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
4.05
 filterLogListForUserRights
88.89% covered (warning)
88.89%
8 / 9
0.00% covered (danger)
0.00%
0 / 1
7.07
 readStringParameter
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 readNumberParameter
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3/**
4 * @package Zmsadmin
5 * @copyright BerlinOnline Stadtportal GmbH & Co. KG
6 **/
7
8namespace BO\Zmsadmin;
9
10use BO\Slim\Render;
11use BO\Zmsentities\Collection\LogList;
12use BO\Zmsentities\Log as LogEntity;
13use BO\Zmsentities\Collection\ProcessList;
14
15class Search extends BaseController
16{
17    private const int DEFAULT_RESULTS_PER_PAGE = 100;
18
19    private const int MAX_RESULTS_PER_PAGE = 1000;
20
21    /**
22     * @SuppressWarnings(Param)
23     * @return \Psr\Http\Message\ResponseInterface
24     */
25    #[\Override]
26    public function readResponse(
27        \Psr\Http\Message\RequestInterface $request,
28        \Psr\Http\Message\ResponseInterface $response,
29        array $args
30    ): \Psr\Http\Message\ResponseInterface {
31        $workstation = \App::$http->readGetResult('/workstation/', ['resolveReferences' => 2])->getEntity();
32        $parameters = $this->readSearchParameters($request->getAttribute('validator'));
33        if ($workstation->getUseraccount()->hasRole('audit_viewer')) {
34            $parameters['hideNavigation'] = 1;
35        }
36        $scopeIds = $workstation->getUseraccount()->getDepartmentList()->getUniqueScopeList()->getIds();
37
38        [$processList, $processSearchTotal] = $this->readProcessSearchResults(
39            $workstation,
40            $parameters,
41            $scopeIds
42        );
43        $logList = $this->readLogSearchResults($workstation, $parameters, $scopeIds);
44
45        return Render::withHtml(
46            $response,
47            'page/search.twig',
48            array(
49                'title' => 'Suche',
50                'hideNavigation' => (bool) $parameters['hideNavigation'],
51                'service' => $parameters['service'],
52                'provider' => $parameters['provider'],
53                'userAction' => $parameters['userAction'],
54                'date' => $parameters['date'],
55                'page' => $parameters['page'],
56                'perPage' => $parameters['perPage'],
57                'workstation' => $workstation,
58                'processList' => $processList,
59                'logList' => $logList ?? [],
60                'searchProcessQuery' => $parameters['queryString'],
61                'processSearchTotal' => $processSearchTotal,
62                'menuActive' => 'search'
63            )
64        );
65    }
66
67    private function readSearchParameters($validator): array
68    {
69        $queryString = $validator->getParameter('query')
70            ->isString('', false)
71            ->getValue();
72        if ($queryString !== null && $queryString !== '') {
73            $queryString = html_entity_decode((string) $queryString, ENT_QUOTES | ENT_HTML5, 'UTF-8');
74        } else {
75            $queryString = $queryString ?? '';
76        }
77
78        $service = $this->readStringParameter($validator, 'service');
79        $provider = $this->readStringParameter($validator, 'provider');
80        $date = $validator->getParameter('date')->isString()->setDefault(null)->getValue();
81        $page = $this->readNumberParameter($validator, 'page', 1);
82        $userAction = $this->readNumberParameter($validator, 'user', 0);
83        $requestedResultsPerPage = $this->readNumberParameter($validator, 'perPage', self::DEFAULT_RESULTS_PER_PAGE);
84        $resultsPerPage = min($requestedResultsPerPage, self::MAX_RESULTS_PER_PAGE);
85        $hideNavigation = $this->readNumberParameter($validator, 'hideNavigation', 0);
86
87        return [
88            'queryString' => $queryString,
89            'page' => $page,
90            'service' => $service ? trim($service) : null,
91            'provider' => $provider ? trim($provider) : null,
92            'date' => $date !== null && trim($date) !== '' ? trim($date) : null,
93            'userAction' => $userAction,
94            'perPage' => $resultsPerPage,
95            'hideNavigation' => $hideNavigation,
96            'isSearchRequested' => (
97                trim($queryString) !== ''
98                || trim($service) !== ''
99                || trim($provider) !== ''
100                || ($date !== null && trim($date) !== '')
101                || $userAction !== 0
102            ),
103        ];
104    }
105
106    private function readProcessSearchResults($workstation, array $parameters, array $scopeIds): array
107    {
108        if (!$this->shouldRunProcessSearch($workstation, $parameters)) {
109            return [new ProcessList(), 0];
110        }
111
112        $searchParameters = $this->buildProcessSearchParameters($workstation, $parameters, $scopeIds);
113        $searchResult = \App::$http->readGetResult('/process/search/', $searchParameters);
114        $processList = $searchResult->getCollection();
115        $searchMeta = $searchResult->getMeta();
116        $processSearchTotal = isset($searchMeta->totalCount)
117            ? (int) $searchMeta->totalCount
118            : $processList->count();
119
120        if (!empty($processList) && !$workstation->getUseraccount()->isSuperUser()) {
121            $processList = $this->filterProcessListForUserRights($processList, $scopeIds);
122        }
123
124        return [$processList, $processSearchTotal];
125    }
126
127    private function shouldRunProcessSearch($workstation, array $parameters): bool
128    {
129        if (!$parameters['isSearchRequested']) {
130            return false;
131        }
132
133        if (!$workstation->getUseraccount()->hasPermissions(['customersearch'])) {
134            return false;
135        }
136
137        $queryString = trim((string) $parameters['queryString']);
138
139        return $queryString !== '' || $this->hasStructuredSearchFilters($parameters);
140    }
141
142    private function hasStructuredSearchFilters(array $parameters): bool
143    {
144        return $parameters['service'] !== null
145            || $parameters['provider'] !== null
146            || $parameters['date'] !== null;
147    }
148
149    private function buildProcessSearchParameters($workstation, array $parameters, array $scopeIds): array
150    {
151        $queryString = trim((string) $parameters['queryString']);
152        $searchParameters = [
153            'resolveReferences' => 1,
154            'page' => $parameters['page'],
155            'limit' => $parameters['perPage'],
156        ];
157        if ($queryString !== '') {
158            $searchParameters['query'] = $queryString;
159        }
160        if ($parameters['service'] !== null) {
161            $searchParameters['service'] = $parameters['service'];
162        }
163        if ($parameters['provider'] !== null) {
164            $searchParameters['provider'] = $parameters['provider'];
165        }
166        if ($parameters['date'] !== null) {
167            $searchParameters['date'] = $parameters['date'];
168        }
169        if (!$workstation->getUseraccount()->isSuperUser()) {
170            $searchParameters['scopeIds'] = implode(',', $scopeIds);
171        }
172
173        return $searchParameters;
174    }
175
176    private function readLogSearchResults($workstation, array $parameters, array $scopeIds): ?LogList
177    {
178        if (!$workstation->getUseraccount()->hasPermissions(['logs'])) {
179            return null;
180        }
181
182        if (!$parameters['isSearchRequested'] && !$workstation->getUseraccount()->isSuperUser()) {
183            return null;
184        }
185
186        $logParameters = [
187            'searchQuery' => urlencode((string) $parameters['queryString']),
188            'page' => $parameters['page'],
189            'perPage' => $parameters['perPage'],
190            'service' => $parameters['service'],
191            'provider' => $parameters['provider'],
192            'userAction' => $parameters['userAction'],
193            'date' => $parameters['date'],
194        ];
195        if (!$workstation->getUseraccount()->isSuperUser()) {
196            $logParameters['scopeIds'] = implode(',', $scopeIds);
197        }
198
199        $logList = \App::$http
200            ->readGetResult("/log/process/", $logParameters)
201            ->getCollection();
202
203        return $this->filterLogListForUserRights(
204            $logList,
205            $scopeIds,
206            $workstation->getUseraccount()->isSuperUser()
207        );
208    }
209
210    private function filterProcessListForUserRights(?ProcessList $processList, array $scopeIds): ProcessList
211    {
212        if (empty($processList)) {
213            return new ProcessList();
214        }
215
216        $list = new ProcessList();
217
218        foreach ($processList as $process) {
219            if (in_array($process->scope->id, $scopeIds)) {
220                $list->addEntity(clone $process);
221            }
222        }
223
224        return $list;
225    }
226
227    private function filterLogListForUserRights(
228        ?LogList $logList,
229        array $scopeIds,
230        bool $bypassScopeFilter = false
231    ): LogList {
232        if (!isset($logList) || !$logList) {
233            $logList = new LogList();
234        }
235
236        $list = new LogList();
237
238        foreach ($logList as $log) {
239            $log->display = LogEntity::formatDisplayFields($log->getArrayCopy());
240
241            if (
242                $bypassScopeFilter
243                || (isset($log->scope_id) && in_array($log->scope_id, $scopeIds))
244            ) {
245                $list->addEntity(clone $log);
246            }
247        }
248
249        return $list;
250    }
251
252    private function readStringParameter($validator, string $name, string $default = ''): string
253    {
254        return $validator->getParameter($name)
255            ->isString()
256            ->setDefault($default)
257            ->getValue() ?? $default;
258    }
259
260    private function readNumberParameter($validator, string $name, int $default): int
261    {
262        return (int) $validator->getParameter($name)
263            ->isNumber()
264            ->setDefault($default)
265            ->getValue();
266    }
267}