autogroup.php 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  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. * Create and allocate users to groups
  18. *
  19. * @package core_group
  20. * @copyright Matt Clarkson mattc@catalyst.net.nz
  21. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  22. */
  23. require_once('../config.php');
  24. require_once('lib.php');
  25. require_once('autogroup_form.php');
  26. if (!defined('AUTOGROUP_MIN_RATIO')) {
  27. define('AUTOGROUP_MIN_RATIO', 0.7); // means minimum member count is 70% in the smallest group
  28. }
  29. $courseid = required_param('courseid', PARAM_INT);
  30. $PAGE->set_url('/group/autogroup.php', array('courseid' => $courseid));
  31. if (!$course = $DB->get_record('course', array('id'=>$courseid))) {
  32. print_error('invalidcourseid');
  33. }
  34. // Make sure that the user has permissions to manage groups.
  35. require_login($course);
  36. $context = context_course::instance($courseid);
  37. require_capability('moodle/course:managegroups', $context);
  38. $returnurl = $CFG->wwwroot.'/group/index.php?id='.$course->id;
  39. $strgroups = get_string('groups');
  40. $strparticipants = get_string('participants');
  41. $strautocreategroups = get_string('autocreategroups', 'group');
  42. $PAGE->set_title($strgroups);
  43. $PAGE->set_heading($course->fullname. ': '.$strgroups);
  44. $PAGE->set_pagelayout('admin');
  45. navigation_node::override_active_url(new moodle_url('/group/index.php', array('id' => $courseid)));
  46. // Print the page and form
  47. $preview = '';
  48. $error = '';
  49. /// Get applicable roles - used in menus etc later on
  50. $rolenames = role_fix_names(get_profile_roles($context), $context, ROLENAME_ALIAS, true);
  51. /// Create the form
  52. $editform = new autogroup_form(null, array('roles' => $rolenames));
  53. $editform->set_data(array('courseid' => $courseid, 'seed' => time()));
  54. /// Handle form submission
  55. if ($editform->is_cancelled()) {
  56. redirect($returnurl);
  57. } elseif ($data = $editform->get_data()) {
  58. /// Allocate members from the selected role to groups
  59. switch ($data->allocateby) {
  60. case 'no':
  61. case 'random':
  62. case 'lastname':
  63. $orderby = 'lastname, firstname'; break;
  64. case 'firstname':
  65. $orderby = 'firstname, lastname'; break;
  66. case 'idnumber':
  67. $orderby = 'idnumber'; break;
  68. default:
  69. print_error('unknoworder');
  70. }
  71. $source = array();
  72. if ($data->cohortid) {
  73. $source['cohortid'] = $data->cohortid;
  74. }
  75. if ($data->groupingid) {
  76. $source['groupingid'] = $data->groupingid;
  77. }
  78. if ($data->groupid) {
  79. $source['groupid'] = $data->groupid;
  80. }
  81. // Display only active users if the option was selected or they do not have the capability to view suspended users.
  82. $onlyactive = !empty($data->includeonlyactiveenrol) || !has_capability('moodle/course:viewsuspendedusers', $context);
  83. $users = groups_get_potential_members($data->courseid, $data->roleid, $source, $orderby, !empty($data->notingroup),
  84. $onlyactive);
  85. $usercnt = count($users);
  86. if ($data->allocateby == 'random') {
  87. srand($data->seed);
  88. shuffle($users);
  89. }
  90. $groups = array();
  91. // Plan the allocation
  92. if ($data->groupby == 'groups') {
  93. $numgrps = $data->number;
  94. $userpergrp = floor($usercnt/$numgrps);
  95. } else { // members
  96. $numgrps = ceil($usercnt/$data->number);
  97. $userpergrp = $data->number;
  98. if (!empty($data->nosmallgroups) and $usercnt % $data->number != 0) {
  99. // If there would be one group with a small number of member reduce the number of groups
  100. $missing = $userpergrp * $numgrps - $usercnt;
  101. if ($missing > $userpergrp * (1-AUTOGROUP_MIN_RATIO)) {
  102. // spread the users from the last small group
  103. $numgrps--;
  104. $userpergrp = floor($usercnt/$numgrps);
  105. }
  106. }
  107. }
  108. // allocate the users - all groups equal count first
  109. for ($i=0; $i<$numgrps; $i++) {
  110. $groups[$i] = array();
  111. $groups[$i]['name'] = groups_parse_name(trim($data->namingscheme), $i);
  112. $groups[$i]['members'] = array();
  113. if ($data->allocateby == 'no') {
  114. continue; // do not allocate users
  115. }
  116. for ($j=0; $j<$userpergrp; $j++) {
  117. if (empty($users)) {
  118. break 2;
  119. }
  120. $user = array_shift($users);
  121. $groups[$i]['members'][$user->id] = $user;
  122. }
  123. }
  124. // now distribute the rest
  125. if ($data->allocateby != 'no') {
  126. for ($i=0; $i<$numgrps; $i++) {
  127. if (empty($users)) {
  128. break 1;
  129. }
  130. $user = array_shift($users);
  131. $groups[$i]['members'][$user->id] = $user;
  132. }
  133. }
  134. if (isset($data->preview)) {
  135. $table = new html_table();
  136. if ($data->allocateby == 'no') {
  137. $table->head = array(get_string('groupscount', 'group', $numgrps));
  138. $table->size = array('100%');
  139. $table->align = array('left');
  140. $table->width = '40%';
  141. } else {
  142. $table->head = array(get_string('groupscount', 'group', $numgrps), get_string('groupmembers', 'group'), get_string('usercounttotal', 'group', $usercnt));
  143. $table->size = array('20%', '70%', '10%');
  144. $table->align = array('left', 'left', 'center');
  145. $table->width = '90%';
  146. }
  147. $table->data = array();
  148. foreach ($groups as $group) {
  149. $line = array();
  150. if (groups_get_group_by_name($courseid, $group['name'])) {
  151. $line[] = '<span class="notifyproblem">'.get_string('groupnameexists', 'group', $group['name']).'</span>';
  152. $error = get_string('groupnameexists', 'group', $group['name']);
  153. } else {
  154. $line[] = $group['name'];
  155. }
  156. if ($data->allocateby != 'no') {
  157. $unames = array();
  158. foreach ($group['members'] as $user) {
  159. $unames[] = fullname($user, true);
  160. }
  161. $line[] = implode(', ', $unames);
  162. $line[] = count($group['members']);
  163. }
  164. $table->data[] = $line;
  165. }
  166. $preview .= html_writer::table($table);
  167. } else {
  168. $grouping = null;
  169. $createdgrouping = null;
  170. $createdgroups = array();
  171. $failed = false;
  172. // prepare grouping
  173. if (!empty($data->grouping)) {
  174. if ($data->grouping < 0) {
  175. $grouping = new stdClass();
  176. $grouping->courseid = $COURSE->id;
  177. $grouping->name = trim($data->groupingname);
  178. $grouping->id = groups_create_grouping($grouping);
  179. $createdgrouping = $grouping->id;
  180. } else {
  181. $grouping = groups_get_grouping($data->grouping);
  182. }
  183. }
  184. // Save the groups data
  185. foreach ($groups as $key=>$group) {
  186. if (groups_get_group_by_name($courseid, $group['name'])) {
  187. $error = get_string('groupnameexists', 'group', $group['name']);
  188. $failed = true;
  189. break;
  190. }
  191. $newgroup = new stdClass();
  192. $newgroup->courseid = $data->courseid;
  193. $newgroup->name = $group['name'];
  194. $groupid = groups_create_group($newgroup);
  195. $createdgroups[] = $groupid;
  196. foreach($group['members'] as $user) {
  197. groups_add_member($groupid, $user->id);
  198. }
  199. if ($grouping) {
  200. // Ask this function not to invalidate the cache, we'll do that manually once at the end.
  201. groups_assign_grouping($grouping->id, $groupid, null, false);
  202. }
  203. }
  204. // Invalidate the course groups cache seeing as we've changed it.
  205. cache_helper::invalidate_by_definition('core', 'groupdata', array(), array($courseid));
  206. if ($failed) {
  207. foreach ($createdgroups as $groupid) {
  208. groups_delete_group($groupid);
  209. }
  210. if ($createdgrouping) {
  211. groups_delete_grouping($createdgrouping);
  212. }
  213. } else {
  214. redirect($returnurl);
  215. }
  216. }
  217. }
  218. $PAGE->navbar->add($strparticipants, new moodle_url('/user/index.php', array('id'=>$courseid)));
  219. $PAGE->navbar->add($strgroups, new moodle_url('/group/index.php', array('id'=>$courseid)));
  220. $PAGE->navbar->add($strautocreategroups);
  221. echo $OUTPUT->header();
  222. echo $OUTPUT->heading($strautocreategroups);
  223. if ($error != '') {
  224. echo $OUTPUT->notification($error);
  225. }
  226. /// Display the form
  227. $editform->display();
  228. if($preview !== '') {
  229. echo $OUTPUT->heading(get_string('groupspreview', 'group'));
  230. echo $preview;
  231. }
  232. echo $OUTPUT->footer();