ReportsController   A
last analyzed

Complexity

Total Complexity 36

Size/Duplication

Total Lines 394
Duplicated Lines 0 %

Test Coverage

Coverage 92.07%

Importance

Changes 8
Bugs 0 Features 0
Metric Value
eloc 226
dl 0
loc 394
ccs 209
cts 227
cp 0.9207
rs 9.52
c 8
b 0
f 0
wmc 36

11 Methods

Rating   Name   Duplication   Size   Complexity  
A unmark_related_to() 0 22 3
B data_tables() 0 95 3
A setSimilarFields() 0 15 2
A initialize() 0 15 1
A index() 0 31 1
A mark_related_to() 0 28 5
A view() 0 41 5
A change_state() 0 31 5
B mass_action() 0 46 6
A findAllDataTable() 0 14 3
A findArrayList() 0 8 2
1
<?php
2
3
/**
4
 * Reports controller handling reports creation and rendering.
5
 *
6
 * phpMyAdmin Error reporting server
7
 * Copyright (c) phpMyAdmin project (https://www.phpmyadmin.net/)
8
 *
9
 * Licensed under The MIT License
10
 * For full copyright and license information, please see the LICENSE.txt
11
 * Redistributions of files must retain the above copyright notice.
12
 *
13
 * @copyright Copyright (c) phpMyAdmin project (https://www.phpmyadmin.net/)
14
 * @license   https://opensource.org/licenses/mit-license.php MIT License
15
 *
16
 * @see      https://www.phpmyadmin.net/
17
 */
18
19
namespace App\Controller;
20
21
use App\Model\Table\ReportsTable;
22
use Cake\Core\Configure;
23
use Cake\Http\Exception\NotFoundException;
24
use Cake\Http\Response;
25
use Cake\Log\Log;
26
use Cake\ORM\TableRegistry;
27
28
use function __;
29
use function array_key_exists;
30
use function array_push;
31
use function array_unshift;
32
use function count;
33
use function json_encode;
34
35
/**
36
 * Reports controller handling reports modification and rendering.
37
 *
38
 * @property ReportsTable $Reports
39
 */
