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 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_files\reportbuilder\local\entities;
  20  
  21  use context;
  22  use context_helper;
  23  use lang_string;
  24  use license_manager;
  25  use html_writer;
  26  use stdClass;
  27  use core_reportbuilder\local\entities\base;
  28  use core_reportbuilder\local\helpers\format;
  29  use core_reportbuilder\local\filters\{boolean_select, date, number, select, text};
  30  use core_reportbuilder\local\report\{column, filter};
  31  
  32  /**
  33   * File entity
  34   *
  35   * @package     core_files
  36   * @copyright   2022 Paul Holden <paulh@moodle.com>
  37   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  38   */
  39  class file extends base {
  40  
  41      /**
  42       * Database tables that this entity uses and their default aliases
  43       *
  44       * @return array
  45       */
  46      protected function get_default_table_aliases(): array {
  47          return [
  48              'files' => 'f',
  49              'context' => 'fctx',
  50          ];
  51      }
  52  
  53      /**
  54       * The default title for this entity
  55       *
  56       * @return lang_string
  57       */
  58      protected function get_default_entity_title(): lang_string {
  59          return new lang_string('file');
  60      }
  61  
  62      /**
  63       * Initialise the entity
  64       *
  65       * @return base
  66       */
  67      public function initialise(): base {
  68          $columns = $this->get_all_columns();
  69          foreach ($columns as $column) {
  70              $this->add_column($column);
  71          }
  72  
  73          // All the filters defined by the entity can also be used as conditions.
  74          $filters = $this->get_all_filters();
  75          foreach ($filters as $filter) {
  76              $this
  77                  ->add_filter($filter)
  78                  ->add_condition($filter);
  79          }
  80  
  81          return $this;
  82      }
  83  
  84      /**
  85       * Returns list of all available columns
  86       *
  87       * @return column[]
  88       */
  89      protected function get_all_columns(): array {
  90          $filesalias = $this->get_table_alias('files');
  91          $contextalias = $this->get_table_alias('context');
  92  
  93          // Name.
  94          $columns[] = (new column(
  95              'name',
  96              new lang_string('filename', 'core_repository'),
  97              $this->get_entity_name()
  98          ))
  99              ->add_joins($this->get_joins())
 100              ->set_type(column::TYPE_TEXT)
 101              ->add_field("{$filesalias}.filename")
 102              ->set_is_sortable(true);
 103  
 104          // Size.
 105          $columns[] = (new column(
 106              'size',
 107              new lang_string('size'),
 108              $this->get_entity_name()
 109          ))
 110              ->add_joins($this->get_joins())
 111              ->set_type(column::TYPE_INTEGER)
 112              ->add_field("{$filesalias}.filesize")
 113              ->add_field("CASE WHEN {$filesalias}.filename = '.' THEN 1 ELSE 0 END", 'directory')
 114              ->set_is_sortable(true)
 115              ->add_callback(static function($filesize, stdClass $fileinfo): string {
 116                  // Absent file size and/or directory should not return output.
 117                  if ($fileinfo->filesize === null || $fileinfo->directory) {
 118                      return '';
 119                  }
 120                  return display_size($fileinfo->filesize);
 121              });
 122  
 123          // Path.
 124          $columns[] = (new column(
 125              'path',
 126              new lang_string('path'),
 127              $this->get_entity_name()
 128          ))
 129              ->add_joins($this->get_joins())
 130              ->set_type(column::TYPE_TEXT)
 131              ->add_field("{$filesalias}.filepath")
 132              ->set_is_sortable(true);
 133  
 134          // Type.
 135          $columns[] = (new column(
 136              'type',
 137              new lang_string('type', 'core_repository'),
 138              $this->get_entity_name()
 139          ))
 140              ->add_joins($this->get_joins())
 141              ->set_type(column::TYPE_TEXT)
 142              ->add_field("{$filesalias}.mimetype")
 143              ->add_field("CASE WHEN {$filesalias}.filename = '.' THEN 1 ELSE 0 END", 'directory')
 144              ->set_is_sortable(true)
 145              ->add_callback(static function($mimetype, stdClass $fileinfo): string {
 146                  global $CFG;
 147                  require_once("{$CFG->libdir}/filelib.php");
 148  
 149                  // Absent mime type and/or directory has pre-determined output.
 150                  if ($fileinfo->mimetype === null && !$fileinfo->directory) {
 151                      return '';
 152                  } else if ($fileinfo->directory) {
 153                      return get_string('directory');
 154                  }
 155  
 156                  return get_mimetype_description($fileinfo->mimetype);
 157              });
 158  
 159          // Author.
 160          $columns[] = (new column(
 161              'author',
 162              new lang_string('author', 'core_repository'),
 163              $this->get_entity_name()
 164          ))
 165              ->add_joins($this->get_joins())
 166              ->set_type(column::TYPE_TEXT)
 167              ->add_field("{$filesalias}.author")
 168              ->set_is_sortable(true);
 169  
 170          // License.
 171          $columns[] = (new column(
 172              'license',
 173              new lang_string('license', 'core_repository'),
 174              $this->get_entity_name()
 175          ))
 176              ->add_joins($this->get_joins())
 177              ->set_type(column::TYPE_TEXT)
 178              ->add_field("{$filesalias}.license")
 179              ->set_is_sortable(true)
 180              ->add_callback(static function(?string $license): string {
 181                  global $CFG;
 182                  require_once("{$CFG->libdir}/licenselib.php");
 183  
 184                  $licenses = license_manager::get_licenses();
 185                  if ($license === null || !array_key_exists($license, $licenses)) {
 186                      return '';
 187                  }
 188                  return $licenses[$license]->fullname;
 189              });
 190  
 191          // Context.
 192          $columns[] = (new column(
 193              'context',
 194              new lang_string('context'),
 195              $this->get_entity_name()
 196          ))
 197              ->add_joins($this->get_joins())
 198              ->set_type(column::TYPE_TEXT)
 199              ->add_join("LEFT JOIN {context} {$contextalias} ON {$contextalias}.id = {$filesalias}.contextid")
 200              ->add_fields("{$filesalias}.contextid, " . context_helper::get_preload_record_columns_sql($contextalias))
 201              // Sorting may not order alphabetically, but will at least group contexts together.
 202              ->set_is_sortable(true)
 203              ->add_callback(static function($contextid, stdClass $context): string {
 204                  if ($contextid === null) {
 205                      return '';
 206                  }
 207  
 208                  context_helper::preload_from_record($context);
 209                  return context::instance_by_id($contextid)->get_context_name();
 210              });
 211  
 212          // Context link.
 213          $columns[] = (new column(
 214              'contexturl',
 215              new lang_string('contexturl'),
 216              $this->get_entity_name()
 217          ))
 218              ->add_joins($this->get_joins())
 219              ->set_type(column::TYPE_TEXT)
 220              ->add_join("LEFT JOIN {context} {$contextalias} ON {$contextalias}.id = {$filesalias}.contextid")
 221              ->add_fields("{$filesalias}.contextid, " . context_helper::get_preload_record_columns_sql($contextalias))
 222              // Sorting may not order alphabetically, but will at least group contexts together.
 223              ->set_is_sortable(true)
 224              ->add_callback(static function($contextid, stdClass $context): string {
 225                  if ($contextid === null) {
 226                      return '';
 227                  }
 228  
 229                  context_helper::preload_from_record($context);
 230                  $context = context::instance_by_id($contextid);
 231  
 232                  return html_writer::link($context->get_url(), $context->get_context_name());
 233              });
 234  
 235          // Component.
 236          $columns[] = (new column(
 237              'component',
 238              new lang_string('plugin'),
 239              $this->get_entity_name()
 240          ))
 241              ->add_joins($this->get_joins())
 242              ->set_type(column::TYPE_TEXT)
 243              ->add_fields("{$filesalias}.component")
 244              ->set_is_sortable(true);
 245  
 246          // Area.
 247          $columns[] = (new column(
 248              'area',
 249              new lang_string('pluginarea'),
 250              $this->get_entity_name()
 251          ))
 252              ->add_joins($this->get_joins())
 253              ->set_type(column::TYPE_TEXT)
 254              ->add_fields("{$filesalias}.filearea")
 255              ->set_is_sortable(true);
 256  
 257          // Item ID.
 258          $columns[] = (new column(
 259              'itemid',
 260              new lang_string('pluginitemid'),
 261              $this->get_entity_name()
 262          ))
 263              ->add_joins($this->get_joins())
 264              ->set_type(column::TYPE_INTEGER)
 265              ->add_fields("{$filesalias}.itemid")
 266              ->set_is_sortable(true)
 267              ->set_disabled_aggregation_all();
 268  
 269          // Time created.
 270          $columns[] = (new column(
 271              'timecreated',
 272              new lang_string('timecreated', 'core_reportbuilder'),
 273              $this->get_entity_name()
 274          ))
 275              ->add_joins($this->get_joins())
 276              ->set_type(column::TYPE_TIMESTAMP)
 277              ->add_field("{$filesalias}.timecreated")
 278              ->add_callback([format::class, 'userdate'])
 279              ->set_is_sortable(true);
 280  
 281          return $columns;
 282      }
 283  
 284      /**
 285       * Return list of all available filters
 286       *
 287       * @return filter[]
 288       */
 289      protected function get_all_filters(): array {
 290          $filesalias = $this->get_table_alias('files');
 291  
 292          // Directory.
 293          $filters[] = (new filter(
 294              boolean_select::class,
 295              'directory',
 296              new lang_string('directory'),
 297              $this->get_entity_name(),
 298              "CASE WHEN {$filesalias}.filename = '.' THEN 1 ELSE 0 END"
 299          ))
 300              ->add_joins($this->get_joins());
 301  
 302          // Draft.
 303          $filters[] = (new filter(
 304              boolean_select::class,
 305              'draft',
 306              new lang_string('areauserdraft', 'core_repository'),
 307              $this->get_entity_name(),
 308              "CASE WHEN {$filesalias}.component = 'user' AND {$filesalias}.filearea = 'draft' THEN 1 ELSE 0 END"
 309          ))
 310              ->add_joins($this->get_joins());
 311  
 312          // Name.
 313          $filters[] = (new filter(
 314              text::class,
 315              'name',
 316              new lang_string('filename', 'core_repository'),
 317              $this->get_entity_name(),
 318              "{$filesalias}.filename"
 319          ))
 320              ->add_joins($this->get_joins());
 321  
 322          // Size.
 323          $filters[] = (new filter(
 324              number::class,
 325              'size',
 326              new lang_string('size'),
 327              $this->get_entity_name(),
 328              "{$filesalias}.filesize"
 329          ))
 330              ->add_joins($this->get_joins())
 331              ->set_limited_operators([
 332                  number::ANY_VALUE,
 333                  number::LESS_THAN,
 334                  number::GREATER_THAN,
 335                  number::RANGE,
 336              ]);
 337  
 338          // License (consider null = 'unknown/license not specified' for filtering purposes).
 339          $filters[] = (new filter(
 340              select::class,
 341              'license',
 342              new lang_string('license', 'core_repository'),
 343              $this->get_entity_name(),
 344              "COALESCE({$filesalias}.license, 'unknown')"
 345          ))
 346              ->add_joins($this->get_joins())
 347              ->set_options_callback(static function(): array {
 348                  global $CFG;
 349                  require_once("{$CFG->libdir}/licenselib.php");
 350  
 351                  $licenses = license_manager::get_licenses();
 352  
 353                  return array_map(static function(stdClass $license): string {
 354                      return $license->fullname;
 355                  }, $licenses);
 356              });
 357  
 358          // Time created.
 359          $filters[] = (new filter(
 360              date::class,
 361              'timecreated',
 362              new lang_string('timecreated', 'core_reportbuilder'),
 363              $this->get_entity_name(),
 364              "{$filesalias}.timecreated"
 365          ))
 366              ->add_joins($this->get_joins())
 367              ->set_limited_operators([
 368                  date::DATE_ANY,
 369                  date::DATE_RANGE,
 370                  date::DATE_LAST,
 371                  date::DATE_CURRENT,
 372              ]);
 373  
 374          return $filters;
 375      }
 376  }