Completed
Push — master ( 94c5c3...385f07 )
by Michal
15:44
created

ReportsController::mass_action()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 40
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 40
ccs 0
cts 32
cp 0
rs 8.439
cc 5
eloc 31
nc 4
nop 0
crap 30
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
164
			|| !$relatedTo
165
			|| $reportId == $relatedTo
166
		) {
167
			throw new NotFoundException(__('Invalid Report'));
168
		}
169
170
		$report = $this->Reports->get($reportId);
171
		if (!$report) {
172
			throw new NotFoundException(__('Invalid Report'));
173
		}
174
175
		$this->Reports->addToRelatedGroup($report, $relatedTo);
176
		$this->Flash->success("This report has been marked the same as #"
177
				. $relatedTo, "default", array("class" => "alert alert-success"));
0 ignored issues
show
Unused Code introduced by
The call to FlashComponent::success() has too many arguments starting with array('class' => 'alert alert-success').

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...
178
		$this->redirect("/reports/view/$reportId");
179
	}
180
181
	public function unmark_related_to($reportId) {
182
		if (!$reportId) {
183
			throw new NotFoundException(__('Invalid Report'));
184
		}
185
186
		$report = $this->Reports->get($reportId);
187
		if (!$report) {
188
			throw new NotFoundException(__('Invalid Report'));
189
		}
190
191
		$this->Reports->removeFromRelatedGroup($report);
192
		$this->Flash->success("This report has been marked as different."
193
				, "default", array("class" => "alert alert-success"));
0 ignored issues
show
Unused Code introduced by
The call to FlashComponent::success() has too many arguments starting with array('class' => 'alert alert-success').

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...
194
		$this->redirect("/reports/view/$reportId");
195
	}
196
197
	public function change_state($reportId) {
198
		if (!$reportId) {
199
			throw new NotFoundException(__('Invalid Report'));
200
		}
201
202
		$report = $this->Reports->get($reportId);
203
		if (!$report) {
204
			throw new NotFoundException(__('Invalid Report'));
205
		}
206
207
		$state = $this->request->data['state'];
208
		$newState = $this->Reports->status[$state];
209
		if (!$newState) {
210
			throw new NotFoundException(__('Invalid State'));
211
		}
212
        $report->status = $state;
213
		$this->Reports->save($report);
214
		$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...
215
				, array("class" => "alert alert-success"));
216
		$this->redirect("/reports/view/$reportId");
217
	}
218
219
	/**
220
	 * To carry out mass actions on Reports
221
	 * Currently only to change their statuses.
222
	 * Can be Extended for other mass operations as well.
223
	 * Expects an array of Report Ids as a POST parameter.
224
	 *
225
	 */
226
	public function mass_action()
227
	{
228
		$flash_class = "alert alert-error";
229
		$state = $this->request->data['state'];
230
		$newState = $this->Reports->status[$state];
231
		if (!$newState) {
232
			Log::write(
233
				'error',
234
				'ERRORED: Invalid param "state" in ReportsController::mass_action()',
235
				'alert'
236
			);
237
			$msg = "ERROR: Invalid State!!";
238
		} else if (count($this->request->data['reports']) == 0) {
239
			$msg = "No Reports Selected!! Please Select Reports and try again.";
240
		} else {
241
			$msg = "Status has been changed to '"
242
				. $this->request->data['state']
243
				. "' for selected Reports!";
244
			$flash_class = "alert alert-success";
245
			foreach($this->request->data['reports'] as $report_id)
246
			{
247
				$report = $this->Reports->get($report_id);
248
				if (!$report) {
249
					Log::write(
250
						'error',
251
						'ERRORED: Invalid report_id in ReportsController::mass_action()',
252
						'alert'
253
					);
254
					$msg = "ERROR:Invalid Report ID:" . $report_id;
255
					$flash_class = "alert alert-error";
256
					break;
257
				}
258
                $report->status = $state;
259
				$this->Reports->save($report);
260
			}
261
		}
262
263
		$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...
264
		$this->redirect("/reports/");
265
	}
266
267
## HELPERS
268 1
	protected function _setSimilarFields($id) {
269 1
		$this->Reports->id = $id;
270
271 1
		$this->set('columns', TableRegistry::get('Incidents')->summarizableFields);
272 1
		$relatedEntries = array();
273
274 1
		foreach (TableRegistry::get('Incidents')->summarizableFields as $field) {
275
			list($entriesWithCount, $totalEntries) =
276 1
					$this->Reports->getRelatedByField($field, 25, true);
277 1
			$relatedEntries[$field] = $entriesWithCount;
278 1
			$this->set("${field}_distinct_count", $totalEntries);
279 1
		}
280
        //error_log(json_encode($relatedEntries));
281
		$this->set("related_entries", $relatedEntries);
282
	}
283
284
	/**
285
	 * Indexes are +1'ed because first column is of checkboxes
286
	 * and hence it should be ingnored.
287
	 * @param string[] $aColumns
288
	 */
289
	protected function _getSearchConditions($aColumns) {
290 2
		$searchConditions = array('OR' => array());
291 1
		$keys = array_keys($aColumns);
292
293 1
		if ( $this->request->query('sSearch') != "" ) {
294 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...
295 1
				if ($this->request->query('bSearchable_' . ($i+1)) == "true") {
296 1
					$searchConditions['OR'][] = array($aColumns[$keys[$i]] . " LIKE" =>
297 1
							"%" . $this->request->query('sSearch') . "%");
298 1
				}
299 1
			}
300 1
		}
301
302
		/* Individual column filtering */
303 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...
304 1
			if ($this->request->query('sSearch_' . ($i+1)) != '') {
305 1
				$searchConditions[] = array($aColumns[$keys[$i]] . " LIKE" =>
306 1
						$this->request->query('sSearch_' . ($i+1)));
307 1
			}
308 1
		}
309 1
		return $searchConditions;
310
	}
311
312
	/**
313
	 * @param string[] $aColumns
314
	 */
315
	protected function _getOrder($aColumns) {
316 1
		if ( $this->request->query('iSortCol_0') != null ) {
317 1
			$order = [];
318
			//Seems like we need to sort with only one column each time, so no need to loop
319 1
			$sort_column_index = intval($this->request->query('iSortCol_0'));
320
321 1
			$keys = array_keys($aColumns);
322
323 1
			if ($sort_column_index > 0 && $this->request->query('bSortable_' . $sort_column_index) == "true") {
324
				$order[$aColumns[$keys[$sort_column_index - 1]]] = $this->request->query('sSortDir_0');
325
			}
326 1
			return $order;
327
		} else {
328 1
			return null;
329
		}
330
	}
331
    protected function _findArrayList($results, $key) {
332 1
        $output = array();
333 1
		foreach ($results as $row) {
334 1
			$output[] = $row[$key];
335 1
		}
336 1
		return $output;
337
    }
338
    protected function _findAllDataTable($results) {
339 1
		$output = array();
340 1
		foreach ($results as $row) {
341 1
			$output_row = array();
342 1
            $row = $row->toArray();
343 1
			foreach ($row as $key => $value) {
344 1
				$output_row[] = $value;
345 1
			}
346 1
			$output[] = $output_row;
347 1
		}
348 1
		return $output;
349
	}
350
}
351