Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 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  /**
  18   * Class for rendering user filters on the course participants page.
  19   *
  20   * @package    core_user
  21   * @copyright  2020 Michael Hawkins <michaelh@moodle.com>
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  namespace core_user\output;
  25  
  26  use context_course;
  27  use renderable;
  28  use renderer_base;
  29  use stdClass;
  30  use templatable;
  31  
  32  /**
  33   * Class for rendering user filters on the course participants page.
  34   *
  35   * @copyright  2020 Michael Hawkins <michaelh@moodle.com>
  36   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  37   */
  38  class participants_filter implements renderable, templatable {
  39  
  40      /** @var context_course $context The context where the filters are being rendered. */
  41      protected $context;
  42  
  43      /** @var string $tableregionid The table to be updated by this filter */
  44      protected $tableregionid;
  45  
  46      /** @var stdClass $course The course shown */
  47      protected $course;
  48  
  49      /**
  50       * Participants filter constructor.
  51       *
  52       * @param context_course $context The context where the filters are being rendered.
  53       * @param string $tableregionid The table to be updated by this filter
  54       */
  55      public function __construct(context_course $context, string $tableregionid) {
  56          $this->context = $context;
  57          $this->tableregionid = $tableregionid;
  58  
  59          $this->course = get_course($context->instanceid);
  60      }
  61  
  62      /**
  63       * Get data for all filter types.
  64       *
  65       * @return array
  66       */
  67      protected function get_filtertypes(): array {
  68          $filtertypes = [];
  69  
  70          $filtertypes[] = $this->get_keyword_filter();
  71  
  72          if ($filtertype = $this->get_enrolmentstatus_filter()) {
  73              $filtertypes[] = $filtertype;
  74          }
  75  
  76          if ($filtertype = $this->get_roles_filter()) {
  77              $filtertypes[] = $filtertype;
  78          }
  79  
  80          if ($filtertype = $this->get_enrolments_filter()) {
  81              $filtertypes[] = $filtertype;
  82          }
  83  
  84          if ($filtertype = $this->get_groups_filter()) {
  85              $filtertypes[] = $filtertype;
  86          }
  87  
  88          if ($filtertype = $this->get_accesssince_filter()) {
  89              $filtertypes[] = $filtertype;
  90          }
  91  
  92          return $filtertypes;
  93      }
  94  
  95      /**
  96       * Get data for the enrolment status filter.
  97       *
  98       * @return stdClass|null
  99       */
 100      protected function get_enrolmentstatus_filter(): ?stdClass {
 101          if (!has_capability('moodle/course:enrolreview', $this->context)) {
 102              return null;
 103          }
 104  
 105          return $this->get_filter_object(
 106              'status',
 107              get_string('participationstatus', 'core_enrol'),
 108              false,
 109              true,
 110              null,
 111              [
 112                  (object) [
 113                      'value' => ENROL_USER_ACTIVE,
 114                      'title' => get_string('active'),
 115                  ],
 116                  (object) [
 117                      'value' => ENROL_USER_SUSPENDED,
 118                      'title'  => get_string('inactive'),
 119                  ],
 120              ]
 121          );
 122      }
 123  
 124      /**
 125       * Get data for the roles filter.
 126       *
 127       * @return stdClass|null
 128       */
 129      protected function get_roles_filter(): ?stdClass {
 130          $roles = [];
 131          $roles += [-1 => get_string('noroles', 'role')];
 132          $roles += get_viewable_roles($this->context);
 133  
 134          if (has_capability('moodle/role:assign', $this->context)) {
 135              $roles += get_assignable_roles($this->context, ROLENAME_ALIAS);
 136          }
 137  
 138          return $this->get_filter_object(
 139              'roles',
 140              get_string('roles', 'core_role'),
 141              false,
 142              true,
 143              null,
 144              array_map(function($id, $title) {
 145                  return (object) [
 146                      'value' => $id,
 147                      'title' => $title,
 148                  ];
 149              }, array_keys($roles), array_values($roles))
 150          );
 151      }
 152  
 153      /**
 154       * Get data for the roles filter.
 155       *
 156       * @return stdClass|null
 157       */
 158      protected function get_enrolments_filter(): ?stdClass {
 159          if (!has_capability('moodle/course:enrolreview', $this->context)) {
 160              return null;
 161          }
 162  
 163          if ($this->course->id == SITEID) {
 164              // No enrolment methods for the site.
 165              return null;
 166          }
 167  
 168          $instances = enrol_get_instances($this->course->id, true);
 169          $plugins = enrol_get_plugins(false);
 170  
 171          return $this->get_filter_object(
 172              'enrolments',
 173              get_string('enrolmentinstances', 'core_enrol'),
 174              false,
 175              true,
 176              null,
 177              array_filter(array_map(function($instance) use ($plugins): ?stdClass {
 178                  if (!array_key_exists($instance->enrol, $plugins)) {
 179                      return null;
 180                  }
 181  
 182                  return (object) [
 183                      'value' => $instance->id,
 184                      'title' => $plugins[$instance->enrol]->get_instance_name($instance),
 185                  ];
 186              }, array_values($instances)))
 187          );
 188      }
 189  
 190      /**
 191       * Get data for the groups filter.
 192       *
 193       * @return stdClass|null
 194       */
 195      protected function get_groups_filter(): ?stdClass {
 196          global $USER;
 197  
 198          // Filter options for groups, if available.
 199          $seeallgroups = has_capability('moodle/site:accessallgroups', $this->context);
 200          $seeallgroups = $seeallgroups || ($this->course->groupmode != SEPARATEGROUPS);
 201          if ($seeallgroups) {
 202              $groups = [];
 203              $groups += [USERSWITHOUTGROUP => (object) [
 204                      'id' => USERSWITHOUTGROUP,
 205                      'name' => get_string('nogroup', 'group'),
 206                  ]];
 207              $groups += groups_get_all_groups($this->course->id);
 208          } else {
 209              // Otherwise, just list the groups the user belongs to.
 210              $groups = groups_get_all_groups($this->course->id, $USER->id);
 211          }
 212  
 213          // Return no data if no groups found (which includes if the only value is 'No group').
 214          if (empty($groups) || (count($groups) === 1 && array_key_exists(-1, $groups))) {
 215              return null;
 216          }
 217  
 218          return $this->get_filter_object(
 219              'groups',
 220              get_string('groups', 'core_group'),
 221              false,
 222              true,
 223              null,
 224              array_map(function($group) {
 225                  return (object) [
 226                      'value' => $group->id,
 227                      'title' => format_string($group->name, true, ['context' => $this->context]),
 228                  ];
 229              }, array_values($groups))
 230          );
 231      }
 232  
 233      /**
 234       * Get data for the accesssince filter.
 235       *
 236       * @return stdClass|null
 237       */
 238      protected function get_accesssince_filter(): ?stdClass {
 239          global $CFG, $DB;
 240  
 241          $hiddenfields = [];
 242          if (!has_capability('moodle/course:viewhiddenuserfields', $this->context)) {
 243              $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
 244          }
 245  
 246          if (array_key_exists('lastaccess', $hiddenfields)) {
 247              return null;
 248          }
 249  
 250          // Get minimum lastaccess for this course and display a dropbox to filter by lastaccess going back this far.
 251          // We need to make it diferently for normal courses and site course.
 252          if (!$this->course->id == SITEID) {
 253              // Regular course.
 254              $params = [
 255                  'courseid' => $this->course->id,
 256                  'timeaccess' => 0,
 257              ];
 258              $select = 'courseid = :courseid AND timeaccess != :timeaccess';
 259              $minlastaccess = $DB->get_field_select('user_lastaccess', 'MIN(timeaccess)', $select, $params);
 260              $lastaccess0exists = $DB->record_exists('user_lastaccess', $params);
 261          } else {
 262              // Front page.
 263              $params = ['lastaccess' => 0];
 264              $select = 'lastaccess != :lastaccess';
 265              $minlastaccess = $DB->get_field_select('user', 'MIN(lastaccess)', $select, $params);
 266              $lastaccess0exists = $DB->record_exists('user', $params);
 267          }
 268  
 269          $now = usergetmidnight(time());
 270          $timeoptions = [];
 271          $criteria = get_string('usersnoaccesssince');
 272  
 273          $getoptions = function(int $count, string $singletype, string $type) use ($now, $minlastaccess): array {
 274              $values = [];
 275              for ($i = 1; $i <= $count; $i++) {
 276                  $timestamp = strtotime("-{$i} {$type}", $now);
 277                  if ($timestamp < $minlastaccess) {
 278                      break;
 279                  }
 280  
 281                  if ($i === 1) {
 282                      $title = get_string("num{$singletype}", 'moodle', $i);
 283                  } else {
 284                      $title = get_string("num{$type}", 'moodle', $i);
 285                  }
 286  
 287                  $values[] = [
 288                      'value' => $timestamp,
 289                      'title' => $title,
 290                  ];
 291              }
 292  
 293              return $values;
 294          };
 295  
 296          $values = array_merge(
 297              $getoptions(6, 'day', 'days'),
 298              $getoptions(10, 'week', 'weeks'),
 299              $getoptions(11, 'month', 'months'),
 300              $getoptions(1, 'year', 'years')
 301          );
 302  
 303          if ($lastaccess0exists) {
 304              $values[] = [
 305                  'value' => time(),
 306                  'title' => get_string('never', 'moodle'),
 307              ];
 308          }
 309  
 310          if (count($values) <= 1) {
 311              // Nothing to show.
 312              return null;
 313          }
 314  
 315          return $this->get_filter_object(
 316              'accesssince',
 317              get_string('usersnoaccesssince'),
 318              false,
 319              false,
 320              null,
 321              $values
 322          );
 323      }
 324  
 325      /**
 326       * Get data for the keywords filter.
 327       *
 328       * @return stdClass|null
 329       */
 330      protected function get_keyword_filter(): ?stdClass {
 331          return $this->get_filter_object(
 332              'keywords',
 333              get_string('filterbykeyword', 'core_user'),
 334              true,
 335              true,
 336              'core_user/local/participantsfilter/filtertypes/keyword',
 337              [],
 338              true
 339          );
 340      }
 341  
 342      /**
 343       * Export the renderer data in a mustache template friendly format.
 344       *
 345       * @param renderer_base $output Unused.
 346       * @return stdClass Data in a format compatible with a mustache template.
 347       */
 348      public function export_for_template(renderer_base $output): stdClass {
 349          return (object) [
 350              'tableregionid' => $this->tableregionid,
 351              'courseid' => $this->context->instanceid,
 352              'filtertypes' => $this->get_filtertypes(),
 353              'rownumber' => 1,
 354          ];
 355  
 356          return $data;
 357      }
 358  
 359      /**
 360       * Get a standardised filter object.
 361       *
 362       * @param string $name
 363       * @param string $title
 364       * @param bool $custom
 365       * @param bool $multiple
 366       * @param string|null $filterclass
 367       * @param array $values
 368       * @param bool $allowempty
 369       * @return stdClass|null
 370       */
 371      protected function get_filter_object(
 372          string $name,
 373          string $title,
 374          bool $custom,
 375          bool $multiple,
 376          ?string $filterclass,
 377          array $values,
 378          bool $allowempty = false
 379      ): ?stdClass {
 380  
 381          if (!$allowempty && empty($values)) {
 382              // Do not show empty filters.
 383              return null;
 384          }
 385  
 386          return (object) [
 387              'name' => $name,
 388              'title' => $title,
 389              'allowcustom' => $custom,
 390              'allowmultiple' => $multiple,
 391              'filtertypeclass' => $filterclass,
 392              'values' => $values,
 393          ];
 394      }
 395  }