Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 400 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\local\systemreports;
  20  
  21  use html_writer;
  22  use lang_string;
  23  use moodle_url;
  24  use pix_icon;
  25  use stdClass;
  26  use core_reportbuilder\datasource;
  27  use core_reportbuilder\manager;
  28  use core_reportbuilder\system_report;
  29  use core_reportbuilder\local\entities\user;
  30  use core_reportbuilder\local\filters\date;
  31  use core_reportbuilder\local\filters\text;
  32  use core_reportbuilder\local\filters\select;
  33  use core_reportbuilder\local\helpers\audience;
  34  use core_reportbuilder\local\helpers\format;
  35  use core_reportbuilder\local\report\action;
  36  use core_reportbuilder\local\report\column;
  37  use core_reportbuilder\local\report\filter;
  38  use core_reportbuilder\output\report_name_editable;
  39  use core_reportbuilder\local\models\report;
  40  use core_reportbuilder\permission;
  41  
  42  /**
  43   * Reports list
  44   *
  45   * @package     core_reportbuilder
  46   * @copyright   2021 David Matamoros <davidmc@moodle.com>
  47   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  48   */
  49  class reports_list extends system_report {
  50  
  51      /**
  52       * The name of our internal report entity
  53       *
  54       * @return string
  55       */
  56      private function get_report_entity_name(): string {
  57          return 'report';
  58      }
  59  
  60      /**
  61       * Initialise the report
  62       */
  63      protected function initialise(): void {
  64          $this->set_main_table('reportbuilder_report', 'rb');
  65          $this->add_base_condition_simple('rb.type', self::TYPE_CUSTOM_REPORT);
  66  
  67          // Select fields required for actions, permission checks, and row class callbacks.
  68          $this->add_base_fields('rb.id, rb.name, rb.source, rb.type, rb.usercreated, rb.contextid');
  69  
  70          // Limit the returned list to those reports the current user can access.
  71          [$where, $params] = audience::user_reports_list_access_sql('rb');
  72          $this->add_base_condition_sql($where, $params);
  73  
  74          // Join user entity for "User modified" column.
  75          $entityuser = new user();
  76          $entityuseralias = $entityuser->get_table_alias('user');
  77  
  78          $this->add_entity($entityuser
  79              ->add_join("JOIN {user} {$entityuseralias} ON {$entityuseralias}.id = rb.usermodified")
  80          );
  81  
  82          // Define our internal entity for report elements.
  83          $this->annotate_entity($this->get_report_entity_name(),
  84              new lang_string('customreports', 'core_reportbuilder'));
  85  
  86          $this->add_columns();
  87          $this->add_filters();
  88          $this->add_actions();
  89  
  90          $this->set_downloadable(false);
  91      }
  92  
  93      /**
  94       * Ensure we can view the report
  95       *
  96       * @return bool
  97       */
  98      protected function can_view(): bool {
  99          return permission::can_view_reports_list();
 100      }
 101  
 102      /**
 103       * Dim the table row for invalid datasource
 104       *
 105       * @param stdClass $row
 106       * @return string
 107       */
 108      public function get_row_class(stdClass $row): string {
 109          return $this->report_source_valid($row->source) ? '' : 'text-muted';
 110      }
 111  
 112      /**
 113       * Add columns to report
 114       */
 115      protected function add_columns(): void {
 116          $tablealias = $this->get_main_table_alias();
 117  
 118          // Report name column.
 119          $this->add_column((new column(
 120              'name',
 121              new lang_string('name'),
 122              $this->get_report_entity_name()
 123          ))
 124              ->set_type(column::TYPE_TEXT)
 125              // We need enough fields to re-create the persistent and pass to the editable component.
 126              ->add_fields(implode(', ', [
 127                  "{$tablealias}.id",
 128                  "{$tablealias}.name",
 129                  "{$tablealias}.contextid",
 130                  "{$tablealias}.type",
 131                  "{$tablealias}.usercreated",
 132              ]))
 133              ->set_is_sortable(true, ["{$tablealias}.name"])
 134              ->add_callback(static function(string $value, stdClass $report): string {
 135                  global $PAGE;
 136  
 137                  $editable = new report_name_editable(0, new report(0, $report));
 138                  return $editable->render($PAGE->get_renderer('core'));
 139              })
 140          );
 141  
 142          // Report source column.
 143          $this->add_column((new column(
 144              'source',
 145              new lang_string('reportsource', 'core_reportbuilder'),
 146              $this->get_report_entity_name()
 147          ))
 148              ->set_type(column::TYPE_TEXT)
 149              ->add_fields("{$tablealias}.source")
 150              ->set_is_sortable(true)
 151              ->add_callback(function(string $value, stdClass $row) {
 152                  if (!$this->report_source_valid($value)) {
 153                      // Add danger badge if report source is not valid (either it's missing, or has errors).
 154                      return html_writer::span(get_string('errorsourceinvalid', 'core_reportbuilder'), 'badge badge-danger');
 155                  }
 156  
 157                  return call_user_func([$value, 'get_name']);
 158              })
 159          );
 160  
 161          // Time created column.
 162          $this->add_column((new column(
 163              'timecreated',
 164              new lang_string('timecreated', 'core_reportbuilder'),
 165              $this->get_report_entity_name()
 166          ))
 167              ->set_type(column::TYPE_TIMESTAMP)
 168              ->add_fields("{$tablealias}.timecreated")
 169              ->set_is_sortable(true)
 170              ->add_callback([format::class, 'userdate'])
 171          );
 172  
 173          // Time modified column.
 174          $this->add_column((new column(
 175              'timemodified',
 176              new lang_string('timemodified', 'core_reportbuilder'),
 177              $this->get_report_entity_name()
 178          ))
 179              ->set_type(column::TYPE_TIMESTAMP)
 180              ->add_fields("{$tablealias}.timemodified")
 181              ->set_is_sortable(true)
 182              ->add_callback([format::class, 'userdate'])
 183          );
 184  
 185          // The user who modified the report.
 186          $this->add_column_from_entity('user:fullname')
 187              ->set_title(new lang_string('usermodified', 'reportbuilder'));
 188  
 189          // Initial sorting.
 190          $this->set_initial_sort_column('report:timecreated', SORT_DESC);
 191      }
 192  
 193      /**
 194       * Add filters to report
 195       */
 196      protected function add_filters(): void {
 197          $tablealias = $this->get_main_table_alias();
 198  
 199          // Name filter.
 200          $this->add_filter((new filter(
 201              text::class,
 202              'name',
 203              new lang_string('name'),
 204              $this->get_report_entity_name(),
 205              "{$tablealias}.name"
 206          )));
 207  
 208          // Source filter.
 209          $this->add_filter((new filter(
 210              select::class,
 211              'source',
 212              new lang_string('reportsource', 'core_reportbuilder'),
 213              $this->get_report_entity_name(),
 214              "{$tablealias}.source"
 215          ))
 216              ->set_options_callback(static function(): array {
 217                  return manager::get_report_datasources();
 218              })
 219          );
 220  
 221          // Time created filter.
 222          $this->add_filter((new filter(
 223              date::class,
 224              'timecreated',
 225              new lang_string('timecreated', 'core_reportbuilder'),
 226              $this->get_report_entity_name(),
 227              "{$tablealias}.timecreated"
 228          ))
 229              ->set_limited_operators([
 230                  date::DATE_ANY,
 231                  date::DATE_RANGE,
 232              ])
 233          );
 234      }
 235  
 236      /**
 237       * Add actions to report
 238       */
 239      protected function add_actions(): void {
 240          // Edit content action.
 241          $this->add_action((new action(
 242              new moodle_url('/reportbuilder/edit.php', ['id' => ':id']),
 243              new pix_icon('t/right', ''),
 244              [],
 245              false,
 246              new lang_string('editreportcontent', 'core_reportbuilder')
 247          ))
 248              ->add_callback(function(stdClass $row): bool {
 249                  return $this->report_source_valid($row->source) && permission::can_edit_report(new report(0, $row));
 250              })
 251          );
 252  
 253          // Edit details action.
 254          $this->add_action((new action(
 255              new moodle_url('#'),
 256              new pix_icon('t/edit', ''),
 257              ['data-action' => 'report-edit', 'data-report-id' => ':id'],
 258              false,
 259              new lang_string('editreportdetails', 'core_reportbuilder')
 260          ))
 261              ->add_callback(function(stdClass $row): bool {
 262                  return $this->report_source_valid($row->source) && permission::can_edit_report(new report(0, $row));
 263              })
 264          );
 265  
 266          // Preview action.
 267          $this->add_action((new action(
 268              new moodle_url('/reportbuilder/view.php', ['id' => ':id']),
 269              new pix_icon('i/search', ''),
 270              [],
 271              false,
 272              new lang_string('viewreport', 'core_reportbuilder')
 273          ))
 274              ->add_callback(function(stdClass $row): bool {
 275                  // We check this only to give the action to editors, because normal users can just click on the report name.
 276                  return $this->report_source_valid($row->source) && permission::can_edit_report(new report(0, $row));
 277              })
 278          );
 279  
 280          // Delete action.
 281          $this->add_action((new action(
 282              new moodle_url('#'),
 283              new pix_icon('t/delete', ''),
 284              ['data-action' => 'report-delete', 'data-report-id' => ':id', 'data-report-name' => ':name'],
 285              false,
 286              new lang_string('deletereport', 'core_reportbuilder')
 287          ))
 288              ->add_callback(function(stdClass $row): bool {
 289  
 290                  // Ensure data name attribute is properly formatted.
 291                  $report = new report(0, $row);
 292                  $row->name = $report->get_formatted_name();
 293  
 294                  // We don't check whether report is valid to ensure editor can always delete them.
 295                  return permission::can_edit_report($report);
 296              })
 297          );
 298      }
 299  
 300      /**
 301       * Helper to determine whether given report source is valid (it both exists, and is available)
 302       *
 303       * @param string $source
 304       * @return bool
 305       */
 306      private function report_source_valid(string $source): bool {
 307          return manager::report_source_exists($source, datasource::class) && manager::report_source_available($source);
 308      }
 309  }