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.
/group/ -> overview.php (source)

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [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  
  18  /**
  19   * Print an overview of groupings & group membership
  20   *
  21   * @copyright  Matt Clarkson mattc@catalyst.net.nz
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   * @package    core_group
  24   */
  25  
  26  require_once('../config.php');
  27  require_once($CFG->libdir . '/filelib.php');
  28  
  29  define('OVERVIEW_NO_GROUP', -1); // The fake group for users not in a group.
  30  define('OVERVIEW_GROUPING_GROUP_NO_GROUPING', -1); // The fake grouping for groups that have no grouping.
  31  define('OVERVIEW_GROUPING_NO_GROUP', -2); // The fake grouping for users with no group.
  32  
  33  $courseid   = required_param('id', PARAM_INT);
  34  $groupid    = optional_param('group', 0, PARAM_INT);
  35  $groupingid = optional_param('grouping', 0, PARAM_INT);
  36  $dataformat = optional_param('dataformat', '', PARAM_ALPHA);
  37  
  38  $returnurl = $CFG->wwwroot.'/group/index.php?id='.$courseid;
  39  $rooturl   = $CFG->wwwroot.'/group/overview.php?id='.$courseid;
  40  
  41  if (!$course = $DB->get_record('course', array('id'=>$courseid))) {
  42      throw new \moodle_exception('invalidcourse');
  43  }
  44  
  45  $url = new moodle_url('/group/overview.php', array('id'=>$courseid));
  46  if ($groupid !== 0) {
  47      $url->param('group', $groupid);
  48  }
  49  if ($groupingid !== 0) {
  50      $url->param('grouping', $groupingid);
  51  }
  52  $PAGE->set_url($url);
  53  
  54  // Make sure that the user has permissions to manage groups.
  55  require_login($course);
  56  
  57  $context = context_course::instance($courseid);
  58  require_capability('moodle/course:managegroups', $context);
  59  
  60  $strgroups           = get_string('groups');
  61  $strparticipants     = get_string('participants');
  62  $stroverview         = get_string('overview', 'group');
  63  $strgrouping         = get_string('grouping', 'group');
  64  $strgroup            = get_string('group', 'group');
  65  $strnotingrouping    = get_string('notingrouping', 'group');
  66  $strfiltergroups     = get_string('filtergroups', 'group');
  67  $strnogroups         = get_string('nogroups', 'group');
  68  $strdescription      = get_string('description');
  69  $strnotingroup       = get_string('notingrouplist', 'group');
  70  $strnogroup          = get_string('nogroup', 'group');
  71  $strnogrouping       = get_string('nogrouping', 'group');
  72  
  73  // This can show all users and all groups in a course.
  74  // This is lots of data so allow this script more resources.
  75  raise_memory_limit(MEMORY_EXTRA);
  76  
  77  // Get all groupings and sort them by formatted name.
  78  $groupings = $DB->get_records('groupings', array('courseid'=>$courseid), 'name');
  79  foreach ($groupings as $gid => $grouping) {
  80      $groupings[$gid]->formattedname = format_string($grouping->name, true, array('context' => $context));
  81  }
  82  core_collator::asort_objects_by_property($groupings, 'formattedname');
  83  $members = array();
  84  foreach ($groupings as $grouping) {
  85      $members[$grouping->id] = array();
  86  }
  87  // Groups not in a grouping.
  88  $members[OVERVIEW_GROUPING_GROUP_NO_GROUPING] = array();
  89  
  90  // Get all groups and sort them by formatted name.
  91  $groups = $DB->get_records('groups', array('courseid'=>$courseid), 'name');
  92  foreach ($groups as $id => $group) {
  93      $groups[$id]->formattedname = format_string($group->name, true, ['context' => $context]);
  94  }
  95  core_collator::asort_objects_by_property($groups, 'formattedname');
  96  
  97  $params = array('courseid'=>$courseid);
  98  if ($groupid) {
  99      $groupwhere = "AND g.id = :groupid";
 100      $params['groupid']   = $groupid;
 101  } else {
 102      $groupwhere = "";
 103  }
 104  
 105  if ($groupingid) {
 106      if ($groupingid < 0) { // No grouping filter.
 107          $groupingwhere = "AND gg.groupingid IS NULL";
 108      } else {
 109          $groupingwhere = "AND gg.groupingid = :groupingid";
 110          $params['groupingid'] = $groupingid;
 111      }
 112  } else {
 113      $groupingwhere = "";
 114  }
 115  
 116  list($sort, $sortparams) = users_order_by_sql('u');
 117  
 118  $userfieldsapi = \core_user\fields::for_identity($context)->with_userpic();
 119  [
 120      'selects' => $userfieldsselects,
 121      'joins' => $userfieldsjoin,
 122      'params' => $userfieldsparams
 123  ] = (array)$userfieldsapi->get_sql('u', true);
 124  $extrafields = $userfieldsapi->get_required_fields([\core_user\fields::PURPOSE_IDENTITY]);
 125  $allnames = 'u.id ' . $userfieldsselects;
 126  
 127  $sql = "SELECT g.id AS groupid, gg.groupingid, u.id AS userid, $allnames, u.idnumber, u.username
 128            FROM {groups} g
 129                 LEFT JOIN {groupings_groups} gg ON g.id = gg.groupid
 130                 LEFT JOIN {groups_members} gm ON g.id = gm.groupid
 131                 LEFT JOIN {user} u ON gm.userid = u.id
 132                 $userfieldsjoin
 133           WHERE g.courseid = :courseid $groupwhere $groupingwhere
 134        ORDER BY g.name, $sort";
 135  
 136  $rs = $DB->get_recordset_sql($sql, array_merge($params, $sortparams, $userfieldsparams));
 137  foreach ($rs as $row) {
 138      $user = username_load_fields_from_object((object) [], $row, null,
 139          array_merge(['id' => 'userid', 'username', 'idnumber'], $extrafields));
 140  
 141      if (!$row->groupingid) {
 142          $row->groupingid = OVERVIEW_GROUPING_GROUP_NO_GROUPING;
 143      }
 144      if (!array_key_exists($row->groupid, $members[$row->groupingid])) {
 145          $members[$row->groupingid][$row->groupid] = array();
 146      }
 147      if (!empty($user->id)) {
 148          $members[$row->groupingid][$row->groupid][] = $user;
 149      }
 150  }
 151  $rs->close();
 152  
 153  // Add 'no groupings' / 'no groups' selectors.
 154  $groupings[OVERVIEW_GROUPING_GROUP_NO_GROUPING] = (object)array(
 155      'id' => OVERVIEW_GROUPING_GROUP_NO_GROUPING,
 156      'formattedname' => $strnogrouping,
 157  );
 158  $groups[OVERVIEW_NO_GROUP] = (object)array(
 159      'id' => OVERVIEW_NO_GROUP,
 160      'courseid' => $courseid,
 161      'idnumber' => '',
 162      'name' => $strnogroup,
 163      'formattedname' => $strnogroup,
 164      'description' => '',
 165      'descriptionformat' => FORMAT_HTML,
 166      'enrolmentkey' => '',
 167      'picture' => 0,
 168      'timecreated' => 0,
 169      'timemodified' => 0,
 170  );
 171  
 172  // Add users who are not in a group.
 173  if ($groupid <= 0 && $groupingid <= 0) {
 174      list($esql, $params) = get_enrolled_sql($context, null, 0, true);
 175      $sql = "SELECT u.id, $allnames, u.idnumber, u.username
 176                FROM {user} u
 177                JOIN ($esql) e ON e.id = u.id
 178           LEFT JOIN (
 179                    SELECT gm.userid
 180                      FROM {groups_members} gm
 181                      JOIN {groups} g ON g.id = gm.groupid
 182                     WHERE g.courseid = :courseid
 183                     ) grouped ON grouped.userid = u.id
 184                    $userfieldsjoin
 185               WHERE grouped.userid IS NULL
 186               ORDER BY $sort";
 187      $params['courseid'] = $courseid;
 188  
 189      $nogroupusers = $DB->get_records_sql($sql, array_merge($params, $userfieldsparams));
 190  
 191      if ($nogroupusers) {
 192          $members[OVERVIEW_GROUPING_NO_GROUP][OVERVIEW_NO_GROUP] = $nogroupusers;
 193      }
 194  }
 195  
 196  // Export groups if requested.
 197  if ($dataformat !== '') {
 198      $columnnames = array(
 199          'grouping' => $strgrouping,
 200          'group' => $strgroup,
 201          'firstname' => get_string('firstname'),
 202          'lastname' => get_string('lastname'),
 203      );
 204      $extrafields = \core_user\fields::get_identity_fields($context, false);
 205      foreach ($extrafields as $field) {
 206          $columnnames[$field] = \core_user\fields::get_display_name($field);
 207      }
 208      $alldata = array();
 209      // Generate file name.
 210      $shortname = format_string($course->shortname, true, array('context' => $context))."_groups";
 211      $i = 0;
 212      foreach ($members as $gpgid => $groupdata) {
 213          if ($groupingid and $groupingid != $gpgid) {
 214              if ($groupingid > 0 || $gpgid > 0) {
 215                  // Still show 'not in group' when 'no grouping' selected.
 216                  continue; // Do not export.
 217              }
 218          }
 219          if ($gpgid < 0) {
 220              // Display 'not in group' for grouping id == OVERVIEW_GROUPING_NO_GROUP.
 221              if ($gpgid == OVERVIEW_GROUPING_NO_GROUP) {
 222                  $groupingname = $strnotingroup;
 223              } else {
 224                  $groupingname = $strnotingrouping;
 225              }
 226          } else {
 227              $groupingname = $groupings[$gpgid]->formattedname;
 228          }
 229          if (empty($groupdata)) {
 230              $alldata[$i] = array_fill_keys(array_keys($columnnames), '');
 231              $alldata[$i]['grouping'] = $groupingname;
 232              $i++;
 233          }
 234          foreach ($groupdata as $gpid => $users) {
 235              if ($groupid and $groupid != $gpid) {
 236                  continue;
 237              }
 238              if (empty($users)) {
 239                  $alldata[$i] = array_fill_keys(array_keys($columnnames), '');
 240                  $alldata[$i]['grouping'] = $groupingname;
 241                  $alldata[$i]['group'] = $groups[$gpid]->formattedname;
 242                  $i++;
 243              }
 244              foreach ($users as $option => $user) {
 245                  $alldata[$i]['grouping'] = $groupingname;
 246                  $alldata[$i]['group'] = $groups[$gpid]->formattedname;
 247                  $alldata[$i]['firstname'] = $user->firstname;
 248                  $alldata[$i]['lastname'] = $user->lastname;
 249                  foreach ($extrafields as $field) {
 250                      $alldata[$i][$field] = $user->$field;
 251                  }
 252                  $i++;
 253              }
 254          }
 255      }
 256  
 257      \core\dataformat::download_data(
 258          $shortname,
 259          $dataformat,
 260          $columnnames,
 261          $alldata,
 262          function($record, $supportshtml) use ($extrafields) {
 263              if ($supportshtml) {
 264                  foreach ($extrafields as $extrafield) {
 265                      $record[$extrafield] = s($record[$extrafield]);
 266                  }
 267              }
 268              return $record;
 269          });
 270      die;
 271  }
 272  
 273  // Main page content.
 274  navigation_node::override_active_url(new moodle_url('/group/index.php', array('id'=>$courseid)));
 275  $PAGE->navbar->add(get_string('overview', 'group'));
 276  
 277  /// Print header
 278  $PAGE->set_title($strgroups);
 279  $PAGE->set_heading($course->fullname);
 280  $PAGE->set_pagelayout('standard');
 281  echo $OUTPUT->header();
 282  
 283  echo $OUTPUT->render_participants_tertiary_nav($course);
 284  /// Print overview
 285  echo $OUTPUT->heading(format_string($course->shortname, true, array('context' => $context)) .' '.$stroverview, 3);
 286  
 287  echo $strfiltergroups;
 288  
 289  $options = array();
 290  $options[0] = get_string('all');
 291  foreach ($groupings as $grouping) {
 292      $options[$grouping->id] = strip_tags($grouping->formattedname);
 293  }
 294  $popupurl = new moodle_url($rooturl.'&group='.$groupid);
 295  $select = new single_select($popupurl, 'grouping', $options, $groupingid, array());
 296  $select->label = $strgrouping;
 297  $select->formid = 'selectgrouping';
 298  echo $OUTPUT->render($select);
 299  
 300  $options = array();
 301  $options[0] = get_string('all');
 302  foreach ($groups as $group) {
 303      $options[$group->id] = $group->formattedname;
 304  }
 305  $popupurl = new moodle_url($rooturl.'&grouping='.$groupingid);
 306  $select = new single_select($popupurl, 'group', $options, $groupid, array());
 307  $select->label = $strgroup;
 308  $select->formid = 'selectgroup';
 309  echo $OUTPUT->render($select);
 310  
 311  /// Print table
 312  $printed = false;
 313  foreach ($members as $gpgid=>$groupdata) {
 314      if ($groupingid and $groupingid != $gpgid) {
 315          if ($groupingid > 0 || $gpgid > 0) { // Still show 'not in group' when 'no grouping' selected.
 316              continue; // Do not show.
 317          }
 318      }
 319      $table = new html_table();
 320      $table->head  = array(get_string('groupscount', 'group', count($groupdata)), get_string('groupmembers', 'group'), get_string('usercount', 'group'));
 321      $table->size  = array('20%', '70%', '10%');
 322      $table->align = array('left', 'left', 'center');
 323      $table->width = '90%';
 324      $table->data  = array();
 325      foreach ($groupdata as $gpid=>$users) {
 326          if ($groupid and $groupid != $gpid) {
 327              continue;
 328          }
 329          $line = array();
 330          $name = print_group_picture($groups[$gpid], $course->id, false, true, false) . $groups[$gpid]->formattedname;
 331          $description = file_rewrite_pluginfile_urls($groups[$gpid]->description, 'pluginfile.php', $context->id, 'group', 'description', $gpid);
 332          $options = new stdClass;
 333          $options->noclean = true;
 334          $options->overflowdiv = true;
 335          $line[] = $name;
 336          $viewfullnames = has_capability('moodle/site:viewfullnames', $context);
 337          $fullnames = array();
 338          foreach ($users as $user) {
 339              $displayname = fullname($user, $viewfullnames);
 340              if ($extrafields) {
 341                  $extrafieldsdisplay = [];
 342                  foreach ($extrafields as $field) {
 343                      $extrafieldsdisplay[] = s($user->{$field});
 344                  }
 345                  $displayname .= ' (' . implode(', ', $extrafieldsdisplay) . ')';
 346              }
 347  
 348              $fullnames[] = html_writer::link(new moodle_url('/user/view.php', ['id' => $user->id, 'course' => $course->id]),
 349                  $displayname);
 350          }
 351          $line[] = implode(', ', $fullnames);
 352          $line[] = count($users);
 353          $table->data[] = $line;
 354      }
 355      if ($groupid and empty($table->data)) {
 356          continue;
 357      }
 358      if ($gpgid < 0) {
 359          // Display 'not in group' for grouping id == OVERVIEW_GROUPING_NO_GROUP.
 360          if ($gpgid == OVERVIEW_GROUPING_NO_GROUP) {
 361              echo $OUTPUT->heading($strnotingroup, 3);
 362          } else {
 363              echo $OUTPUT->heading($strnotingrouping, 3);
 364          }
 365      } else {
 366          echo $OUTPUT->heading($groupings[$gpgid]->formattedname, 3);
 367          $description = file_rewrite_pluginfile_urls($groupings[$gpgid]->description, 'pluginfile.php', $context->id, 'grouping', 'description', $gpgid);
 368          $options = new stdClass;
 369          $options->overflowdiv = true;
 370          echo $OUTPUT->box(format_text($description, $groupings[$gpgid]->descriptionformat, $options), 'generalbox boxwidthnarrow boxaligncenter');
 371      }
 372      echo html_writer::table($table);
 373      $printed = true;
 374  }
 375  
 376  // Add buttons for exporting groups/groupings.
 377  echo $OUTPUT->download_dataformat_selector(get_string('exportgroupsgroupings', 'group'), 'overview.php', 'dataformat', [
 378      'id' => $courseid,
 379      'group' => $groupid,
 380      'grouping' => $groupingid,
 381  ]);
 382  
 383  echo $OUTPUT->footer();