Index: cohort/index.php =================================================================== --- cohort/index.php (revision ) +++ cohort/index.php (revision ) @@ -0,0 +1,115 @@ +. + +/** + * Cohort related management functions, this file needs to be included manually. + * + * @package moodlecore + * @subpackage cohort + * @copyright 2010 Petr Skoda (info@skodak.org) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require('../config.php'); +require_once($CFG->libdir.'/adminlib.php'); + +$contextid = optional_param('contextid', 0, PARAM_INT); + +require_login(); + +if ($contextid) { + $context = get_context_instance_by_id($contextid, MUST_EXIST); +} else { + $context = get_context_instance(CONTEXT_SYSTEM); +} + +if ($context->contextlevel != CONTEXT_COURSECAT and $context->contextlevel != CONTEXT_SYSTEM) { + print_error('invalidcontext'); +} + +$category = null; +if ($context->contextlevel == CONTEXT_COURSECAT) { + $category = $DB->get_record('course_categories', array('id'=>$context->instanceid), '*', MUST_EXIST); +} + +$manager = has_capability('moodle/cohort:manage', $context); +if (!$manager) { + require_capability('moodle/cohort:view', $context); +} + +$strcohorts = get_string('cohorts', 'cohort'); + +if ($category) { + $PAGE->set_url('/cohort/index.php', array('contextid'=>$context->id)); + $PAGE->set_title($strcohorts); + $PAGE->set_context($context); + $PAGE->navbar->add($category->name, new moodle_url('/course/index.php', array('categoryedit'=>'1'))); + $PAGE->navbar->add($strcohorts); +} else { + admin_externalpage_setup('cohorts'); +} + +echo $OUTPUT->header(); + +echo $OUTPUT->heading(get_string('cohortsin', 'cohort', print_context_name($context))); + +$cohorts = $DB->get_records('cohort', array('contextid'=>$context->id)); + +$data = array(); +foreach($cohorts as $cohort) { + $line = array(); + $line[] = format_string($cohort->name); + $line[] = $cohort->idnumber; + $line[] = format_text($cohort->description, $cohort->descriptionformat); + + $line[] = $DB->count_records('cohort_members', array('cohortid'=>$cohort->id)); + + if (empty($cohort->component)) { + $line[] = get_string('nocomponent', 'cohort'); + } else { + $line[] = get_string('pluginname', $cohort->component); + } + + if ($manager) { + if (empty($cohort->component)) { + $buttons = html_writer::link(new moodle_url('/cohort/edit.php', array('id'=>$cohort->id)), get_string('edit')); + $buttons .= ' '.html_writer::link(new moodle_url('/cohort/edit.php', array('id'=>$cohort->id, 'delete'=>1)), get_string('delete')); + $buttons .= ' '.html_writer::link(new moodle_url('/cohort/assign.php', array('id'=>$cohort->id)), get_string('assign', 'cohort')); + } else { + $buttons = ''; + } + } else { + $buttons = ''; + } + $line[] = $buttons; + + $data[] = $line; +} +$table = new html_table(); +$table->head = array(get_string('name', 'cohort'), get_string('idnumber', 'cohort'), get_string('description', 'cohort'), + get_string('memberscount', 'cohort'), get_string('component', 'cohort'), get_string('edit')); +$table->size = array('20%', '10%', '40%', '10%', '10%', '10%'); +$table->align = array('left', 'left', 'left', 'left','center', 'center'); +$table->width = '80%'; +$table->data = $data; +echo html_writer::table($table); + +if ($manager) { + echo $OUTPUT->single_button(new moodle_url('/cohort/edit.php', array('contextid'=>$context->id)), get_string('add')); +} + +echo $OUTPUT->footer(); \ No newline at end of file Index: cohort/edit.php =================================================================== --- cohort/edit.php (revision ) +++ cohort/edit.php (revision ) @@ -0,0 +1,129 @@ +. + + +/** + * Cohort related management functions, this file needs to be included manually. + * + * @package moodlecore + * @subpackage cohort + * @copyright 2010 Petr Skoda (info@skodak.org) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require('../config.php'); +require($CFG->dirroot.'/cohort/lib.php'); +require($CFG->dirroot.'/cohort/edit_form.php'); + +$id = optional_param('id', 0, PARAM_INT); +$contextid = optional_param('contextid', 0, PARAM_INT); +$delete = optional_param('delete', 0, PARAM_BOOL); +$confirm = optional_param('confirm', 0, PARAM_BOOL); + +require_login(); + +$category = null; +if ($id) { + $cohort = $DB->get_record('cohort', array('id'=>$id), '*', MUST_EXIST); + $context = get_context_instance_by_id($cohort->contextid, MUST_EXIST); +} else { + $context = get_context_instance_by_id($contextid, MUST_EXIST); + if ($context->contextlevel != CONTEXT_COURSECAT and $context->contextlevel != CONTEXT_SYSTEM) { + print_error('invalidcontext'); + } + $cohort = new object(); + $cohort->id = 0; + $cohort->contextid = $context->id; + $cohort->name = ''; + $cohort->description = ''; +} + +require_capability('moodle/cohort:manage', $context); + +$returnurl = new moodle_url('/cohort/index.php', array('contextid'=>$context->id)); + +if (!empty($cohort->component)) { + // we can not manually edit cohorts that were created by external systems, sorry + redirect($returnurl); +} + +$PAGE->set_context($context); +$PAGE->set_url('/cohort/edit.php', array('contextid'=>$context->id, 'id'=>$cohort->id)); +$PAGE->set_context($context); + +if ($context->contextlevel == CONTEXT_COURSECAT) { + $category = $DB->get_record('course_categories', array('id'=>$context->instanceid), '*', MUST_EXIST); + $PAGE->navbar->add($category->name, new moodle_url('/course/index.php', array('categoryedit'=>'1'))); +} +$PAGE->navbar->add(get_string('cohorts', 'cohort'), new moodle_url('/cohort/', array('contextid'=>$context->id))); + +if ($delete and $cohort->id) { + $PAGE->url->param('delete', 1); + if ($confirm and confirm_sesskey()) { + cohort_delete_cohort($cohort); + redirect($returnurl); + } + $strheading = get_string('delcohort', 'cohort'); + $PAGE->navbar->add($strheading); + $PAGE->set_title($strheading); + echo $OUTPUT->header(); + echo $OUTPUT->heading($strheading); + $yesurl = new moodle_url('/cohort/edit.php', array('id'=>$cohort->id, 'delete'=>1, 'confirm'=>1,'sesskey'=>sesskey())); + $message = get_string('delconfirm', 'cohort', format_string($cohort->name)); + echo $OUTPUT->confirm($message, $yesurl, $returnurl); + echo $OUTPUT->footer(); + die; +} + +$editoroptions = array('maxfiles'=>0, 'context'=>$context); +if ($cohort->id) { + // edit existing + $cohort = file_prepare_standard_editor($cohort, 'description', $editoroptions); + $strheading = get_string('editcohort', 'cohort'); + +} else { + // add new + $cohort = file_prepare_standard_editor($cohort, 'description', $editoroptions); + $strheading = get_string('addcohort', 'cohort'); +} + +$PAGE->set_title($strheading); +$PAGE->navbar->add($strheading); + +$editform = new cohort_edit_form(null, array('editoroptions'=>$editoroptions, 'data'=>$cohort)); + +if ($editform->is_cancelled()) { + redirect($returnurl); + +} else if ($data = $editform->get_data()) { + $data = file_postupdate_standard_editor($data, 'description', $editoroptions, $context); + + if ($data->id) { + cohort_update_cohort($data); + } else { + cohort_add_cohort($data); + } + + // use new context id, it could have been changed + redirect(new moodle_url('/cohort/index.php', array('contextid'=>$data->contextid))); +} + +echo $OUTPUT->header(); +echo $OUTPUT->heading($strheading); +echo $editform->display(); +echo $OUTPUT->footer(); + Index: lib/db/upgrade.php =================================================================== --- lib/db/upgrade.php (revision fd1d5455fde49baa64a37126f25f3d3fd6b6f3f2) +++ lib/db/upgrade.php (revision ) @@ -3517,6 +3517,61 @@ upgrade_main_savepoint($result, 2010041301); } + if ($result && $oldversion < 2010041800) { + // Define table cohort to be created + $table = new xmldb_table('cohort'); + + // Adding fields to table cohort + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('contextid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null); + $table->add_field('name', XMLDB_TYPE_CHAR, '254', null, XMLDB_NOTNULL, null, null); + $table->add_field('idnumber', XMLDB_TYPE_CHAR, '100', null, null, null, null); + $table->add_field('description', XMLDB_TYPE_TEXT, 'small', null, null, null, null); + $table->add_field('descriptionformat', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null); + $table->add_field('component', XMLDB_TYPE_CHAR, '100', null, null, null, null); + $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null); + $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null); + + // Adding keys to table cohort + $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); + $table->add_key('context', XMLDB_KEY_FOREIGN, array('contextid'), 'context', array('id')); + + // Conditionally launch create table for cohort + if (!$dbman->table_exists($table)) { + $dbman->create_table($table); + } + + upgrade_main_savepoint($result, 2010041800); + } + + if ($result && $oldversion < 2010041801) { + // Define table cohort_members to be created + $table = new xmldb_table('cohort_members'); + + // Adding fields to table cohort_members + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('cohortid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0'); + $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0'); + $table->add_field('timeadded', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0'); + + // Adding keys to table cohort_members + $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); + $table->add_key('cohortid', XMLDB_KEY_FOREIGN, array('cohortid'), 'cohort', array('id')); + $table->add_key('userid', XMLDB_KEY_FOREIGN, array('userid'), 'user', array('id')); + + // Adding indexes to table cohort_members + $table->add_index('cohortid-userid', XMLDB_INDEX_UNIQUE, array('cohortid', 'userid')); + + // Conditionally launch create table for cohort_members + if (!$dbman->table_exists($table)) { + $dbman->create_table($table); + } + + // Main savepoint reached + upgrade_main_savepoint($result, 2010041801); + } + + return $result; } @@ -3524,4 +3579,4 @@ // 1/ remove the automatic enabling of completion lib if debug enabled ( in 2008121701 block) // 2/ move 2009061300 block to the top of the file so that we may log upgrade queries // 3/ remove 2010033101 block -// 4/ remove 2010032400 block \ No newline at end of file +// 4/ remove 2010032400 block Index: lib/moodlelib.php =================================================================== --- lib/moodlelib.php (revision fd1d5455fde49baa64a37126f25f3d3fd6b6f3f2) +++ lib/moodlelib.php (revision ) @@ -3422,6 +3422,9 @@ //move unread messages from this user to read message_move_userfrom_unread2read($user->id); + // remove from all cohorts + $DB->delete_records('cohort_members', array('userid'=>$user->id)); + // remove from all groups $DB->delete_records('groups_members', array('userid'=>$user->id)); @@ -6837,6 +6840,7 @@ 'blog' => 'blog', 'bulkusers' => NULL, 'calendar' => 'calendar', + 'cohort' => 'cohort', 'condition' => NULL, 'completion' => NULL, 'countries' => NULL, Index: lang/en/cohort.php =================================================================== --- lang/en/cohort.php (revision ) +++ lang/en/cohort.php (revision ) @@ -0,0 +1,52 @@ +. + +/** + * Strings for component 'cohort', language 'en', branch 'MOODLE_20_STABLE' + * + * @package moodlecore + * @subpackage cohort + * @copyright 2010 Petr Skoda (info@skodak.org) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +$string['addcohort'] = 'Add new cohort'; +$string['anycohort'] = 'Any'; +$string['assign'] = 'Assign'; +$string['assignto'] = 'Cohort \'{$a}\' members'; +$string['backtocohorts'] = 'Back to cohorts'; +$string['cohort'] = 'Cohort'; +$string['cohorts'] = 'Cohorts'; +$string['cohortsin'] = '{$a}: available cohorts'; +$string['cohort:assign'] = 'Assign cohort members'; +$string['cohort:manage'] = 'Manager cohorts'; +$string['cohort:view'] = 'Use cohorts and view members'; +$string['component'] = 'Source'; +$string['currentusers'] = 'Current users'; +$string['currentusersmatching'] = 'Current users matching'; +$string['delcohort'] = 'Delete cohort'; +$string['delconfirm'] = 'Do you really want to delete cohort \'{$a}\'?'; +$string['description'] = 'Description'; +$string['duplicateidnumber'] = 'Cohort with the same ID number already exists'; +$string['editcohort'] = 'Edit cohort'; +$string['idnumber'] = 'Cohort ID'; +$string['memberscount'] = 'Cohort size'; +$string['name'] = 'Name'; +$string['nocomponent'] = 'Created manually'; +$string['potusers'] = 'Potential users'; +$string['potusersmatching'] = 'Potential matching users'; +$string['selectfromcohort'] = 'Select members from cohort'; Index: group/lib.php =================================================================== --- group/lib.php (revision fd1d5455fde49baa64a37126f25f3d3fd6b6f3f2) +++ group/lib.php (revision ) @@ -513,10 +513,11 @@ * Gets potential group members for grouping * @param int $courseid The id of the course * @param int $roleid The role to select users from + * @param int $cohortid restrict to cohort id * @param string $orderby The colum to sort users by * @return array An array of the users */ -function groups_get_potential_members($courseid, $roleid = null, $orderby = 'lastname ASC, firstname ASC') { +function groups_get_potential_members($courseid, $roleid = null, $cohortid = null, $orderby = 'lastname ASC, firstname ASC') { global $DB; $context = get_context_instance(CONTEXT_COURSE, $courseid); @@ -535,9 +536,17 @@ $where = ""; } + if ($cohortid) { + $cohortjoin = "JOIN {cohort_members} cm ON cm.userid = u.id + JOIN {cohort} c ON c.id = cm.cohortid"; + } else { + $cohortjoin = ""; + } + $sql = "SELECT u.id, u.username, u.firstname, u.lastname, u.idnumber FROM {user} u JOIN ($esql) e ON e.id = u.id + $cohortjoin $where ORDER BY $orderby"; Index: admin/settings/users.php =================================================================== --- admin/settings/users.php (revision fd1d5455fde49baa64a37126f25f3d3fd6b6f3f2) +++ admin/settings/users.php (revision ) @@ -12,7 +12,9 @@ or has_capability('moodle/user:update', $systemcontext) or has_capability('moodle/user:delete', $systemcontext) or has_capability('moodle/role:manage', $systemcontext) - or has_capability('moodle/role:assign', $systemcontext)) { // speedup for non-admins, add all caps used on this page + or has_capability('moodle/role:assign', $systemcontext) + or has_capability('moodle/cohort:manage', $systemcontext) + or has_capability('moodle/cohort:view', $systemcontext)) { // speedup for non-admins, add all caps used on this page $temp = new admin_settingpage('manageauths', get_string('authsettings', 'admin')); @@ -75,6 +77,7 @@ $ADMIN->add('accounts', new admin_externalpage('uploadusers', get_string('uploadusers'), "$CFG->wwwroot/$CFG->admin/uploaduser.php", 'moodle/site:uploadusers')); $ADMIN->add('accounts', new admin_externalpage('uploadpictures', get_string('uploadpictures','admin'), "$CFG->wwwroot/$CFG->admin/uploadpicture.php", 'moodle/site:uploadusers')); $ADMIN->add('accounts', new admin_externalpage('profilefields', get_string('profilefields','admin'), "$CFG->wwwroot/user/profile/index.php", 'moodle/site:config')); + $ADMIN->add('accounts', new admin_externalpage('cohorts', get_string('cohorts', 'cohort'), $CFG->wwwroot . '/cohort/index.php', array('moodle/cohort:manage', 'moodle/cohort:view'))); // stuff under the "roles" subcategory Index: version.php =================================================================== --- version.php (revision fd1d5455fde49baa64a37126f25f3d3fd6b6f3f2) +++ version.php (revision ) @@ -6,7 +6,7 @@ // This is compared against the values stored in the database to determine // whether upgrades should be performed (see lib/db/*.php) - $version = 2010041301; // YYYYMMDD = date of the last version bump + $version = 2010041801; // YYYYMMDD = date of the last version bump // XX = daily increments $release = '2.0 dev (Build: 20100421)'; // Human-friendly version name Index: course/index.php =================================================================== --- course/index.php (revision fd1d5455fde49baa64a37126f25f3d3fd6b6f3f2) +++ course/index.php (revision ) @@ -295,6 +295,7 @@ $str->edit = get_string('editthiscategory'); $str->hide = get_string('hide'); $str->show = get_string('show'); + $str->cohorts = get_string('cohorts', 'cohort'); $str->spacer = ' '; } @@ -333,6 +334,11 @@ ' src="'.$OUTPUT->pix_url('t/show') . '" class="iconsmall" alt="'.$str->show.'" /> '; } + if (has_capability('moodle/cohort:manage', $category->context) or has_capability('moodle/cohort:view', $category->context)) { + echo ' '; + } + if ($up) { echo ' '; Index: cohort/lib.php =================================================================== --- cohort/lib.php (revision ) +++ cohort/lib.php (revision ) @@ -0,0 +1,278 @@ +. + +/** + * Cohort related management functions, this file needs to be included manually. + * + * @package moodlecore + * @subpackage cohort + * @copyright 2010 Petr Skoda (info@skodak.org) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once($CFG->dirroot . '/user/selector/lib.php'); + +/** + * Add new cohort. + * @param object $data + * @return void + */ +function cohort_add_cohort($data) { + global $DB; + $data->timecreated = time(); + $data->timemodified = $data->timecreated; + $DB->insert_record('cohort', $data); +} + +/** + * Update existing cohort. + * @param object $data + * @return void + */ +function cohort_update_cohort($data) { + global $DB; + $data->timemodified = time(); + $DB->update_record('cohort', $data); +} + +/** + * Delete cohort. + * @param object $cohort + * @return void + */ +function cohort_delete_cohort($cohort) { + global $DB; + + if ($cohort->component) { + // TODO: add component delete callback + } + + $DB->delete_records('cohort_members', array('cohortid'=>$cohort->id)); + $DB->delete_records('cohort', array('id'=>$cohort->id)); +} + +/** + * Somehow deal with cohorts when deleting course category, + * we can not just delete them because they might be used in enrol + * plugins or referenced in external systems. + * @param object $category + * @return void + */ +function cohort_delete_category($category) { + global $DB; + // TODO: make sure that cohorts are really, really not used anywhere and delete, for now just move to parent or system context + + $oldcontext = get_context_instance(CONTEXT_COURSECAT, $category->id, MUST_EXIST); + + if ($category->parent and $parent = $DB->get_record('course_categories', array('id'=>$category->parent))) { + $parentcontext = get_context_instance(CONTEXT_COURSECAT, $parent->id, MUST_EXIST); + $sql = "UPDATE {cohort} SET contextid = :newcontext WHERE contextid = :oldcontext"; + $params = array('oldcontext'=>$oldcontext->id, 'newcontext'=>$parentcontext->id); + } else { + $syscontext = get_context_instance(CONTEXT_SYSTEM); + $sql = "UPDATE {cohort} SET contextid = :newcontext WHERE contextid = :oldcontext"; + $params = array('oldcontext'=>$oldcontext->id, 'newcontext'=>$syscontext->id); + } + + $DB->execute($sql, $params); +} + +/** + * Remove cohort member + * @param int $cohortid + * @param int $userid + * @return void + */ +function cohort_add_member($cohortid, $userid) { + global $DB; + $record = new object(); + $record->cohortid = $cohortid; + $record->userid = $userid; + $record->timeadded = time(); + $DB->insert_record('cohort_members', $record); +} + +/** + * Add cohort member + * @param int $cohortid + * @param int $userid + * @return void + */ +function cohort_remove_member($cohortid, $userid) { + global $DB; + $DB->delete_records('cohort_members', array('cohortid'=>$cohortid, 'userid'=>$userid)); +} + +/** + * Returns list of visible cohorts in course. + * + * @param object $course + * @param bool $enrolled true means include only cohorts with enrolled users + * @return array + */ +function cohort_get_visible_list($course) { + global $DB, $USER; + + $context = get_context_instance(CONTEXT_COURSE, $course->id, MUST_EXIST); + list($esql, $params) = get_enrolled_sql($context); + $parentsql = get_related_contexts_string($context); + + $sql = "SELECT c.id, c.name, c.idnumber, COUNT(u.id) AS cnt + FROM {cohort} c + JOIN {cohort_members} cm ON cm.cohortid = c.id + JOIN ($esql) u ON u.id = cm.userid + WHERE c.contextid $parentsql + GROUP BY c.id, c.name, c.idnumber + HAVING COUNT(u.id) > 0 + ORDER BY c.name, c.idnumber"; + $params['ctx'] = $context->id; + + $cohorts = $DB->get_records_sql($sql, $params); + + foreach ($cohorts as $cid=>$cohort) { + $cohorts[$cid] = format_string($cohort->name); + if ($cohort->idnumber) { + $cohorts[$cid] .= ' (' . $cohort->cnt . ')'; + } + } + + return $cohorts; +} + +/** + * Cohort assignment candidates + */ +class cohort_candidate_selector extends user_selector_base { + protected $cohortid; + + public function __construct($name, $options) { + $this->cohortid = $options['cohortid']; + parent::__construct($name, $options); + } + + /** + * Candidate users + * @param $search + * @return array + */ + public function find_users($search) { + global $DB; + //by default wherecondition retrieves all users except the deleted, not confirmed and guest + list($wherecondition, $params) = $this->search_sql($search, 'u'); + $params['cohortid'] = $this->cohortid; + + $fields = 'SELECT ' . $this->required_fields_sql('u'); + $countfields = 'SELECT COUNT(1)'; + + $sql = " FROM {user} u + LEFT JOIN {cohort_members} cm ON (cm.userid = u.id AND cm.cohortid = :cohortid) + WHERE cm.id IS NULL AND $wherecondition"; + + $order = ' ORDER BY u.lastname ASC, u.firstname ASC'; + + if (!$this->is_validating()) { + $potentialmemberscount = $DB->count_records_sql($countfields . $sql, $params); + if ($potentialmemberscount > 100) { + return $this->too_many_results($search, $potentialmemberscount); + } + } + + $availableusers = $DB->get_records_sql($fields . $sql . $order, $params); + + if (empty($availableusers)) { + return array(); + } + + + if ($search) { + $groupname = get_string('potusersmatching', 'cohort', $search); + } else { + $groupname = get_string('potusers', 'cohort'); + } + + return array($groupname => $availableusers); + } + + protected function get_options() { + $options = parent::get_options(); + $options['cohortid'] = $this->cohortid; + $options['file'] = 'cohort/lib.php'; + return $options; + } +} + +/** + * Cohort assignment candidates + */ +class cohort_existing_selector extends user_selector_base { + protected $cohortid; + + public function __construct($name, $options) { + $this->cohortid = $options['cohortid']; + parent::__construct($name, $options); + } + + /** + * Candidate users + * @param $search + * @return array + */ + public function find_users($search) { + global $DB; + //by default wherecondition retrieves all users except the deleted, not confirmed and guest + list($wherecondition, $params) = $this->search_sql($search, 'u'); + $params['cohortid'] = $this->cohortid; + + $fields = 'SELECT ' . $this->required_fields_sql('u'); + $countfields = 'SELECT COUNT(1)'; + + $sql = " FROM {user} u + JOIN {cohort_members} cm ON (cm.userid = u.id AND cm.cohortid = :cohortid) + WHERE $wherecondition"; + + $order = ' ORDER BY u.lastname ASC, u.firstname ASC'; + + if (!$this->is_validating()) { + $potentialmemberscount = $DB->count_records_sql($countfields . $sql, $params); + if ($potentialmemberscount > 100) { + return $this->too_many_results($search, $potentialmemberscount); + } + } + + $availableusers = $DB->get_records_sql($fields . $sql . $order, $params); + + if (empty($availableusers)) { + return array(); + } + + + if ($search) { + $groupname = get_string('currentusersmatching', 'cohort', $search); + } else { + $groupname = get_string('currentusers', 'cohort'); + } + + return array($groupname => $availableusers); + } + + protected function get_options() { + $options = parent::get_options(); + $options['cohortid'] = $this->cohortid; + $options['file'] = 'cohort/lib.php'; + return $options; + } +} Index: course/lib.php =================================================================== --- course/lib.php (revision fd1d5455fde49baa64a37126f25f3d3fd6b6f3f2) +++ course/lib.php (revision ) @@ -3025,6 +3025,7 @@ global $CFG, $DB; require_once($CFG->libdir.'/gradelib.php'); require_once($CFG->libdir.'/questionlib.php'); + require_once($CFG->dirroot.'/cohort/lib.php'); if ($children = $DB->get_records('course_categories', array('parent'=>$category->id), 'sortorder ASC')) { foreach ($children as $childcat) { @@ -3042,6 +3043,9 @@ } } + // move or delete cohorts in this context + cohort_delete_category($category); + // now delete anything that may depend on course category context grade_course_category_delete($category->id, 0, $showfeedback); if (!question_delete_course_category($category, 0, $showfeedback)) { @@ -3067,6 +3071,7 @@ global $CFG, $DB, $OUTPUT; require_once($CFG->libdir.'/gradelib.php'); require_once($CFG->libdir.'/questionlib.php'); + require_once($CFG->dirroot.'/cohort/lib.php'); if (!$newparentcat = $DB->get_record('course_categories', array('id'=>$newparentid))) { return false; @@ -3086,6 +3091,9 @@ echo $OUTPUT->notification(get_string('coursesmovedout', '', format_string($category->name)), 'notifysuccess'); } + // move or delete cohorts in this context + cohort_delete_category($category); + // now delete anything that may depend on course category context grade_course_category_delete($category->id, $newparentid, $showfeedback); if (!question_delete_course_category($category, $newparentcat, $showfeedback)) { Index: cohort/assign.php =================================================================== --- cohort/assign.php (revision ) +++ cohort/assign.php (revision ) @@ -0,0 +1,130 @@ +. + +/** + * Cohort related management functions, this file needs to be included manually. + * + * @package moodlecore + * @subpackage cohort + * @copyright 2010 Petr Skoda (info@skodak.org) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once('../config.php'); +require_once($CFG->dirroot.'/cohort/lib.php'); + +$id = required_param('id', PARAM_INT); + +require_login(); + +$cohort = $DB->get_record('cohort', array('id'=>$id), '*', MUST_EXIST); +$context = get_context_instance_by_id($cohort->contextid, MUST_EXIST); + +require_capability('moodle/cohort:assign', $context); + +$PAGE->set_url('/cohort/assign.php', array('id'=>$id)); +$PAGE->set_context($context); + +$returnurl = new moodle_url('/cohort/index.php', array('contextid'=>$cohort->contextid)); + +if (!empty($cohort->component)) { + // we can not manually edit cohorts that were created by external systems, sorry + redirect($returnurl); +} + +if (optional_param('cancel', false, PARAM_BOOL)) { + redirect($returnurl); +} + +if ($context->contextlevel == CONTEXT_COURSECAT) { + $category = $DB->get_record('course_categories', array('id'=>$context->instanceid), '*', MUST_EXIST); + $PAGE->navbar->add($category->name, new moodle_url('/course/index.php', array('categoryedit'=>'1'))); +} +$PAGE->navbar->add(get_string('cohorts', 'cohort'), new moodle_url('/cohort/', array('contextid'=>$context->id))); +$PAGE->navbar->add(get_string('assign', 'cohort')); + +echo $OUTPUT->header(); +echo $OUTPUT->heading(get_string('assignto', 'cohort', format_string($cohort->name))); + +// Get the user_selector we will need. +$potentialuserselector = new cohort_candidate_selector('addselect', array('cohortid'=>$cohort->id)); +$existinguserselector = new cohort_existing_selector('removeselect', array('cohortid'=>$cohort->id)); + +// Process incoming user assignments to the cohort + +if (optional_param('add', false, PARAM_BOOL) && confirm_sesskey()) { + $userstoassign = $potentialuserselector->get_selected_users(); + if (!empty($userstoassign)) { + + foreach ($userstoassign as $adduser) { + // no duplicates please + if (!$DB->record_exists('cohort_members', array('cohortid'=>$cohort->id, 'userid'=>$adduser->id))) { + cohort_add_member($cohort->id, $adduser->id); + } + } + + $potentialuserselector->invalidate_selected_users(); + $existinguserselector->invalidate_selected_users(); + } +} + +// Process removing user assignments to the cohort +if (optional_param('remove', false, PARAM_BOOL) && confirm_sesskey()) { + $userstoremove = $existinguserselector->get_selected_users(); + if (!empty($userstoremove)) { + foreach ($userstoremove as $removeuser) { + cohort_remove_member($cohort->id, $removeuser->id); + } + $potentialuserselector->invalidate_selected_users(); + $existinguserselector->invalidate_selected_users(); + } +} + +// Print the form. +?> +
+ + + + + + + + + +
+

+ display() ?> +
+
+
+
+ +
+ +
+
+

+ display() ?> +
+ +
+
+ +footer(); Index: cohort/edit_form.php =================================================================== --- cohort/edit_form.php (revision ) +++ cohort/edit_form.php (revision ) @@ -0,0 +1,104 @@ +. + +/** + * Cohort related management functions, this file needs to be included manually. + * + * @package moodlecore + * @subpackage cohort + * @copyright 2010 Petr Skoda (info@skodak.org) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once($CFG->dirroot . '/lib/formslib.php'); + +class cohort_edit_form extends moodleform { + + /** + * Define the cohort edit form + */ + public function definition() { + + $mform = $this->_form; + $editoroptions = $this->_customdata['editoroptions']; + $cohort = $this->_customdata['data']; + + $mform->addElement('text', 'name', get_string('name', 'cohort'), 'maxlength="254" size="50"'); + $mform->addRule('name', get_string('required'), 'required', null, 'client'); + $mform->setType('name', PARAM_MULTILANG); + + $options = $this->get_category_options($cohort->contextid); + $mform->addElement('select', 'contextid', get_string('context', 'role'), $options); + + $mform->addElement('text', 'idnumber', get_string('idnumber', 'cohort'), 'maxlength="254" size="50"'); + $mform->setType('name', PARAM_RAW); + + $mform->addElement('editor', 'description_editor', get_string('description', 'cohort'), null, $editoroptions); + $mform->setType('description_editor', PARAM_RAW); + + $mform->addElement('hidden', 'id'); + $mform->setType('id', PARAM_INT); + + $this->add_action_buttons(); + + $this->set_data($cohort); + } + + public function validation($data, $files) { + global $DB; + + $errors = parent::validation($data, $files); + + $idnumber = trim($data['idnumber']); + if ($data['id']) { + $current = $DB->get_record('cohort', array('id'=>$data['id']), '*', MUST_EXIST); + if ($current->idnumber !== $idnumber) { + if ($DB->record_exists('cohort', array('idnumber'=>$idnumber))) { + $errors['idnumber'] = get_string('duplicateidnumber', 'cohort'); + } + } + } else { + if ($DB->record_exists('cohort', array('idnumber'=>$idnumber))) { + $errors['idnumber'] = get_string('duplicateidnumber', 'cohort'); + } + } + + return $errors; + } + + protected function get_category_options($currentcontextid) { + $displaylist = array(); + $parentlist = array(); + make_categories_list($displaylist, $parentlist, 'moodle/cohort:manage'); + $options = array(); + $syscontext = get_context_instance(CONTEXT_SYSTEM); + if (has_capability('moodle/cohort:manage', $syscontext)) { + $options[$syscontext->id] = print_context_name($syscontext); + } + foreach ($displaylist as $cid=>$name) { + $context = get_context_instance(CONTEXT_COURSECAT, $cid, MUST_EXIST); + $options[$context->id] = $name; + } + // always add current - this is not likely, but if the logic get's changed it might be a problem + if (!isset($options[$currentcontextid])) { + $context = get_context_instance_by_id($currentcontextid, MUST_EXIST); + $options[$context->id] = print_context_name($syscontext); + } + return $options; + } +} + \ No newline at end of file Index: group/autogroup.php =================================================================== --- group/autogroup.php (revision fd1d5455fde49baa64a37126f25f3d3fd6b6f3f2) +++ group/autogroup.php (revision ) @@ -71,7 +71,7 @@ default: print_error('unknoworder'); } - $users = groups_get_potential_members($data->courseid, $data->roleid, $orderby); + $users = groups_get_potential_members($data->courseid, $data->roleid, $data->cohortid, $orderby); $usercnt = count($users); if ($data->allocateby == 'random') { Index: group/autogroup_form.php =================================================================== --- group/autogroup_form.php (revision fd1d5455fde49baa64a37126f25f3d3fd6b6f3f2) +++ group/autogroup_form.php (revision ) @@ -1,6 +1,7 @@ dirroot.'/lib/formslib.php'); +require_once($CFG->dirroot.'/cohort/lib.php'); /// get url variables class autogroup_form extends moodleform { @@ -22,6 +23,24 @@ $mform->setDefault('roleid', $CFG->defaultcourseroleid); } + $context = get_context_instance(CONTEXT_COURSE, $COURSE->id); + if (has_capability('moodle/cohort:view', $context)) { + $options = cohort_get_visible_list($COURSE); + if ($options) { + $options = array(0=>get_string('anycohort', 'cohort')) + $options; + $mform->addElement('select', 'cohortid', get_string('selectfromcohort', 'cohort'), $options); + $mform->setDefault('cohortid', '0'); + } else { + $mform->addElement('hidden','cohortid'); + $mform->setType('cohortid', PARAM_INT); + $mform->setConstant('cohortid', '0'); + } + } else { + $mform->addElement('hidden','cohortid'); + $mform->setType('cohortid', PARAM_INT); + $mform->setConstant('cohortid', '0'); + } + $options = array('groups' => get_string('numgroups', 'group'), 'members' => get_string('nummembers', 'group')); $mform->addElement('select', 'groupby', get_string('groupby', 'group'), $options); @@ -91,7 +110,7 @@ $errors = parent::validation($data, $files); if ($data['allocateby'] != 'no') { - if (!$users = groups_get_potential_members($data['courseid'], $data['roleid'])) { + if (!$users = groups_get_potential_members($data['courseid'], $data['roleid'], $data['cohortid'])) { $errors['roleid'] = get_string('nousersinrole', 'group'); } Index: lib/db/access.php =================================================================== --- lib/db/access.php (revision fd1d5455fde49baa64a37126f25f3d3fd6b6f3f2) +++ lib/db/access.php (revision ) @@ -470,6 +470,39 @@ 'clonepermissionsfrom' => 'moodle/category:visibility' ), + // create, delete, move cohorts in system and course categories, + // (cohorts with component !== null can be only moved) + 'moodle/cohort:manage' => array( + + 'captype' => 'write', + 'contextlevel' => CONTEXT_COURSECAT, + 'legacy' => array( + 'manager' => CAP_ALLOW + ) + ), + + // add and remove cohort members (only for cohorts where component !== null) + 'moodle/cohort:assign' => array( + + 'captype' => 'write', + 'contextlevel' => CONTEXT_COURSECAT, + 'legacy' => array( + 'manager' => CAP_ALLOW + ) + ), + + // view members of a cohort, this can be used in course context too, + // this also controls the ability to actually use cohort + 'moodle/cohort:view' => array( + + 'captype' => 'read', + 'contextlevel' => CONTEXT_COURSE, + 'legacy' => array( + 'editingteacher' => CAP_ALLOW, + 'manager' => CAP_ALLOW + ) + ), + 'moodle/course:create' => array( 'riskbitmask' => RISK_XSS, Index: lib/db/install.xml =================================================================== --- lib/db/install.xml (revision fd1d5455fde49baa64a37126f25f3d3fd6b6f3f2) +++ lib/db/install.xml (revision ) @@ -1,5 +1,5 @@ - @@ -1879,7 +1879,7 @@ - +
@@ -1892,8 +1892,41 @@
- +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ + @@ -2424,4 +2457,4 @@
-
\ No newline at end of file +