Completed
Push — master ( eac780...233777 )
by Michal
58:26 queued 43:32
created

ReportsController::_getSearchConditions()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 13.3262

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 22
ccs 7
cts 17
cp 0.4118
rs 8.6738
cc 6
eloc 13
nc 6
nop 1
crap 13.3262
1
<?php
2
/* vim: set noexpandtab sw=2 ts=2 sts=2: */
3
namespace App\Controller;
4
5
use App\Controller\AppController;
6
use App\Utility\Sanitize;
7
use Cake\Core\Configure;
8
use Cake\Log\Log;
9
use Cake\ORM\Table;
10
use Cake\ORM\TableRegistry;
11
use Cake\Network\Exception\NotFoundException;
12
/**
13
 * Reports controller handling reports creation and rendering
14
 *
15
 * phpMyAdmin Error reporting server
16
 * Copyright (c) phpMyAdmin project (http://www.phpmyadmin.net)
17
 *
18
 * Licensed under The MIT License
19
 * For full copyright and license information, please see the LICENSE.txt
20
 * Redistributions of files must retain the above copyright notice.
21
 *
22
 * @copyright     Copyright (c) phpMyAdmin project (http://www.phpmyadmin.net)
23
 * @package       Server.Controller
24
 * @link          http://www.phpmyadmin.net
25
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
26
 */
27
28
/**
29
 * Reports controller handling reports modification and rendering
30
 *
31
 * @package       Server.Controller
32
 */
