Commit c8351261 authored by Mihail Geshoski's avatar Mihail Geshoski
Browse files

MDL-60579 enrolments: Applied filters lost on certain actions

AMOS BEGIN
    CPY [invalidrequest,enrol_lti],[invalidrequest,enrol]
AMOS END
parent 159b4e5d
...@@ -909,6 +909,89 @@ class core_enrol_external extends external_api { ...@@ -909,6 +909,89 @@ class core_enrol_external extends external_api {
) )
); );
} }
/**
* Returns description of unenrol_user_enrolment() parameters
*
* @return external_function_parameters
*/
public static function unenrol_user_enrolment_parameters() {
return new external_function_parameters(
array(
'ueid' => new external_value(PARAM_INT, 'User enrolment ID')
)
);
}
/**
* External function that unenrols a given user enrolment.
*
* @param int $ueid The user enrolment ID.
* @return array An array consisting of the processing result, errors.
*/
public static function unenrol_user_enrolment($ueid) {
global $CFG, $DB, $PAGE;
$params = self::validate_parameters(self::unenrol_user_enrolment_parameters(), [
'ueid' => $ueid
]);
$result = false;
$errors = [];
$userenrolment = $DB->get_record('user_enrolments', ['id' => $params['ueid']], '*');
if ($userenrolment) {
$userid = $userenrolment->userid;
$enrolid = $userenrolment->enrolid;
$enrol = $DB->get_record('enrol', ['id' => $enrolid], '*', MUST_EXIST);
$courseid = $enrol->courseid;
$course = get_course($courseid);
$context = context_course::instance($course->id);
self::validate_context($context);
} else {
$validationerrors['invalidrequest'] = get_string('invalidrequest', 'enrol');
}
// If the userenrolment exists, unenrol the user.
if (!isset($validationerrors)) {
require_once($CFG->dirroot . '/enrol/locallib.php');
$manager = new course_enrolment_manager($PAGE, $course);
$result = $manager->unenrol_user($userenrolment);
} else {
foreach ($validationerrors as $key => $errormessage) {
$errors[] = (object)[
'key' => $key,
'message' => $errormessage
];
}
}
return [
'result' => $result,
'errors' => $errors,
];
}
/**
* Returns description of unenrol_user_enrolment() result value
*
* @return external_description
*/
public static function unenrol_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'
),
)
);
}
} }
/** /**
......
...@@ -764,4 +764,69 @@ class core_enrol_externallib_testcase extends externallib_advanced_testcase { ...@@ -764,4 +764,69 @@ class core_enrol_externallib_testcase extends externallib_advanced_testcase {
$ue = $DB->get_record('user_enrolments', ['id' => $ueid], '*', MUST_EXIST); $ue = $DB->get_record('user_enrolments', ['id' => $ueid], '*', MUST_EXIST);
$this->assertEquals(ENROL_USER_SUSPENDED, $ue->status); $this->assertEquals(ENROL_USER_SUSPENDED, $ue->status);
} }
/**
* Test for core_enrol_external::unenrol_user_enrolment().
*/
public function test_unenerol_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);
// Invalid data by passing invalid ueid.
$data = core_enrol_external::unenrol_user_enrolment(101010);
$data = external_api::clean_returnvalue(core_enrol_external::unenrol_user_enrolment_returns(), $data);
$this->assertFalse($data['result']);
$this->assertNotEmpty($data['errors']);
// Valid data.
$data = core_enrol_external::unenrol_user_enrolment($ueid);
$data = external_api::clean_returnvalue(core_enrol_external::unenrol_user_enrolment_returns(), $data);
$this->assertTrue($data['result']);
$this->assertEmpty($data['errors']);
// Check unenrol user enrolment.
$ue = $DB->count_records('user_enrolments', ['id' => $ueid]);
$this->assertEquals(0, $ue);
}
} }
...@@ -96,6 +96,7 @@ $string['instanceeditselfwarning'] = 'Warning:'; ...@@ -96,6 +96,7 @@ $string['instanceeditselfwarning'] = 'Warning:';
$string['instanceeditselfwarningtext'] = 'You are enrolled into this course through this enrolment method, changes may affect your access to this course.'; $string['instanceeditselfwarningtext'] = 'You are enrolled into this course through this enrolment method, changes may affect your access to this course.';
$string['invalidenrolinstance'] = 'Invalid enrolment instance'; $string['invalidenrolinstance'] = 'Invalid enrolment instance';
$string['invalidrole'] = 'Invalid role'; $string['invalidrole'] = 'Invalid role';
$string['invalidrequest'] = 'Invalid request';
$string['manageenrols'] = 'Manage enrol plugins'; $string['manageenrols'] = 'Manage enrol plugins';
$string['manageinstance'] = 'Manage'; $string['manageinstance'] = 'Manage';
$string['migratetomanual'] = 'Migrate to manual enrolments'; $string['migratetomanual'] = 'Migrate to manual enrolments';
......
...@@ -546,6 +546,14 @@ $functions = array( ...@@ -546,6 +546,14 @@ $functions = array(
'type' => 'write', 'type' => 'write',
'ajax' => true, 'ajax' => true,
), ),
'core_enrol_unenrol_user_enrolment' => array(
'classname' => 'core_enrol_external',
'methodname' => 'unenrol_user_enrolment',
'classpath' => 'enrol/externallib.php',
'description' => 'External function that unenrols a given user enrolment',
'type' => 'write',
'ajax' => true,
),
'core_fetch_notifications' => array( 'core_fetch_notifications' => array(
'classname' => 'core_external', 'classname' => 'core_external',
'methodname' => 'fetch_notifications', 'methodname' => 'fetch_notifications',
......
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
// 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/>.
/**
* Name and page filter JS module for the course participants page.
*
* @module core_user/name_page_filter
* @package core_user
* @copyright 2017 Mihail Geshoski
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(['jquery', 'core_user/unified_filter'],
function($, UnifiedFilter) {
/**
* Selectors.
*
* @access private
* @type {{NAME_FILTERS: string, PAGE_FILTERS: string}}
*/
var SELECTORS = {
NAME_FILTERS: 'a.letter',
PAGE_FILTERS: 'a.page-link'
};
/**
* Init function.
*
* @method init
* @private
*/
var init = function() {
$(SELECTORS.NAME_FILTERS + ', ' + SELECTORS.PAGE_FILTERS).on('click', function(e) {
e.preventDefault();
var href = $(this).attr('href');
UnifiedFilter.getForm().attr('action', href);
UnifiedFilter.getForm().submit();
});
};
return /** @alias module:core/form-autocomplete */ {
// Public variables and functions.
/**
* Initialise the name and page user filter.
*
* @method init
*/
'init': function() {
init();
}
};
});
...@@ -126,6 +126,8 @@ define(['core/templates', ...@@ -126,6 +126,8 @@ define(['core/templates',
* @private * @private
*/ */
StatusFieldActions.prototype.bindUnenrol = function() { StatusFieldActions.prototype.bindUnenrol = function() {
var statusFieldInstsance = this;
$(SELECTORS.UNENROL).click(function(e) { $(SELECTORS.UNENROL).click(function(e) {
e.preventDefault(); e.preventDefault();
var unenrolLink = $(this); var unenrolLink = $(this);
...@@ -161,12 +163,12 @@ define(['core/templates', ...@@ -161,12 +163,12 @@ define(['core/templates',
modal.getRoot().on(ModalEvents.save, function() { modal.getRoot().on(ModalEvents.save, function() {
// Build params. // Build params.
var unenrolParams = { var unenrolParams = {
confirm: 1, 'ueid': $(unenrolLink).attr('rel')
sesskey: Config.sesskey,
ue: $(unenrolLink).attr('rel')
}; };
// Send data to unenrol page (which will redirect back to the participants page after unenrol). // Don't close the modal yet.
window.location.href = Config.wwwroot + '/enrol/unenroluser.php?' + $.param(unenrolParams); e.preventDefault();
// Submit data.
statusFieldInstsance.submitUnenrolFormAjax(modal, unenrolParams);
}); });
// Handle hidden event. // Handle hidden event.
...@@ -306,7 +308,6 @@ define(['core/templates', ...@@ -306,7 +308,6 @@ define(['core/templates',
window.M.core_formchangechecker.reset_form_dirty_state(); window.M.core_formchangechecker.reset_form_dirty_state();
} }
window.location.reload(); window.location.reload();
} else { } else {
// Serialise the form data and reload the form fragment to show validation errors. // Serialise the form data and reload the form fragment to show validation errors.
var formData = JSON.stringify(form.serialize()); var formData = JSON.stringify(form.serialize());
...@@ -315,6 +316,37 @@ define(['core/templates', ...@@ -315,6 +316,37 @@ define(['core/templates',
}).fail(Notification.exception); }).fail(Notification.exception);
}; };
/**
* Private method
*
* @method submitUnenrolFormAjax
* @param {Object} modal The the AMD modal object containing the form.
* @param {Object} unenrolParams The unenrol parameters.
* @private
*/
StatusFieldActions.prototype.submitUnenrolFormAjax = function(modal, unenrolParams) {
var request = {
methodname: 'core_enrol_unenrol_user_enrolment',
args: unenrolParams
};
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 {
// Display an alert containing the error message
Notification.alert(data.errors[0].key, data.errors[0].message);
}
}).fail(Notification.exception);
};
/** /**
* Private method * Private method
* *
......
...@@ -104,6 +104,17 @@ define(['jquery', 'core/form-autocomplete', 'core/str', 'core/notification'], ...@@ -104,6 +104,17 @@ define(['jquery', 'core/form-autocomplete', 'core/str', 'core/notification'],
}); });
}; };
/**
* Return the unified user filter form.
*
* @method getForm
* @private
* @return {Form object}
*/
var getForm = function() {
return $(SELECTORS.UNIFIED_FILTERS).closest('form');
};
return /** @alias module:core/form-autocomplete */ { return /** @alias module:core/form-autocomplete */ {
// Public variables and functions. // Public variables and functions.
/** /**
...@@ -113,6 +124,15 @@ define(['jquery', 'core/form-autocomplete', 'core/str', 'core/notification'], ...@@ -113,6 +124,15 @@ define(['jquery', 'core/form-autocomplete', 'core/str', 'core/notification'],
*/ */
'init': function() { 'init': function() {
init(); init();
},
/**
* Return the unified user filter form.
*
* @method getForm
* @return {Form object}
*/
'getForm': function() {
return getForm();
} }
}; };
}); });
...@@ -249,6 +249,8 @@ if ($bulkoperations) { ...@@ -249,6 +249,8 @@ if ($bulkoperations) {
echo $participanttablehtml; echo $participanttablehtml;
$PAGE->requires->js_call_amd('core_user/name_page_filter', 'init');
$perpageurl = clone($baseurl); $perpageurl = clone($baseurl);
$perpageurl->remove_params('perpage'); $perpageurl->remove_params('perpage');
if ($perpage == SHOW_ALL_PAGE_SIZE && $participanttable->totalrows > DEFAULT_PAGE_SIZE) { if ($perpage == SHOW_ALL_PAGE_SIZE && $participanttable->totalrows > DEFAULT_PAGE_SIZE) {
......
...@@ -212,31 +212,34 @@ class core_user_renderer extends plugin_renderer_base { ...@@ -212,31 +212,34 @@ class core_user_renderer extends plugin_renderer_base {
// Days. // Days.
for ($i = 1; $i < 7; $i++) { for ($i = 1; $i < 7; $i++) {
$timestamp = strtotime('-' . $i . ' days', $now); $timestamp = strtotime('-' . $i . ' days', $now);
if ($timestamp >= $minlastaccess) { if ($timestamp < $minlastaccess) {
$value = get_string('numdays', 'moodle', $i); break;
$timeoptions += $this->format_filter_option(USER_FILTER_LAST_ACCESS, $criteria, $timestamp, $value);
} }
$value = get_string('numdays', 'moodle', $i);
$timeoptions += $this->format_filter_option(USER_FILTER_LAST_ACCESS, $criteria, $timestamp, $value);
} }
// Weeks. // Weeks.
for ($i = 1; $i < 10; $i++) { for ($i = 1; $i < 10; $i++) {
$timestamp = strtotime('-'.$i.' weeks', $now); $timestamp = strtotime('-'.$i.' weeks', $now);
if ($timestamp >= $minlastaccess) { if ($timestamp < $minlastaccess) {
$value = get_string('numweeks', 'moodle', $i); break;
$timeoptions += $this->format_filter_option(USER_FILTER_LAST_ACCESS, $criteria, $timestamp, $value);
} }
$value = get_string('numweeks', 'moodle', $i);
$timeoptions += $this->format_filter_option(USER_FILTER_LAST_ACCESS, $criteria, $timestamp, $value);
} }
// Months. // Months.
for ($i = 2; $i < 12; $i++) { for ($i = 2; $i < 12; $i++) {
$timestamp = strtotime('-'.$i.' months', $now); $timestamp = strtotime('-'.$i.' months', $now);
if ($timestamp >= $minlastaccess) { if ($timestamp < $minlastaccess) {
$value = get_string('nummonths', 'moodle', $i); break;
$timeoptions += $this->format_filter_option(USER_FILTER_LAST_ACCESS, $criteria, $timestamp, $value);
} }
$value = get_string('nummonths', 'moodle', $i);
$timeoptions += $this->format_filter_option(USER_FILTER_LAST_ACCESS, $criteria, $timestamp, $value);
} }
// Try a year. // Try a year.
$timestamp = strtotime('-'.$i.' year', $now); $timestamp = strtotime('-1 year', $now);
if ($timestamp >= $minlastaccess) { if ($timestamp >= $minlastaccess) {
$value = get_string('lastyear', 'moodle'); $value = get_string('numyear', 'moodle', 1);
$timeoptions += $this->format_filter_option(USER_FILTER_LAST_ACCESS, $criteria, $timestamp, $value); $timeoptions += $this->format_filter_option(USER_FILTER_LAST_ACCESS, $criteria, $timestamp, $value);
} }
if (!empty($lastaccess0exists)) { if (!empty($lastaccess0exists)) {
...@@ -298,6 +301,9 @@ class core_user_renderer extends plugin_renderer_base { ...@@ -298,6 +301,9 @@ class core_user_renderer extends plugin_renderer_base {
get_string('inactive')); get_string('inactive'));
} }
// Add missing applied filters to the filter options.
$filteroptions = $this->handle_missing_applied_filters($filtersapplied, $filteroptions);
$indexpage = new \core_user\output\unified_filter($filteroptions, $filtersapplied); $indexpage = new \core_user\output\unified_filter($filteroptions, $filtersapplied);
$context = $indexpage->export_for_template($this->output); $context = $indexpage->export_for_template($this->output);
...@@ -318,6 +324,71 @@ class core_user_renderer extends plugin_renderer_base { ...@@ -318,6 +324,71 @@ class core_user_renderer extends plugin_renderer_base {
$optionvalue = "$filtertype:$value"; $optionvalue = "$filtertype:$value";
return [$optionvalue => $optionlabel]; return [$optionvalue => $optionlabel];
} }
/**
* Handles cases when after reloading the applied filters are missing in the filter options.
*
* @param array $filtersapplied The applied filters.
* @param array $filteroptions The filter options.
* @return array The formatted options with the ['filtertype:value' => 'criteria: label'] format.
*/
private function handle_missing_applied_filters($filtersapplied, $filteroptions) {
global $DB;
foreach ($filtersapplied as $filter) {
if (!array_key_exists($filter, $filteroptions)) {
$filtervalue = explode(':', $filter);
$key = $filtervalue[0];
$value = $filtervalue[1];
switch($key) {
case USER_FILTER_LAST_ACCESS:
$now = usergetmidnight(time());
$criteria = get_string('usersnoaccesssince');
// Days.
for ($i = 1; $i < 7; $i++) {
$timestamp = strtotime('-' . $i . ' days', $now);
if ($timestamp < $value) {
break;
}
$val = get_string('numdays', 'moodle', $i);
$filteroptions += $this->format_filter_option(USER_FILTER_LAST_ACCESS, $criteria, $timestamp, $val);
}
// Weeks.
for ($i = 1; $i < 10; $i++) {
$timestamp = strtotime('-'.$i.' weeks', $now);
if ($timestamp < $value) {
break;
}
$val = get_string('numweeks', 'moodle', $i);
$filteroptions += $this->format_filter_option(USER_FILTER_LAST_ACCESS, $criteria, $timestamp, $val);
}
// Months.
for ($i = 2; $i < 12; $i++) {
$timestamp = strtotime('-'.$i.' months', $now);
if ($timestamp < $value) {
break;
}
$val = get_string('nummonths', 'moodle', $i);
$filteroptions += $this->format_filter_option(USER_FILTER_LAST_ACCESS, $criteria, $timestamp, $val);
}
// Try a year.
$timestamp = strtotime('-1 year', $now);
if ($timestamp >= $value) {
$val = get_string('numyear', 'moodle', 1);
$filteroptions += $this->format_filter_option(USER_FILTER_LAST_ACCESS, $criteria, $timestamp, $val);
}
case USER_FILTER_ROLE:
$criteria = get_string('role');
if ($role = $DB->get_record('role', array('id' => $value))) {
$role = role_get_name($role);
$filteroptions += $this->format_filter_option(USER_FILTER_ROLE, $criteria, $value, $role);
}
}
}
}
return $filteroptions;
}
} }
/** /**
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die(); defined('MOODLE_INTERNAL') || die();
$version = 2017110300.00; // YYYYMMDD = weekly release date of this DEV branch. $version = 2017110100.01; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches. // RR = release increments - 00 in DEV branches.
// .XX = incremental changes. // .XX = incremental changes.
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment