From c835126185c9f570b337bb9ca09a0ab81f3807cf Mon Sep 17 00:00:00 2001 From: Mihail Geshoski Date: Tue, 7 Nov 2017 16:17:00 +0800 Subject: [PATCH] MDL-60579 enrolments: Applied filters lost on certain actions AMOS BEGIN CPY [invalidrequest,enrol_lti],[invalidrequest,enrol] AMOS END --- enrol/externallib.php | 83 +++++++++++++++++++++++ enrol/tests/externallib_test.php | 65 ++++++++++++++++++ lang/en/enrol.php | 1 + lib/db/services.php | 8 +++ user/amd/build/name_page_filter.min.js | 1 + user/amd/build/status_field.min.js | 2 +- user/amd/build/unified_filter.min.js | 2 +- user/amd/src/name_page_filter.js | 64 ++++++++++++++++++ user/amd/src/status_field.js | 44 ++++++++++-- user/amd/src/unified_filter.js | 20 ++++++ user/index.php | 2 + user/renderer.php | 93 +++++++++++++++++++++++--- version.php | 2 +- 13 files changed, 367 insertions(+), 20 deletions(-) create mode 100644 user/amd/build/name_page_filter.min.js create mode 100644 user/amd/src/name_page_filter.js 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 34425f6262a..a581ba356a0 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 e89aab71007..8f13c128a6d 100644 --- a/user/index.php +++ b/user/index.php @@ -249,6 +249,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 89ceb2bca3e..bf775b45f73 100644 --- a/user/renderer.php +++ b/user/renderer.php @@ -212,31 +212,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)) { @@ -298,6 +301,9 @@ class core_user_renderer extends plugin_renderer_base { 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); $context = $indexpage->export_for_template($this->output); @@ -318,6 +324,71 @@ 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 1a90c1fb3be..90ea9994e3f 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ 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. // .XX = incremental changes.