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]

   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 core_collator;
  22  use core_component;
  23  use core_plugin_manager;
  24  use stdClass;
  25  use core_reportbuilder\local\models\report;
  26  use core_reportbuilder\local\report\base;
  27  
  28  /**
  29   * Report management class
  30   *
  31   * @package     core_reportbuilder
  32   * @copyright   2020 Paul Holden <paulh@moodle.com>
  33   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  34   */
  35  class manager {
  36  
  37      /** @var base $instances */
  38      private static $instances = [];
  39  
  40      /**
  41       * Return an instance of a report class from the given report persistent
  42       *
  43       * We statically cache the list of loaded reports per user during request lifecycle, to allow this method to be called
  44       * repeatedly without potential performance problems initialising the same report multiple times
  45       *
  46       * @param report $report
  47       * @param array $parameters
  48       * @return base
  49       * @throws source_invalid_exception
  50       * @throws source_unavailable_exception
  51       */
  52      public static function get_report_from_persistent(report $report, array $parameters = []): base {
  53          global $USER;
  54  
  55          // Cached instance per report/user, to account for initialization dependent on current user.
  56          $instancekey = $report->get('id') . ':' . ($USER->id ?? 0);
  57  
  58          if (!array_key_exists($instancekey, static::$instances)) {
  59              $source = $report->get('source');
  60  
  61              // Throw exception for invalid or unavailable report source.
  62              if (!self::report_source_exists($source)) {
  63                  throw new source_invalid_exception($source);
  64              } else if (!self::report_source_available($source)) {
  65                  throw new source_unavailable_exception($source);
  66              }
  67  
  68              static::$instances[$instancekey] = new $source($report, $parameters);
  69          }
  70  
  71          return static::$instances[$instancekey];
  72      }
  73  
  74      /**
  75       * Run reset code after tests to reset the instance cache
  76       */
  77      public static function reset_caches(): void {
  78          if (PHPUNIT_TEST || defined('BEHAT_TEST')) {
  79              static::$instances = [];
  80          }
  81      }
  82  
  83      /**
  84       * Return an instance of a report class from the given report ID
  85       *
  86       * @param int $reportid
  87       * @param array $parameters
  88       * @return base
  89       */
  90      public static function get_report_from_id(int $reportid, array $parameters = []): base {
  91          $report = new report($reportid);
  92  
  93          return self::get_report_from_persistent($report, $parameters);
  94      }
  95  
  96      /**
  97       * Verify that report source exists and extends appropriate base classes
  98       *
  99       * @param string $source Full namespaced path to report definition
 100       * @param string $additionalbaseclass Specify addition base class that given classname should extend
 101       * @return bool
 102       */
 103      public static function report_source_exists(string $source, string $additionalbaseclass = ''): bool {
 104          return (class_exists($source) && is_subclass_of($source, base::class) &&
 105              (empty($additionalbaseclass) || is_subclass_of($source, $additionalbaseclass)));
 106      }
 107  
 108      /**
 109       * Verify given report source is available. Note that it is assumed caller has already checked that it exists
 110       *
 111       * @param string $source
 112       * @return bool
 113       */
 114      public static function report_source_available(string $source): bool {
 115          return call_user_func([$source, 'is_available']);
 116      }
 117  
 118      /**
 119       * Create new report persistent
 120       *
 121       * @param stdClass $reportdata
 122       * @return report
 123       */
 124      public static function create_report_persistent(stdClass $reportdata): report {
 125          return (new report(0, $reportdata))->create();
 126      }
 127  
 128      /**
 129       * Return an array of all valid report sources across the site
 130       *
 131       * @return array[][] Indexed by [component => [class => name]]
 132       */
 133      public static function get_report_datasources(): array {
 134          $sources = array();
 135  
 136          $datasources = core_component::get_component_classes_in_namespace(null, 'reportbuilder\\datasource');
 137          foreach ($datasources as $class => $path) {
 138              if (self::report_source_exists($class, datasource::class) && self::report_source_available($class)) {
 139  
 140                  // Group each report source by the component that it belongs to.
 141                  [$component] = explode('\\', $class);
 142                  if ($plugininfo = core_plugin_manager::instance()->get_plugin_info($component)) {
 143                      $componentname = $plugininfo->displayname;
 144                  } else {
 145                      $componentname = get_string('site');
 146                  }
 147  
 148                  $sources[$componentname][$class] = call_user_func([$class, 'get_name']);
 149              }
 150          }
 151  
 152          // Order source for each component alphabetically.
 153          array_walk($sources, static function(array &$componentsources): void {
 154              core_collator::asort($componentsources);
 155          });
 156  
 157          return $sources;
 158      }
 159  
 160      /**
 161       * Configured site limit for number of custom reports threshold has been reached
 162       *
 163       * @return bool
 164       */
 165      public static function report_limit_reached(): bool {
 166          global $CFG;
 167  
 168          return (!empty($CFG->customreportslimit) &&
 169              (int) $CFG->customreportslimit <= report::count_records(['type' => base::TYPE_CUSTOM_REPORT]));
 170      }
 171  }