MDL-49399 tool_task: Link to log viewer
[moodle.git] / admin / classes / task_log_table.php
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/>.
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  */
25 namespace core_admin;
27 defined('MOODLE_INTERNAL') || die();
29 require_once($CFG->libdir . '/tablelib.php');
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  */
37 class task_log_table extends \table_sql {
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;
48         if (-1 === $resultfilter) {
49             $resultfilter = null;
50         }
52         parent::__construct('tasklogs');
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));
67         // The name column is a header.
68         $this->define_header_column('classname');
70         // This table is not collapsible.
71         $this->collapsible(false);
73         // The actions class should not wrap. Use the BS text utility class.
74         $this->column_class('actions', 'text-nowrap');
76         // Allow pagination.
77         $this->pageable(true);
79         // Allow sorting. Default to sort by timestarted DESC.
80         $this->sortable(true, 'timestart', SORT_DESC);
82         // Add filtering.
83         $where = [];
84         $params = [];
85         if (!empty($filter)) {
86             $orwhere = [];
87             $filter = str_replace('\\', '\\\\', $filter);
89             // Check the class name.
90             $orwhere[] = $DB->sql_like('classname', ':classfilter', false, false);
91             $params['classfilter'] = '%' . $DB->sql_like_escape($filter) . '%';
93             $orwhere[] = $DB->sql_like('output', ':outputfilter', false, false);
94             $params['outputfilter'] = '%' . $DB->sql_like_escape($filter) . '%';
96             $where[] = "(" . implode(' OR ', $orwhere) . ")";
97         }
99         if (null !== $resultfilter) {
100             $where[] = 'tl.result = :result';
101             $params['result'] = $resultfilter;
102         }
104         $where = implode(' AND ', $where);
106         $this->set_sql('', '', $where, $params);
107     }
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;
119         // Fetch the attempts.
120         $sort = $this->get_sql_sort();
121         if ($sort) {
122             $sort = "ORDER BY $sort";
123         }
125         $extrafields = get_extra_user_fields(\context_system::instance());
126         $userfields = \user_picture::fields('u', $extrafields, 'userid2', 'user');
128         $where = '';
129         if (!empty($this->sql->where)) {
130             $where = "WHERE {$this->sql->where}";
131         }
133         $sql = "SELECT
134                     tl.*,
135                     tl.dbreads + tl.dbwrites AS db,
136                     tl.timeend - tl.timestart AS duration,
137                     {$userfields}
138                 FROM {task_log} tl
139            LEFT JOIN {user} u ON u.id = tl.userid
140                 {$where}
141                 {$sort}";
143         $this->pagesize($pagesize, $DB->count_records_sql("SELECT COUNT('x') FROM {task_log} tl {$where}", $this->sql->params));
144         if (!$this->is_downloading()) {
145             $this->rawdata = $DB->get_records_sql($sql, $this->sql->params, $this->get_page_start(), $this->get_page_size());
146         } else {
147             $this->rawdata = $DB->get_records_sql($sql, $this->sql->params);
148         }
149     }
151     /**
152      * Format the name cell.
153      *
154      * @param   \stdClass $row
155      * @return  string
156      */
157     public function col_classname($row) : string {
158         $output = '';
159         if (class_exists($row->classname)) {
160             $task = new $row->classname;
161             if ($task instanceof \core\task\scheduled_task) {
162                 $output = $task->get_name();
163             }
164         }
166         $output .= \html_writer::tag('div', "\\{$row->classname}", [
167                 'class' => 'task-class',
168             ]);
169         return $output;
170     }
172     /**
173      * Format the type cell.
174      *
175      * @param   \stdClass $row
176      * @return  string
177      */
178     public function col_type($row) : string {
179         if (\core\task\database_logger::TYPE_SCHEDULED == $row->type) {
180             return get_string('task_type:scheduled', 'admin');
181         } else {
182             return get_string('task_type:adhoc', 'admin');
183         }
184     }
186     /**
187      * Format the timestart cell.
188      *
189      * @param   \stdClass $row
190      * @return  string
191      */
192     public function col_result($row) : string {
193         if ($row->result) {
194             return get_string('task_result:failed', 'admin');
195         } else {
196             return get_string('success');
197         }
198     }
200     /**
201      * Format the timestart cell.
202      *
203      * @param   \stdClass $row
204      * @return  string
205      */
206     public function col_timestart($row) : string {
207         return userdate($row->timestart, get_string('strftimedatetimeshort', 'langconfig'));
208     }
210     /**
211      * Format the duration cell.
212      *
213      * @param   \stdClass $row
214      * @return  string
215      */
216     public function col_duration($row) : string {
217         $duration = round($row->timeend - $row->timestart, 2);
219         if (empty($duration)) {
220             // The format_time function returns 'now' when the difference is exactly 0.
221             // Note: format_time performs concatenation in exactly this fashion so we should do this for consistency.
222             return '0 ' . get_string('secs', 'moodle');
223         }
225         return format_time($duration);
226     }
228     /**
229      * Format the DB details cell.
230      *
231      * @param   \stdClass $row
232      * @return  string
233      */
234     public function col_db($row) : string {
235         $output = '';
237         $output .= \html_writer::div(get_string('task_stats:dbreads', 'admin', $row->dbreads));
238         $output .= \html_writer::div(get_string('task_stats:dbwrites', 'admin', $row->dbwrites));
240         return $output;
241     }
243     /**
244      * Format the actions cell.
245      *
246      * @param   \stdClass $row
247      * @return  string
248      */
249     public function col_actions($row) : string {
250         global $OUTPUT;
252         $actions = [];
254         $url = new \moodle_url('/admin/tasklogs.php', ['logid' => $row->id]);
256         // Quick view.
257         $actions[] = $OUTPUT->action_icon(
258             $url,
259             new \pix_icon('e/search', get_string('view')),
260             new \popup_action('click', $url)
261         );
263         // Download.
264         $actions[] = $OUTPUT->action_icon(
265             new \moodle_url($url, ['download' => true]),
266             new \pix_icon('t/download', get_string('download'))
267         );
269         return implode('&nbsp;', $actions);
270     }
272     /**
273      * Format the user cell.
274      *
275      * @param   \stdClass $row
276      * @return  string
277      */
278     public function col_userid($row) : string {
279         if (empty($row->userid)) {
280             return '';
281         }
283         $user = (object) [];
284         username_load_fields_from_object($user, $row, 'user');
286         return fullname($user);
287     }