33
class ReportsController extends AppController {
34
35
	public $components = array('RequestHandler');
36
37
	public $helpers = array('Html', 'Form', 'Reports', 'Incidents');
38
	public $uses = array('Incidents', 'Reports', 'Notifications', 'Developers');
39
40 1
	public function index() {
41 1
		$this->Reports->recursive = -1;
42 1
		$this->set('distinct_statuses',
43 1
			$this->_findArrayList($this->Reports->find()->select(['status'])->distinct(['status']),
44 1
			'status')
45 1
		);
46 1
		$this->set('distinct_versions',
47 1
			$this->_findArrayList($this->Reports->find()->select(['pma_version'])->distinct(['pma_version']), 'pma_version')
48 1
		);
49 1
		$this->set('distinct_error_names',
50 1
			$this->_findArrayList($this->Reports->find('all', array(
51 1
				'fields' => array('error_name'),
52 1
				'conditions' => array('error_name !=' => ''),
53 1
			))->distinct(['error_name']), 'error_name')
54 1
		);
55 1
		$this->set('statuses', $this->Reports->status);
56 1
        $this->autoRender = true;
57 1
	}
58
59 1
	public function view($reportId) {
60 1
		if (!$reportId) {
61
			throw new NotFoundException(__('Invalid Report'));
62
		}
63 1
		$report = $this->Reports->findById($reportId)->toArray();
64 1
		if (!$report) {
65 1
			throw new NotFoundException(__('Invalid Report'));
66
		}
67
68 1
		$this->set('report', $report);
69 1
		$this->set('project_name', Configure::read('GithubRepoPath'));
70 1
		$this->Reports->id = $reportId;
71 1
		$this->set('incidents', $this->Reports->getIncidents()->toArray());
72 1
		$this->set('incidents_with_description',
73 1
            $this->Reports->getIncidentsWithDescription());
74 1
		$this->set('incidents_with_stacktrace',
75 1
				$this->Reports->getIncidentsWithDifferentStacktrace());
76 1
		$this->set('related_reports', $this->Reports->getRelatedReports());
77 1
		$this->set('status', $this->Reports->status);
78 1
		$this->_setSimilarFields($reportId);
79
80
		// if there is an unread notification for this report, then mark it as read
81 1
		$current_developer = TableRegistry::get('Developers')->
0 ignored issues
show
Documentation Bug introduced by
The method findById does not exist on object<Cake\ORM\Table>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
82 1
					findById($this->request->session()->read('Developer.id'))->all()->first();
83
		//$current_developer = Sanitize::clean($current_developer);
84 1
		if ($current_developer) {
85 1
			TableRegistry::get('Notifications')->deleteAll(
86 1
				array('developer_id' => $current_developer['Developer']['id'],
87
					'report_id' => $reportId
88 1
				),
89
				false
0 ignored issues
show
Unused Code introduced by
The call to Table::deleteAll() has too many arguments starting with false.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
90 1
			);
91 1
		}
92 1
	}
93
94 1
	public function data_tables() {
95
		$subquery_params = [
96
			'fields' => [
97 1
				'report_id' => 'report_id',
98
				'inci_count' => 'COUNT(id)'
99 1
				],
100
			'group' => 'report_id'
101 1
		];
102 1
		$subquery = TableRegistry::get('incidents')->find('all', $subquery_params);
103
104
		// override automatic aliasing, for proper usage in joins
105
		$aColumns = [
106 1
			'id' => 'id',
107 1
			'error_name' => 'error_name',
108 1
			'error_message' => 'error_message',
109 1
			'pma_version' => 'pma_version',
110 1
			'status' => 'status',
111 1
			'exception_type' => 'exception_type',
112
			'inci_count' => 'inci_count'
113 1
		];
114
115 1
		$searchConditions = $this->_getSearchConditions($aColumns);
116 1
		$orderConditions = $this->_getOrder($aColumns);
117
118
		$params = [
119 1
			'fields' => $aColumns,
120
			'conditions' => [
121 1
					$searchConditions,
122
					'related_to is NULL'
123 1
				],
124 1
			'order' => $orderConditions,
125 1
		];
126
127 1
		$pagedParams = $params;
128 1
		$pagedParams['limit'] = intval($this->request->query('iDisplayLength'));
129 1
		$pagedParams['offset'] = intval($this->request->query('iDisplayStart'));
130
131 1
		$rows = $this->_findAllDataTable(
132 1
			$this->Reports->find('all', $pagedParams)->innerJoin(
133 1
				['incidents' => $subquery], ['incidents.report_id = Reports.id']
134 1
			)
135 1
		);
136
		//$rows = Sanitize::clean($rows);
137 1
		$totalFiltered = $this->Reports->find('all', $params)->count();
138
139
		// change exception_type from boolean values to strings
140 1
		$dispRows = array();
141 1
		foreach($rows as $row) {
142 1
			$row[4] = $this->Reports->status[$row[4]];
143 1
			$row[5] = (intval($row[5]))?('php'):('js');
144
			$input_elem = "<input type='checkbox' name='reports[]' value='"
145 1
				. $row[0]
146 1
				. "'/>";
147 1
			array_unshift($row, $input_elem);
148 1
			array_push($dispRows, $row);
149 1
		}
150
		$response = array(
151 1
			'iTotalRecords' => $this->Reports->find('all')->count(),
152 1
			'iTotalDisplayRecords' => $totalFiltered,
153 1
			'sEcho' => intval($this->request->query('sEcho')),
154
			'aaData' => $dispRows
155 1
		);
156 1
		$this->autoRender = false;
157 1
		$this->response->body(json_encode($response));
158 1
        return $this->response;
159
	}
160
161
	public function mark_related_to($reportId) {
162
		$relatedTo = $this->request->query("related_to");
163
		if (!$reportId || !$relatedTo) {
164
			throw new NotFoundException(__('Invalid Report'));
165
		}
166
167
		$report = $this->Report->read(null, $reportId);
168
		if (!$report) {
169
			throw new NotFoundException(__('Invalid Report'));
170
		}
171
172
		$this->Report->addToRelatedGroup($relatedTo);
173
		$this->Session->setFlash("This report has been marked the same as #"
174
				. $relatedTo, "default", array("class" => "alert alert-success"));
175
		$this->redirect("/reports/view/$reportId");
176
	}
177
178
	public function unmark_related_to($reportId) {
179
		if (!$reportId) {
180
			throw new NotFoundException(__('Invalid Report'));
181
		}
182
183
		$report = $this->Report->read(null, $reportId);
184
		if (!$report) {
185
			throw new NotFoundException(__('Invalid Report'));
186
		}
187
188
		$this->Report->removeFromRelatedGroup();
189
		$this->Session->setFlash("This report has been marked as different."
190
				, "default", array("class" => "alert alert-success"));
191
		$this->redirect("/reports/view/$reportId");
192
	}
193
194
	public function change_state($reportId) {
195
		if (!$reportId) {
196
			throw new NotFoundException(__('Invalid Report'));
197
		}
198
199
		$report = $this->Reports->get($reportId);
200
		if (!$report) {
201
			throw new NotFoundException(__('Invalid Report'));
202
		}
203
204
		$state = $this->request->data['state'];
205
		$newState = $this->Reports->status[$state];
206
		if (!$newState) {
207
			throw new NotFoundException(__('Invalid State'));
208
		}
209
        $report->status = $state;
210
		$this->Reports->save($report);
211
		$this->Flash->default("The state has been successfully changed."
0 ignored issues
show
Documentation Bug introduced by
The method default does not exist on object<Cake\Controller\Component\FlashComponent>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
212
				, array("class" => "alert alert-success"));
213
		$this->redirect("/reports/view/$reportId");
214
	}
215
216
	/**
217
	 * To carry out mass actions on Reports
218
	 * Currently only to change their statuses.
219
	 * Can be Extended for other mass operations as well.
220
	 * Expects an array of Report Ids as a POST parameter.
221
	 *
222
	 */
223
	public function mass_action()
224
	{
225
		$flash_class = "alert alert-error";
226
		$state = $this->request->data['state'];
227
		$newState = $this->Reports->status[$state];
228
		if (!$newState) {
229
			Log::write(
230
				'error',
231
				'ERRORED: Invalid param "state" in ReportsController::mass_action()',
232
				'alert'
233
			);
234
			$msg = "ERROR: Invalid State!!";
235
		} else if (count($this->request->data['reports']) == 0) {
236
			$msg = "No Reports Selected!! Please Select Reports and try again.";
237
		} else {
238
			$msg = "Status has been changed to '"
239
				. $this->request->data['state']
240
				. "' for selected Reports!";
241
			$flash_class = "alert alert-success";
242
			foreach($this->request->data['reports'] as $report_id)
243
			{
244
				$report = $this->Reports->get($report_id);
245
				if (!$report) {
246
					Log::write(
247
						'error',
248
						'ERRORED: Invalid report_id in ReportsController::mass_action()',
249
						'alert'
250
					);
251
					$msg = "ERROR:Invalid Report ID:" . $report_id;
252
					$flash_class = "alert alert-error";
253
					break;
254
				}
255
                $report->status = $state;
256
				$this->Reports->save($report);
257
			}
258
		}
259
260
		$this->Flash->default($msg, array("class" => $flash_class));
0 ignored issues
show
Documentation Bug introduced by
The method default does not exist on object<Cake\Controller\Component\FlashComponent>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
261
		$this->redirect("/reports/");
262
	}
263
264
## HELPERS
265 1
	protected function _setSimilarFields($id) {
266 1
		$this->Reports->id = $id;
267
268 1
		$this->set('columns', TableRegistry::get('Incidents')->summarizableFields);
269 1
		$relatedEntries = array();
270
271 1
		foreach (TableRegistry::get('Incidents')->summarizableFields as $field) {
272
			list($entriesWithCount, $totalEntries) =
273 1
					$this->Reports->getRelatedByField($field, 25, true);
274 1
			$relatedEntries[$field] = $entriesWithCount;
275 1
			$this->set("${field}_distinct_count", $totalEntries);
276 1
		}
277
        //error_log(json_encode($relatedEntries));
278
		$this->set("related_entries", $relatedEntries);
279
	}
280
281
	/**
282
	 * Indexes are +1'ed because first column is of checkboxes
283
	 * and hence it should be ingnored.
284
	 * @param string[] $aColumns
285
	 */
286
	protected function _getSearchConditions($aColumns) {
287 1
		$searchConditions = array('OR' => array());
288 1
		$keys = array_keys($aColumns);
289
290 2
		if ( $this->request->query('sSearch') != "" ) {
291
			for ( $i = 0; $i < count($aColumns); $i++ ) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
292
				if ($this->request->query('bSearchable_' . ($i+1)) == "true") {
293
					$searchConditions['OR'][] = array($aColumns[$keys[$i]] . " LIKE" =>
294
							"%" . $this->request->query('sSearch') . "%");
295
				}
296
			}
297
		}
298
299
		/* Individual column filtering */
300 1
		for ( $i = 0; $i < count($aColumns); $i++ ) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
301 1
			if ($this->request->query('sSearch_' . ($i+1)) != '') {
302
				$searchConditions[] = array($aColumns[$keys[$i]] . " LIKE" =>
303
						$this->request->query('sSearch_' . ($i+1)));
304
			}
305 1
		}
