Merge branch 'MDL-59369-master' of git://github.com/junpataleta/moodle

This commit is contained in:
Andrew Nicols
2017-07-26 09:47:26 +08:00
29 changed files with 1247 additions and 61 deletions
+3 -1
View File
@@ -236,7 +236,9 @@ class enrol_cohort_plugin extends enrol_plugin {
$params['ue'] = $ue->id;
if ($this->allow_unenrol_user($instance, $ue) && has_capability('enrol/cohort:unenrol', $context)) {
$url = new moodle_url('/enrol/unenroluser.php', $params);
$actions[] = new user_enrolment_action(new pix_icon('t/delete', ''), get_string('unenrol', 'enrol'), $url, array('class'=>'unenrollink', 'rel'=>$ue->id));
$actionparams = array('class' => 'unenrollink', 'rel' => $ue->id, 'data-action' => ENROL_ACTION_UNENROL);
$actions[] = new user_enrolment_action(new pix_icon('t/delete', ''), get_string('unenrol', 'enrol'), $url,
$actionparams);
}
return $actions;
}
+3 -1
View File
@@ -97,7 +97,9 @@ class enrol_database_plugin extends enrol_plugin {
$params['ue'] = $ue->id;
if ($this->allow_unenrol_user($instance, $ue) && has_capability('enrol/database:unenrol', $context)) {
$url = new moodle_url('/enrol/unenroluser.php', $params);
$actions[] = new user_enrolment_action(new pix_icon('t/delete', ''), get_string('unenrol', 'enrol'), $url, array('class'=>'unenrollink', 'rel'=>$ue->id));
$actionparams = array('class' => 'unenrollink', 'rel' => $ue->id, 'data-action' => ENROL_ACTION_UNENROL);
$actions[] = new user_enrolment_action(new pix_icon('t/delete', ''), get_string('unenrol', 'enrol'), $url,
$actionparams);
}
return $actions;
}
+1 -1
View File
@@ -41,7 +41,7 @@ $instance = $DB->get_record('enrol', array('id'=>$ue->enrolid), '*', MUST_EXIST)
$course = $DB->get_record('course', array('id'=>$instance->courseid), '*', MUST_EXIST);
// The URL of the enrolled users page for the course.
$usersurl = new moodle_url('/enrol/users.php', array('id' => $course->id));
$usersurl = new moodle_url('/user/index.php', array('id' => $course->id));
// Do not allow any changes if plugin disabled, not available or not suitable.
if (!$plugin = enrol_get_plugin($instance->enrol)) {
+7 -10
View File
@@ -28,15 +28,10 @@ require_once("$CFG->libdir/formslib.php");
class enrol_user_enrolment_form extends moodleform {
function definition() {
global $CFG, $DB;
$mform = $this->_form;
$user = $this->_customdata['user'];
$course = $this->_customdata['course'];
$ue = $this->_customdata['ue'];
$mform->addElement('header','general', '');
$ue = $this->_customdata['ue'];
$modal = !empty($this->_customdata['modal']);
$options = array(ENROL_USER_ACTIVE => get_string('participationactive', 'enrol'),
ENROL_USER_SUSPENDED => get_string('participationsuspended', 'enrol'));
@@ -56,7 +51,10 @@ class enrol_user_enrolment_form extends moodleform {
$mform->addElement('hidden', 'ifilter');
$mform->setType('ifilter', PARAM_ALPHA);
$this->add_action_buttons();
// Show action buttons if this is not being rendered as a fragment.
if (!$modal) {
$this->add_action_buttons();
}
$this->set_data(array(
'ue' => $ue->id,
@@ -71,8 +69,7 @@ class enrol_user_enrolment_form extends moodleform {
if (!empty($data['timestart']) and !empty($data['timeend'])) {
if ($data['timestart'] >= $data['timeend']) {
$errors['timestart'] = get_string('error');
$errors['timeend'] = get_string('error');
$errors['timeend'] = get_string('enroltimeendinvalid', 'enrol');
}
}
+101
View File
@@ -805,6 +805,107 @@ class core_enrol_external extends external_api {
)
);
}
/**
* Returns description of edit_user_enrolment() parameters
*
* @return external_function_parameters
*/
public static function edit_user_enrolment_parameters() {
return new external_function_parameters(
array(
'courseid' => new external_value(PARAM_INT, 'User enrolment ID'),
'ueid' => new external_value(PARAM_INT, 'User enrolment ID'),
'status' => new external_value(PARAM_INT, 'Enrolment status'),
'timestart' => new external_value(PARAM_INT, 'Enrolment start timestamp', VALUE_DEFAULT, 0),
'timeend' => new external_value(PARAM_INT, 'Enrolment end timestamp', VALUE_DEFAULT, 0),
)
);
}
/**
* External function that updates a given user enrolment.
*
* @param int $courseid The course ID.
* @param int $ueid The user enrolment ID.
* @param int $status The enrolment status.
* @param int $timestart Enrolment start timestamp.
* @param int $timeend Enrolment end timestamp.
* @return array An array consisting of the processing result, errors and form output, if available.
*/
public static function edit_user_enrolment($courseid, $ueid, $status, $timestart = 0, $timeend = 0) {
global $CFG, $DB, $PAGE;
$params = self::validate_parameters(self::edit_user_enrolment_parameters(), [
'courseid' => $courseid,
'ueid' => $ueid,
'status' => $status,
'timestart' => $timestart,
'timeend' => $timeend,
]);
$course = get_course($courseid);
$context = context_course::instance($course->id);
self::validate_context($context);
$userenrolment = $DB->get_record('user_enrolments', ['id' => $params['ueid']], '*', MUST_EXIST);
$userenroldata = [
'status' => $params['status'],
'timestart' => $params['timestart'],
'timeend' => $params['timeend'],
];
$result = false;
$errors = [];
// Validate data against the edit user enrolment form.
require_once("$CFG->dirroot/enrol/editenrolment_form.php");
$customformdata = [
'ue' => $userenrolment,
'modal' => true,
];
$mform = new \enrol_user_enrolment_form(null, $customformdata, 'post', '', null, true, $userenroldata);
$mform->set_data($userenroldata);
$validationerrors = $mform->validation($userenroldata, null);
if (empty($validationerrors)) {
require_once($CFG->dirroot . '/enrol/locallib.php');
$manager = new course_enrolment_manager($PAGE, $course);
$result = $manager->edit_enrolment($userenrolment, (object)$userenroldata);
} else {
foreach ($validationerrors as $key => $errormessage) {
$errors[] = (object)[
'key' => $key,
'message' => $errormessage
];
}
}
return [
'result' => $result,
'errors' => $errors,
];
}
/**
* Returns description of edit_user_enrolment() result value
*
* @return external_description
*/
public static function edit_user_enrolment_returns() {
return new external_single_structure(
array(
'result' => new external_value(PARAM_BOOL, 'True if the user\'s enrolment was successfully updated'),
'errors' => new external_multiple_structure(
new external_single_structure(
array(
'key' => new external_value(PARAM_TEXT, 'The data that failed the validation'),
'message' => new external_value(PARAM_TEXT, 'The error message'),
)
), 'List of validation errors'
),
)
);
}
}
/**
+5 -2
View File
@@ -132,11 +132,14 @@ class enrol_flatfile_plugin extends enrol_plugin {
$params['ue'] = $ue->id;
if ($this->allow_unenrol_user($instance, $ue) && has_capability("enrol/flatfile:unenrol", $context)) {
$url = new moodle_url('/enrol/unenroluser.php', $params);
$actions[] = new user_enrolment_action(new pix_icon('t/delete', ''), get_string('unenrol', 'enrol'), $url, array('class'=>'unenrollink', 'rel'=>$ue->id));
$actionparams = array('class' => 'unenrollink', 'rel' => $ue->id, 'data-action' => ENROL_ACTION_UNENROL);
$actions[] = new user_enrolment_action(new pix_icon('t/delete', ''), get_string('unenrol', 'enrol'), $url,
$actionparams);
}
if ($this->allow_manage($instance) && has_capability("enrol/flatfile:manage", $context)) {
$url = new moodle_url('/enrol/editenrolment.php', $params);
$actions[] = new user_enrolment_action(new pix_icon('t/edit', ''), get_string('edit'), $url, array('class'=>'editenrollink', 'rel'=>$ue->id));
$actionparams = array('class' => 'editenrollink', 'rel' => $ue->id, 'data-action' => ENROL_ACTION_EDIT);
$actions[] = new user_enrolment_action(new pix_icon('t/edit', ''), get_string('edit'), $url, $actionparams);
}
return $actions;
}
+4 -3
View File
@@ -392,13 +392,14 @@ class enrol_lti_plugin extends enrol_plugin {
$params['ue'] = $ue->id;
if ($this->allow_unenrol_user($instance, $ue) && has_capability("enrol/lti:unenrol", $context)) {
$url = new moodle_url('/enrol/unenroluser.php', $params);
$actionparams = array('class' => 'unenrollink', 'rel' => $ue->id, 'data-action' => ENROL_ACTION_UNENROL);
$actions[] = new user_enrolment_action(new pix_icon('t/delete', ''), get_string('unenrol', 'enrol'), $url,
array('class' => 'unenrollink', 'rel' => $ue->id));
$actionparams);
}
if ($this->allow_manage($instance) && has_capability("enrol/lti:manage", $context)) {
$url = new moodle_url('/enrol/editenrolment.php', $params);
$actions[] = new user_enrolment_action(new pix_icon('t/edit', ''), get_string('edit'), $url,
array('class' => 'editenrollink', 'rel' => $ue->id));
$actionparams = array('class' => 'editenrollink', 'rel' => $ue->id, 'data-action' => ENROL_ACTION_EDIT);
$actions[] = new user_enrolment_action(new pix_icon('t/edit', ''), get_string('edit'), $url, $actionparams);
}
return $actions;
}
+5 -2
View File
@@ -376,11 +376,14 @@ class enrol_manual_plugin extends enrol_plugin {
$params['ue'] = $ue->id;
if ($this->allow_unenrol_user($instance, $ue) && has_capability("enrol/manual:unenrol", $context)) {
$url = new moodle_url('/enrol/unenroluser.php', $params);
$actions[] = new user_enrolment_action(new pix_icon('t/delete', ''), get_string('unenrol', 'enrol'), $url, array('class'=>'unenrollink', 'rel'=>$ue->id));
$actionparams = array('class' => 'unenrollink', 'rel' => $ue->id, 'data-action' => ENROL_ACTION_UNENROL);
$actions[] = new user_enrolment_action(new pix_icon('t/delete', ''), get_string('unenrol', 'enrol'), $url,
$actionparams);
}
if ($this->allow_manage($instance) && has_capability("enrol/manual:manage", $context)) {
$url = new moodle_url('/enrol/editenrolment.php', $params);
$actions[] = new user_enrolment_action(new pix_icon('t/edit', ''), get_string('edit'), $url, array('class'=>'editenrollink', 'rel'=>$ue->id));
$actionparams = array('class' => 'editenrollink', 'rel' => $ue->id, 'data-action' => ENROL_ACTION_EDIT);
$actions[] = new user_enrolment_action(new pix_icon('t/edit', ''), get_string('edit'), $url, $actionparams);
}
return $actions;
}
+3 -1
View File
@@ -110,7 +110,9 @@ class enrol_meta_plugin extends enrol_plugin {
$params['ue'] = $ue->id;
if ($this->allow_unenrol_user($instance, $ue) && has_capability('enrol/meta:unenrol', $context)) {
$url = new moodle_url('/enrol/unenroluser.php', $params);
$actions[] = new user_enrolment_action(new pix_icon('t/delete', ''), get_string('unenrol', 'enrol'), $url, array('class'=>'unenrollink', 'rel'=>$ue->id));
$actionparams = array('class' => 'unenrollink', 'rel' => $ue->id, 'data-action' => ENROL_ACTION_UNENROL);
$actions[] = new user_enrolment_action(new pix_icon('t/delete', ''), get_string('unenrol', 'enrol'), $url,
$actionparams);
}
return $actions;
}
+20 -7
View File
@@ -16,18 +16,21 @@ Feature: Enrolments are synchronised with meta courses
| Course 1 | C1C1 |
| Course 2 | C2C2 |
| Course 3 | C3C3 |
| Course 4 | C4C4 |
And the following "groups" exist:
| name | course | idnumber |
| Groupcourse 1 | C3C3 | G1 |
| Groupcourse 2 | C3C3 | G2 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1C1 | student |
| student2 | C1C1 | student |
| student3 | C1C1 | student |
| student4 | C1C1 | student |
| student1 | C2C2 | student |
| student2 | C2C2 | student |
| user | course | role | status |
| student1 | C1C1 | student | 0 |
| student2 | C1C1 | student | 0 |
| student3 | C1C1 | student | 0 |
| student4 | C1C1 | student | 0 |
| student1 | C2C2 | student | 0 |
| student2 | C2C2 | student | 0 |
| student1 | C4C4 | student | 0 |
| student2 | C4C4 | student | 1 |
And I log in as "admin"
And I navigate to "Manage enrol plugins" node in "Site administration > Plugins > Enrolments"
And I click on "Enable" "link" in the "Course meta link" "table_row"
@@ -105,3 +108,13 @@ Feature: Enrolments are synchronised with meta courses
And I should see "Groupcourse 1" in the "Student 4" "table_row"
And I should see "Course 2" in the "Student 1" "table_row"
And I should not see "Course 2" in the "Student 3" "table_row"
Scenario: Unenrol a user from the course participants page that was enrolled via course meta link.
Given I am on "Course 3" course homepage
And I add "Course meta link" enrolment method with:
| Link course | C4C4 |
And I navigate to course participants
# Suspended users can be unenrolled.
When I click on "//a[@data-action='unenrol']" "xpath_element" in the "student2" "table_row"
And I click on "Yes" "button"
Then I should not see "Student 2" in the "participants" "table"
+5 -2
View File
@@ -291,11 +291,14 @@ class enrol_paypal_plugin extends enrol_plugin {
$params['ue'] = $ue->id;
if ($this->allow_unenrol($instance) && has_capability("enrol/paypal:unenrol", $context)) {
$url = new moodle_url('/enrol/unenroluser.php', $params);
$actions[] = new user_enrolment_action(new pix_icon('t/delete', ''), get_string('unenrol', 'enrol'), $url, array('class'=>'unenrollink', 'rel'=>$ue->id));
$actionparams = array('class' => 'unenrollink', 'rel' => $ue->id, 'data-action' => ENROL_ACTION_UNENROL);
$actions[] = new user_enrolment_action(new pix_icon('t/delete', ''), get_string('unenrol', 'enrol'), $url,
$actionparams);
}
if ($this->allow_manage($instance) && has_capability("enrol/paypal:manage", $context)) {
$url = new moodle_url('/enrol/editenrolment.php', $params);
$actions[] = new user_enrolment_action(new pix_icon('t/edit', ''), get_string('edit'), $url, array('class'=>'editenrollink', 'rel'=>$ue->id));
$actionparams = array('class' => 'editenrollink', 'rel' => $ue->id, 'data-action' => ENROL_ACTION_EDIT);
$actions[] = new user_enrolment_action(new pix_icon('t/edit', ''), get_string('edit'), $url, $actionparams);
}
return $actions;
}
+5 -2
View File
@@ -536,11 +536,14 @@ class enrol_self_plugin extends enrol_plugin {
$params['ue'] = $ue->id;
if ($this->allow_unenrol($instance) && has_capability("enrol/self:unenrol", $context)) {
$url = new moodle_url('/enrol/unenroluser.php', $params);
$actions[] = new user_enrolment_action(new pix_icon('t/delete', ''), get_string('unenrol', 'enrol'), $url, array('class'=>'unenrollink', 'rel'=>$ue->id));
$actionparams = array('class' => 'unenrollink', 'rel' => $ue->id, 'data-action' => ENROL_ACTION_UNENROL);
$actions[] = new user_enrolment_action(new pix_icon('t/delete', ''), get_string('unenrol', 'enrol'), $url,
$actionparams);
}
if ($this->allow_manage($instance) && has_capability("enrol/self:manage", $context)) {
$url = new moodle_url('/enrol/editenrolment.php', $params);
$actions[] = new user_enrolment_action(new pix_icon('t/edit', ''), get_string('edit'), $url, array('class'=>'editenrollink', 'rel'=>$ue->id));
$actionparams = array('class' => 'editenrollink', 'rel' => $ue->id, 'data-action' => ENROL_ACTION_EDIT);
$actions[] = new user_enrolment_action(new pix_icon('t/edit', ''), get_string('edit'), $url, $actionparams);
}
return $actions;
}
@@ -87,3 +87,41 @@ Feature: Users can auto-enrol themself in courses where self enrolment is allowe
Then I should see "Topic 1"
And I should not see "Enrolment options"
And I should not see "Enrol me in this course"
@javascript
Scenario: Edit a self-enrolled user's enrolment from the course participants page
Given I log in as "teacher1"
And I am on "Course 1" course homepage
When I add "Self enrolment" enrolment method with:
| Custom instance name | Test student enrolment |
And I log out
And I log in as "student1"
And I am on "Course 1" course homepage
And I press "Enrol me"
And I log out
And I log in as "teacher1"
And I am on "Course 1" course homepage
And I navigate to course participants
When I click on "//a[@data-action='editenrolment']" "xpath_element" in the "student1" "table_row"
And I should see "Edit Student 1's enrolment"
And I set the field "Status" to "Suspended"
And I click on "Save changes" "button"
Then I should see "Suspended" in the "student1" "table_row"
@javascript
Scenario: Unenrol a self-enrolled student from the course participants page
Given I log in as "teacher1"
And I am on "Course 1" course homepage
When I add "Self enrolment" enrolment method with:
| Custom instance name | Test student enrolment |
And I log out
And I log in as "student1"
And I am on "Course 1" course homepage
And I press "Enrol me"
And I log out
And I log in as "teacher1"
And I am on "Course 1" course homepage
And I navigate to course participants
When I click on "//a[@data-action='unenrol']" "xpath_element" in the "student1" "table_row"
And I click on "Yes" "button"
Then I should not see "Student 1" in the "participants" "table"
+82
View File
@@ -682,4 +682,86 @@ class core_enrol_externallib_testcase extends externallib_advanced_testcase {
$this->assertEquals($data->student1->id, $expecteduser['id']);
}
/**
* Test for core_enrol_external::edit_user_enrolment().
*/
public function test_edit_user_enrolment() {
global $DB;
$this->resetAfterTest(true);
$datagen = $this->getDataGenerator();
/** @var enrol_manual_plugin $manualplugin */
$manualplugin = enrol_get_plugin('manual');
$this->assertNotNull($manualplugin);
$studentroleid = $DB->get_field('role', 'id', ['shortname' => 'student'], MUST_EXIST);
$teacherroleid = $DB->get_field('role', 'id', ['shortname' => 'editingteacher'], MUST_EXIST);
$course = $datagen->create_course();
$user = $datagen->create_user();
$teacher = $datagen->create_user();
$instanceid = null;
$instances = enrol_get_instances($course->id, true);
foreach ($instances as $inst) {
if ($inst->enrol == 'manual') {
$instanceid = (int)$inst->id;
break;
}
}
if (empty($instanceid)) {
$instanceid = $manualplugin->add_default_instance($course);
if (empty($instanceid)) {
$instanceid = $manualplugin->add_instance($course);
}
}
$this->assertNotNull($instanceid);
$instance = $DB->get_record('enrol', ['id' => $instanceid], '*', MUST_EXIST);
$manualplugin->enrol_user($instance, $user->id, $studentroleid, 0, 0, ENROL_USER_ACTIVE);
$manualplugin->enrol_user($instance, $teacher->id, $teacherroleid, 0, 0, ENROL_USER_ACTIVE);
$ueid = (int)$DB->get_field(
'user_enrolments',
'id',
['enrolid' => $instance->id, 'userid' => $user->id],
MUST_EXIST
);
// Login as teacher.
$this->setUser($teacher);
$now = new DateTime();
$nowtime = $now->getTimestamp();
// Invalid data.
$data = core_enrol_external::edit_user_enrolment($course->id, $ueid, ENROL_USER_ACTIVE, $nowtime, $nowtime);
$data = external_api::clean_returnvalue(core_enrol_external::edit_user_enrolment_returns(), $data);
$this->assertFalse($data['result']);
$this->assertNotEmpty($data['errors']);
// Valid data.
$nextmonth = clone($now);
$nextmonth->add(new DateInterval('P1M'));
$nextmonthtime = $nextmonth->getTimestamp();
$data = core_enrol_external::edit_user_enrolment($course->id, $ueid, ENROL_USER_ACTIVE, $nowtime, $nextmonthtime);
$data = external_api::clean_returnvalue(core_enrol_external::edit_user_enrolment_returns(), $data);
$this->assertTrue($data['result']);
$this->assertEmpty($data['errors']);
// Check updated user enrolment.
$ue = $DB->get_record('user_enrolments', ['id' => $ueid], '*', MUST_EXIST);
$this->assertEquals(ENROL_USER_ACTIVE, $ue->status);
$this->assertEquals($nowtime, $ue->timestart);
$this->assertEquals($nextmonthtime, $ue->timeend);
// Suspend user.
$data = core_enrol_external::edit_user_enrolment($course->id, $ueid, ENROL_USER_SUSPENDED);
$data = external_api::clean_returnvalue(core_enrol_external::edit_user_enrolment_returns(), $data);
$this->assertTrue($data['result']);
$this->assertEmpty($data['errors']);
// Check updated user enrolment.
$ue = $DB->get_record('user_enrolments', ['id' => $ueid], '*', MUST_EXIST);
$this->assertEquals(ENROL_USER_SUSPENDED, $ue->status);
}
}
+3 -4
View File
@@ -58,8 +58,7 @@ if (!$plugin->allow_unenrol_user($instance, $ue) or !has_capability("enrol/$inst
$manager = new course_enrolment_manager($PAGE, $course, $filter);
$table = new course_enrolment_users_table($manager, $PAGE);
$returnurl = new moodle_url('/enrol/users.php', array('id' => $course->id)+$manager->get_url_params()+$table->get_url_params());
$usersurl = new moodle_url('/enrol/users.php', array('id' => $course->id));
$usersurl = new moodle_url('/user/index.php', array('id' => $course->id));
$PAGE->set_pagelayout('admin');
navigation_node::override_active_url($usersurl);
@@ -67,7 +66,7 @@ navigation_node::override_active_url($usersurl);
// If the unenrolment has been confirmed and the sesskey is valid unenrol the user.
if ($confirm && confirm_sesskey()) {
$plugin->unenrol_user($instance, $ue->userid);
redirect($returnurl);
redirect($usersurl);
}
$yesurl = new moodle_url($PAGE->url, array('confirm'=>1, 'sesskey'=>sesskey()));
@@ -82,5 +81,5 @@ $PAGE->navbar->add($fullname);
echo $OUTPUT->header();
echo $OUTPUT->heading($fullname);
echo $OUTPUT->confirm($message, $yesurl, $returnurl);
echo $OUTPUT->confirm($message, $yesurl, $usersurl);
echo $OUTPUT->footer();
+5
View File
@@ -43,11 +43,13 @@ $string['deleteinstancenousersconfirm'] = 'You are about to delete the enrolment
$string['disableinstanceconfirmself'] = 'Are you really sure you want to disable instance "{$a->name}" that gives you access to this course? It is possible that you will not be able to access this course if you continue.';
$string['durationdays'] = '{$a} days';
$string['editenrolment'] = 'Edit enrolment';
$string['edituserenrolment'] = 'Edit {$a}\'s enrolment';
$string['enrol'] = 'Enrol';
$string['enrolcandidates'] = 'Not enrolled users';
$string['enrolcandidatesmatching'] = 'Matching not enrolled users';
$string['enrolcohort'] = 'Enrol cohort';
$string['enrolcohortusers'] = 'Enrol users';
$string['enroldetails'] = 'Enrolment details';
$string['eventenrolinstancecreated'] = 'Enrolment instance created';
$string['eventenrolinstancedeleted'] = 'Enrolment instance deleted';
$string['eventenrolinstanceupdated'] = 'Enrolment instance updated';
@@ -58,6 +60,7 @@ $string['enrolme'] = 'Enrol me in this course';
$string['enrolmentinstances'] = 'Enrolment methods';
$string['enrolmentnew'] = 'New enrolment in {$a}';
$string['enrolmentnewuser'] = '{$a->user} has enrolled in course "{$a->course}"';
$string['enrolmentmethod'] = 'Enrolment method';
$string['enrolments'] = 'Enrolments';
$string['enrolmentoptions'] = 'Enrolment options';
$string['enrolnotpermitted'] = 'You do not have permission or are not allowed to enrol someone in this course';
@@ -67,6 +70,7 @@ $string['enrolusers'] = 'Enrol users';
$string['enrolxusers'] = 'Enrol {$a} users';
$string['enroltimecreated'] = 'Enrolment created';
$string['enroltimeend'] = 'Enrolment ends';
$string['enroltimeendinvalid'] = 'Enrolment end date must be after the enrolment start date';
$string['enroltimestart'] = 'Enrolment starts';
$string['errajaxfailedenrol'] = 'Failed to enrol user';
$string['errajaxsearch'] = 'Error when searching users';
@@ -104,6 +108,7 @@ $string['notenrollable'] = 'You can not enrol yourself in this course.';
$string['notenrolledusers'] = 'Other users';
$string['otheruserdesc'] = 'The following users are not enrolled in this course but do have roles, inherited or assigned within it.';
$string['participationactive'] = 'Active';
$string['participationnotcurrent'] = 'Not current';
$string['participationstatus'] = 'Status';
$string['participationsuspended'] = 'Suspended';
$string['periodend'] = 'until {$a}';
+8
View File
@@ -482,6 +482,14 @@ $functions = array(
'capabilities' => 'moodle/course:viewparticipants',
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE),
),
'core_enrol_edit_user_enrolment' => array(
'classname' => 'core_enrol_external',
'methodname' => 'edit_user_enrolment',
'classpath' => 'enrol/externallib.php',
'description' => 'External function that updates a given user enrolment',
'type' => 'write',
'ajax' => true,
),
'core_fetch_notifications' => array(
'classname' => 'core_external',
'methodname' => 'fetch_notifications',
+44
View File
@@ -87,6 +87,12 @@ define('ENROL_SEND_EMAIL_FROM_KEY_HOLDER', 2);
*/
define('ENROL_SEND_EMAIL_FROM_NOREPLY', 3);
/** Edit enrolment action. */
define('ENROL_ACTION_EDIT', 'editenrolment');
/** Unenrol action. */
define('ENROL_ACTION_UNENROL', 'unenrol');
/**
* Returns instances of enrol plugins
* @param bool $enabled return enabled only
@@ -1414,6 +1420,44 @@ function enrol_send_welcome_email_options() {
];
}
/**
* Serve the user enrolment form as a fragment.
*
* @param array $args List of named arguments for the fragment loader.
* @return string
*/
function enrol_output_fragment_user_enrolment_form($args) {
global $CFG, $DB;
$args = (object) $args;
$context = $args->context;
require_capability('moodle/course:enrolreview', $context);
$ueid = $args->ueid;
$userenrolment = $DB->get_record('user_enrolments', ['id' => $ueid], '*', MUST_EXIST);
$customdata = [
'ue' => $userenrolment,
'modal' => true,
];
// Set the data if applicable.
$data = [];
if (isset($args->formdata)) {
$serialiseddata = json_decode($args->formdata);
parse_str($serialiseddata, $data);
}
require_once("$CFG->dirroot/enrol/editenrolment_form.php");
$mform = new \enrol_user_enrolment_form(null, $customdata, 'post', '', null, true, $data);
if (!empty($data)) {
$mform->set_data($data);
$mform->is_validated();
}
return $mform->render();
}
/**
* Returns the course where a user enrolment belong to.
*
+7
View File
@@ -25,6 +25,13 @@ information provided here is intended especially for developers.
every form. The method should accept a single parameter with the submitted value. It should return a string with the
eventual validation error, or an empty value if the validation passes.
* New user_picture attribute $includefullname to determine whether to include the user's full name with the user's picture.
* Enrol plugins which provide enrolment actions can now declare the following "data-action" attributes in their implementation of
enrol_plugin::get_user_enrolment_actions() whenever applicable:
* "editenrolment" - For editing a user'e enrolment details. Defined by constant ENROL_ACTION_EDIT.
* "unenrol" - For unenrolling a student. Defined by constant ENROL_ACTION_UNENROL.
These attributes enable enrol actions to be rendered via modals. If not added, clicking on the enrolment action buttons will still
redirect the user to the appropriate enrolment action page. Though optional, it is recommended to add these attributes for a
better user experience when performing enrol actions.
=== 3.3.1 ===
+1
View File
@@ -0,0 +1 @@
define(["core/templates","jquery","core/str","core/config","core/notification","core/modal_factory","core/modal_events","core/fragment","core/ajax"],function(a,b,c,d,e,f,g,h,i){var j={EDIT_ENROLMENT:'[data-action="editenrolment"]',SHOW_DETAILS:'[data-action="showdetails"]',UNENROL:'[data-action="unenrol"]'},k=function(a){this.contextid=a.contextid,this.courseid=a.courseid,this.bindEditEnrol(),this.bindUnenrol(),this.bindStatusDetails()};return k.prototype.courseid=0,k.prototype.bindEditEnrol=function(){var a=this;b(j.EDIT_ENROLMENT).click(function(d){d.preventDefault();var h=b(this),i=h.parent(),j=i.data("fullname"),k=h.attr("rel");b.when(c.get_string("edituserenrolment","enrol",j)).then(function(a){return f.create({large:!0,title:a,type:f.types.SAVE_CANCEL})}).done(function(b){b.getRoot().on(g.save,function(c){c.preventDefault(),a.submitEditFormAjax(b)}),b.getRoot().on(g.hidden,function(){b.destroy()}),b.setBody(a.getBody(k)),b.show()}).fail(e.exception)})},k.prototype.bindUnenrol=function(){b(j.UNENROL).click(function(a){a.preventDefault();var h=b(this),i=h.parent(),j=[{key:"unenrol",component:"enrol"},{key:"unenrolconfirm",component:"enrol",param:{user:i.data("fullname"),course:i.data("coursename")}}];b.when(c.get_strings(j)).then(function(a){var b=a[0],c=a[1];return f.create({body:c,large:!0,title:b,type:f.types.CONFIRM})}).done(function(a){a.getRoot().on(g.yes,function(){var a={confirm:1,sesskey:d.sesskey,ue:b(h).attr("rel")};window.location.href=d.wwwroot+"/enrol/unenroluser.php?"+b.param(a)}),a.getRoot().on(g.hidden,function(){a.destroy()}),a.show()}).fail(e.exception)})},k.prototype.bindStatusDetails=function(){b(j.SHOW_DETAILS).click(function(d){d.preventDefault();var h=b(this),i=h.parent(),j={fullname:i.data("fullname"),coursename:i.data("coursename"),enrolinstancename:i.data("enrolinstancename"),status:i.data("status"),statusclass:i.find("span").attr("class"),timestart:i.data("timestart"),timeend:i.data("timeend")},k=c.get_string("enroldetails","enrol"),l=f.create({large:!0,type:f.types.CANCEL});b.when(k,l).done(function(b,c){var d=a.render("core_user/status_details",j);c.setTitle(b),c.setBody(d),c.show(),c.getRoot().on(g.hidden,function(){c.destroy()})}).fail(e.exception)})},k.prototype.submitEditFormAjax=function(a){var c=this,d=a.getRoot().find("form"),f=b(d).find('[name="ue"]').val(),g=b(d).find('[name="status"]').val(),h={courseid:this.courseid,ueid:f,status:g},j=b(d).find('[name="timestart[enabled]"]');if(j.is(":checked")){var k=b(d).find('[name="timestart[year]"]').val(),l=b(d).find('[name="timestart[month]"]').val()-1,m=b(d).find('[name="timestart[day]"]').val(),n=b(d).find('[name="timestart[hour]"]').val(),o=b(d).find('[name="timestart[minute]"]').val(),p=new Date(k,l,m,n,o);h.timestart=p.getTime()/1e3}var q=b(d).find('[name="timeend[enabled]"]');if(q.is(":checked")){var r=b(d).find('[name="timeend[year]"]').val(),s=b(d).find('[name="timeend[month]"]').val()-1,t=b(d).find('[name="timeend[day]"]').val(),u=b(d).find('[name="timeend[hour]"]').val(),v=b(d).find('[name="timeend[minute]"]').val(),w=new Date(r,s,t,u,v);h.timeend=w.getTime()/1e3}var x={methodname:"core_enrol_edit_user_enrolment",args:h};i.call([x])[0].done(function(b){if(b.result)a.hide(),"undefined"!=typeof window.M.core_formchangechecker&&window.M.core_formchangechecker.reset_form_dirty_state(),window.location.reload();else{var e=JSON.stringify(d.serialize());a.setBody(c.getBody(f,e))}}).fail(e.exception)},k.prototype.getBody=function(a,b){var c={ueid:a};return"undefined"!=typeof b&&(c.formdata=b),h.loadFragment("enrol","user_enrolment_form",this.contextid,c).fail(e.exception)},{init:function(a){new k(a)}}});
+323
View File
@@ -0,0 +1,323 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* AMD module for the user enrolment status field in the course participants page.
*
* @module core_user/status_field
* @copyright 2017 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(['core/templates',
'jquery',
'core/str',
'core/config',
'core/notification',
'core/modal_factory',
'core/modal_events',
'core/fragment',
'core/ajax'
],
function(Template, $, Str, Config, Notification, ModalFactory, ModalEvents, Fragment, Ajax) {
/**
* Action selectors.
*
* @access private
* @type {{EDIT_ENROLMENT: string, SHOW_DETAILS: string, UNENROL: string}}
*/
var SELECTORS = {
EDIT_ENROLMENT: '[data-action="editenrolment"]',
SHOW_DETAILS: '[data-action="showdetails"]',
UNENROL: '[data-action="unenrol"]'
};
/**
* Constructor
*
* @param {Object} options Object containing options. The only valid option at this time is contextid.
* Each call to templates.render gets it's own instance of this class.
*/
var StatusFieldActions = function(options) {
this.contextid = options.contextid;
this.courseid = options.courseid;
// Bind click event to editenrol buttons.
this.bindEditEnrol();
// Bind click event to unenrol buttons.
this.bindUnenrol();
// Bind click event to status details buttons.
this.bindStatusDetails();
};
// Class variables and functions.
/** @var {number} courseid The course ID. */
StatusFieldActions.prototype.courseid = 0;
/**
* Private method
*
* @method initModal
* @private
*/
StatusFieldActions.prototype.bindEditEnrol = function() {
var statusFieldInstsance = this;
$(SELECTORS.EDIT_ENROLMENT).click(function(e) {
e.preventDefault();
// The particular edit button that was clicked.
var clickedEditTrigger = $(this);
// Get the parent container (it contains the data attributes associated with the status field).
var parentContainer = clickedEditTrigger.parent();
// Get the name of the user whose enrolment status is being edited.
var fullname = parentContainer.data('fullname');
// Get the user enrolment ID.
var ueid = clickedEditTrigger.attr('rel');
$.when(Str.get_string('edituserenrolment', 'enrol', fullname)).then(function(modalTitle) {
return ModalFactory.create({
large: true,
title: modalTitle,
type: ModalFactory.types.SAVE_CANCEL
});
}).done(function(modal) {
// Handle save event.
modal.getRoot().on(ModalEvents.save, function(e) {
// Don't close the modal yet.
e.preventDefault();
// Submit form data.
statusFieldInstsance.submitEditFormAjax(modal);
});
// Handle hidden event.
modal.getRoot().on(ModalEvents.hidden, function() {
// Destroy when hidden.
modal.destroy();
});
// Set the modal body.
modal.setBody(statusFieldInstsance.getBody(ueid));
// Show the modal!
modal.show();
}).fail(Notification.exception);
});
};
/**
* Private method
*
* @method bindUnenrol
* @private
*/
StatusFieldActions.prototype.bindUnenrol = function() {
$(SELECTORS.UNENROL).click(function(e) {
e.preventDefault();
var unenrolLink = $(this);
var parentContainer = unenrolLink.parent();
var strings = [
{
key: 'unenrol',
component: 'enrol'
},
{
key: 'unenrolconfirm',
component: 'enrol',
param: {
user: parentContainer.data('fullname'),
course: parentContainer.data('coursename')
}
}
];
$.when(Str.get_strings(strings)).then(function(results) {
var title = results[0];
var confirmMessage = results[1];
return ModalFactory.create({
body: confirmMessage,
large: true,
title: title,
type: ModalFactory.types.CONFIRM
});
}).done(function(modal) {
// Handle confirm event.
modal.getRoot().on(ModalEvents.yes, function() {
// Build params.
var unenrolParams = {
confirm: 1,
sesskey: Config.sesskey,
ue: $(unenrolLink).attr('rel')
};
// Send data to unenrol page (which will redirect back to the participants page after unenrol).
window.location.href = Config.wwwroot + '/enrol/unenroluser.php?' + $.param(unenrolParams);
});
// Handle hidden event.
modal.getRoot().on(ModalEvents.hidden, function() {
// Destroy when hidden.
modal.destroy();
});
// Display the delete confirmation modal.
modal.show();
}).fail(Notification.exception);
});
};
/**
* Private method
*
* @method bindStatusDetails
* @private
*/
StatusFieldActions.prototype.bindStatusDetails = function() {
$(SELECTORS.SHOW_DETAILS).click(function(e) {
e.preventDefault();
var detailsButton = $(this);
var parentContainer = detailsButton.parent();
var context = {
"fullname": parentContainer.data('fullname'),
"coursename": parentContainer.data('coursename'),
"enrolinstancename": parentContainer.data('enrolinstancename'),
"status": parentContainer.data('status'),
"statusclass": parentContainer.find('span').attr('class'),
"timestart": parentContainer.data('timestart'),
"timeend": parentContainer.data('timeend')
};
var modalTitlePromise = Str.get_string('enroldetails', 'enrol');
var modalPromise = ModalFactory.create({large: true, type: ModalFactory.types.CANCEL});
$.when(modalTitlePromise, modalPromise).done(function(modalTitle, modal) {
var modalBodyPromise = Template.render('core_user/status_details', context);
modal.setTitle(modalTitle);
modal.setBody(modalBodyPromise);
modal.show();
// Handle hidden event.
modal.getRoot().on(ModalEvents.hidden, function() {
// Destroy when hidden.
modal.destroy();
});
}).fail(Notification.exception);
});
};
/**
* Private method
*
* @method submitEditFormAjax
* @param {Object} modal The the AMD modal object containing the form.
* @private
*/
StatusFieldActions.prototype.submitEditFormAjax = function(modal) {
var statusFieldInstsance = this;
var form = modal.getRoot().find('form');
// User enrolment ID.
var ueid = $(form).find('[name="ue"]').val();
// Status.
var status = $(form).find('[name="status"]').val();
var params = {
'courseid': this.courseid,
'ueid': ueid,
'status': status
};
// Enrol time start.
var timeStartEnabled = $(form).find('[name="timestart[enabled]"]');
if (timeStartEnabled.is(':checked')) {
var timeStartYear = $(form).find('[name="timestart[year]"]').val();
var timeStartMonth = $(form).find('[name="timestart[month]"]').val() - 1;
var timeStartDay = $(form).find('[name="timestart[day]"]').val();
var timeStartHour = $(form).find('[name="timestart[hour]"]').val();
var timeStartMinute = $(form).find('[name="timestart[minute]"]').val();
var timeStart = new Date(timeStartYear, timeStartMonth, timeStartDay, timeStartHour, timeStartMinute);
params.timestart = timeStart.getTime() / 1000;
}
// Enrol time end.
var timeEndEnabled = $(form).find('[name="timeend[enabled]"]');
if (timeEndEnabled.is(':checked')) {
var timeEndYear = $(form).find('[name="timeend[year]"]').val();
var timeEndMonth = $(form).find('[name="timeend[month]"]').val() - 1;
var timeEndDay = $(form).find('[name="timeend[day]"]').val();
var timeEndHour = $(form).find('[name="timeend[hour]"]').val();
var timeEndMinute = $(form).find('[name="timeend[minute]"]').val();
var timeEnd = new Date(timeEndYear, timeEndMonth, timeEndDay, timeEndHour, timeEndMinute);
params.timeend = timeEnd.getTime() / 1000;
}
var request = {
methodname: 'core_enrol_edit_user_enrolment',
args: params
};
Ajax.call([request])[0].done(function(data) {
if (data.result) {
// Dismiss the modal.
modal.hide();
// Reload the page, don't show changed data warnings.
if (typeof window.M.core_formchangechecker !== "undefined") {
window.M.core_formchangechecker.reset_form_dirty_state();
}
window.location.reload();
} else {
// Serialise the form data and reload the form fragment to show validation errors.
var formData = JSON.stringify(form.serialize());
modal.setBody(statusFieldInstsance.getBody(ueid, formData));
}
}).fail(Notification.exception);
};
/**
* Private method
*
* @method getBody
* @private
* @param {Number} ueid The user enrolment ID associated with the user.
* @param {string} formData Serialized string of the edit enrolment form data.
* @return {Promise}
*/
StatusFieldActions.prototype.getBody = function(ueid, formData) {
var params = {
'ueid': ueid
};
if (typeof formData !== 'undefined') {
params.formdata = formData;
}
return Fragment.loadFragment('enrol', 'user_enrolment_form', this.contextid, params).fail(Notification.exception);
};
return /** @alias module:core_user/editenrolment */ {
// Public variables and functions.
/**
* Every call to init creates a new instance of the class with it's own event listeners etc.
*
* @method init
* @public
* @param {object} config - config variables for the module.
*/
init: function(config) {
(new StatusFieldActions(config));
}
};
});
+163
View File
@@ -0,0 +1,163 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Class containing the data necessary for rendering the status field in the course participants page.
*
* @package core_user
* @copyright 2017 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_user\output;
defined('MOODLE_INTERNAL') || die();
use renderable;
use renderer_base;
use stdClass;
use templatable;
use user_enrolment_action;
/**
* Class containing the data for the status field.
*
* @package core_user
* @copyright 2017 Jun Pataleta
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class status_field implements renderable, templatable {
/** Active user enrolment status constant. */
const STATUS_ACTIVE = 0;
/** Suspended user enrolment status constant. */
const STATUS_SUSPENDED = 1;
/** Not current user enrolment status constant. */
const STATUS_NOT_CURRENT = 2;
/** @var string $enrolinstancename The enrolment instance name. */
protected $enrolinstancename;
/** @var string $coursename The course's full name. */
protected $coursename;
/** @var string $fullname The user's full name. */
protected $fullname;
/** @var string $status The user enrolment status. */
protected $status;
/** @var int $timestart The timestamp when the user's enrolment starts. */
protected $timestart;
/** @var int $timeend The timestamp when the user's enrolment ends. */
protected $timeend;
/** @var user_enrolment_action[] $enrolactions Array of enrol action objects for the given enrolment method. */
protected $enrolactions;
/** @var bool $statusactive Indicates whether a user enrolment status should be rendered as active. */
protected $statusactive = false;
/** @var bool $statusactive Indicates whether a user enrolment status should be rendered as suspended. */
protected $statussuspended = false;
/** @var bool $statusactive Indicates whether a user enrolment status should be rendered as not current. */
protected $statusnotcurrent = false;
/**
* status_field constructor.
*
* @param string $enrolinstancename The enrolment instance name.
* @param string $coursename The course's full name.
* @param string $fullname The user's full name.
* @param string $status The user enrolment status.
* @param int|null $timestart The timestamp when the user's enrolment starts.
* @param int|null $timeend The timestamp when the user's enrolment ends.
* @param user_enrolment_action[] $enrolactions Array of enrol action objects for the given enrolment method.
*/
public function __construct($enrolinstancename, $coursename, $fullname, $status, $timestart = null, $timeend = null,
$enrolactions = []) {
$this->enrolinstancename = $enrolinstancename;
$this->coursename = $coursename;
$this->fullname = $fullname;
$this->status = $status;
$this->timestart = $timestart;
$this->timeend = $timeend;
$this->enrolactions = $enrolactions;
}
/**
* Function to export the renderer data in a format that is suitable for a
* mustache template. This means:
* 1. No complex types - only stdClass, array, int, string, float, bool
* 2. Any additional info that is required for the template is pre-calculated (e.g. capability checks).
*
* @param renderer_base $output Used to do a final render of any components that need to be rendered for export.
* @return stdClass|array
*/
public function export_for_template(renderer_base $output) {
$data = new stdClass();
$data->enrolinstancename = $this->enrolinstancename;
$data->coursename = $this->coursename;
$data->fullname = $this->fullname;
$data->status = $this->status;
$data->active = $this->statusactive;
$data->suspended = $this->statussuspended;
$data->notcurrent = $this->statusnotcurrent;
if ($this->timestart) {
$data->timestart = userdate($this->timestart);
}
if ($this->timeend) {
$data->timeend = userdate($this->timeend);
}
$data->enrolactions = [];
foreach ($this->enrolactions as $enrolaction) {
$action = new stdClass();
$action->url = $enrolaction->get_url()->out(false);
$action->icon = $output->render($enrolaction->get_icon());
$action->attributes = [];
foreach ($enrolaction->get_attributes() as $name => $value) {
$attribute = (object) [
'name' => $name,
'value' => $value
];
$action->attributes[] = $attribute;
}
$data->enrolactions[] = $action;
}
return $data;
}
/**
* Status setter.
*
* @param int $status The user enrolment status representing one of this class' STATUS_* constants.
* @return status_field This class' instance. Useful for chaining.
*/
public function set_status($status = self::STATUS_ACTIVE) {
$this->statusactive = $status == static::STATUS_ACTIVE;
$this->statussuspended = $status == static::STATUS_SUSPENDED;
$this->statusnotcurrent = $status == static::STATUS_NOT_CURRENT;
return $this;
}
}
+82 -2
View File
@@ -24,6 +24,10 @@
namespace core_user;
use context;
use core_user\output\status_field;
use DateTime;
defined('MOODLE_INTERNAL') || die;
global $CFG;
@@ -86,12 +90,12 @@ class participants_table extends \table_sql {
protected $extrafields;
/**
* @var \stdClass The course details.
* @var \stdClass $course The course details.
*/
protected $course;
/**
* @var \context The course context.
* @var context $context The course context.
*/
protected $context;
@@ -125,6 +129,7 @@ class participants_table extends \table_sql {
// Get the context.
$this->course = get_course($courseid);
$context = \context_course::instance($courseid, MUST_EXIST);
$this->context = $context;
// Define the headers and columns.
$headers = [];
@@ -168,6 +173,13 @@ class participants_table extends \table_sql {
$columns[] = 'lastaccess';
}
$canreviewenrol = has_capability('moodle/course:enrolreview', $context);
if ($canreviewenrol) {
$columns[] = 'status';
$headers[] = get_string('participationstatus', 'enrol');
$this->no_sorting('status');
};
$this->define_columns($columns);
$this->define_headers($headers);
@@ -192,6 +204,24 @@ class participants_table extends \table_sql {
$this->assignableroles = get_assignable_roles($this->context, ROLENAME_ALIAS, false);
}
/**
* Render the participants table.
*
* @param int $pagesize Size of page for paginated displayed table.
* @param bool $useinitialsbar Whether to use the initials bar which will only be used if there is a fullname column defined.
* @param string $downloadhelpbutton
*/
public function out($pagesize, $useinitialsbar, $downloadhelpbutton = '') {
global $PAGE;
parent::out($pagesize, $useinitialsbar, $downloadhelpbutton);
if (has_capability('moodle/course:enrolreview', $this->context)) {
$params = ['contextid' => $this->context->id, 'courseid' => $this->course->id];
$PAGE->requires->js_call_amd('core_user/status_field', 'init', [$params]);
}
}
/**
* Generate the select column.
*
@@ -290,6 +320,56 @@ class participants_table extends \table_sql {
return get_string('never');
}
/**
* Generate the status column.
*
* @param \stdClass $data The data object.
* @return string
*/
public function col_status($data) {
global $CFG, $OUTPUT, $PAGE;
$enrolstatusoutput = '';
$canreviewenrol = has_capability('moodle/course:enrolreview', $this->context);
if ($canreviewenrol) {
$fullname = fullname($data);
$coursename = $this->course->fullname;
require_once($CFG->dirroot . '/enrol/locallib.php');
$manager = new \course_enrolment_manager($PAGE, $this->course);
$userenrolments = $manager->get_user_enrolments($data->id);
foreach ($userenrolments as $ue) {
$timestart = $ue->timestart;
$timeend = $ue->timeend;
$actions = $ue->enrolmentplugin->get_user_enrolment_actions($manager, $ue);
$instancename = $ue->enrolmentinstancename;
// Default status field label and value.
$status = get_string('participationactive', 'enrol');
$statusval = status_field::STATUS_ACTIVE;
switch ($ue->status) {
case ENROL_USER_ACTIVE:
$currentdate = new DateTime();
$now = $currentdate->getTimestamp();
// If user enrolment status has not yet started/already ended.
if ($timestart > $now || ($timeend > 0 && $timeend < $now)) {
$status = get_string('participationnotcurrent', 'enrol');
$statusval = status_field::STATUS_NOT_CURRENT;
}
break;
case ENROL_USER_SUSPENDED:
$status = get_string('participationsuspended', 'enrol');
$statusval = status_field::STATUS_SUSPENDED;
break;
}
$statusfield = new status_field($instancename, $coursename, $fullname, $status, $timestart, $timeend, $actions);
$statusfielddata = $statusfield->set_status($statusval)->export_for_template($OUTPUT);
$enrolstatusoutput .= $OUTPUT->render_from_template('core_user/status_field', $statusfielddata);
}
}
return $enrolstatusoutput;
}
/**
* This function is used for the extra user fields.
*
+3 -1
View File
@@ -1234,7 +1234,9 @@ function user_get_participants_sql($courseid, $groupid = 0, $accesssince = 0, $r
$isfrontpage = ($courseid == SITEID);
list($esql, $params) = get_enrolled_sql($context, null, $groupid, true);
// Show active users only if the user doesn't have the 'moodle/course:enrolreview' capability.
$onlyactive = !has_capability('moodle/course:enrolreview', $context);
list($esql, $params) = get_enrolled_sql($context, null, $groupid, $onlyactive);
$joins = array('FROM {user} u');
$wheres = array();
+97
View File
@@ -0,0 +1,97 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template core_user/status_details
Template for the user enrolment status details.
Context variables required for this template:
* fullname string - The user's full name.
* coursename string - The course name.
* enrolinstancename string - The enrolment instance name.
* status string - The enrolment status.
* statusclass string - The CSS class for the enrolment status.
* timestart string - Optional. The enrolment time start.
* timeend string - Optional. The enrolment time end.
Example context (json):
{
"fullname": "Student John",
"coursename": "TC 1",
"enrolinstancename": "Manual enrolment",
"status": "Active",
"statusclass": "label label-success",
"timestart": "1 January 2017",
"timeend": "31 January 2018"
}
}}
<table class="table user-enrol-details">
<tr>
<th>
{{#str}}fullname{{/str}}
</th>
<td class="user-fullname">
{{fullname}}
</td>
</tr>
<tr>
<th>
{{#str}}course{{/str}}
</th>
<td class="user-course">
{{coursename}}
</td>
</tr>
<tr>
<th>
{{#str}}enrolmentmethod, enrol{{/str}}
</th>
<td class="user-enrol-instance">
{{enrolinstancename}}
</td>
</tr>
<tr>
<th>
{{#str}}participationstatus, enrol{{/str}}
</th>
<td class="user-enrol-status">
<span class="{{statusclass}}">
{{status}}
</span>
</td>
</tr>
{{#timestart}}
<tr>
<th>
{{#str}}enroltimestart, enrol{{/str}}
</th>
<td class="user-enrol-timestart">
{{timestart}}
</td>
</tr>
{{/timestart}}
{{#timeend}}
<tr>
<th>
{{#str}}enroltimeend, enrol{{/str}}
</th>
<td class="user-enrol-timeend">
{{timeend}}
</td>
</tr>
{{/timeend}}
</table>
+71
View File
@@ -0,0 +1,71 @@
{{!
This file is part of Moodle - http://moodle.org/
Moodle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Moodle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template core_user/status_field
Template for the enrolment status field.
Context variables required for this template:
* fullname string - The user's full name.
* coursename string - The course name.
* enrolinstancename string - The enrolment instance name.
* status string - The enrolment status.
* active boolean - Flag to indicate whether the user has an active enrolment.
* suspended boolean - Flag to indicate whether the user is suspended.
* notcurrent boolean - Flag to indicate whether the user's enrolment is active, but not current.
* timestart string - Optional. The enrolment time start.
* timeend string - Optional. The enrolment time end.
* enrolactions array - Optional. Array of enrol actions consisting of:
* url string - The URL of the enrol action link.
* attributes array - The array of attributes for the enrol action link.
* icon string - The HTML output for the enrol action icon.
Example context (json):
{
"fullname": "Student John",
"coursename": "TC 1",
"enrolinstancename": "Manual enrolment",
"status": "Active",
"active": true,
"timestart": "1 January 2017",
"timeend": "31 January 2018",
"enrolactions": [
{
"url": "#",
"attributes": [
{
"name": "class",
"value": "action-class"
}
],
"icon": "<i class=\"icon fa fa-cog fa-fw\" aria-hidden=\"true\" title=\"Edit enrolment\" aria-label=\"\"></i>"
}
]
}
}}
<div data-fullname="{{fullname}}" data-coursename="{{coursename}}" data-enrolinstancename="{{enrolinstancename}}"
data-status="{{status}}" data-timestart="{{timestart}}" data-timeend="{{timeend}}">
<span class="label {{#active}}label-success{{/active}}{{#suspended}}label-warning{{/suspended}}{{#notcurrent}}label-default{{/notcurrent}}">{{status}}</span>
<a data-action="showdetails" role="button" tabindex="0">
{{#pix}}docs, core, {{#str}}enroldetails, enrol{{/str}}{{/pix}}
</a>
{{#enrolactions}}
<a href="{{url}}" role="button" {{#attributes}}{{name}}="{{value}}" {{/attributes}}>
{{{icon}}}
</a>
{{/enrolactions}}
</div>
@@ -0,0 +1,93 @@
@core @core_user
Feature: Edit user enrolment
In order to manage students' enrolments
As a teacher
I need to be able to view enrolment details and edit student enrolments in the course participants page
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
And the following "courses" exist:
| fullname | shortname | format |
| Course 1 | C1 | topics |
And the following "course enrolments" exist:
| user | course | role | status |
| teacher1 | C1 | editingteacher | 0 |
| student1 | C1 | student | 0 |
| student2 | C1 | student | 1 |
@javascript
Scenario: Edit a user's enrolment
Given I log in as "teacher1"
And I am on "Course 1" course homepage
And I navigate to course participants
When I click on "//a[@data-action='editenrolment']" "xpath_element" in the "student1" "table_row"
And I should see "Edit Student 1's enrolment"
And I set the field "Status" to "Suspended"
And I click on "Save changes" "button"
And I click on "//a[@data-action='editenrolment']" "xpath_element" in the "student2" "table_row"
And I should see "Edit Student 2's enrolment"
And I set the field "timeend[enabled]" to "1"
And I set the field "timeend[day]" to "1"
And I set the field "timeend[month]" to "January"
And I set the field "timeend[year]" to "2017"
And I set the field "Status" to "Active"
And I click on "Save changes" "button"
Then I should see "Suspended" in the "student1" "table_row"
And I should see "Not current" in the "student2" "table_row"
@javascript
Scenario: Unenrol a student
Given I log in as "teacher1"
And I am on "Course 1" course homepage
And I navigate to course participants
When I click on "//a[@data-action='unenrol']" "xpath_element" in the "student1" "table_row"
And I click on "Yes" "button"
Then I should not see "Student 1" in the "participants" "table"
@javascript
Scenario: View a student's enrolment details
Given I log in as "teacher1"
And I am on "Course 1" course homepage
And I navigate to course participants
When I click on "//a[@data-action='showdetails']" "xpath_element" in the "student1" "table_row"
Then I should see "Enrolment details"
And I should see "Student 1" in the "Full name" "table_row"
And I should see "Active" in the "//td[@class='user-enrol-status']" "xpath_element"
And I press "Cancel"
And I click on "//a[@data-action='showdetails']" "xpath_element" in the "student2" "table_row"
And I should see "Enrolment details"
And I should see "Student 2" in the "Full name" "table_row"
And I should see "Suspended" in the "//td[@class='user-enrol-status']" "xpath_element"
# Without JS, the user should be redirected to the original edit enrolment form.
Scenario: Edit a user's enrolment without JavaScript
Given I log in as "teacher1"
And I am on "Course 1" course homepage
And I navigate to course participants
When I click on "//a[@data-action='editenrolment']" "xpath_element" in the "student1" "table_row"
And I should see "Student 1"
And I set the field "Status" to "Suspended"
And I click on "Save changes" "button"
And I click on "//a[@data-action='editenrolment']" "xpath_element" in the "student2" "table_row"
And I should see "Student 2"
And I set the field "timeend[enabled]" to "1"
And I set the field "timeend[day]" to "1"
And I set the field "timeend[month]" to "January"
And I set the field "timeend[year]" to "2017"
And I set the field "Status" to "Active"
And I click on "Save changes" "button"
Then I should see "Suspended" in the "student1" "table_row"
And I should see "Not current" in the "student2" "table_row"
# Without JS, the user should be redirected to the original unenrol confirmation page.
Scenario: Unenrol a student
Given I log in as "teacher1"
And I am on "Course 1" course homepage
And I navigate to course participants
When I click on "//a[@data-action='unenrol']" "xpath_element" in the "student1" "table_row"
And I click on "Continue" "button"
Then I should not see "Student 1" in the "participants" "table"
+64 -21
View File
@@ -32,27 +32,27 @@ Feature: View course participants
| fullname | shortname | format |
| Course 1 | C1 | topics |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
| student3 | C1 | student |
| student4 | C1 | student |
| student5 | C1 | student |
| student6 | C1 | student |
| student7 | C1 | student |
| student8 | C1 | student |
| student9 | C1 | student |
| student10 | C1 | student |
| student11 | C1 | student |
| student12 | C1 | student |
| student13 | C1 | student |
| student14 | C1 | student |
| student15 | C1 | student |
| student16 | C1 | student |
| student17 | C1 | student |
| student18 | C1 | student |
| student19 | C1 | student |
| user | course | role | status | timeend |
| teacher1 | C1 | editingteacher | 0 | 0 |
| student1 | C1 | student | 0 | 0 |
| student2 | C1 | student | 0 | 0 |
| student3 | C1 | student | 0 | 0 |
| student4 | C1 | student | 0 | 0 |
| student5 | C1 | student | 0 | 0 |
| student6 | C1 | student | 0 | 0 |
| student7 | C1 | student | 0 | 0 |
| student8 | C1 | student | 0 | 0 |
| student9 | C1 | student | 0 | 0 |
| student10 | C1 | student | 1 | 0 |
| student11 | C1 | student | 0 | 100 |
| student12 | C1 | student | 0 | 0 |
| student13 | C1 | student | 0 | 0 |
| student14 | C1 | student | 0 | 0 |
| student15 | C1 | student | 0 | 0 |
| student16 | C1 | student | 0 | 0 |
| student17 | C1 | student | 0 | 0 |
| student18 | C1 | student | 0 | 0 |
| student19 | C1 | student | 0 | 0 |
@javascript
Scenario: Use select and deselect all buttons
@@ -222,3 +222,46 @@ Feature: View course participants
And the field with xpath "//tbody//tr[19]//input[@class='usercheckbox']" matches value "0"
And the field with xpath "//tbody//tr[20]//input[@class='usercheckbox']" matches value "0"
And the field with xpath "//tbody//tr[21]//input[@class='usercheckbox']" matches value "0"
Scenario: View the participants page as a teacher
Given I log in as "teacher1"
And I am on "Course 1" course homepage
When I navigate to course participants
Then I should see "Active" in the "student1" "table_row"
And I should see "Active" in the "student2" "table_row"
And I should see "Active" in the "student3" "table_row"
And I should see "Active" in the "student4" "table_row"
And I should see "Active" in the "student5" "table_row"
And I should see "Active" in the "student6" "table_row"
And I should see "Active" in the "student7" "table_row"
And I should see "Active" in the "student8" "table_row"
And I should see "Active" in the "student9" "table_row"
And I should see "Suspended" in the "student10" "table_row"
And I should see "Not current" in the "student11" "table_row"
And I should see "Active" in the "student12" "table_row"
And I should see "Active" in the "student13" "table_row"
And I should see "Active" in the "student14" "table_row"
And I should see "Active" in the "student15" "table_row"
And I should see "Active" in the "student16" "table_row"
And I should see "Active" in the "student17" "table_row"
And I should see "Active" in the "student18" "table_row"
And I should see "Active" in the "student19" "table_row"
Scenario: View the participants page as a student
Given I log in as "student1"
And I am on "Course 1" course homepage
When I navigate to course participants
# Student should not see the status column.
Then I should not see "Status" in the "participants" "table"
# Student should be able to see the other actively-enrolled students.
And I should see "Student 1" in the "participants" "table"
And I should see "Student 2" in the "participants" "table"
And I should see "Student 3" in the "participants" "table"
And I should see "Student 4" in the "participants" "table"
And I should see "Student 5" in the "participants" "table"
And I should see "Student 6" in the "participants" "table"
And I should see "Student 7" in the "participants" "table"
And I should see "Student 8" in the "participants" "table"
# Suspended and non-current students should not be rendered.
And I should not see "Student 10" in the "participants" "table"
And I should not see "Student 11" in the "participants" "table"
+1 -1
View File
@@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
$version = 2017072500.00; // YYYYMMDD = weekly release date of this DEV branch.
$version = 2017072600.00; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.