MDL-53638 mod_feedback: corrections during rebase:
[moodle.git] / mod / feedback / classes / responses_table.php
CommitLineData
9bb87baf
DP
1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Contains class mod_feedback_responses_table
19 *
20 * @package mod_feedback
21 * @copyright 2016 Marina Glancy
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24
25defined('MOODLE_INTERNAL') || die();
26
27global $CFG;
28require_once($CFG->libdir . '/tablelib.php');
29
30/**
31 * Class mod_feedback_responses_table
32 *
33 * @package mod_feedback
34 * @copyright 2016 Marina Glancy
35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36 */
37class mod_feedback_responses_table extends table_sql {
38
3fdba729
MG
39 /** @var mod_feedback_structure */
40 protected $feedbackstructure;
9bb87baf
DP
41
42 /** @var int */
43 protected $grandtotal = null;
44
45 /** @var bool */
46 protected $showall = false;
47
48 /** @var string */
49 protected $showallparamname = 'showall';
50
3fdba729
MG
51 /** @var string */
52 protected $downloadparamname = 'download';
53
9bb87baf
DP
54 /**
55 * Constructor
56 *
3fdba729 57 * @param mod_feedback_structure $feedbackstructure
9bb87baf 58 */
3fdba729
MG
59 public function __construct(mod_feedback_structure $feedbackstructure) {
60 $this->feedbackstructure = $feedbackstructure;
9bb87baf 61
3fdba729 62 parent::__construct('feedback-showentry-list-' . $feedbackstructure->get_cm()->instance);
9bb87baf
DP
63
64 $this->showall = optional_param($this->showallparamname, 0, PARAM_BOOL);
65 $this->define_baseurl(new moodle_url('/mod/feedback/show_entries.php',
3fdba729
MG
66 ['id' => $this->feedbackstructure->get_cm()->id]));
67 if ($courseid = $this->feedbackstructure->get_courseid()) {
68 $this->baseurl->param('courseid', $courseid);
69 }
9bb87baf
DP
70 if ($this->showall) {
71 $this->baseurl->param($this->showallparamname, $this->showall);
72 }
73
3fdba729
MG
74 $this->is_downloadable(true);
75 $this->is_downloading(optional_param($this->downloadparamname, 0, PARAM_ALPHA),
76 'feedback_test');
77
78 $this->useridfield = 'userid';
9bb87baf
DP
79 $this->init();
80 }
81
82 /**
83 * Initialises table
84 */
85 protected function init() {
86
3fdba729
MG
87 $tablecolumns = array('userpic', 'fullname');
88 $tableheaders = array(get_string('userpic'), get_string('fullnameuser'));
89
90 $extrafields = get_extra_user_fields($this->get_context());
91 $ufields = user_picture::fields('u', $extrafields, $this->useridfield);
92 $fields = 'c.id, c.timemodified as completed_timemodified, c.courseid, '.$ufields;
93 $from = '{feedback_completed} c '
94 . 'JOIN {user} u ON u.id = c.userid AND u.deleted = :notdeleted';
95 $where = 'c.anonymous_response = :anon
96 AND c.feedback = :instance';
97 if ($this->feedbackstructure->get_courseid()) {
98 $where .= ' AND c.courseid = :courseid';
99 }
9bb87baf 100
3fdba729
MG
101 if ($this->is_downloading()) {
102 // When downloading data:
103 // Remove 'userpic' from downloaded data.
104 array_shift($tablecolumns);
105 array_shift($tableheaders);
106
107 // Add all identity fields as separate columns.
108 foreach ($extrafields as $field) {
109 $fields .= ", u.{$field}";
110 $tablecolumns[] = $field;
111 $tableheaders[] = get_user_field_name($field);
112 }
9bb87baf
DP
113 }
114
3fdba729
MG
115 if ($this->feedbackstructure->get_feedback()->course == SITEID && !$this->feedbackstructure->get_courseid()) {
116 $tablecolumns[] = 'courseid';
117 $tableheaders[] = get_string('course');
118 }
119
120 $tablecolumns[] = 'completed_timemodified';
121 $tableheaders[] = get_string('date');
122
9bb87baf
DP
123 $this->define_columns($tablecolumns);
124 $this->define_headers($tableheaders);
125
126 $this->sortable(true, 'lastname', SORT_ASC);
3fdba729 127 $this->collapsible(true);
9bb87baf
DP
128 $this->set_attribute('id', 'showentrytable');
129
130 $params = array();
131 $params['anon'] = FEEDBACK_ANONYMOUS_NO;
3fdba729 132 $params['instance'] = $this->feedbackstructure->get_feedback()->id;
9bb87baf 133 $params['notdeleted'] = 0;
3fdba729 134 $params['courseid'] = $this->feedbackstructure->get_courseid();
9bb87baf 135
3fdba729 136 $group = groups_get_activity_group($this->feedbackstructure->get_cm(), true);
9bb87baf 137 if ($group) {
3fdba729 138 $where .= ' AND c.userid IN (SELECT g.userid FROM {groups_members} g WHERE g.groupid = :group)';
9bb87baf
DP
139 $params['group'] = $group;
140 }
141
142 $this->set_sql($fields, $from, $where, $params);
3fdba729
MG
143 $this->set_count_sql("SELECT COUNT(c.id) FROM $from WHERE $where", $params);
144 }
145
146 /**
147 * Current context
148 * @return context_module
149 */
150 protected function get_context() {
151 return context_module::instance($this->feedbackstructure->get_cm()->id);
152 }
153
154 /**
155 * Allows to set the display column value for all columns without "col_xxxxx" method.
156 * @param string $column column name
157 * @param stdClass $row current record result of SQL query
158 */
159 public function other_cols($column, $row) {
160 if (preg_match('/^val(\d+)$/', $column, $matches)) {
161 $items = $this->feedbackstructure->get_items();
162 $itemobj = feedback_get_item_class($items[$matches[1]]->typ);
163 return trim($itemobj->get_printval($items[$matches[1]], (object) ['value' => $row->$column] ));
164 }
165 return $row->$column;
9bb87baf
DP
166 }
167
168 /**
169 * Prepares column userpic for display
170 * @param stdClass $row
171 * @return string
172 */
173 public function col_userpic($row) {
174 global $OUTPUT;
3fdba729
MG
175 $user = user_picture::unalias($row, [], $this->useridfield);
176 return $OUTPUT->user_picture($user, array('courseid' => $this->feedbackstructure->get_cm()->course));
9bb87baf
DP
177 }
178
179 /**
180 * Prepares column deleteentry for display
181 * @param stdClass $row
182 * @return string
183 */
184 public function col_deleteentry($row) {
3fdba729 185 global $OUTPUT;
094c57dd
MG
186 $deleteentryurl = new moodle_url($this->baseurl, ['delete' => $row->id, 'sesskey' => sesskey()]);
187 $deleteaction = new confirm_action(get_string('confirmdeleteentry', 'feedback'));
188 return $OUTPUT->action_icon($deleteentryurl,
189 new pix_icon('t/delete', get_string('delete_entry', 'feedback')), $deleteaction);
9bb87baf
DP
190 }
191
192 /**
193 * Returns a link for viewing a single response
194 * @param stdClass $row
195 * @return \moodle_url
196 */
197 protected function get_link_single_entry($row) {
3fdba729 198 return new moodle_url($this->baseurl, ['userid' => $row->{$this->useridfield}, 'showcompleted' => $row->id]);
9bb87baf
DP
199 }
200
201 /**
202 * Prepares column completed_timemodified for display
203 * @param stdClass $student
204 * @return string
205 */
206 public function col_completed_timemodified($student) {
3fdba729
MG
207 if ($this->is_downloading()) {
208 return userdate($student->completed_timemodified);
209 } else {
210 return html_writer::link($this->get_link_single_entry($student),
211 userdate($student->completed_timemodified));
212 }
213 }
214
215 /**
216 * Prepares column courseid for display
217 * @param array $row
218 * @return string
219 */
220 public function col_courseid($row) {
221 $courses = $this->feedbackstructure->get_completed_courses();
222 $name = '';
223 if (isset($courses[$row->courseid])) {
224 $name = $courses[$row->courseid];
225 if (!$this->is_downloading()) {
226 $name = html_writer::link(course_get_url($row->courseid), $name);
227 }
228 }
229 return $name;
230 }
231
232 /**
233 * Adds common values to the table that do not change the number or order of entries and
234 * are only needed when outputting or downloading data.
235 */
236 protected function add_all_values_to_output() {
237 $tablecolumns = array_keys($this->columns);
238 $tableheaders = $this->headers;
239
240 // Add all feedback response values.
241 $items = $this->feedbackstructure->get_items(true);
242 foreach ($items as $nr => $item) {
243 $this->sql->fields .= ", v{$nr}.value AS val{$nr}";
244 $this->sql->from .= " LEFT OUTER JOIN {feedback_value} v{$nr} " .
245 "ON v{$nr}.completed = c.id AND v{$nr}.item = :itemid{$nr}";
246 $this->sql->params["itemid{$nr}"] = $item->id;
247 $tablecolumns[] = "val{$nr}";
248 $itemobj = feedback_get_item_class($item->typ);
249 $tableheaders[] = $itemobj->get_display_name($item);
250 }
251
252 // Add 'Delete entry' column.
253 if (!$this->is_downloading() && has_capability('mod/feedback:deletesubmissions', $this->get_context())) {
254 $tablecolumns[] = 'deleteentry';
255 $tableheaders[] = '';
256 }
257
258 $this->define_columns($tablecolumns);
259 $this->define_headers($tableheaders);
9bb87baf
DP
260 }
261
262 /**
263 * Query the db. Store results in the table object for use by build_table.
264 *
265 * @param int $pagesize size of page for paginated displayed table.
266 * @param bool $useinitialsbar do you want to use the initials bar. Bar
267 * will only be used if there is a fullname column defined for the table.
268 */
269 public function query_db($pagesize, $useinitialsbar=true) {
270 global $DB;
271 $this->totalrows = $grandtotal = $this->get_total_responses_count();
3fdba729
MG
272 if (!$this->is_downloading()) {
273 $this->initialbars($useinitialsbar);
9bb87baf 274
3fdba729
MG
275 list($wsql, $wparams) = $this->get_sql_where();
276 if ($wsql) {
277 $this->countsql .= ' AND '.$wsql;
278 $this->countparams = array_merge($this->countparams, $wparams);
9bb87baf 279
3fdba729
MG
280 $this->sql->where .= ' AND '.$wsql;
281 $this->sql->params = array_merge($this->sql->params, $wparams);
9bb87baf 282
3fdba729
MG
283 $this->totalrows = $DB->count_records_sql($this->countsql, $this->countparams);
284 }
9bb87baf 285
3fdba729
MG
286 if ($this->totalrows > $pagesize) {
287 $this->pagesize($pagesize, $this->totalrows);
288 }
9bb87baf
DP
289 }
290
291 if ($sort = $this->get_sql_sort()) {
292 $sort = "ORDER BY $sort";
293 }
294 $sql = "SELECT
295 {$this->sql->fields}
296 FROM {$this->sql->from}
297 WHERE {$this->sql->where}
298 {$sort}";
299
3fdba729
MG
300 if (!$this->is_downloading()) {
301 $this->rawdata = $DB->get_recordset_sql($sql, $this->sql->params, $this->get_page_start(), $this->get_page_size());
302 } else {
303 $this->rawdata = $DB->get_recordset_sql($sql, $this->sql->params);
304 }
9bb87baf
DP
305 }
306
307 /**
308 * Returns total number of reponses (without any filters applied)
309 * @return int
310 */
311 public function get_total_responses_count() {
312 global $DB;
313 if ($this->grandtotal === null) {
314 $this->grandtotal = $DB->count_records_sql($this->countsql, $this->countparams);
315 }
316 return $this->grandtotal;
317 }
318
97d71cbc
MG
319 /**
320 * Defines columns
321 * @param array $columns an array of identifying names for columns. If
322 * columns are sorted then column names must correspond to a field in sql.
323 */
3fdba729 324 public function define_columns($columns) {
97d71cbc
MG
325 parent::define_columns($columns);
326 foreach ($this->columns as $column => $column) {
327 // Automatically assign classes to columns.
328 $this->column_class[$column] = ' ' . $column;
329 }
330 }
331
3fdba729
MG
332 /**
333 * Convenience method to call a number of methods for you to display the
334 * table.
335 * @param int $pagesize
336 * @param bool $useinitialsbar
337 * @param string $downloadhelpbutton
338 */
339 public function out($pagesize, $useinitialsbar, $downloadhelpbutton='') {
340 $this->add_all_values_to_output();
341 parent::out($pagesize, $useinitialsbar, $downloadhelpbutton);
342 }
343
9bb87baf
DP
344 /**
345 * Displays the table
346 */
347 public function display() {
348 global $OUTPUT;
3fdba729 349 groups_print_activity_menu($this->feedbackstructure->get_cm(), $this->baseurl->out());
9bb87baf
DP
350 $grandtotal = $this->get_total_responses_count();
351 if (!$grandtotal) {
352 echo $OUTPUT->box(get_string('nothingtodisplay'), 'generalbox nothingtodisplay');
353 return;
354 }
355 $this->out($this->showall ? $grandtotal : FEEDBACK_DEFAULT_PAGE_COUNT,
356 $grandtotal > FEEDBACK_DEFAULT_PAGE_COUNT);
357
358 // Toggle 'Show all' link.
359 if ($this->totalrows > FEEDBACK_DEFAULT_PAGE_COUNT) {
360 if (!$this->use_pages) {
361 echo html_writer::div(html_writer::link(new moodle_url($this->baseurl, [$this->showallparamname => 0]),
362 get_string('showperpage', '', FEEDBACK_DEFAULT_PAGE_COUNT)), 'showall');
363 } else {
364 echo html_writer::div(html_writer::link(new moodle_url($this->baseurl, [$this->showallparamname => 1]),
365 get_string('showall', '', $this->totalrows)), 'showall');
366 }
367 }
368 }
369
370 /**
371 * Returns links to previous/next responses in the list
372 * @param stdClass $record
373 * @return array array of three elements [$prevresponseurl, $returnurl, $nextresponseurl]
374 */
375 public function get_reponse_navigation_links($record) {
376 $this->setup();
377 $grandtotal = $this->get_total_responses_count();
378 $this->query_db($grandtotal);
379 $lastrow = $thisrow = $nextrow = null;
380 $counter = 0;
381 $page = 0;
382 while ($this->rawdata->valid()) {
383 $row = $this->rawdata->current();
384 if ($row->id == $record->id) {
385 $page = $this->showall ? 0 : floor($counter / FEEDBACK_DEFAULT_PAGE_COUNT);
386 $thisrow = $row;
387 $this->rawdata->next();
388 $nextrow = $this->rawdata->valid() ? $this->rawdata->current() : null;
389 break;
390 }
391 $lastrow = $row;
392 $this->rawdata->next();
393 $counter++;
394 }
395 $this->rawdata->close();
396 if (!$thisrow) {
397 $lastrow = null;
398 }
399 return [
400 $lastrow ? $this->get_link_single_entry($lastrow) : null,
401 new moodle_url($this->baseurl, [$this->request[TABLE_VAR_PAGE] => $page]),
402 $nextrow ? $this->get_link_single_entry($nextrow) : null,
403 ];
404 }
3fdba729
MG
405
406 /**
407 * Download the data.
408 */
409 public function download() {
410 \core\session\manager::write_close();
411 $this->out($this->get_total_responses_count(), false);
412 exit;
413 }
414
415 /**
416 * Returns html code for displaying "Download" button if applicable.
417 */
418 public function download_buttons() {
0a0632ce 419 global $OUTPUT;
3fdba729 420
0a0632ce
MG
421 if ($this->is_downloadable() && !$this->is_downloading()) {
422 return $OUTPUT->download_dataformat_selector(get_string('downloadas', 'table'),
423 $this->baseurl->out_omit_querystring(), $this->downloadparamname, $this->baseurl->params());
3fdba729
MG
424 } else {
425 return '';
426 }
427 }
9bb87baf 428}