diff --git a/enrol/externallib.php b/enrol/externallib.php
index 670be50703f..ea5f971c9e0 100644
--- a/enrol/externallib.php
+++ b/enrol/externallib.php
@@ -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'
+ ),
+ )
+ );
+ }
}
/**
diff --git a/enrol/tests/externallib_test.php b/enrol/tests/externallib_test.php
index 8b787a145ec..d85f4d30fd4 100644
--- a/enrol/tests/externallib_test.php
+++ b/enrol/tests/externallib_test.php
@@ -764,4 +764,69 @@ class core_enrol_externallib_testcase extends externallib_advanced_testcase {
$ue = $DB->get_record('user_enrolments', ['id' => $ueid], '*', MUST_EXIST);
$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);
+ }
}
diff --git a/lang/en/enrol.php b/lang/en/enrol.php
index 8c870a6667e..1d29ca68ae8 100644
--- a/lang/en/enrol.php
+++ b/lang/en/enrol.php
@@ -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['invalidenrolinstance'] = 'Invalid enrolment instance';
$string['invalidrole'] = 'Invalid role';
+$string['invalidrequest'] = 'Invalid request';
$string['manageenrols'] = 'Manage enrol plugins';
$string['manageinstance'] = 'Manage';
$string['migratetomanual'] = 'Migrate to manual enrolments';
diff --git a/lib/db/services.php b/lib/db/services.php
index 3bef679ab53..e8e422792ea 100644
--- a/lib/db/services.php
+++ b/lib/db/services.php
@@ -546,6 +546,14 @@ $functions = array(
'type' => 'write',
'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(
'classname' => 'core_external',
'methodname' => 'fetch_notifications',
diff --git a/user/amd/build/name_page_filter.min.js b/user/amd/build/name_page_filter.min.js
new file mode 100644
index 00000000000..2d4fcac62c3
--- /dev/null
+++ b/user/amd/build/name_page_filter.min.js
@@ -0,0 +1 @@
+define(["jquery","core_user/unified_filter"],function(a,b){var c={NAME_FILTERS:"a.letter",PAGE_FILTERS:"a.page-link"},d=function(){a(c.NAME_FILTERS+", "+c.PAGE_FILTERS).on("click",function(c){c.preventDefault();var d=a(this).attr("href");b.getForm().attr("action",d),b.getForm().submit()})};return{init:function(){d()}}});
\ No newline at end of file
diff --git a/user/amd/build/status_field.min.js b/user/amd/build/status_field.min.js
index 29f8eef5bc6..2079e476386 100644
--- a/user/amd/build/status_field.min.js
+++ b/user/amd/build/status_field.min.js
@@ -1 +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"),enrolinstancename:i.data("enrolinstancename")}}],k=f.create({type:f.types.SAVE_CANCEL});b.when(c.get_strings(j),k).done(function(a,c){var e=a[0],f=a[1];c.setTitle(e),c.setBody(f),c.setSaveButtonText(e),c.getRoot().on(g.save,function(){var a={confirm:1,sesskey:d.sesskey,ue:b(h).attr("rel")};window.location.href=d.wwwroot+"/enrol/unenroluser.php?"+b.param(a)}),c.getRoot().on(g.hidden,function(){c.destroy()}),c.show()}).fail(e.exception)})},k.prototype.bindStatusDetails=function(){b(j.SHOW_DETAILS).click(function(d){d.preventDefault();var h=b(this),i=h.parent(),k={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")},l=[{key:"enroldetails",component:"enrol"}],m=h.next(j.EDIT_ENROLMENT);m.length&&(k.editenrollink=b("
").append(m.clone()).html());var n=c.get_strings(l),o=f.create({large:!0,type:f.types.CANCEL});b.when(n,o).done(function(c,d){var e=a.render("core_user/status_details",k);d.setTitle(c[0]),d.setBody(e),m.length&&d.getRoot().on("click",j.EDIT_ENROLMENT,function(a){a.preventDefault(),d.hide(),b(m).trigger("click")}),d.show(),d.getRoot().on(g.hidden,function(){d.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)}}});
\ No newline at end of file
+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(){var a=this;b(j.UNENROL).click(function(d){d.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"),enrolinstancename:i.data("enrolinstancename")}}],k=f.create({type:f.types.SAVE_CANCEL});b.when(c.get_strings(j),k).done(function(c,e){var f=c[0],i=c[1];e.setTitle(f),e.setBody(i),e.setSaveButtonText(f),e.getRoot().on(g.save,function(){var c={ueid:b(h).attr("rel")};d.preventDefault(),a.submitUnenrolFormAjax(e,c)}),e.getRoot().on(g.hidden,function(){e.destroy()}),e.show()}).fail(e.exception)})},k.prototype.bindStatusDetails=function(){b(j.SHOW_DETAILS).click(function(d){d.preventDefault();var h=b(this),i=h.parent(),k={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")},l=[{key:"enroldetails",component:"enrol"}],m=h.next(j.EDIT_ENROLMENT);m.length&&(k.editenrollink=b("
").append(m.clone()).html());var n=c.get_strings(l),o=f.create({large:!0,type:f.types.CANCEL});b.when(n,o).done(function(c,d){var e=a.render("core_user/status_details",k);d.setTitle(c[0]),d.setBody(e),m.length&&d.getRoot().on("click",j.EDIT_ENROLMENT,function(a){a.preventDefault(),d.hide(),b(m).trigger("click")}),d.show(),d.getRoot().on(g.hidden,function(){d.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.submitUnenrolFormAjax=function(a,b){var c={methodname:"core_enrol_unenrol_user_enrolment",args:b};i.call([c])[0].done(function(b){b.result?(a.hide(),"undefined"!=typeof window.M.core_formchangechecker&&window.M.core_formchangechecker.reset_form_dirty_state(),window.location.reload()):e.alert(b.errors[0].key,b.errors[0].message)}).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)}}});
\ No newline at end of file
diff --git a/user/amd/build/unified_filter.min.js b/user/amd/build/unified_filter.min.js
index bb8a6035926..eca6bea47df 100644
--- a/user/amd/build/unified_filter.min.js
+++ b/user/amd/build/unified_filter.min.js
@@ -1 +1 @@
-define(["jquery","core/form-autocomplete","core/str","core/notification"],function(a,b,c,d){var e={UNIFIED_FILTERS:"#unified-filters"},f=function(){var f=[{key:"userfilterplaceholder",component:"moodle"},{key:"nofiltersapplied",component:"moodle"}];c.get_strings(f).done(function(a){var c=a[0],d=a[1];b.enhance(e.UNIFIED_FILTERS,!0,"core_user/unified_filter_datasource",c,!1,!0,d,!0)}).fail(d.exception);var g=a(e.UNIFIED_FILTERS).val();a(e.UNIFIED_FILTERS).on("change",function(){var b=a(this).val(),c=[],d=!1;if(a.each(b,function(a,b){var e=b.split(":",2);if(2!==e.length)return!0;var f=e[0],g=e[1];return"undefined"!=typeof c[f]&&(d=!0),c[f]=g,!0}),d){var e=[];for(var f in c)e.push(f+":"+c[f]);a(this).val(e)}g.join(",")!=b.join(",")&&this.form.submit()})};return{init:function(){f()}}});
\ No newline at end of file
+define(["jquery","core/form-autocomplete","core/str","core/notification"],function(a,b,c,d){var e={UNIFIED_FILTERS:"#unified-filters"},f=function(){var f=[{key:"userfilterplaceholder",component:"moodle"},{key:"nofiltersapplied",component:"moodle"}];c.get_strings(f).done(function(a){var c=a[0],d=a[1];b.enhance(e.UNIFIED_FILTERS,!0,"core_user/unified_filter_datasource",c,!1,!0,d,!0)}).fail(d.exception);var g=a(e.UNIFIED_FILTERS).val();a(e.UNIFIED_FILTERS).on("change",function(){var b=a(this).val(),c=[],d=!1;if(a.each(b,function(a,b){var e=b.split(":",2);if(2!==e.length)return!0;var f=e[0],g=e[1];return"undefined"!=typeof c[f]&&(d=!0),c[f]=g,!0}),d){var e=[];for(var f in c)e.push(f+":"+c[f]);a(this).val(e)}g.join(",")!=b.join(",")&&this.form.submit()})},g=function(){return a(e.UNIFIED_FILTERS).closest("form")};return{init:function(){f()},getForm:function(){return g()}}});
\ No newline at end of file
diff --git a/user/amd/src/name_page_filter.js b/user/amd/src/name_page_filter.js
new file mode 100644
index 00000000000..8ebf4e76e0d
--- /dev/null
+++ b/user/amd/src/name_page_filter.js
@@ -0,0 +1,64 @@
+// 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 .
+
+/**
+ * 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();
+ }
+ };
+});
diff --git a/user/amd/src/status_field.js b/user/amd/src/status_field.js
index c17844fa73c..23f5b7f9409 100644
--- a/user/amd/src/status_field.js
+++ b/user/amd/src/status_field.js
@@ -126,6 +126,8 @@ define(['core/templates',
* @private
*/
StatusFieldActions.prototype.bindUnenrol = function() {
+ var statusFieldInstsance = this;
+
$(SELECTORS.UNENROL).click(function(e) {
e.preventDefault();
var unenrolLink = $(this);
@@ -161,12 +163,12 @@ define(['core/templates',
modal.getRoot().on(ModalEvents.save, function() {
// Build params.
var unenrolParams = {
- confirm: 1,
- sesskey: Config.sesskey,
- ue: $(unenrolLink).attr('rel')
+ 'ueid': $(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);
+ // Don't close the modal yet.
+ e.preventDefault();
+ // Submit data.
+ statusFieldInstsance.submitUnenrolFormAjax(modal, unenrolParams);
});
// Handle hidden event.
@@ -306,7 +308,6 @@ define(['core/templates',
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());
@@ -315,6 +316,37 @@ define(['core/templates',
}).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
*
diff --git a/user/amd/src/unified_filter.js b/user/amd/src/unified_filter.js
index bfb2df1eeef..2fddee3bb92 100644
--- a/user/amd/src/unified_filter.js
+++ b/user/amd/src/unified_filter.js
@@ -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 */ {
// Public variables and functions.
/**
@@ -113,6 +124,15 @@ define(['jquery', 'core/form-autocomplete', 'core/str', 'core/notification'],
*/
'init': function() {
init();
+ },
+ /**
+ * Return the unified user filter form.
+ *
+ * @method getForm
+ * @return {Form object}
+ */
+ 'getForm': function() {
+ return getForm();
}
};
});
diff --git a/user/index.php b/user/index.php
index 69f8f946c5a..22ae57edf21 100644
--- a/user/index.php
+++ b/user/index.php
@@ -250,6 +250,8 @@ if ($bulkoperations) {
echo $participanttablehtml;
+$PAGE->requires->js_call_amd('core_user/name_page_filter', 'init');
+
$perpageurl = clone($baseurl);
$perpageurl->remove_params('perpage');
if ($perpage == SHOW_ALL_PAGE_SIZE && $participanttable->totalrows > DEFAULT_PAGE_SIZE) {
diff --git a/user/renderer.php b/user/renderer.php
index eb2026ec1ab..40574c39436 100644
--- a/user/renderer.php
+++ b/user/renderer.php
@@ -202,31 +202,34 @@ class core_user_renderer extends plugin_renderer_base {
// Days.
for ($i = 1; $i < 7; $i++) {
$timestamp = strtotime('-' . $i . ' days', $now);
- if ($timestamp >= $minlastaccess) {
- $value = get_string('numdays', 'moodle', $i);
- $timeoptions += $this->format_filter_option(USER_FILTER_LAST_ACCESS, $criteria, $timestamp, $value);
+ if ($timestamp < $minlastaccess) {
+ break;
}
+ $value = get_string('numdays', 'moodle', $i);
+ $timeoptions += $this->format_filter_option(USER_FILTER_LAST_ACCESS, $criteria, $timestamp, $value);
}
// Weeks.
for ($i = 1; $i < 10; $i++) {
$timestamp = strtotime('-'.$i.' weeks', $now);
- if ($timestamp >= $minlastaccess) {
- $value = get_string('numweeks', 'moodle', $i);
- $timeoptions += $this->format_filter_option(USER_FILTER_LAST_ACCESS, $criteria, $timestamp, $value);
+ if ($timestamp < $minlastaccess) {
+ break;
}
+ $value = get_string('numweeks', 'moodle', $i);
+ $timeoptions += $this->format_filter_option(USER_FILTER_LAST_ACCESS, $criteria, $timestamp, $value);
}
// Months.
for ($i = 2; $i < 12; $i++) {
$timestamp = strtotime('-'.$i.' months', $now);
- if ($timestamp >= $minlastaccess) {
- $value = get_string('nummonths', 'moodle', $i);
- $timeoptions += $this->format_filter_option(USER_FILTER_LAST_ACCESS, $criteria, $timestamp, $value);
+ if ($timestamp < $minlastaccess) {
+ break;
}
+ $value = get_string('nummonths', 'moodle', $i);
+ $timeoptions += $this->format_filter_option(USER_FILTER_LAST_ACCESS, $criteria, $timestamp, $value);
}
// Try a year.
- $timestamp = strtotime('-'.$i.' year', $now);
+ $timestamp = strtotime('-1 year', $now);
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);
}
if (!empty($lastaccess0exists)) {
@@ -239,6 +242,9 @@ class core_user_renderer extends plugin_renderer_base {
}
+ // 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);
$context = $indexpage->export_for_template($this->output);
@@ -259,4 +265,69 @@ class core_user_renderer extends plugin_renderer_base {
$optionvalue = "$filtertype:$value";
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;
+ }
}
diff --git a/version.php b/version.php
index 37626e3d5a1..cfbcef78b41 100644
--- a/version.php
+++ b/version.php
@@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
-$version = 2017110800.00; // YYYYMMDD = weekly release date of this DEV branch.
+$version = 2017110800.01; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.