Search moodle.org's
Developer Documentation

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.
  • Differences Between: [Versions 310 and 311] [Versions 39 and 311]

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