Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 400 and 401] [Versions 401 and 402] [Versions 401 and 403]

   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  declare(strict_types=1);
  18  
  19  namespace core_reportbuilder;
  20  
  21  use action_menu_filler;
  22  use coding_exception;
  23  use stdClass;
  24  use core_reportbuilder\local\models\report;
  25  use core_reportbuilder\local\report\action;
  26  use core_reportbuilder\local\report\base;
  27  use core_reportbuilder\local\report\column;
  28  
  29  /**
  30   * Base class for system reports
  31   *
  32   * @package     core_reportbuilder
  33   * @copyright   2020 Paul Holden <paulh@moodle.com>
  34   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   */
  36  abstract class system_report extends base {
  37  
  38      /** @var array $parameters */
  39      private $parameters;
  40  
  41      /** @var string[] $basefields List of base fields */
  42      private $basefields = [];
  43  
  44      /** @var bool $filterformdefault Whether to use the default filters form */
  45      private $filterformdefault = true;
  46  
  47      /** @var action|action_menu_filler[] $actions */
  48      private $actions = [];
  49  
  50      /** @var column $initialsortcolumn */
  51      private $initialsortcolumn;
  52  
  53      /** @var int $initialsortdirection */
  54      private $initialsortdirection;
  55  
  56      /**
  57       * System report constructor.
  58       *
  59       * @param report $report
  60       * @param array $parameters
  61       */
  62      final public function __construct(report $report, array $parameters) {
  63          $this->parameters = $parameters;
  64  
  65          parent::__construct($report);
  66      }
  67  
  68      /**
  69       * Validates access to view this report
  70       *
  71       * This is necessary to implement independently of the page that would typically embed the report because
  72       * subsequent pages are requested via AJAX requests, and access should be validated each time
  73       *
  74       * @return bool
  75       */
  76      abstract protected function can_view(): bool;
  77  
  78      /**
  79       * Validate access to the report
  80       *
  81       * @throws report_access_exception
  82       */
  83      final public function require_can_view(): void {
  84          if (!$this->can_view()) {
  85              throw new report_access_exception();
  86          }
  87      }
  88  
  89      /**
  90       * Report validation
  91       *
  92       * @throws report_access_exception If user cannot access the report
  93       * @throws coding_exception If no default column are specified
  94       */
  95      protected function validate(): void {
  96          parent::validate();
  97  
  98          $this->require_can_view();
  99  
 100          // Ensure the report has some default columns specified.
 101          if (empty($this->get_columns())) {
 102              throw new coding_exception('No columns added');
 103          }
 104      }
 105  
 106      /**
 107       * Add list of fields that have to be always included in SQL query for actions and row classes
 108       *
 109       * Base fields are only available in system reports because they are not compatible with aggregation
 110       *
 111       * @param string $sql SQL clause for the list of fields that only uses main table or base joins
 112       */
 113      final protected function add_base_fields(string $sql): void {
 114          $this->basefields[] = $sql;
 115      }
 116  
 117      /**
 118       * Return report base fields
 119       *
 120       * @return array
 121       */
 122      final public function get_base_fields(): array {
 123          return $this->basefields;
 124      }
 125  
 126      /**
 127       * Override whether to use the default system report filters form, for instance this can be disabled if the UI requires
 128       * it's own custom filter management form for a specific report
 129       *
 130       * @param bool $filterformdefault
 131       */
 132      final public function set_filter_form_default(bool $filterformdefault = true): void {
 133          $this->filterformdefault = $filterformdefault;
 134      }
 135  
 136      /**
 137       * Whether to use the default filters form
 138       *
 139       * @return bool
 140       */
 141      final public function get_filter_form_default(): bool {
 142          return $this->filterformdefault;
 143      }
 144  
 145      /**
 146       * Adds an action to the report
 147       *
 148       * @param action $action
 149       */
 150      final public function add_action(action $action): void {
 151          $this->actions[] = $action;
 152      }
 153  
 154      /**
 155       * Adds action divider to the report
 156       *
 157       */
 158      final public function add_action_divider(): void {
 159          $divider = new action_menu_filler();
 160          // We need to set as not primary action because we just need add an action divider, not a new action item.
 161          $divider->primary = false;
 162          $this->actions[] = $divider;
 163      }
 164  
 165      /**
 166       * Whether report has any actions
 167       *
 168       * @return bool
 169       */
 170      final public function has_actions(): bool {
 171          return !empty($this->actions);
 172      }
 173  
 174      /**
 175       * Return report actions
 176       *
 177       * @return action|action_menu_filler[]
 178       */
 179      final public function get_actions(): array {
 180          return $this->actions;
 181      }
 182  
 183      /**
 184       * Set all report parameters
 185       *
 186       * @param array $parameters
 187       */
 188      final public function set_parameters(array $parameters): void {
 189          $this->parameters = $parameters;
 190      }
 191  
 192      /**
 193       * Return all report parameters
 194       *
 195       * @return array
 196       */
 197      final public function get_parameters(): array {
 198          return $this->parameters;
 199      }
 200  
 201      /**
 202       * Return specific report parameter
 203       *
 204       * @param string $param
 205       * @param mixed $default
 206       * @param string $type
 207       * @return mixed
 208       */
 209      final public function get_parameter(string $param, $default, string $type) {
 210          if (!array_key_exists($param, $this->parameters)) {
 211              return $default;
 212          }
 213  
 214          return clean_param($this->parameters[$param], $type);
 215      }
 216  
 217      /**
 218       * Output the report
 219       *
 220       * @uses \core_reportbuilder\output\renderer::render_system_report()
 221       *
 222       * @return string
 223       */
 224      final public function output(): string {
 225          global $PAGE;
 226  
 227          /** @var \core_reportbuilder\output\renderer $renderer */
 228          $renderer = $PAGE->get_renderer('core_reportbuilder');
 229          $report = new \core_reportbuilder\output\system_report($this->get_report_persistent(), $this, $this->parameters);
 230  
 231          return $renderer->render($report);
 232      }
 233  
 234      /**
 235       * CSS classes to add to the row. Can be overridden by system reports do define class to be added to output according to
 236       * content of each row
 237       *
 238       * @param stdClass $row
 239       * @return string
 240       */
 241      public function get_row_class(stdClass $row): string {
 242          return '';
 243      }
 244  
 245      /**
 246       * Called before rendering each row. Can be overridden to pre-fetch/create objects and store them in the class, which can
 247       * later be used in column and action callbacks
 248       *
 249       * @param stdClass $row
 250       */
 251      public function row_callback(stdClass $row): void {
 252          return;
 253      }
 254  
 255      /**
 256       * Validates access to download this report.
 257       *
 258       * @return bool
 259       */
 260      final public function can_be_downloaded(): bool {
 261          return $this->can_view() && $this->is_downloadable();
 262      }
 263  
 264      /**
 265       * Return list of column names that will be excluded when table is downloaded. Extending classes should override this method
 266       * as appropriate
 267       *
 268       * @return string[] Array of column unique identifiers
 269       */
 270      public function get_exclude_columns_for_download(): array {
 271          return [];
 272      }
 273  
 274      /**
 275       * Set initial sort column and sort direction for the report
 276       *
 277       * @param string $uniqueidentifier
 278       * @param int $sortdirection One of SORT_ASC or SORT_DESC
 279       * @throws coding_exception
 280       */
 281      public function set_initial_sort_column(string $uniqueidentifier, int $sortdirection): void {
 282          if (!$sortcolumn = $this->get_column($uniqueidentifier)) {
 283              throw new coding_exception('Unknown column identifier', $uniqueidentifier);
 284          }
 285  
 286          $this->initialsortcolumn = $sortcolumn;
 287          $this->initialsortdirection = $sortdirection;
 288      }
 289  
 290      /**
 291       * Get initial sort column
 292       *
 293       * @return column|null
 294       */
 295      public function get_initial_sort_column(): ?column {
 296          return $this->initialsortcolumn;
 297      }
 298  
 299      /**
 300       * Get initial sort column direction
 301       *
 302       * @return int
 303       */
 304      public function get_initial_sort_direction(): int {
 305          return $this->initialsortdirection;
 306      }
 307  }