MDL-67274 Tasks: Log display fails with memory errors
[moodle.git] / admin / classes / task_log_table.php
CommitLineData
8c69e86c
AN
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 * Task log table.
19 *
20 * @package core_admin
21 * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24
25namespace core_admin;
26
27defined('MOODLE_INTERNAL') || die();
28
29require_once($CFG->libdir . '/tablelib.php');
30
31/**
32 * Table to display list of task logs.
33 *
34 * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36 */
37class task_log_table extends \table_sql {
38
39 /**
40 * Constructor for the task_log table.
41 *
42 * @param string $filter
43 * @param int $resultfilter
44 */
45 public function __construct(string $filter = '', int $resultfilter = null) {
46 global $DB;
47
48 if (-1 === $resultfilter) {
49 $resultfilter = null;
50 }
51
52 parent::__construct('tasklogs');
53
54 $columnheaders = [
55 'classname' => get_string('name'),
56 'type' => get_string('tasktype', 'admin'),
57 'userid' => get_string('user', 'admin'),
58 'timestart' => get_string('task_starttime', 'admin'),
59 'duration' => get_string('task_duration', 'admin'),
60 'db' => get_string('task_dbstats', 'admin'),
61 'result' => get_string('task_result', 'admin'),
62 'actions' => '',
63 ];
64 $this->define_columns(array_keys($columnheaders));
65 $this->define_headers(array_values($columnheaders));
66
67 // The name column is a header.
68 $this->define_header_column('classname');
69
70 // This table is not collapsible.
71 $this->collapsible(false);
72
73 // The actions class should not wrap. Use the BS text utility class.
74 $this->column_class('actions', 'text-nowrap');
75
76 // Allow pagination.
77 $this->pageable(true);
78
79 // Allow sorting. Default to sort by timestarted DESC.
80 $this->sortable(true, 'timestart', SORT_DESC);
81
82 // Add filtering.
83 $where = [];
84 $params = [];
85 if (!empty($filter)) {
251ee97c 86 $orwhere = [];
8c69e86c 87 $filter = str_replace('\\', '\\\\', $filter);
251ee97c
AN
88
89 // Check the class name.
90 $orwhere[] = $DB->sql_like('classname', ':classfilter', false, false);
91 $params['classfilter'] = '%' . $DB->sql_like_escape($filter) . '%';
92
93 $orwhere[] = $DB->sql_like('output', ':outputfilter', false, false);
94 $params['outputfilter'] = '%' . $DB->sql_like_escape($filter) . '%';
95
96 $where[] = "(" . implode(' OR ', $orwhere) . ")";
8c69e86c
AN
97 }
98
99 if (null !== $resultfilter) {
100 $where[] = 'tl.result = :result';
101 $params['result'] = $resultfilter;
102 }
103
104 $where = implode(' AND ', $where);
105
106 $this->set_sql('', '', $where, $params);
107 }
108
109 /**
110 * Query the db. Store results in the table object for use by build_table.
111 *
112 * @param int $pagesize size of page for paginated displayed table.
113 * @param bool $useinitialsbar do you want to use the initials bar. Bar
114 * will only be used if there is a fullname column defined for the table.
115 */
116 public function query_db($pagesize, $useinitialsbar = true) {
117 global $DB;
118
119 // Fetch the attempts.
120 $sort = $this->get_sql_sort();
121 if ($sort) {
122 $sort = "ORDER BY $sort";
123 }
124
125 $extrafields = get_extra_user_fields(\context_system::instance());
126 $userfields = \user_picture::fields('u', $extrafields, 'userid2', 'user');
127
128 $where = '';
129 if (!empty($this->sql->where)) {
130 $where = "WHERE {$this->sql->where}";
131 }
132
133 $sql = "SELECT
b4e2b99c 134 tl.id, tl.type, tl.component, tl.classname, tl.userid, tl.timestart, tl.timeend,
135 tl.dbreads, tl.dbwrites, tl.result,
8c69e86c
AN
136 tl.dbreads + tl.dbwrites AS db,
137 tl.timeend - tl.timestart AS duration,
138 {$userfields}
139 FROM {task_log} tl
140 LEFT JOIN {user} u ON u.id = tl.userid
141 {$where}
142 {$sort}";
143
144 $this->pagesize($pagesize, $DB->count_records_sql("SELECT COUNT('x') FROM {task_log} tl {$where}", $this->sql->params));
145 if (!$this->is_downloading()) {
146 $this->rawdata = $DB->get_records_sql($sql, $this->sql->params, $this->get_page_start(), $this->get_page_size());
147 } else {
148 $this->rawdata = $DB->get_records_sql($sql, $this->sql->params);
149 }
150 }
151
152 /**
153 * Format the name cell.
154 *
155 * @param \stdClass $row
156 * @return string
157 */
158 public function col_classname($row) : string {
159 $output = '';
160 if (class_exists($row->classname)) {
161 $task = new $row->classname;
162 if ($task instanceof \core\task\scheduled_task) {
163 $output = $task->get_name();
164 }
165 }
166
167 $output .= \html_writer::tag('div', "\\{$row->classname}", [
168 'class' => 'task-class',
169 ]);
170 return $output;
171 }
172
173 /**
174 * Format the type cell.
175 *
176 * @param \stdClass $row
177 * @return string
178 */
179 public function col_type($row) : string {
180 if (\core\task\database_logger::TYPE_SCHEDULED == $row->type) {
181 return get_string('task_type:scheduled', 'admin');
182 } else {
183 return get_string('task_type:adhoc', 'admin');
184 }
185 }
186
187 /**
188 * Format the timestart cell.
189 *
190 * @param \stdClass $row
191 * @return string
192 */
193 public function col_result($row) : string {
194 if ($row->result) {
195 return get_string('task_result:failed', 'admin');
196 } else {
197 return get_string('success');
198 }
199 }
200
201 /**
202 * Format the timestart cell.
203 *
204 * @param \stdClass $row
205 * @return string
206 */
207 public function col_timestart($row) : string {
208 return userdate($row->timestart, get_string('strftimedatetimeshort', 'langconfig'));
209 }
210
211 /**
212 * Format the duration cell.
213 *
214 * @param \stdClass $row
215 * @return string
216 */
217 public function col_duration($row) : string {
218 $duration = round($row->timeend - $row->timestart, 2);
219
220 if (empty($duration)) {
221 // The format_time function returns 'now' when the difference is exactly 0.
222 // Note: format_time performs concatenation in exactly this fashion so we should do this for consistency.
223 return '0 ' . get_string('secs', 'moodle');
224 }
225
226 return format_time($duration);
227 }
228
229 /**
230 * Format the DB details cell.
231 *
232 * @param \stdClass $row
233 * @return string
234 */
235 public function col_db($row) : string {
236 $output = '';
237
238 $output .= \html_writer::div(get_string('task_stats:dbreads', 'admin', $row->dbreads));
239 $output .= \html_writer::div(get_string('task_stats:dbwrites', 'admin', $row->dbwrites));
240
241 return $output;
242 }
243
244 /**
245 * Format the actions cell.
246 *
247 * @param \stdClass $row
248 * @return string
249 */
250 public function col_actions($row) : string {
251 global $OUTPUT;
252
253 $actions = [];
254
255 $url = new \moodle_url('/admin/tasklogs.php', ['logid' => $row->id]);
256
257 // Quick view.
258 $actions[] = $OUTPUT->action_icon(
259 $url,
260 new \pix_icon('e/search', get_string('view')),
261 new \popup_action('click', $url)
262 );
263
264 // Download.
265 $actions[] = $OUTPUT->action_icon(
266 new \moodle_url($url, ['download' => true]),
267 new \pix_icon('t/download', get_string('download'))
268 );
269
270 return implode('&nbsp;', $actions);
271 }
272
273 /**
274 * Format the user cell.
275 *
276 * @param \stdClass $row
277 * @return string
278 */
279 public function col_userid($row) : string {
280 if (empty($row->userid)) {
281 return '';
282 }
283
284 $user = (object) [];
285 username_load_fields_from_object($user, $row, 'user');
286
287 return fullname($user);
288 }
289}