40
class ReportsController extends AppController
41
{
42
    /**
43
     * Initialization hook method.
44
     *
45
     * Use this method to add common initialization code like loading components.
46
     *
47
     * @return void Nothing
48
     */
49 49
    public function initialize(): void
50
    {
51 49
        parent::initialize();
52 49
        $this->loadComponent('RequestHandler');
53 49
        $this->loadComponent('OrderSearch');
54 49
        $this->viewBuilder()->setHelpers([
55 49
            'Html',
56
            'Form',
57
            'Reports',
58
            'Incidents',
59
        ]);
60 49
        $this->loadModel('Incidents');
61 49
        $this->loadModel('Reports');
62 49
        $this->loadModel('Notifications');
63 49
        $this->loadModel('Developers');
64 49
    }
65
66 7
    public function index(): void
67
    {
68 7
        $this->Reports->recursive = -1;
69 7
        $this->set(
70 7
            'distinct_statuses',
71 7
            $this->findArrayList(
72 7
                $this->Reports->find()->select(['status'])->distinct(['status']),
73 7
                'status'
74
            )
75
        );
76 7
        $this->set(
77 7
            'distinct_locations',
78 7
            $this->findArrayList(
79 7
                $this->Reports->find()->select(['location'])
80 7
                    ->distinct(['location']),
81 7
                'location'
82
            )
83
        );
84 7
        $this->set(
85 7
            'distinct_versions',
86 7
            $this->findArrayList($this->Reports->find()->select(['pma_version'])->distinct(['pma_version']), 'pma_version')
87
        );
88 7
        $this->set(
89 7
            'distinct_error_names',
90 7
            $this->findArrayList($this->Reports->find('all', [
91 7
                'fields' => ['error_name'],
92
                'conditions' => ['error_name !=' => ''],
93 7
            ])->distinct(['error_name']), 'error_name')
94
        );
95 7
        $this->set('statuses', $this->Reports->status);
96 7
        $this->autoRender = true;
97 7
    }
98
99 7
    public function view(?string $reportId): void
100
    {
101 7
        if (empty($reportId)) {
102
            throw new NotFoundException(__('Invalid report Id.'));
103
        }
104
105 7
        $reportId = (int) $reportId;
106
107 7
        $report = $this->Reports->findById($reportId)->toArray();
108 7
        if (! $report) {
109 7
            throw new NotFoundException(__('The report does not exist.'));
110
        }
111
112 7
        $this->set('report', $report);
113 7
        $this->set('project_name', Configure::read('GithubRepoPath'));
114 7
        $this->Reports->id = $reportId;
115 7
        $this->set('incidents', $this->Reports->getIncidents()->toArray());
116 7
        $this->set(
117 7
            'incidents_with_description',
118 7
            $this->Reports->getIncidentsWithDescription()
119
        );
120 7
        $this->set(
121 7
            'incidents_with_stacktrace',
122 7
            $this->Reports->getIncidentsWithDifferentStacktrace()
123
        );
124 7
        $this->set('related_reports', $this->Reports->getRelatedReports());
125 7
        $this->set('status', $this->Reports->status);
126 7
        $this->setSimilarFields($reportId);
127
128
        // if there is an unread notification for this report, then mark it as read
129 7
        $current_developer = TableRegistry::getTableLocator()->get('Developers')->
130 7
                    findById($this->request->getSession()->read('Developer.id'))->all()->first();
131
132 7
        if (! $current_developer || ! $current_developer['Developer']) {
133 7
            return;
134
        }
135
136
        TableRegistry::getTableLocator()->get('Notifications')->deleteAll(
137
            [
138
                'developer_id' => $current_developer['Developer']['id'],
139
                'report_id' => $reportId,
140
            ]
141
        );
142
    }
143
144 7
    public function data_tables(): ?Response
145
    {
146 2
        $subquery_params = [
147 1
            'fields' => [
148 4
                'report_id' => 'report_id',
149
                'inci_count' => 'COUNT(id)',
150
            ],
151
            'group' => 'report_id',
152
        ];
153 7
        $subquery = TableRegistry::getTableLocator()->get('incidents')->find('all', $subquery_params);
154
155
        // override automatic aliasing, for proper usage in joins
156 2
        $aColumns = [
157 5
            'id' => 'id',
158
            'error_name' => 'error_name',
159
            'error_message' => 'error_message',
160
            'location' => 'location',
161
            'pma_version' => 'pma_version',
162
            'status' => 'status',
163
            'exception_type' => 'exception_type',
164
            'inci_count' => 'inci_count',
165
        ];
166
167 7
        $searchConditions = $this->OrderSearch->getSearchConditions($aColumns, $this->request);
168 7
        $orderConditions = $this->OrderSearch->getOrder($aColumns, $this->request);
169
170 2
        $params = [
171 7
            'fields' => $aColumns,
172
            'conditions' => [
173 7
                $searchConditions,
174 7
                'related_to is NULL',
175
            ],
176 7
            'order' => $orderConditions,
177
        ];
178
179 7
        $pagedParams = $params;
180 7
        $pagedParams['limit'] = (int) $this->request->getQuery('iDisplayLength');
181 7
        $pagedParams['offset'] = (int) $this->request->getQuery('iDisplayStart');
182
183 7
        $rows = $this->findAllDataTable(
184 7
            $this->Reports->find('all', $pagedParams)->innerJoin(
185 7
                ['incidents' => $subquery],
186 7
                ['incidents.report_id = Reports.id']
187
            )
188
        );
189
        //$rows = Sanitize::clean($rows);
190 7
        $totalFiltered = $this->Reports->find('all', $params)->count();
191
192
        // change exception_type from boolean values to strings
193
        // add incident count for related reports
194 7
        $dispRows = [];
195 7
        foreach ($rows as $row) {
196 7
            $row[5] = $this->Reports->status[$row[5]];
197 7
            $row[6] = (int) $row[6] ? 'php' : 'js';
198 2
            $input_elem = '<input type="checkbox" name="reports[]" value="'
199 7
                . $row[0]
200 7
                . '"/>';
201
202 2
            $subquery_params_count = [
203 5
                'fields' => ['report_id' => 'report_id'],
204
            ];
205 7
            $subquery_count = TableRegistry::getTableLocator()->get('incidents')->find(
206 7
                'all',
207 1
                $subquery_params_count
208
            );
209
210 2
            $params_count = [
211 7
                'fields' => ['inci_count' => 'inci_count'],
212
                'conditions' => [
213 7
                    'related_to = ' . $row[0],
214
                ],
215
            ];
216
217 7
            $inci_count_related = $this->Reports->find('all', $params_count)->innerJoin(
218 7
                ['incidents' => $subquery_count],
219 7
                ['incidents.report_id = Reports.related_to']
220 7
            )->count();
221
222 7
            $row[7] += $inci_count_related;
223
224 7
            array_unshift($row, $input_elem);
225 7
            array_push($dispRows, $row);
226
        }
227
228 2
        $response = [
229 7
            'iTotalRecords' => $this->Reports->find('all')->count(),
230 7
            'iTotalDisplayRecords' => $totalFiltered,
231 7
            'sEcho' => (int) $this->request->getQuery('sEcho'),
232 7
            'aaData' => $dispRows,
233
        ];
234 7
        $this->disableAutoRender();
235
236 7
        return $this->response
237 7
            ->withType('application/json')
238 7
            ->withStringBody(json_encode($response));
239
    }
240
241 14
    public function mark_related_to(?string $reportId): void
242
    {
243
        // Only allow POST requests
244 14
        $this->request->allowMethod(['post']);
245
246 14
        $relatedTo = $this->request->getData('related_to');
247
        if (
248 14
            ! $reportId
249 14
            || ! $relatedTo
250 14
            || $reportId === $relatedTo
251
        ) {
252
            throw new NotFoundException(__('Invalid Report'));
253
        }
254
255 14
        $report = $this->Reports->get($reportId);
256 14
        if (! $report) {
257
            throw new NotFoundException(__('The report does not exist.'));
258
        }
259
260 14
        $this->Reports->addToRelatedGroup($report, $relatedTo);
261
262 14
        $flash_class = 'alert alert-success';
263 14
        $this->Flash->set(
264
            'This report has been marked the same as #'
265 14
                . $relatedTo,
266 14
            ['params' => ['class' => $flash_class]]
267
        );
268 14
        $this->redirect('/reports/view/' . $reportId);
269 14
    }
270
271 7
    public function unmark_related_to(?string $reportId): void
272
    {
273
        // Only allow POST requests
274 7
        $this->request->allowMethod(['post']);
275
276 7
        if (! $reportId) {
277
            throw new NotFoundException(__('Invalid Report'));
278
        }
279
280 7
        $report = $this->Reports->get($reportId);
281 7
        if (! $report) {
282
            throw new NotFoundException(__('The report does not exist.'));
283
        }
284
285 7
        $this->Reports->removeFromRelatedGroup($report);
286
287 7
        $flash_class = 'alert alert-success';
288 7
        $this->Flash->set(
289 7
            'This report has been marked as different.',
290 7
            ['params' => ['class' => $flash_class]]
291
        );
292 7
        $this->redirect('/reports/view/' . $reportId);
293 7
    }
294
295 7
    public function change_state(?string $reportId): void
296
    {
297 7
        if (! $reportId) {
298
            throw new NotFoundException(__('Invalid Report'));
299
        }
300
301 7
        $report = $this->Reports->get($reportId);
302 7
        if (! $report) {
303
            throw new NotFoundException(__('The report does not exist.'));
304
        }
305
306 7
        $state = $this->request->getData('state');
307 7
        $newState = null;
308
309 7
        if (array_key_exists($state, $this->Reports->status)) {
310 7
            $newState = $this->Reports->status[$state];
311
        }
312
313 7
        if (! $newState) {
314 7
            throw new NotFoundException(__('Invalid State'));
315
        }
316
317 7
        $report->status = $state;
318 7
        $this->Reports->save($report);
319
320 7
        $flash_class = 'alert alert-success';
321 7
        $this->Flash->set(
322 7
            'The state has been successfully changed.',
323 7
            ['params' => ['class' => $flash_class]]
324
        );
325 7
        $this->redirect('/reports/view/' . $reportId);
326 7
    }
327
328
    /**
329
     * To carry out mass actions on Reports
330
     * Currently only to change their statuses.
331
     * Can be Extended for other mass operations as well.
332
     * Expects an array of Report Ids as a POST parameter.
333
     *
334
     * @return void Nothing
335
     */
336 7
    public function mass_action(): void
337
    {
338 7
        $flash_class = 'alert alert-error';
339 7
        $state = $this->request->getData('state');
340 7
        $newState = null;
341 7
        if (array_key_exists($state, $this->Reports->status)) {
342 7
            $newState = $this->Reports->status[$state];
343
        }
344
345 7
        if (! $newState) {
346 7
            Log::write(
347 7
                'error',
348 7
                'ERRORED: Invalid param "state" in ReportsController::mass_action()',
349 7
                'alert'
350
            );
351 7
            $msg = 'ERROR: Invalid State!!';
352 7
        } elseif (count($this->request->getData('reports')) === 0) {
353 7
            $msg = 'No Reports Selected!! Please Select Reports and try again.';
354
        } else {
355 2
            $msg = "Status has been changed to '"
356 7
                . $this->request->getData('state')
357 7
                . "' for selected Reports!";
358 7
            $flash_class = 'alert alert-success';
359 7
            foreach ($this->request->getData('reports') as $report_id) {
360 7
                $report = $this->Reports->get($report_id);
361 7
                if (! $report) {
362
                    Log::write(
363
                        'error',
364
                        'ERRORED: Invalid report_id in ReportsController::mass_action()',
365
                        'alert'
366
                    );
367
                    $msg = 'ERROR:Invalid Report ID:' . $report_id;
368
                    $flash_class = 'alert alert-error';
369
                    break;
370
                }
371
372 7
                $report->status = $state;
373 7
                $this->Reports->save($report);
374
            }
375
        }
376
377 7
        $this->Flash->set(
378 7
            $msg,
379 7
            ['params' => ['class' => $flash_class]]
380
        );
381 7
        $this->redirect('/reports/');
382 7
    }
383
384 7
    protected function setSimilarFields(int $id): void
385
    {
386 7
        $this->Reports->id = $id;
387
388 7
        $this->set('columns', TableRegistry::getTableLocator()->get('Incidents')->summarizableFields);
389 7
        $relatedEntries = [];
390
391 7
        foreach (TableRegistry::getTableLocator()->get('Incidents')->summarizableFields as $field) {
392 2
            [$entriesWithCount, $totalEntries] =
393 7
                    $this->Reports->getRelatedByField($field, 25, true);
394 7
            $relatedEntries[$field] = $entriesWithCount->toArray();
395 7
            $this->set("${field}_distinct_count", $totalEntries);
396
        }
397
398 7
        $this->set('related_entries', $relatedEntries);
399 7
    }
400
401
    /**
402
     * @param array|mixed $results The row
403
     * @param string      $key     The search in the row
404
     * @return array Results
405
     */
406 7
    protected function findArrayList($results, string $key): array
407
    {
408 7
        $output = [];
409 7
        foreach ($results as $row) {
410 7
            $output[] = $row[$key];
411
        }
412
413 7
        return $output;
414
    }
415
416
    /**
417
     * @param mixed $results
418
     * @return array
419
     */
420 7
    protected function findAllDataTable($results): array
421
    {
422 7
        $output = [];
423 7
        foreach ($results as $row) {
424 7
            $output_row = [];
425 7
            $row = $row->toArray();
426 7
            foreach ($row as $key => $value) {
427 7
                $output_row[] = $value;
428
            }
429
430 7
            $output[] = $output_row;
431
        }
432
433 7
        return $output;
434
    }
435
}
436