306 1
		return $searchConditions;
307
	}
308
309
	/**
310
	 * @param string[] $aColumns
311
	 */
312
	protected function _getOrder($aColumns) {
313 1
		if ( $this->request->query('iSortCol_0') != null ) {
314
			$order = [];
315
			//Seems like we need to sort with only one column each time, so no need to loop
316
			$sort_column_index = intval($this->request->query('iSortCol_0'));
317
318
			$keys = array_keys($aColumns);
319
320
			if ($sort_column_index > 0 && $this->request->query('bSortable_' . $sort_column_index) == "true") {
321
				$order[$aColumns[$keys[$sort_column_index - 1]]] = $this->request->query('sSortDir_0');
322
			}
323
			return $order;
324
		} else {
325 1
			return null;
326
		}
327
	}
328
    protected function _findArrayList($results, $key) {
329 1
        $output = array();
330 1
		foreach ($results as $row) {
331 1
			$output[] = $row[$key];
332 1
		}
333 1
		return $output;
334
    }
335
    protected function _findAllDataTable($results) {
336 1
		$output = array();
337 1
		foreach ($results as $row) {
338 1
			$output_row = array();
339 1
            $row = $row->toArray();
340 1
			foreach ($row as $key => $value) {
341 1
				$output_row[] = $value;
342 1
			}
343 1
			$output[] = $output_row;
344 1
		}
345 1
		return $output;
346
	}
347
}
348