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 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\local\audiences;
  20  
  21  use core_plugin_manager;
  22  use MoodleQuickForm;
  23  use stdClass;
  24  use core\output\notification;
  25  use core_reportbuilder\local\models\audience;
  26  use core_reportbuilder\report_access_exception;
  27  
  28  /**
  29   * Audience base class
  30   *
  31   * @package     core_reportbuilder
  32   * @copyright   2021 David Matamoros <davidmc@moodle.com>
  33   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  34   */
  35  abstract class base {
  36  
  37      /** @var int Maximim number of multi-select elements to show in description, before appending "plus X more" */
  38      private const MULTI_SELECT_LIMIT = 5;
  39  
  40      /** @var audience The persistent object associated with this audience */
  41      protected $audience;
  42  
  43      /**
  44       * Protected constructor, please use the static instance method.
  45       */
  46      protected function __construct() {
  47      }
  48  
  49      /**
  50       * Loads an existing instance of audience with persistent
  51       *
  52       * @param int $id
  53       * @param null|stdClass $record
  54       * @return self|null
  55       */
  56      final public static function instance(int $id = 0, ?stdClass $record = null): ?self {
  57          $persistent = new audience($id, $record);
  58          // Needed for get_audience_types() method.
  59          if (!$classname = $persistent->get('classname')) {
  60              // Use the called class name.
  61              $classname = get_called_class();
  62              $persistent->set('classname', $classname);
  63          }
  64  
  65          // Check if audience type class still exists in the system.
  66          if (!class_exists($classname)) {
  67              return null;
  68          }
  69  
  70          $instance = new $classname();
  71          $instance->audience = $persistent;
  72          return $instance;
  73      }
  74  
  75      /**
  76       * Creates a new audience and saves it to database
  77       *
  78       * @param int $reportid
  79       * @param array $configdata
  80       * @return self
  81       */
  82      final public static function create(int $reportid, array $configdata): self {
  83          $record = new stdClass();
  84          $record->reportid = $reportid;
  85          $record->classname = get_called_class();
  86          $record->configdata = json_encode($configdata);
  87          $instance = self::instance(0, $record);
  88          $instance->audience->save();
  89          return $instance;
  90      }
  91  
  92      /**
  93       * Helps to build SQL to retrieve users that matches the current audience
  94       *
  95       * Implementations must use api::generate_alias() for table/column aliases
  96       * and api::generate_param_name() for named parameters
  97       *
  98       * @param string $usertablealias
  99       * @return array array of three elements [$join, $where, $params]
 100       */
 101      abstract public function get_sql(string $usertablealias): array;
 102  
 103      /**
 104       * Returns string for audience category.
 105       *
 106       * @return string
 107       */
 108      final public function get_category(): string {
 109          [$component] = explode('\\', get_class($this));
 110  
 111          if ($plugininfo = core_plugin_manager::instance()->get_plugin_info($component)) {
 112              return $plugininfo->displayname;
 113          }
 114  
 115          return get_string('site');
 116      }
 117  
 118      /**
 119       * If the current user is able to add this audience type
 120       *
 121       * @return bool
 122       */
 123      abstract public function user_can_add(): bool;
 124  
 125      /**
 126       * If the current user is able to edit this audience type
 127       *
 128       * @return bool
 129       */
 130      abstract public function user_can_edit(): bool;
 131  
 132      /**
 133       * If the current user is able to use this audience type
 134       *
 135       * This method needs to return true if audience type is available to user for
 136       * reasons other than permission check, which is done in {@see user_can_add}.
 137       * (e.g. user can add cohort audience type only if there is at least one cohort
 138       * they can access).
 139       *
 140       * @return bool
 141       */
 142      public function is_available(): bool {
 143          return true;
 144      }
 145  
 146      /**
 147       * Return user friendly name of the audience type
 148       *
 149       * @return string
 150       */
 151      abstract public function get_name(): string;
 152  
 153      /**
 154       * Return the description of this audience type
 155       *
 156       * @return string
 157       */
 158      abstract public function get_description(): string;
 159  
 160      /**
 161       * Helper to format descriptions for audience types that may contain many selected elements, limiting number show according
 162       * to {@see MULTI_SELECT_LIMIT} constant value
 163       *
 164       * @param array $elements
 165       * @return string
 166       */
 167      protected function format_description_for_multiselect(array $elements): string {
 168          global $OUTPUT;
 169  
 170          // Warn user if there are no elements (because they may no longer exist).
 171          $elementcount = count($elements);
 172          if ($elementcount === 0) {
 173              $notification = new notification(get_string('nothingtodisplay'), notification::NOTIFY_WARNING);
 174              return $OUTPUT->render($notification);
 175          }
 176  
 177          $listseparator = get_string('listsep', 'langconfig') . ' ';
 178          if ($elementcount > self::MULTI_SELECT_LIMIT) {
 179              $elements = array_slice($elements, 0, self::MULTI_SELECT_LIMIT);
 180  
 181              // Append overflow element.
 182              $elementoverflow = $elementcount - self::MULTI_SELECT_LIMIT;
 183              $params = [
 184                  'elements' => implode($listseparator, $elements),
 185                  'morecount' => $elementoverflow,
 186              ];
 187              $description = get_string('audiencemultiselectpostfix', 'core_reportbuilder', $params);
 188          } else {
 189              $description = implode($listseparator, $elements);
 190          }
 191  
 192          return $description;
 193      }
 194  
 195      /**
 196       * Adds audience-specific form elements
 197       *
 198       * @param MoodleQuickForm $mform The form to add elements to
 199       */
 200      abstract public function get_config_form(MoodleQuickForm $mform): void;
 201  
 202      /**
 203       * Validates the configform of the condition.
 204       *
 205       * @param array $data Data from the form
 206       * @return array Array with errors for each element
 207       */
 208      public function validate_config_form(array $data): array {
 209          return [];
 210      }
 211  
 212      /**
 213       * Returns configdata as an associative array
 214       *
 215       * @return array decoded configdata
 216       */
 217      final public function get_configdata(): array {
 218          return json_decode($this->audience->get('configdata'), true);
 219      }
 220  
 221      /**
 222       * Update configdata in audience persistent
 223       *
 224       * @param array $configdata
 225       */
 226      final public function update_configdata(array $configdata): void {
 227          $this->audience->set('configdata', json_encode($configdata));
 228          $this->audience->save();
 229      }
 230  
 231      /**
 232       * Returns $configdata from form data suitable for use in DB record.
 233       *
 234       * @param stdClass $data data obtained from $mform->get_data()
 235       * @return array $configdata
 236       */
 237      final public static function retrieve_configdata(stdClass $data): array {
 238          $configdata = (array) $data;
 239          $invalidkeys = array_fill_keys(['id', 'reportid', 'classname'], '');
 240          return array_diff_key($configdata, $invalidkeys);
 241      }
 242  
 243      /**
 244       * Return audience persistent.
 245       *
 246       * @return audience
 247       */
 248      public function get_persistent(): audience {
 249          return $this->audience;
 250      }
 251  
 252      /**
 253       * Require current user is able to add this audience type
 254       *
 255       * @throws report_access_exception
 256       */
 257      final public function require_user_can_add(): void {
 258          if (!$this->user_can_add()) {
 259              throw new report_access_exception('errorreportedit');
 260          }
 261      }
 262  
 263      /**
 264       * Require current user is able to edit this audience type
 265       *
 266       * @throws report_access_exception
 267       */
 268      final public function require_user_can_edit(): void {
 269          if (!$this->user_can_edit()) {
 270              throw new report_access_exception('errorreportedit');
 271          }
 272      }
 273  }