Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 39 and 311]

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