ÿØÿà JFIF    ÿÛ „  ( %!1"%)+...383-7(-.+  -%%--------+------------/----------------------------ÿÀ  ¶" ÿÄ     ÿÄ @    !1AQaq‘¡"±ÁÑð2’BRráS‚ñ3b¢CT²Ò #cÿÄ    ÿÄ '   1!AQ2a"‘±#ÿÚ   ? ò(/p‘—–¢·,M|¯G»R£üešT. ôŸEoCFñ‡ 4ÔkåóxR|µÁ¨ÓDà!Ç´µ˜‡p ÍQ‹§!þž^õWsŒy  åÁ§$á«%ºq˜™JyyË0y«^Ϗ -3ÃÏËÈ£Æ\P’ä„s&•‹Üs üô'àWt¹pñCp$bÚF`·7.^)¾!)ÒÙ˜–ÅÏš^h¾ø³·ìe±ŒaeÖ—Í,e=B}Jéߦ$$½—šÙÜCC¯¶è8β–Åkèwx LÉngT±æ¹£Ócœ¿Í/©Éÿ ‰ÚCÒ­mq;Tt¶–}0"zÖPcd1š­¤tˆ„ÐÓâqú[™öUCŠ2޿د­É(¸ßúçšmW,Ò ºf‹Ån™ÄLêÁ8üÁkÁ€L¥ÍuJ1œôrÂSŒÓöt!7EUíïb)AËi¬â-ÙHk8òÁiXYwÅ)O^"ÇØriÛô{»wRUKÙÏÚlÓ¬€‚’שT ] ªb—ÊîKC^×M“xùäñz¬zJ× {4[¹M'IÆeH1LCWÕ]œûº WDæà°OÅ‚¹c²ÞuA–rL`HšpCez‡V’— Ž‚Ë„–ñ­'µR0ÕÖ=À:±G»C\nÆÉ5*¢ †3šhQ4ýÒi$Õ1"Ü]¢p-q3zcQ$ªö¨¥æf›‘»´ÝÚHá„e²JÇ–iÊ:·à¬Øc5Åk»Laª*wi [0Ówk ¦a¦0Õ²ÅÅ‚T¸’³q:Æ<¹ú Ò˜Æ}2VE•Ѐp¡Ô3Úº …h³^LJºçì¥Ánó|˜ÖˆÎˆ+« ¦dø‘Mº–d\ªDðÛ„Ì×LÝ——Ð9Ç|•FèçVm™¨€™3õS–9Äå¢<̉Ìm¢±`·º˜&Z²ž Ë®D«¶»6™‰©­HÈ Àá«h­i³04æ’hxœi3ï¨(ÓL·†‹¶ËpŒ ƒÜÒ? :õdsÀ –3æÓ·Üjâ¬9—]8n¼&]I F£¼…ì–—Œ0v°LǐÅfÛ2TÖ’aµ˜N@ÖBSÏ^à´4-±°b×nÈÈœÆD=KdŒø…ÄÈK,›ÖR§fjü$^ÔÎ!„6^ s:Üun\ôkCÞò÷<—Lúl Í›G¹ÀPá¼Nc¡šÒ…ÙWͳœ±vÊáЄ×9‹P‰cEØ‘™›µŸP<×g1 º©Ø¬wiÁ^l5× I/';Ë+´Z‡ÓÄFÕp[Èð’ HÄ ó 9¬Dl4“é±Ï”V=^Hð;žLädÉa‚°Ë,#/Ž~²BÔÛ QãñQtIeórVB,ÏÂf‹4#Μc‹É3ZIÖ Œ¢¥·üxa‡Ãçè„jŠ[5 Ä!U?,3žÎ×…ðdÓ÷háŠA‰ìÄ4÷ÃÜZÃE{‰®+W\ZÍE[‰»µk»MÝ­`¢©†˜ÃV‹-f¢¡†¢a«eŠ%«Y¨©q%jâd,Ôq`) ¤¦…šˆ ¦E>0Ùb²t–Ÿ  ÷jsςҒKÈcø(é›;k(’#ôŒQ ™C> õ³M9óºÆµ¤çâ;uÀ)5>RÔ¸fÓ~Ȧ—’-$fe«"FâUˆÊwµPÏu w+"åÉó¨6s™Û9(À¨ù©hE°½†n`m~“@©›p!Vkhµ Á1šbEqqÊ@ºèl›2 –Yã%‘‚hÛ{a¶‡ÅÞM²¬çL34®Çl]®‹ŽØŒm'RÜe35æöˆ­Á­ cP9šeÁköo´L³Ì=p8]”‡B¶,šº|ɏdzXˆØk*ÇÚ{#ñ‰pã(€¶‡9ý'šÜ³½¯kƒ†Â’ëSOƒ•Å®H6+a¢µˆ‹Y¨b˜b+XˆÖ!°u!¢"µˆˆn2ˆ Å0Ä`Å Ô®c(ÓÜGº•Ä»Œ âWî%q.áÐÄ÷î%umÍ£+ÜQ,Vn¦-GphU¸¢X­&,Gt ·K¢ÅÅ·¥RÄÊÉbKn N­R2g$VµÕó Qr#–ÓZUÏð0ÎtÙÀœz ëöo“Îk¯n„T°"SÞ+³/…€½"ižãO%Í%&Δâ‘ËØ´IˆÓ°Ë‰ÞM3.™yçÁzit›`4C`k¢¼á!álÄï sÂ[×!¤l¾æo"ƒIÔ'@ªu)3›ô®“@Z à¾] üœ'‡4¸PÞkÁ-qÄH‰ÓÍBd´ÿ —¹°ÄA&ÊUC„öz‚‚RæŠtf¬>˜ÑidšEçÑL÷áý¡V±h§­c¿¥¨ƒ@g¼…Ýö^ÞËc.ž„æ“I[ ª+·zÐÒÚ,½Ð»¶€C›7jk$๖€Ž¾ÅÙpp¶ÍþéΤ ½ØÎŒq ñpæžÅ¥#Øâ49ÃÂK^MYV“,¶Éz_òæ:…;Õ«¦I3âJ£¤û5$7Èxün\â]Êe¯(Ÿ&Ž‹¶2<6Ćf× æ¢ƱqWı[(ÙÂŽç‚T7¨@ÈÉÍß‚ïÙ"&3TYlGށ†)†#5ŠA©^T2ÆÁ†)©†©†¤yGXÁ†©"†)©¼ÅV WS†#©]R–r‹q5ÕbâWþBh¯u+ªÅÄ×ü„nÑ^êkªÁj‰jeœWˆÕÕ`µDµ2Ì+Ä µDµµ5Ô{¤Þ0RF’H÷AÛ<ÊÁ§ ƈæ0Ñ£ê4 öÚµ[¬/1²€â<-¬šö²r×0o×w ´xÐÒË„ g01¬„åEM蛇Ã\6µE›‘fÙO k÷CºnÓ1`iîÒ² áÂ!ÑLÄÿ ³¬\õ*:M=Ç»c‹2[‰ØsÕLò\ŒK L^™üÄN\¶#» Ç\……ßïIœÉ$¸ÎñÍ®ÔHÇ Ûí/´>Li”„†g^$ 7NÍÑÑ2M2ªNg=k½Ð}Ÿl6‡ —JWˆ”ÞéT·+µ¦üÂFè¢Vs6ýÆ4»2+2IÝ9ô9®ÇGÙÇ1+Ä8;*Õ§ž½D*îÑWÚçá3Ma¤·ˆ¥t}š²Ü…Q+Æüµ8HíS”üxGéÁˆ¦Ël„éÈ 5 µÄƒ6äÜ(kIš¯S†fÖ&¸®×v|Äyˆ*禌Éù@7-®Ä[]ÎÖ¿ê`º…=:…žJVnݳvðÖKE¼‚ »mà ÷f®:Ë ™–4rð¡4` ܹçž~‹C=œ…¾Ïj|VDtK.ƒ"9 Ï ­–éXá³6WÒïIL-Ї Ç PKB\. /** * This file keeps track of upgrades to the manual enrolment plugin * * @package enrol_manual * @copyright 2012 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ function xmldb_enrol_manual_upgrade($oldversion) { // Automatically generated Moodle v4.2.0 release upgrade line. // Put any upgrade step following this. // Automatically generated Moodle v4.3.0 release upgrade line. // Put any upgrade step following this. // Automatically generated Moodle v4.4.0 release upgrade line. // Put any upgrade step following this. // Automatically generated Moodle v4.5.0 release upgrade line. // Put any upgrade step following this. // Automatically generated Moodle v5.0.0 release upgrade line. // Put any upgrade step following this. return true; } PKB\*Rlang/en/auth_manual.phpnu[. /** * Strings for component 'auth_manual', language 'en'. * * @package auth_manual * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ $string['auth_manualdescription'] = 'This method removes any way for users to create their own accounts. All accounts must be manually created by the admin user.'; $string['expiration'] = 'Enable password expiry'; $string['expiration_desc'] = 'Allow passwords to expire after a specified time.'; $string['expiration_warning'] = 'Notification threshold'; $string['expiration_warning_desc'] = 'Number of days before password expiry that a notification is issued.'; $string['passwdexpiretime'] = 'Password duration'; $string['passwdexpiretime_desc'] = 'Length of time for which a password is valid.'; $string['pluginname'] = 'Manual accounts'; $string['passwdexpire_settings'] = 'Password expiry settings'; $string['privacy:metadata:preference:passwordupdatetime'] = 'The date of the last password change.'; PKB\. /** * Defines the version of the subplugin * * @package workshopallocation * @subpackage manual * @copyright 2009 David Mudrak * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); $plugin->version = 2025041400; $plugin->requires = 2025040800; // Requires this Moodle version. $plugin->component = 'workshopallocation_manual'; PKB\56 classes/privacy/provider.phpnu[. /** * Provides the class {@link workshopallocation_manual\privacy\provider} * * @package workshopallocation_manual * @category privacy * @copyright 2018 David Mudrák * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace workshopallocation_manual\privacy; use core_privacy\local\metadata\collection; use core_privacy\local\request\writer; defined('MOODLE_INTERNAL') || die(); /** * Privacy API implementation for the Manual allocation method. * * @copyright 2018 David Mudrák * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider implements \core_privacy\local\metadata\provider, \core_privacy\local\request\user_preference_provider { /** * Describe all the places where this plugin stores some personal data. * * @param collection $collection Collection of items to add metadata to. * @return collection Collection with our added items. */ public static function get_metadata(collection $collection): collection { $collection->add_user_preference('workshopallocation_manual_perpage', 'privacy:metadata:preference:perpage'); return $collection; } /** * Export user preferences controlled by this plugin. * * @param int $userid ID of the user we are exporting data form. */ public static function export_user_preferences(int $userid) { $perpage = get_user_preferences('workshopallocation_manual_perpage', null, $userid); if ($perpage !== null) { writer::export_user_preference('workshopallocation_manual', 'workshopallocation_manual_perpage', $perpage, get_string('privacy:metadata:preference:perpage', 'workshopallocation_manual')); } } } PKB\?(ɮ upgrade.txtnu[=== 4.5 Onwards === This file has been replaced by UPGRADING.md. See MDL-81125 for further information. === This files describes API changes in /auth/manual/*, information provided here is intended especially for developers. === 3.3 === * The config.html file was migrated to use the admin settings API. The identifier for configuration data stored in config_plugins table was converted from 'auth/manual' to 'auth_manual'. PKB\arvA A tests/manual_test.phpnu[. namespace auth_manual; use auth_plugin_manual; defined('MOODLE_INTERNAL') || die(); global $CFG; require_once($CFG->dirroot.'/auth/manual/auth.php'); /** * Manual authentication tests class. * * @package auth_manual * @category test * @copyright 2014 Gilles-Philippe Leblanc * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ final class manual_test extends \advanced_testcase { /** @var auth_plugin_manual Keeps the authentication plugin. */ protected $authplugin; /** * Setup test data. */ protected function setUp(): void { parent::setUp(); $this->resetAfterTest(true); $this->authplugin = new auth_plugin_manual(); set_config('expiration', '1', 'auth_manual'); set_config('expiration_warning', '2', 'auth_manual'); set_config('expirationtime', '30', 'auth_manual'); $this->authplugin->config = get_config(auth_plugin_manual::COMPONENT_NAME); } /** * Test user_update_password method. */ public function test_user_update_password(): void { $user = $this->getDataGenerator()->create_user(); $expectedtime = time(); $passwordisupdated = $this->authplugin->user_update_password($user, 'MyNewPassword*'); // Assert that the actual time should be equal or a little greater than the expected time. $this->assertGreaterThanOrEqual($expectedtime, get_user_preferences('auth_manual_passwordupdatetime', 0, $user->id)); // Assert that the password was successfully updated. $this->assertTrue($passwordisupdated); } /** * Test test_password_expire method. */ public function test_password_expire(): void { $userrecord = array(); $expirationtime = 31 * DAYSECS; $userrecord['timecreated'] = time() - $expirationtime; $user1 = $this->getDataGenerator()->create_user($userrecord); $user2 = $this->getDataGenerator()->create_user(); // The user 1 was created 31 days ago and has not changed his password yet, so the password has expirated. $this->assertLessThanOrEqual(-1, $this->authplugin->password_expire($user1->username)); // The user 2 just came to be created and has not changed his password yet, so the password has not expirated. $this->assertEquals(30, $this->authplugin->password_expire($user2->username)); $this->authplugin->user_update_password($user1, 'MyNewPassword*'); // The user 1 just updated his password so the password has not expirated. $this->assertEquals(30, $this->authplugin->password_expire($user1->username)); } } PKB\^. tests/privacy/provider_test.phpnu[. /** * Provides the {@see workshopallocation_manual\privacy\provider_test} class. * * @package workshopallocation_manual * @category test * @copyright 2018 David Mudrák * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace workshopallocation_manual\privacy; use core_privacy\local\request\writer; defined('MOODLE_INTERNAL') || die(); /** * Unit tests for the privacy API implementation. * * @copyright 2018 David Mudrák * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ final class provider_test extends \core_privacy\tests\provider_testcase { /** * When no preference exists, there should be no export. */ public function test_no_preference(): void { global $USER; $this->resetAfterTest(); $this->setAdminUser(); \workshopallocation_manual\privacy\provider::export_user_preferences($USER->id); $this->assertFalse(writer::with_context(\context_system::instance())->has_any_data()); } /** * Test that the recently selected perpage is exported. */ public function test_export_preferences(): void { global $USER; $this->resetAfterTest(); $this->setAdminUser(); set_user_preference('workshopallocation_manual_perpage', 81); \workshopallocation_manual\privacy\provider::export_user_preferences($USER->id); $this->assertTrue(writer::with_context(\context_system::instance())->has_any_data()); $prefs = writer::with_context(\context_system::instance())->get_user_preferences('workshopallocation_manual'); $this->assertNotEmpty($prefs->workshopallocation_manual_perpage); $this->assertEquals(81, $prefs->workshopallocation_manual_perpage->value); $this->assertStringContainsString(get_string('privacy:metadata:preference:perpage', 'workshopallocation_manual'), $prefs->workshopallocation_manual_perpage->description); } } PKB\uٶtests/behat/auth_manual.featurenu[@auth @auth_manual Feature: Test manual authentication works. In order to check manual authentication As a teacher I need to go on login page and enter username and password. Background: Given the following "users" exist: | username | | teacher1 | @javascript Scenario: Check login works with javascript. Given I am on homepage And I expand navigation bar And I click on "Log in" "link" in the ".logininfo" "css_element" When I set the field "Username" to "teacher1" And I set the field "Password" to "teacher1" When I press "Log in" Then I should see "You are logged in as" Scenario: Check login works without javascript. Given I am on homepage And I click on "Log in" "link" in the ".logininfo" "css_element" When I set the field "Username" to "teacher1" And I set the field "Password" to "teacher1" When I press "Log in" Then I should see "You are logged in as" PKB\auth.phpnu[. /** * Authentication Plugin: Manual Authentication * Just does a simple check against the moodle database. * * @package auth_manual * @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); require_once($CFG->libdir.'/authlib.php'); /** * Manual authentication plugin. * * @package auth * @subpackage manual * @copyright 1999 onwards Martin Dougiamas (http://dougiamas.com) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class auth_plugin_manual extends auth_plugin_base { /** * The name of the component. Used by the configuration. */ const COMPONENT_NAME = 'auth_manual'; const LEGACY_COMPONENT_NAME = 'auth/manual'; /** * Constructor. */ public function __construct() { $this->authtype = 'manual'; $config = get_config(self::COMPONENT_NAME); $legacyconfig = get_config(self::LEGACY_COMPONENT_NAME); $this->config = (object)array_merge((array)$legacyconfig, (array)$config); } /** * Old syntax of class constructor. Deprecated in PHP7. * * @deprecated since Moodle 3.1 */ public function auth_plugin_manual() { debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER); self::__construct(); } /** * Returns true if the username and password work and false if they are * wrong or don't exist. (Non-mnet accounts only!) * * @param string $username The username * @param string $password The password * @return bool Authentication success or failure. */ function user_login($username, $password) { global $CFG, $DB, $USER; if (!$user = $DB->get_record('user', array('username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id))) { return false; } if (!validate_internal_user_password($user, $password)) { return false; } if ($password === 'changeme') { // force the change - this is deprecated and it makes sense only for manual auth, // because most other plugins can not change password easily or // passwords are always specified by users set_user_preference('auth_forcepasswordchange', true, $user->id); } return true; } /** * Updates the user's password. * * Called when the user password is updated. * * @param object $user User table object * @param string $newpassword Plaintext password * @return boolean result */ function user_update_password($user, $newpassword) { $user = get_complete_user_data('id', $user->id); set_user_preference('auth_manual_passwordupdatetime', time(), $user->id); // This will also update the stored hash to the latest algorithm // if the existing hash is using an out-of-date algorithm (or the // legacy md5 algorithm). return update_internal_user_password($user, $newpassword); } function prevent_local_passwords() { return false; } /** * Returns true if this authentication plugin is 'internal'. * * @return bool */ function is_internal() { return true; } /** * Returns true if this authentication plugin can change the user's * password. * * @return bool */ function can_change_password() { return true; } /** * Returns the URL for changing the user's pw, or empty if the default can * be used. * * @return moodle_url */ function change_password_url() { return null; } /** * Returns true if plugin allows resetting of internal password. * * @return bool */ function can_reset_password() { return true; } /** * Returns true if plugin can be manually set. * * @return bool */ function can_be_manually_set() { return true; } /** * Return number of days to user password expires. * * If user password does not expire, it should return 0 or a positive value. * If user password is already expired, it should return negative value. * * @param mixed $username username (with system magic quotes) * @return integer */ public function password_expire($username) { $result = 0; if (!empty($this->config->expirationtime)) { $user = core_user::get_user_by_username($username, 'id,timecreated'); $lastpasswordupdatetime = get_user_preferences('auth_manual_passwordupdatetime', $user->timecreated, $user->id); $expiretime = $lastpasswordupdatetime + $this->config->expirationtime * DAYSECS; $now = time(); $result = ($expiretime - $now) / DAYSECS; if ($expiretime > $now) { $result = ceil($result); } else { $result = floor($result); } } return $result; } /** * Confirm the new user as registered. This should normally not be used, * but it may be necessary if the user auth_method is changed to manual * before the user is confirmed. * * @param string $username * @param string $confirmsecret */ function user_confirm($username, $confirmsecret = null) { global $DB; $user = get_complete_user_data('username', $username); if (!empty($user)) { if ($user->confirmed) { return AUTH_CONFIRM_ALREADY; } else { $DB->set_field("user", "confirmed", 1, array("id"=>$user->id)); return AUTH_CONFIRM_OK; } } else { return AUTH_CONFIRM_ERROR; } } } PKB\.6 settings.phpnu[. /** * Manual enrolment plugin settings and presets. * * @package enrol_manual * @copyright 2010 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); if ($ADMIN->fulltree) { //--- general settings ----------------------------------------------------------------------------------- $settings->add(new admin_setting_heading('enrol_manual_settings', '', get_string('pluginname_desc', 'enrol_manual'))); // Note: let's reuse the ext sync constants and strings here, internally it is very similar, // it describes what should happend when users are not supposed to be enerolled any more. $options = array( ENROL_EXT_REMOVED_KEEP => get_string('extremovedkeep', 'enrol'), ENROL_EXT_REMOVED_SUSPEND => get_string('extremovedsuspend', 'enrol'), ENROL_EXT_REMOVED_SUSPENDNOROLES => get_string('extremovedsuspendnoroles', 'enrol'), ENROL_EXT_REMOVED_UNENROL => get_string('extremovedunenrol', 'enrol'), ); $settings->add(new admin_setting_configselect('enrol_manual/expiredaction', get_string('expiredaction', 'enrol_manual'), get_string('expiredaction_help', 'enrol_manual'), ENROL_EXT_REMOVED_KEEP, $options)); $options = array(); for ($i=0; $i<24; $i++) { $options[$i] = $i; } $settings->add(new admin_setting_configselect('enrol_manual/expirynotifyhour', get_string('expirynotifyhour', 'core_enrol'), '', 6, $options)); //--- enrol instance defaults ---------------------------------------------------------------------------- $settings->add(new admin_setting_heading('enrol_manual_defaults', get_string('enrolinstancedefaults', 'admin'), get_string('enrolinstancedefaults_desc', 'admin'))); $settings->add(new admin_setting_configcheckbox('enrol_manual/defaultenrol', get_string('defaultenrol', 'enrol'), get_string('defaultenrol_desc', 'enrol'), 1)); $options = array(ENROL_INSTANCE_ENABLED => get_string('yes'), ENROL_INSTANCE_DISABLED => get_string('no')); $settings->add(new admin_setting_configselect('enrol_manual/status', get_string('status', 'enrol_manual'), get_string('status_desc', 'enrol_manual'), ENROL_INSTANCE_ENABLED, $options)); if (!during_initial_install()) { $options = get_default_enrol_roles(context_system::instance()); $student = get_archetype_roles('student'); $student = reset($student); $settings->add(new admin_setting_configselect('enrol_manual/roleid', get_string('defaultrole', 'role'), '', $student->id ?? null, $options)); } $options = array(2 => get_string('coursestart'), 3 => get_string('today'), 4 => get_string('now', 'enrol_manual')); $settings->add( new admin_setting_configselect('enrol_manual/enrolstart', get_string('defaultstart', 'enrol_manual'), '', 4, $options) ); $settings->add(new admin_setting_configduration('enrol_manual/enrolperiod', get_string('defaultperiod', 'enrol_manual'), get_string('defaultperiod_desc', 'enrol_manual'), 0)); $options = array(0 => get_string('no'), 1 => get_string('expirynotifyenroller', 'core_enrol'), 2 => get_string('expirynotifyall', 'core_enrol')); $settings->add(new admin_setting_configselect('enrol_manual/expirynotify', get_string('expirynotify', 'core_enrol'), get_string('expirynotify_help', 'core_enrol'), 0, $options)); $settings->add(new admin_setting_configduration('enrol_manual/expirythreshold', get_string('expirythreshold', 'core_enrol'), get_string('expirythreshold_help', 'core_enrol'), 86400, 86400)); // Course welcome message. $settings->add( new admin_setting_configselect( name: 'enrol_manual/sendcoursewelcomemessage', visiblename: get_string( identifier: 'sendcoursewelcomemessage', component: 'core_enrol', ), description: get_string( identifier: 'sendcoursewelcomemessage_help', component: 'core_enrol', ), defaultsetting: ENROL_SEND_EMAIL_FROM_COURSE_CONTACT, choices: enrol_send_welcome_email_options(), ), ); } PKԄ\>g$g$externallib.phpnu[. use core_external\external_api; use core_external\external_function_parameters; use core_external\external_multiple_structure; use core_external\external_single_structure; use core_external\external_value; /** * External course participation api. * * This api is mostly read only, the actual enrol and unenrol * support is in each enrol plugin. * * @package enrol_manual * @category external * @copyright 2011 Jerome Mouneyrac * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 2.2 */ class enrol_manual_external extends external_api { /** * Returns description of method parameters. * * @return external_function_parameters * @since Moodle 2.2 */ public static function enrol_users_parameters() { return new external_function_parameters( array( 'enrolments' => new external_multiple_structure( new external_single_structure( array( 'roleid' => new external_value(PARAM_INT, 'Role to assign to the user'), 'userid' => new external_value(PARAM_INT, 'The user that is going to be enrolled'), 'courseid' => new external_value(PARAM_INT, 'The course to enrol the user role in'), 'timestart' => new external_value(PARAM_INT, 'Timestamp when the enrolment start', VALUE_OPTIONAL), 'timeend' => new external_value(PARAM_INT, 'Timestamp when the enrolment end', VALUE_OPTIONAL), 'suspend' => new external_value(PARAM_INT, 'set to 1 to suspend the enrolment', VALUE_OPTIONAL) ) ) ) ) ); } /** * Enrolment of users. * * Function throw an exception at the first error encountered. * @param array $enrolments An array of user enrolment * @since Moodle 2.2 */ public static function enrol_users($enrolments) { global $DB, $CFG; require_once($CFG->libdir . '/enrollib.php'); $params = self::validate_parameters(self::enrol_users_parameters(), array('enrolments' => $enrolments)); $transaction = $DB->start_delegated_transaction(); // Rollback all enrolment if an error occurs // (except if the DB doesn't support it). // Retrieve the manual enrolment plugin. $enrol = enrol_get_plugin('manual'); if (empty($enrol)) { throw new moodle_exception('manualpluginnotinstalled', 'enrol_manual'); } foreach ($params['enrolments'] as $enrolment) { // Ensure the current user is allowed to run this function in the enrolment context. $context = context_course::instance($enrolment['courseid'], IGNORE_MISSING); self::validate_context($context); // Check that the user has the permission to manual enrol. require_capability('enrol/manual:enrol', $context); $user = core_user::get_user($enrolment['userid'], strictness: MUST_EXIST); core_user::require_active_user($user); // Throw an exception if user is not able to assign the role. $roles = get_assignable_roles($context); if (!array_key_exists($enrolment['roleid'], $roles)) { $errorparams = new stdClass(); $errorparams->roleid = $enrolment['roleid']; $errorparams->courseid = $enrolment['courseid']; $errorparams->userid = $user->id; throw new moodle_exception('wsusercannotassign', 'enrol_manual', '', $errorparams); } // Check manual enrolment plugin instance is enabled/exist. $instance = null; $enrolinstances = enrol_get_instances($enrolment['courseid'], true); foreach ($enrolinstances as $courseenrolinstance) { if ($courseenrolinstance->enrol == "manual") { $instance = $courseenrolinstance; break; } } if (empty($instance)) { $errorparams = new stdClass(); $errorparams->courseid = $enrolment['courseid']; throw new moodle_exception('wsnoinstance', 'enrol_manual', '', $errorparams); } // Check that the plugin accept enrolment (it should always the case, it's hard coded in the plugin). if (!$enrol->allow_enrol($instance)) { $errorparams = new stdClass(); $errorparams->roleid = $enrolment['roleid']; $errorparams->courseid = $enrolment['courseid']; $errorparams->userid = $user->id; throw new moodle_exception('wscannotenrol', 'enrol_manual', '', $errorparams); } // Finally proceed the enrolment. $enrolment['timestart'] = isset($enrolment['timestart']) ? $enrolment['timestart'] : 0; $enrolment['timeend'] = isset($enrolment['timeend']) ? $enrolment['timeend'] : 0; $enrolment['status'] = (isset($enrolment['suspend']) && !empty($enrolment['suspend'])) ? ENROL_USER_SUSPENDED : ENROL_USER_ACTIVE; $enrol->enrol_user($instance, $user->id, $enrolment['roleid'], $enrolment['timestart'], $enrolment['timeend'], $enrolment['status']); } $transaction->allow_commit(); } /** * Returns description of method result value. * * @return null * @since Moodle 2.2 */ public static function enrol_users_returns() { return null; } /** * Returns description of method parameters. * * @return external_function_parameters */ public static function unenrol_users_parameters() { return new external_function_parameters(array( 'enrolments' => new external_multiple_structure( new external_single_structure( array( 'userid' => new external_value(PARAM_INT, 'The user that is going to be unenrolled'), 'courseid' => new external_value(PARAM_INT, 'The course to unenrol the user from'), 'roleid' => new external_value(PARAM_INT, 'The user role', VALUE_OPTIONAL), ) ) ) )); } /** * Unenrolment of users. * * @param array $enrolments an array of course user and role ids * @throws coding_exception * @throws dml_transaction_exception * @throws invalid_parameter_exception * @throws moodle_exception * @throws required_capability_exception * @throws restricted_context_exception */ public static function unenrol_users($enrolments) { global $CFG, $DB; $params = self::validate_parameters(self::unenrol_users_parameters(), array('enrolments' => $enrolments)); require_once($CFG->libdir . '/enrollib.php'); $transaction = $DB->start_delegated_transaction(); // Rollback all enrolment if an error occurs. $enrol = enrol_get_plugin('manual'); if (empty($enrol)) { throw new moodle_exception('manualpluginnotinstalled', 'enrol_manual'); } foreach ($params['enrolments'] as $enrolment) { $context = context_course::instance($enrolment['courseid']); self::validate_context($context); require_capability('enrol/manual:unenrol', $context); $instance = $DB->get_record('enrol', array('courseid' => $enrolment['courseid'], 'enrol' => 'manual')); if (!$instance) { throw new moodle_exception('wsnoinstance', 'enrol_manual', '', $enrolment); } $user = core_user::get_user($enrolment['userid'], strictness: MUST_EXIST); core_user::require_active_user($user); if (!$enrol->allow_unenrol($instance)) { throw new moodle_exception('wscannotunenrol', 'enrol_manual', '', $enrolment); } $enrol->unenrol_user($instance, $user->id); } $transaction->allow_commit(); } /** * Returns description of method result value. * * @return null */ public static function unenrol_users_returns() { return null; } } PKԄ\$xmmdb/services.phpnu[. /** * Manual plugin external functions and service definitions. * * @package enrol_manual * @category webservice * @copyright 2011 Jerome Mouneyrac * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ $functions = array( // === enrol related functions === 'enrol_manual_enrol_users' => array( 'classname' => 'enrol_manual_external', 'methodname' => 'enrol_users', 'classpath' => 'enrol/manual/externallib.php', 'description' => 'Manual enrol users', 'capabilities'=> 'enrol/manual:enrol', 'type' => 'write', ), 'enrol_manual_unenrol_users' => array( 'classname' => 'enrol_manual_external', 'methodname' => 'unenrol_users', 'classpath' => 'enrol/manual/externallib.php', 'description' => 'Manual unenrol users', 'capabilities'=> 'enrol/manual:unenrol', 'type' => 'write', ), ); PKԄ\-db/install.phpnu[. /** * Manual enrol plugin installation script * * @package enrol_manual * @copyright 2010 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); function xmldb_enrol_manual_install() { global $CFG; } PKԄ\G db/tasks.phpnu[. /** * Task definition for enrol_manual. * @author Farhan Karmali * @copyright Farhan Karmali * @package enrol_manual * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); $tasks = array( array( 'classname' => '\enrol_manual\task\sync_enrolments', 'blocking' => 0, 'minute' => '*/10', 'hour' => '*', 'day' => '*', 'month' => '*', 'dayofweek' => '*', 'disabled' => 0 ), array( 'classname' => '\enrol_manual\task\send_expiry_notifications', 'blocking' => 0, 'minute' => '*/10', 'hour' => '*', 'day' => '*', 'month' => '*', 'dayofweek' => '*', 'disabled' => 0 ) ); PKԄ\T[[ db/hooks.phpnu[. /** * Hook callbacks for enrol_manual * * @package enrol_manual * @copyright 2024 Huong Nguyen * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); $callbacks = [ [ 'hook' => core_enrol\hook\after_user_enrolled::class, 'callback' => 'enrol_manual\user_enrolment_callbacks::send_course_welcome_message', ], ]; PKԄ\F&( db/access.phpnu[. /** * Capabilities for manual enrolment plugin. * * @package enrol_manual * @copyright 2010 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); $capabilities = array( /* Add, edit or remove manual enrol instance. */ 'enrol/manual:config' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW, ) ), /* Enrol anybody. */ 'enrol/manual:enrol' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, ) ), /* Manage enrolments of users. */ 'enrol/manual:manage' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, ) ), /* Unenrol anybody (including self) - watch out for data loss. */ 'enrol/manual:unenrol' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( 'manager' => CAP_ALLOW, 'editingteacher' => CAP_ALLOW, ) ), /* Unenrol self - watch out for data loss. */ 'enrol/manual:unenrolself' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, 'archetypes' => array( ) ), ); PKԄ\\lOdb/messages.phpnu[. /** * Defines message providers for manual enrolments. * * @package enrol_manual * @copyright 2012 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ $messageproviders = array ( 'expiry_notification' => [ 'defaults' => [ 'popup' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_ENABLED, ], ], ); PKԄ\`zCClang/en/enrol_manual.phpnu[. /** * Strings for component 'enrol_manual', language 'en'. * * @package enrol_manual * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ $string['advanced'] = 'Advanced'; $string['alterstatus'] = 'Alter status'; $string['altertimeend'] = 'Alter end time'; $string['altertimestart'] = 'Alter start time'; $string['assignrole'] = 'Assign role'; $string['assignroles'] = 'Assign roles'; $string['browseusers'] = 'Browse users'; $string['browsecohorts'] = 'Browse cohorts'; $string['confirmbulkdeleteenrolment'] = 'Are you sure you want to delete these user enrolments?'; $string['defaultstart'] = 'Default enrolment start'; $string['defaultperiod'] = 'Default enrolment duration'; $string['defaultperiod_desc'] = 'Default length of time that the enrolment is valid. If set to zero, the enrolment duration will be unlimited by default.'; $string['defaultperiod_help'] = 'Default length of time that the enrolment is valid, starting with the moment the user is enrolled. If disabled, the enrolment duration will be unlimited by default.'; $string['deleteselectedusers'] = 'Delete selected user enrolments'; $string['editselectedusers'] = 'Edit selected user enrolments'; $string['enrolledincourserole'] = 'Enrolled in "{$a->course}" as "{$a->role}"'; $string['enrolusers'] = 'Enrol users'; $string['enroluserscohorts'] = 'Enrol selected users and cohorts'; $string['expiredaction'] = 'Enrolment expiry action'; $string['expiredaction_help'] = 'Select action to carry out when user enrolment expires. Please note that some user data and settings are purged from course during course unenrolment.'; $string['expirymessageenrollersubject'] = 'Enrolment expiry notification'; $string['expirymessageenrollerbody'] = 'Enrolment in the course \'{$a->course}\' will expire within the next {$a->threshold} for the following users: {$a->users} To extend their enrolment, go to {$a->extendurl}'; $string['expirymessageenrolledsubject'] = 'Enrolment expiry notification'; $string['expirymessageenrolledbody'] = 'Dear {$a->user}, This is a notification that your enrolment in the course \'{$a->course}\' is due to expire on {$a->timeend}. If you need help, please contact {$a->enroller}.'; $string['managemanualenrolements'] = 'Manage manual enrolments'; $string['manual:config'] = 'Configure manual enrol instances'; $string['manual:enrol'] = 'Enrol users'; $string['manual:manage'] = 'Manage user enrolments'; $string['manual:unenrol'] = 'Unenrol users from the course'; $string['manual:unenrolself'] = 'Unenrol self from the course'; $string['messageprovider:expiry_notification'] = 'Manual enrolment expiry notifications'; $string['now'] = 'Now'; $string['pluginname'] = 'Manual enrolments'; $string['pluginname_desc'] = 'The manual enrolments plugin allows users to be enrolled manually via a link in the course administration settings, by a user with appropriate permissions such as a teacher. The plugin should normally be enabled, since certain other enrolment plugins, such as self enrolment, require it.'; $string['selection'] = 'Selection'; $string['selectusers'] = 'Select users'; $string['selectcohorts'] = 'Select cohorts'; $string['sendexpirynotificationstask'] = "Manual enrolment send expiry notifications task"; $string['status'] = 'Enable manual enrolments'; $string['status_desc'] = 'Allow course access of internally enrolled users. This should be kept enabled in most cases.'; $string['status_help'] = 'This setting determines whether users can be enrolled manually, via a link in the course administration settings, by a user with appropriate permissions such as a teacher.'; $string['statusenabled'] = 'Enabled'; $string['statusdisabled'] = 'Disabled'; $string['syncenrolmentstask'] = 'Synchronise manual enrolments task'; $string['unenrol'] = 'Unenrol user'; $string['unenrolselectedusers'] = 'Unenrol selected users'; $string['unenrolselfconfirm'] = 'Do you really want to unenrol yourself from course "{$a}"?'; $string['unenroluser'] = 'Do you really want to unenrol "{$a->user}" from course "{$a->course}"?'; $string['unenrolusers'] = 'Unenrol users'; $string['wscannotenrol'] = 'Plugin instance cannot manually enrol a user in the course id = {$a->courseid}'; $string['wsnoinstance'] = 'Manual enrolment plugin instance doesn\'t exist or is disabled for the course (id = {$a->courseid})'; $string['wsusercannotassign'] = 'You don\'t have the permission to assign this role ({$a->roleid}) to this user ({$a->userid}) in this course ({$a->courseid}).'; $string['manualpluginnotinstalled'] = 'The "Manual" plugin has not yet been installed'; $string['privacy:metadata'] = 'The Manual enrolments plugin does not store any personal data.'; PKԄ\Y[+CClib.phpnu[. /** * Allows user to allocate the submissions manually * * @package workshopallocation * @subpackage manual * @copyright 2009 David Mudrak * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); require_once(__DIR__ . '/../lib.php'); // interface definition require_once(__DIR__ . '/../../locallib.php'); // workshop internal API /** * Allows users to allocate submissions for review manually */ class workshop_manual_allocator implements workshop_allocator { /** constants that are used to pass status messages between init() and ui() */ const MSG_ADDED = 1; const MSG_NOSUBMISSION = 2; const MSG_EXISTS = 3; const MSG_CONFIRM_DEL = 4; const MSG_DELETED = 5; const MSG_DELETE_ERROR = 6; /** @var workshop instance */ protected $workshop; /** * @param workshop $workshop Workshop API object */ public function __construct(workshop $workshop) { $this->workshop = $workshop; } /** * Allocate submissions as requested by user * * @return workshop_allocation_result */ public function init() { global $PAGE; $mode = optional_param('mode', 'display', PARAM_ALPHA); $perpage = optional_param('perpage', null, PARAM_INT); if ($perpage and $perpage > 0 and $perpage <= 1000) { require_sesskey(); set_user_preference('workshopallocation_manual_perpage', $perpage); redirect($PAGE->url); } $result = new workshop_allocation_result($this); switch ($mode) { case 'new': if (!confirm_sesskey()) { throw new moodle_exception('confirmsesskeybad'); } $reviewerid = required_param('by', PARAM_INT); $authorid = required_param('of', PARAM_INT); $m = array(); // message object to be passed to the next page $submission = $this->workshop->get_submission_by_author($authorid); if (!$submission) { // nothing submitted by the given user $m[] = self::MSG_NOSUBMISSION; $m[] = $authorid; } else { // ok, we have the submission $res = $this->workshop->add_allocation($submission, $reviewerid); if ($res == workshop::ALLOCATION_EXISTS) { $m[] = self::MSG_EXISTS; $m[] = $submission->authorid; $m[] = $reviewerid; } else { $m[] = self::MSG_ADDED; $m[] = $submission->authorid; $m[] = $reviewerid; } } $m = implode('-', $m); // serialize message object to be passed via URL redirect($PAGE->url->out(false, array('m' => $m))); break; case 'del': if (!confirm_sesskey()) { throw new moodle_exception('confirmsesskeybad'); } $assessmentid = required_param('what', PARAM_INT); $confirmed = optional_param('confirm', 0, PARAM_INT); $assessment = $this->workshop->get_assessment_by_id($assessmentid); if ($assessment) { if (!$confirmed) { $m[] = self::MSG_CONFIRM_DEL; $m[] = $assessment->id; $m[] = $assessment->authorid; $m[] = $assessment->reviewerid; if (is_null($assessment->grade)) { $m[] = 0; } else { $m[] = 1; } } else { if($this->workshop->delete_assessment($assessment->id)) { $m[] = self::MSG_DELETED; $m[] = $assessment->authorid; $m[] = $assessment->reviewerid; } else { $m[] = self::MSG_DELETE_ERROR; $m[] = $assessment->authorid; $m[] = $assessment->reviewerid; } } $m = implode('-', $m); // serialize message object to be passed via URL redirect($PAGE->url->out(false, array('m' => $m))); } break; } $result->set_status(workshop_allocation_result::STATUS_VOID); return $result; } /** * Prints user interface - current allocation and a form to edit it */ public function ui() { global $PAGE, $DB; $output = $PAGE->get_renderer('workshopallocation_manual'); $page = optional_param('page', 0, PARAM_INT); $perpage = get_user_preferences('workshopallocation_manual_perpage', 10); $groupid = groups_get_activity_group($this->workshop->cm, true); $hlauthorid = -1; // highlight this author $hlreviewerid = -1; // highlight this reviewer $message = new workshop_message(); $m = optional_param('m', '', PARAM_ALPHANUMEXT); // message code if ($m) { $m = explode('-', $m); switch ($m[0]) { case self::MSG_ADDED: $hlauthorid = $m[1]; $hlreviewerid = $m[2]; $message = new workshop_message(get_string('allocationadded', 'workshopallocation_manual'), workshop_message::TYPE_OK); break; case self::MSG_EXISTS: $hlauthorid = $m[1]; $hlreviewerid = $m[2]; $message = new workshop_message(get_string('allocationexists', 'workshopallocation_manual'), workshop_message::TYPE_INFO); break; case self::MSG_NOSUBMISSION: $hlauthorid = $m[1]; $message = new workshop_message(get_string('nosubmissionfound', 'workshop'), workshop_message::TYPE_ERROR); break; case self::MSG_CONFIRM_DEL: $hlauthorid = $m[2]; $hlreviewerid = $m[3]; if ($m[4] == 0) { $message = new workshop_message(get_string('areyousuretodeallocate', 'workshopallocation_manual'), workshop_message::TYPE_INFO); } else { $message = new workshop_message(get_string('areyousuretodeallocategraded', 'workshopallocation_manual'), workshop_message::TYPE_ERROR); } $url = new moodle_url($PAGE->url, array('mode' => 'del', 'what' => $m[1], 'confirm' => 1, 'sesskey' => sesskey())); $label = get_string('iamsure', 'workshop'); $message->set_action($url, $label); break; case self::MSG_DELETED: $hlauthorid = $m[1]; $hlreviewerid = $m[2]; $message = new workshop_message(get_string('assessmentdeleted', 'workshop'), workshop_message::TYPE_OK); break; case self::MSG_DELETE_ERROR: $hlauthorid = $m[1]; $hlreviewerid = $m[2]; $message = new workshop_message(get_string('assessmentnotdeleted', 'workshop'), workshop_message::TYPE_ERROR); break; } } // fetch the list of ids of all workshop participants $numofparticipants = $this->workshop->count_participants(false, $groupid); $participants = $this->workshop->get_participants(false, $groupid, $perpage * $page, $perpage); if ($hlauthorid > 0 and $hlreviewerid > 0) { // display just those two users $participants = array_intersect_key($participants, array($hlauthorid => null, $hlreviewerid => null)); $button = $output->single_button($PAGE->url, get_string('showallparticipants', 'workshopallocation_manual'), 'get'); } else { $button = ''; } // this will hold the information needed to display user names and pictures $userinfo = $participants; // load the participants' submissions $submissions = $this->workshop->get_submissions(array_keys($participants)); $allnames = \core_user\fields::get_name_fields(); foreach ($submissions as $submission) { if (!isset($userinfo[$submission->authorid])) { $userinfo[$submission->authorid] = new stdclass(); $userinfo[$submission->authorid]->id = $submission->authorid; $userinfo[$submission->authorid]->picture = $submission->authorpicture; $userinfo[$submission->authorid]->imagealt = $submission->authorimagealt; $userinfo[$submission->authorid]->email = $submission->authoremail; foreach ($allnames as $addname) { $temp = 'author' . $addname; $userinfo[$submission->authorid]->$addname = $submission->$temp; } } } // get current reviewers $reviewers = array(); if ($submissions) { list($submissionids, $params) = $DB->get_in_or_equal(array_keys($submissions), SQL_PARAMS_NAMED); $userfieldsapi = \core_user\fields::for_userpic(); $picturefields = $userfieldsapi->get_sql('r', false, '', 'reviewerid', false)->selects; $sql = "SELECT a.id AS assessmentid, a.submissionid, $picturefields, s.id AS submissionid, s.authorid FROM {workshop_assessments} a JOIN {user} r ON (a.reviewerid = r.id) JOIN {workshop_submissions} s ON (a.submissionid = s.id) WHERE a.submissionid $submissionids"; $reviewers = $DB->get_records_sql($sql, $params); foreach ($reviewers as $reviewer) { if (!isset($userinfo[$reviewer->reviewerid])) { $userinfo[$reviewer->reviewerid] = new stdclass(); $userinfo[$reviewer->reviewerid]->id = $reviewer->reviewerid; $userinfo[$reviewer->reviewerid]->picture = $reviewer->picture; $userinfo[$reviewer->reviewerid]->imagealt = $reviewer->imagealt; $userinfo[$reviewer->reviewerid]->email = $reviewer->email; foreach ($allnames as $addname) { $userinfo[$reviewer->reviewerid]->$addname = $reviewer->$addname; } } } } // get current reviewees $reviewees = array(); if ($participants) { list($participantids, $params) = $DB->get_in_or_equal(array_keys($participants), SQL_PARAMS_NAMED); $userfieldsapi = \core_user\fields::for_name(); $namefields = $userfieldsapi->get_sql('e', false, '', '', false)->selects; $params['workshopid'] = $this->workshop->id; $sql = "SELECT a.id AS assessmentid, a.submissionid, u.id AS reviewerid, s.id AS submissionid, e.id AS revieweeid, e.lastname, e.firstname, $namefields, e.picture, e.imagealt, e.email FROM {user} u JOIN {workshop_assessments} a ON (a.reviewerid = u.id) JOIN {workshop_submissions} s ON (a.submissionid = s.id) JOIN {user} e ON (s.authorid = e.id) WHERE u.id $participantids AND s.workshopid = :workshopid AND s.example = 0"; $reviewees = $DB->get_records_sql($sql, $params); foreach ($reviewees as $reviewee) { if (!isset($userinfo[$reviewee->revieweeid])) { $userinfo[$reviewee->revieweeid] = new stdclass(); $userinfo[$reviewee->revieweeid]->id = $reviewee->revieweeid; $userinfo[$reviewee->revieweeid]->firstname = $reviewee->firstname; $userinfo[$reviewee->revieweeid]->lastname = $reviewee->lastname; $userinfo[$reviewee->revieweeid]->picture = $reviewee->picture; $userinfo[$reviewee->revieweeid]->imagealt = $reviewee->imagealt; $userinfo[$reviewee->revieweeid]->email = $reviewee->email; foreach ($allnames as $addname) { $userinfo[$reviewee->revieweeid]->$addname = $reviewee->$addname; } } } } // the information about the allocations $allocations = array(); foreach ($participants as $participant) { $allocations[$participant->id] = new stdClass(); $allocations[$participant->id]->userid = $participant->id; $allocations[$participant->id]->submissionid = null; $allocations[$participant->id]->reviewedby = array(); $allocations[$participant->id]->reviewerof = array(); } unset($participants); foreach ($submissions as $submission) { $allocations[$submission->authorid]->submissionid = $submission->id; $allocations[$submission->authorid]->submissiontitle = $submission->title; $allocations[$submission->authorid]->submissiongrade = $submission->grade; } unset($submissions); foreach($reviewers as $reviewer) { $allocations[$reviewer->authorid]->reviewedby[$reviewer->reviewerid] = $reviewer->assessmentid; } unset($reviewers); foreach($reviewees as $reviewee) { $allocations[$reviewee->reviewerid]->reviewerof[$reviewee->revieweeid] = $reviewee->assessmentid; } unset($reviewees); // prepare data to be rendered $data = new workshopallocation_manual_allocations(); $data->workshop = $this->workshop; $data->allocations = $allocations; $data->userinfo = $userinfo; $data->authors = $this->workshop->get_potential_authors(); $data->reviewers = $this->workshop->get_potential_reviewers(); $data->hlauthorid = $hlauthorid; $data->hlreviewerid = $hlreviewerid; $data->selfassessment = $this->workshop->useselfassessment; // prepare the group selector $groupselector = $output->container(groups_print_activity_menu($this->workshop->cm, $PAGE->url, true), 'groupwidget'); // prepare paging bar $pagingbar = new paging_bar($numofparticipants, $page, $perpage, $PAGE->url, 'page'); $pagingbarout = $output->render($pagingbar); $perpageselector = $output->perpage_selector($perpage); return $groupselector . $pagingbarout . $output->render($message) . $output->render($data) . $button . $pagingbarout . $perpageselector; } /** * Delete all data related to a given workshop module instance * * This plugin does not store any data. * * @see workshop_delete_instance() * @param int $workshopid id of the workshop module instance being deleted * @return void */ public static function delete_instance($workshopid) { return; } } /** * Contains all information needed to render current allocations and the allocator UI * * @see workshop_manual_allocator::ui() */ class workshopallocation_manual_allocations implements renderable { /** @var workshop module instance */ public $workshop; /** @var array of stdClass, indexed by userid, properties userid, submissionid, (array)reviewedby, (array)reviewerof */ public $allocations; /** @var array of stdClass contains the data needed to display the user name and picture */ public $userinfo; /* var array of stdClass potential authors */ public $authors; /* var array of stdClass potential reviewers */ public $reviewers; /* var int the id of the user to highlight as the author */ public $hlauthorid; /* var int the id of the user to highlight as the reviewer */ public $hlreviewerid; /* var bool should the selfassessment be allowed */ public $selfassessment; } PKԄ\*u!! manage.phpnu[. /** * Manual user enrolment UI. * * @package enrol_manual * @copyright 2010 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ require('../../config.php'); require_once($CFG->dirroot.'/enrol/manual/locallib.php'); $enrolid = required_param('enrolid', PARAM_INT); $roleid = optional_param('roleid', -1, PARAM_INT); $extendperiod = optional_param('extendperiod', 0, PARAM_INT); $extendbase = optional_param('extendbase', 0, PARAM_INT); $timeend = optional_param_array('timeend', [], PARAM_INT); $instance = $DB->get_record('enrol', array('id'=>$enrolid, 'enrol'=>'manual'), '*', MUST_EXIST); $course = $DB->get_record('course', array('id'=>$instance->courseid), '*', MUST_EXIST); $context = context_course::instance($course->id, MUST_EXIST); require_login($course); $canenrol = has_capability('enrol/manual:enrol', $context); $canunenrol = has_capability('enrol/manual:unenrol', $context); // Note: manage capability not used here because it is used for editing // of existing enrolments which is not possible here. if (!$canenrol and !$canunenrol) { // No need to invent new error strings here... require_capability('enrol/manual:enrol', $context); require_capability('enrol/manual:unenrol', $context); } if ($roleid < 0) { $roleid = $instance->roleid; } $roles = get_assignable_roles($context); $roles = array('0'=>get_string('none')) + $roles; if (!isset($roles[$roleid])) { // Weird - security always first! $roleid = 0; } if (!$enrol_manual = enrol_get_plugin('manual')) { throw new coding_exception('Can not instantiate enrol_manual'); } $url = new moodle_url('/enrol/manual/manage.php', ['enrolid' => $instance->id]); $title = get_string('managemanualenrolements', 'enrol_manual'); $PAGE->set_url($url); $PAGE->set_pagelayout('admin'); $PAGE->set_title($title); $PAGE->set_heading($course->fullname); navigation_node::override_active_url(new moodle_url('/enrol/instances.php', ['id' => $course->id])); $PAGE->navbar->add($title, $url); // Create the user selector objects. $options = array('enrolid' => $enrolid, 'accesscontext' => $context); $potentialuserselector = new enrol_manual_potential_participant('addselect', $options); $currentuserselector = new enrol_manual_current_participant('removeselect', $options); // Build the list of options for the enrolment period dropdown. $unlimitedperiod = get_string('unlimited'); $periodmenu = array(); for ($i=1; $i<=365; $i++) { $seconds = $i * 86400; $periodmenu[$seconds] = get_string('numdays', '', $i); } // Work out the apropriate default settings. if ($extendperiod) { $defaultperiod = $extendperiod; } else { $defaultperiod = $instance->enrolperiod; } if ($instance->enrolperiod > 0 && !isset($periodmenu[$instance->enrolperiod])) { $periodmenu[$instance->enrolperiod] = format_time($instance->enrolperiod); } if (empty($extendbase)) { if (!$extendbase = get_config('enrol_manual', 'enrolstart')) { // Default to now if there is no system setting. $extendbase = 4; } } // Build the list of options for the starting from dropdown. $now = time(); $today = make_timestamp(date('Y', $now), date('m', $now), date('d', $now), 0, 0, 0); $dateformat = get_string('strftimedatefullshort'); // Enrolment start. $basemenu = array(); if ($course->startdate > 0) { $basemenu[2] = get_string('coursestart') . ' (' . userdate($course->startdate, $dateformat) . ')'; } $basemenu[3] = get_string('today') . ' (' . userdate($today, $dateformat) . ')'; $basemenu[4] = get_string('now', 'enrol_manual') . ' (' . userdate($now, get_string('strftimedatetimeshort')) . ')'; // Process add and removes. if ($canenrol && optional_param('add', false, PARAM_BOOL) && confirm_sesskey()) { $userstoassign = $potentialuserselector->get_selected_users(); if (!empty($userstoassign)) { foreach($userstoassign as $adduser) { switch($extendbase) { case 2: $timestart = $course->startdate; break; case 4: // We mimic get_enrolled_sql round(time(), -2) but always floor as we want users to always access their // courses once they are enrolled. $timestart = intval(substr($now, 0, 8) . '00') - 1; break; case 3: default: $timestart = $today; break; } if ($timeend) { $timeend = make_timestamp($timeend['year'], $timeend['month'], $timeend['day'], $timeend['hour'], $timeend['minute']); } else if ($extendperiod <= 0) { $timeend = 0; } else { $timeend = $timestart + $extendperiod; } $enrol_manual->enrol_user($instance, $adduser->id, $roleid, $timestart, $timeend); } $potentialuserselector->invalidate_selected_users(); $currentuserselector->invalidate_selected_users(); //TODO: log } } // Process incoming role unassignments. if ($canunenrol && optional_param('remove', false, PARAM_BOOL) && confirm_sesskey()) { $userstounassign = $currentuserselector->get_selected_users(); if (!empty($userstounassign)) { foreach($userstounassign as $removeuser) { $enrol_manual->unenrol_user($instance, $removeuser->id); } $potentialuserselector->invalidate_selected_users(); $currentuserselector->invalidate_selected_users(); //TODO: log } } echo $OUTPUT->header(); echo $OUTPUT->heading($title); $addenabled = $canenrol ? '' : 'disabled="disabled"'; $removeenabled = $canunenrol ? '' : 'disabled="disabled"'; ?>

display() ?>
id="add" type="submit" value="larrow() . ' ' . get_string('add'); ?>" title="" />




type="submit" value="rarrow(); ?>" title="" />

display() ?>
footer(); PKԄ\>unenrolself.phpnu[. /** * Manual enrolment plugin - support for user self unenrolment. * * @package enrol_manual * @copyright 2010 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ require('../../config.php'); $enrolid = required_param('enrolid', PARAM_INT); $confirm = optional_param('confirm', 0, PARAM_BOOL); $instance = $DB->get_record('enrol', array('id'=>$enrolid, 'enrol'=>'manual'), '*', MUST_EXIST); $course = $DB->get_record('course', array('id'=>$instance->courseid), '*', MUST_EXIST); $context = context_course::instance($course->id, MUST_EXIST); require_login(); if (!is_enrolled($context)) { redirect(new moodle_url('/')); } require_login($course); $plugin = enrol_get_plugin('manual'); // Security defined inside following function. if (!$plugin->get_unenrolself_link($instance)) { redirect(new moodle_url('/course/view.php', array('id'=>$course->id))); } $PAGE->set_url('/enrol/manual/unenrolself.php', array('enrolid'=>$instance->id)); $PAGE->set_title($plugin->get_instance_name($instance)); if ($confirm and confirm_sesskey()) { $plugin->unenrol_user($instance, $USER->id); redirect(new moodle_url('/index.php')); } echo $OUTPUT->header(); $yesurl = new moodle_url($PAGE->url, array('confirm'=>1, 'sesskey'=>sesskey())); $nourl = new moodle_url('/course/view.php', array('id'=>$course->id)); $message = get_string('unenrolselfconfirm', 'enrol_manual', format_string($course->fullname)); echo $OUTPUT->confirm($message, $yesurl, $nourl); echo $OUTPUT->footer(); PKԄ\O  bulkchangeforms.phpnu[. /** * This file contains form for bulk changing user enrolments. * * @package enrol_manual * @copyright 2011 Sam Hemelryk * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); require_once("$CFG->dirroot/enrol/bulkchange_forms.php"); /** * The form to collect required information when bulk editing users enrolments. * * @copyright 2011 Sam Hemelryk * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class enrol_manual_editselectedusers_form extends enrol_bulk_enrolment_change_form {} /** * The form to confirm the intention to bulk delete users enrolments. * * @copyright 2011 Sam Hemelryk * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class enrol_manual_deleteselectedusers_form extends enrol_bulk_enrolment_confirm_form {} PKԄ\>$classes/user_enrolment_callbacks.phpnu[. namespace enrol_manual; /** * Hook callbacks to get the enrolment information. * * @package enrol_manual * @copyright 2024 Huong Nguyen * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class user_enrolment_callbacks { /** * Callback for the user_enrolment hook. * * @param \core_enrol\hook\after_user_enrolled $hook */ public static function send_course_welcome_message(\core_enrol\hook\after_user_enrolled $hook): void { $instance = $hook->get_enrolinstance(); // Send welcome message. if ($instance->enrol == 'manual' && $instance->customint1 && $instance->customint1 !== ENROL_DO_NOT_SEND_EMAIL) { $plugin = enrol_get_plugin($instance->enrol); $plugin->send_course_welcome_message_to_user( instance: $instance, userid: $hook->get_userid(), sendoption: $instance->customint1, message: $instance->customtext1, roleid: $hook->roleid, ); } } } PKԄ\ *classes/task/send_expiry_notifications.phpnu[. /** * The send expiry notifications task. * * @package enrol_manual * @author Farhan Karmali * @copyright Farhan Karmali * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace enrol_manual\task; defined('MOODLE_INTERNAL') || die(); /** * The send expiry notifications task. * * @package enrol_manual * @author Farhan Karmali * @copyright Farhan Karmali * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class send_expiry_notifications extends \core\task\scheduled_task { /** * Name for this task. * * @return string */ public function get_name() { return get_string('sendexpirynotificationstask', 'enrol_manual'); } /** * Run task for sending expiry notifications. */ public function execute() { $enrol = enrol_get_plugin('manual'); $trace = new \text_progress_trace(); $enrol->send_expiry_notifications($trace); } } PKԄ\oo classes/task/sync_enrolments.phpnu[. /** * Syncing enrolments task. * * @package enrol_manual * @author Farhan Karmali * @copyright Farhan Karmali * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace enrol_manual\task; defined('MOODLE_INTERNAL') || die(); /** * Syncing enrolments task. * * @package enrol_manual * @author Farhan Karmali * @copyright Farhan Karmali * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class sync_enrolments extends \core\task\scheduled_task { /** * Name for this task. * * @return string */ public function get_name() { return get_string('syncenrolmentstask', 'enrol_manual'); } /** * Run task for syncing enrolments. */ public function execute() { $enrol = enrol_get_plugin('manual'); $trace = new \text_progress_trace(); $enrol->sync($trace); } } PKԄ\eqclasses/enrol_users_form.phpnu[. /** * Enrol users form. * * Simple form to search for users and add them using a manual enrolment to this course. * * @package enrol_manual * @copyright 2016 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); require_once($CFG->libdir.'/formslib.php'); class enrol_manual_enrol_users_form extends moodleform { /** * Form definition. * @return void */ public function definition() { global $PAGE, $DB, $CFG; require_once($CFG->dirroot . '/enrol/locallib.php'); $context = $this->_customdata->context; // Get the course and enrolment instance. $coursecontext = $context->get_course_context(); $course = $DB->get_record('course', ['id' => $coursecontext->instanceid]); $manager = new course_enrolment_manager($PAGE, $course); $instance = null; foreach ($manager->get_enrolment_instances() as $tempinstance) { if ($tempinstance->enrol == 'manual') { if ($instance === null) { $instance = $tempinstance; break; } } } $mform = $this->_form; $mform->setDisableShortforms(); $mform->disable_form_change_checker(); $periodmenu = enrol_get_period_list(); // Work out the apropriate default settings. $defaultperiod = $instance->enrolperiod; if ($instance->enrolperiod > 0 && !isset($periodmenu[$instance->enrolperiod])) { $periodmenu[$instance->enrolperiod] = format_time($instance->enrolperiod); } if (empty($extendbase)) { if (!$extendbase = get_config('enrol_manual', 'enrolstart')) { // Default to now if there is no system setting. $extendbase = 4; } } // Build the list of options for the starting from dropdown. $now = time(); $today = make_timestamp(date('Y', $now), date('m', $now), date('d', $now), 0, 0, 0); $dateformat = get_string('strftimedatefullshort'); // Enrolment start. $basemenu = array(); if ($course->startdate > 0) { $basemenu[2] = get_string('coursestart') . ' (' . userdate($course->startdate, $dateformat) . ')'; } $basemenu[3] = get_string('today') . ' (' . userdate($today, $dateformat) . ')'; $basemenu[4] = get_string('now', 'enrol_manual') . ' (' . userdate($now, get_string('strftimedatetimeshort')) . ')'; $mform->addElement('header', 'main', get_string('enrolmentoptions', 'enrol')); $options = array( 'ajax' => 'enrol_manual/form-potential-user-selector', 'multiple' => true, 'courseid' => $course->id, 'enrolid' => $instance->id, 'perpage' => $CFG->maxusersperpage, 'userfields' => implode(',', \core_user\fields::get_identity_fields($context, true)) ); $mform->addElement('autocomplete', 'userlist', get_string('selectusers', 'enrol_manual'), array(), $options); // Confirm the user can search for cohorts before displaying select. if (has_capability('moodle/cohort:manage', $context) || has_capability('moodle/cohort:view', $context)) { // Check to ensure there is at least one visible cohort before displaying the select box. // Ideally it would be better to call external_api::call_external_function('core_cohort_search_cohorts') // (which is used to populate the select box) instead of duplicating logic but there is an issue with globals // being borked (in this case $PAGE) when combining the usage of fragments and call_external_function(). require_once($CFG->dirroot . '/cohort/lib.php'); $availablecohorts = cohort_get_cohorts($context->id, 0, 1, ''); $availablecohorts = $availablecohorts['cohorts']; if (!($context instanceof context_system)) { $availablecohorts = array_merge($availablecohorts, cohort_get_available_cohorts($context, COHORT_ALL, 0, 1, '')); } if (!empty($availablecohorts)) { $options = ['contextid' => $context->id, 'multiple' => true]; $mform->addElement('cohort', 'cohortlist', get_string('selectcohorts', 'enrol_manual'), $options); } } $roles = get_assignable_roles($context, ROLENAME_BOTH); $mform->addElement('select', 'roletoassign', get_string('assignrole', 'enrol_manual'), $roles); $mform->setDefault('roletoassign', $instance->roleid); $mform->addAdvancedStatusElement('main'); $mform->addElement('checkbox', 'recovergrades', get_string('recovergrades', 'enrol')); $mform->setAdvanced('recovergrades'); $mform->setDefault('recovergrades', $CFG->recovergradesdefault); $mform->addElement('select', 'startdate', get_string('startingfrom'), $basemenu); $mform->setDefault('startdate', $extendbase); $mform->setAdvanced('startdate'); $mform->addElement('select', 'duration', get_string('enrolperiod', 'enrol'), $periodmenu); $mform->setDefault('duration', $defaultperiod); $mform->setAdvanced('duration'); $mform->disabledIf('duration', 'timeend[enabled]', 'checked', 1); $mform->addElement('date_time_selector', 'timeend', get_string('enroltimeend', 'enrol'), ['optional' => true]); $mform->setAdvanced('timeend'); $mform->addElement('hidden', 'id', $course->id); $mform->setType('id', PARAM_INT); $mform->addElement('hidden', 'action', 'enrol'); $mform->setType('action', PARAM_ALPHA); $mform->addElement('hidden', 'enrolid', $instance->id); $mform->setType('enrolid', PARAM_INT); } /** * Validate the submitted form data. * * @param array $data array of ("fieldname"=>value) of submitted data * @param array $files array of uploaded files "element_name"=>tmp_file_path * @return array of "element_name"=>"error_description" if there are errors, * or an empty array if everything is OK (true allowed for backwards compatibility too). */ public function validation($data, $files) { $errors = parent::validation($data, $files); if (!empty($data['startdate']) && !empty($data['timeend'])) { if ($data['startdate'] >= $data['timeend']) { $errors['timeend'] = get_string('enroltimeendinvalid', 'enrol'); } } return $errors; } } PKԄ\Gfޣhh0templates/form-user-selector-suggestion.mustachenu[{{! 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 . }} {{! @template enrol_manual/form-user-selector-suggestion Moodle template for the list of valid options in an autocomplate form element. Classes required for JS: * none Data attributes required for JS: * none Context variables required for this template: * profileimageurlsmall Url to a small profile image. * profileimageurl Url to a profile image. * fullname Users full name * hasidentity boolean to say if there are identity fields to show * identity concatenated list of identity fields. * id user id field * email user email field * idnumber user idnumber field * phone1 user phone1 field * phone2 user phone2 field * department user department field * institution user institution field Example context (json): { "id": "1", "fullname": "Admin", "hasidentity": true, "identity": "Admin User, 0144114141", "profileimageurl": "invalid url", "profileimageurlsmall": "invalid url" } }} {{fullname}} {{#hasidentity}} {{identity}} {{/hasidentity}} PKԄ\6I6I locallib.phpnu[. /** * Auxiliary manual user enrolment lib, the main purpose is to lower memory requirements... * * @package enrol_manual * @copyright 2010 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); require_once($CFG->dirroot . '/user/selector/lib.php'); require_once($CFG->dirroot . '/enrol/locallib.php'); /** * Enrol candidates. */ class enrol_manual_potential_participant extends user_selector_base { protected $enrolid; public function __construct($name, $options) { $this->enrolid = $options['enrolid']; $options['includecustomfields'] = true; parent::__construct($name, $options); } /** * Candidate users * @param string $search * @return array */ public function find_users($search) { global $DB; // By default wherecondition retrieves all users except the deleted, not confirmed and guest. list($wherecondition, $params) = $this->search_sql($search, 'u'); $params = array_merge($params, $this->userfieldsparams); $params['enrolid'] = $this->enrolid; $fields = 'SELECT u.id, ' . $this->userfieldsselects; $countfields = 'SELECT COUNT(1)'; $sql = " FROM {user} u LEFT JOIN {user_enrolments} ue ON (ue.userid = u.id AND ue.enrolid = :enrolid) $this->userfieldsjoin WHERE $wherecondition AND ue.id IS NULL"; list($sort, $sortparams) = users_order_by_sql('u', $search, $this->accesscontext, $this->userfieldsmappings); $order = ' ORDER BY ' . $sort; if (!$this->is_validating()) { $potentialmemberscount = $DB->count_records_sql($countfields . $sql, $params); if ($potentialmemberscount > $this->maxusersperpage) { return $this->too_many_results($search, $potentialmemberscount); } } $availableusers = $DB->get_records_sql($fields . $sql . $order, array_merge($params, $sortparams)); if (empty($availableusers)) { return array(); } if ($search) { $groupname = get_string('enrolcandidatesmatching', 'enrol', $search); } else { $groupname = get_string('enrolcandidates', 'enrol'); } return array($groupname => $availableusers); } protected function get_options() { $options = parent::get_options(); $options['enrolid'] = $this->enrolid; $options['file'] = 'enrol/manual/locallib.php'; return $options; } } /** * Enrolled users. */ class enrol_manual_current_participant extends user_selector_base { protected $courseid; protected $enrolid; public function __construct($name, $options) { $this->enrolid = $options['enrolid']; $options['includecustomfields'] = true; parent::__construct($name, $options); } /** * Candidate users * @param string $search * @return array */ public function find_users($search) { global $DB; // By default wherecondition retrieves all users except the deleted, not confirmed and guest. list($wherecondition, $params) = $this->search_sql($search, 'u'); $params = array_merge($params, $this->userfieldsparams); $params['enrolid'] = $this->enrolid; $fields = 'SELECT u.id, ' . $this->userfieldsselects; $countfields = 'SELECT COUNT(1)'; $sql = " FROM {user} u JOIN {user_enrolments} ue ON (ue.userid = u.id AND ue.enrolid = :enrolid) $this->userfieldsjoin WHERE $wherecondition"; list($sort, $sortparams) = users_order_by_sql('u', $search, $this->accesscontext, $this->userfieldsmappings); $order = ' ORDER BY ' . $sort; if (!$this->is_validating()) { $potentialmemberscount = $DB->count_records_sql($countfields . $sql, $params); if ($potentialmemberscount > $this->maxusersperpage) { return $this->too_many_results($search, $potentialmemberscount); } } $availableusers = $DB->get_records_sql($fields . $sql . $order, array_merge($params, $sortparams)); if (empty($availableusers)) { return array(); } if ($search) { $groupname = get_string('enrolledusersmatching', 'enrol', $search); } else { $groupname = get_string('enrolledusers', 'enrol'); } return array($groupname => $availableusers); } protected function get_options() { $options = parent::get_options(); $options['enrolid'] = $this->enrolid; $options['file'] = 'enrol/manual/locallib.php'; return $options; } } /** * A bulk operation for the manual enrolment plugin to edit selected users. * * @copyright 2011 Sam Hemelryk * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class enrol_manual_editselectedusers_operation extends enrol_bulk_enrolment_operation { /** * Returns the title to display for this bulk operation. * * @return string */ public function get_title() { return get_string('editselectedusers', 'enrol_manual'); } /** * Returns the identifier for this bulk operation. This is the key used when the plugin * returns an array containing all of the bulk operations it supports. */ public function get_identifier() { return 'editselectedusers'; } /** * Processes the bulk operation request for the given userids with the provided properties. * * @param course_enrolment_manager $manager * @param array $userids * @param stdClass $properties The data returned by the form. */ public function process(course_enrolment_manager $manager, array $users, stdClass $properties) { global $DB, $USER; if (!has_capability("enrol/manual:manage", $manager->get_context())) { return false; } // Get all of the user enrolment id's. $ueids = array(); $instances = array(); foreach ($users as $user) { foreach ($user->enrolments as $enrolment) { $ueids[] = $enrolment->id; if (!array_key_exists($enrolment->id, $instances)) { $instances[$enrolment->id] = $enrolment; } } } // Check that each instance is manageable by the current user. foreach ($instances as $instance) { if (!$this->plugin->allow_manage($instance)) { return false; } } // Collect the known properties. $status = $properties->status; $timestart = $properties->timestart; $timeend = $properties->timeend; list($ueidsql, $params) = $DB->get_in_or_equal($ueids, SQL_PARAMS_NAMED); $updatesql = array(); if ($status == ENROL_USER_ACTIVE || $status == ENROL_USER_SUSPENDED) { $updatesql[] = 'status = :status'; $params['status'] = (int)$status; } if (!empty($timestart)) { $updatesql[] = 'timestart = :timestart'; $params['timestart'] = (int)$timestart; } if (!empty($timeend)) { $updatesql[] = 'timeend = :timeend'; $params['timeend'] = (int)$timeend; } if (empty($updatesql)) { return true; } // Update the modifierid. $updatesql[] = 'modifierid = :modifierid'; $params['modifierid'] = (int)$USER->id; // Update the time modified. $updatesql[] = 'timemodified = :timemodified'; $params['timemodified'] = time(); // Build the SQL statement. $updatesql = join(', ', $updatesql); $sql = "UPDATE {user_enrolments} SET $updatesql WHERE id $ueidsql"; if ($DB->execute($sql, $params)) { foreach ($users as $user) { foreach ($user->enrolments as $enrolment) { $enrolment->courseid = $enrolment->enrolmentinstance->courseid; $enrolment->enrol = 'manual'; // Trigger event. $event = \core\event\user_enrolment_updated::create( array( 'objectid' => $enrolment->id, 'courseid' => $enrolment->courseid, 'context' => context_course::instance($enrolment->courseid), 'relateduserid' => $user->id, 'other' => array('enrol' => 'manual') ) ); $event->trigger(); } } // Delete cached course contacts for this course because they may be affected. cache::make('core', 'coursecontacts')->delete($manager->get_context()->instanceid); return true; } return false; } /** * Returns a enrol_bulk_enrolment_operation extension form to be used * in collecting required information for this operation to be processed. * * @param string|moodle_url|null $defaultaction * @param mixed $defaultcustomdata * @return enrol_manual_editselectedusers_form */ public function get_form($defaultaction = null, $defaultcustomdata = null) { global $CFG; require_once($CFG->dirroot.'/enrol/manual/bulkchangeforms.php'); return new enrol_manual_editselectedusers_form($defaultaction, $defaultcustomdata); } } /** * A bulk operation for the manual enrolment plugin to delete selected users enrolments. * * @copyright 2011 Sam Hemelryk * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class enrol_manual_deleteselectedusers_operation extends enrol_bulk_enrolment_operation { /** * Returns the title to display for this bulk operation. * * @return string */ public function get_identifier() { return 'deleteselectedusers'; } /** * Returns the identifier for this bulk operation. This is the key used when the plugin * returns an array containing all of the bulk operations it supports. * * @return string */ public function get_title() { return get_string('deleteselectedusers', 'enrol_manual'); } /** * Returns a enrol_bulk_enrolment_operation extension form to be used * in collecting required information for this operation to be processed. * * @param string|moodle_url|null $defaultaction * @param mixed $defaultcustomdata * @return enrol_manual_editselectedusers_form */ public function get_form($defaultaction = null, $defaultcustomdata = null) { global $CFG; require_once($CFG->dirroot.'/enrol/manual/bulkchangeforms.php'); if (!array($defaultcustomdata)) { $defaultcustomdata = array(); } $defaultcustomdata['title'] = $this->get_title(); $defaultcustomdata['message'] = get_string('confirmbulkdeleteenrolment', 'enrol_manual'); $defaultcustomdata['button'] = get_string('unenrolusers', 'enrol_manual'); return new enrol_manual_deleteselectedusers_form($defaultaction, $defaultcustomdata); } /** * Processes the bulk operation request for the given userids with the provided properties. * * @global moodle_database $DB * @param course_enrolment_manager $manager * @param array $userids * @param stdClass $properties The data returned by the form. */ public function process(course_enrolment_manager $manager, array $users, stdClass $properties) { if (!has_capability("enrol/manual:unenrol", $manager->get_context())) { return false; } $counter = 0; foreach ($users as $user) { foreach ($user->enrolments as $enrolment) { $plugin = $enrolment->enrolmentplugin; $instance = $enrolment->enrolmentinstance; if ($plugin->allow_unenrol_user($instance, $enrolment)) { $plugin->unenrol_user($instance, $user->id); $counter++; } } } // Display a notification message after the bulk user unenrollment. if ($counter > 0) { \core\notification::info(get_string('totalunenrolledusers', 'enrol', $counter)); } return true; } } /** * Migrates all enrolments of the given plugin to enrol_manual plugin, * this is used for example during plugin uninstallation. * * NOTE: this function does not trigger role and enrolment related events. * * @param string $enrol The enrolment method. */ function enrol_manual_migrate_plugin_enrolments($enrol) { global $DB; if ($enrol === 'manual') { // We can not migrate to self. return; } $manualplugin = enrol_get_plugin('manual'); $params = array('enrol'=>$enrol); $sql = "SELECT e.id, e.courseid, e.status, MIN(me.id) AS mid, COUNT(ue.id) AS cu FROM {enrol} e JOIN {user_enrolments} ue ON (ue.enrolid = e.id) JOIN {course} c ON (c.id = e.courseid) LEFT JOIN {enrol} me ON (me.courseid = e.courseid AND me.enrol='manual') WHERE e.enrol = :enrol GROUP BY e.id, e.courseid, e.status ORDER BY e.id"; $rs = $DB->get_recordset_sql($sql, $params); foreach($rs as $e) { $minstance = false; if (!$e->mid) { // Manual instance does not exist yet, add a new one. $course = $DB->get_record('course', array('id'=>$e->courseid), '*', MUST_EXIST); if ($minstance = $DB->get_record('enrol', array('courseid'=>$course->id, 'enrol'=>'manual'))) { // Already created by previous iteration. $e->mid = $minstance->id; } else if ($e->mid = $manualplugin->add_default_instance($course)) { $minstance = $DB->get_record('enrol', array('id'=>$e->mid)); if ($e->status != ENROL_INSTANCE_ENABLED) { $DB->set_field('enrol', 'status', ENROL_INSTANCE_DISABLED, array('id'=>$e->mid)); $minstance->status = ENROL_INSTANCE_DISABLED; } } } else { $minstance = $DB->get_record('enrol', array('id'=>$e->mid)); } if (!$minstance) { // This should never happen unless adding of default instance fails unexpectedly. debugging('Failed to find manual enrolment instance', DEBUG_DEVELOPER); continue; } // First delete potential role duplicates. $params = array('id'=>$e->id, 'component'=>'enrol_'.$enrol, 'empty'=>''); $sql = "SELECT ra.id FROM {role_assignments} ra JOIN {role_assignments} mra ON (mra.contextid = ra.contextid AND mra.userid = ra.userid AND mra.roleid = ra.roleid AND mra.component = :empty AND mra.itemid = 0) WHERE ra.component = :component AND ra.itemid = :id"; $ras = $DB->get_records_sql($sql, $params); $ras = array_keys($ras); $DB->delete_records_list('role_assignments', 'id', $ras); unset($ras); // Migrate roles. $sql = "UPDATE {role_assignments} SET itemid = 0, component = :empty WHERE itemid = :id AND component = :component"; $params = array('empty'=>'', 'id'=>$e->id, 'component'=>'enrol_'.$enrol); $DB->execute($sql, $params); // Delete potential enrol duplicates. $params = array('id'=>$e->id, 'mid'=>$e->mid); $sql = "SELECT ue.id FROM {user_enrolments} ue JOIN {user_enrolments} mue ON (mue.userid = ue.userid AND mue.enrolid = :mid) WHERE ue.enrolid = :id"; $ues = $DB->get_records_sql($sql, $params); $ues = array_keys($ues); $DB->delete_records_list('user_enrolments', 'id', $ues); unset($ues); // Migrate to manual enrol instance. $params = array('id'=>$e->id, 'mid'=>$e->mid); if ($e->status != ENROL_INSTANCE_ENABLED and $minstance->status == ENROL_INSTANCE_ENABLED) { $status = ", status = :disabled"; $params['disabled'] = ENROL_USER_SUSPENDED; } else { $status = ""; } $sql = "UPDATE {user_enrolments} SET enrolid = :mid $status WHERE enrolid = :id"; $DB->execute($sql, $params); } $rs->close(); } /** * Gets an array of the cohorts that can be enrolled in this course. * * @param int $enrolid * @param string $search * @param int $page Defaults to 0 * @param int $perpage Defaults to 25 * @param int $addedenrollment * @return array Array(totalcohorts => int, cohorts => array) */ function enrol_manual_get_potential_cohorts($context, $enrolid, $search = '', $page = 0, $perpage = 25, $addedenrollment = 0) { global $CFG; require_once($CFG->dirroot . '/cohort/lib.php'); $allcohorts = cohort_get_available_cohorts($context, COHORT_WITH_NOTENROLLED_MEMBERS_ONLY, 0, 0, $search); $totalcohorts = count($allcohorts); $cohorts = array(); $cnt = 0; foreach ($allcohorts as $c) { if ($cnt >= $page * $perpage && (!$perpage || $cnt < ($page+1)*$perpage)) { $cohorts[] = (object)array( 'id' => $c->id, 'name' => format_string($c->name, true, array('context' => $c->contextid)), 'cnt' => $c->memberscnt - $c->enrolledcnt ); } $cnt++; } return array('totalcohorts' => $totalcohorts, 'cohorts' => $cohorts); } PKԄ\G#ajax.phpnu[. /** * This file processes AJAX enrolment actions and returns JSON for the manual enrolments plugin * * The general idea behind this file is that any errors should throw exceptions * which will be returned and acted upon by the calling AJAX script. * * @package enrol_manual * @copyright 2010 Sam Hemelryk * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ define('AJAX_SCRIPT', true); require('../../config.php'); require_once($CFG->dirroot.'/enrol/locallib.php'); require_once($CFG->dirroot.'/group/lib.php'); require_once($CFG->dirroot.'/enrol/manual/locallib.php'); require_once($CFG->dirroot.'/cohort/lib.php'); require_once($CFG->dirroot . '/enrol/manual/classes/enrol_users_form.php'); $id = required_param('id', PARAM_INT); // Course id. $action = required_param('action', PARAM_ALPHANUMEXT); $PAGE->set_url(new moodle_url('/enrol/ajax.php', array('id'=>$id, 'action'=>$action))); $course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST); $context = context_course::instance($course->id, MUST_EXIST); if ($course->id == SITEID) { throw new moodle_exception('invalidcourse'); } require_login($course); require_capability('moodle/course:enrolreview', $context); require_sesskey(); echo $OUTPUT->header(); // Send headers. $manager = new course_enrolment_manager($PAGE, $course); $outcome = new stdClass(); $outcome->success = true; $outcome->response = new stdClass(); $outcome->error = ''; $outcome->count = 0; $searchanywhere = get_user_preferences('userselector_searchtype') === USER_SEARCH_CONTAINS; switch ($action) { case 'enrol': $enrolid = required_param('enrolid', PARAM_INT); $cohorts = $users = []; $userids = optional_param_array('userlist', [], PARAM_SEQUENCE); $userid = optional_param('userid', 0, PARAM_INT); if ($userid) { $userids[] = $userid; } if ($userids) { foreach ($userids as $userid) { $users[] = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST); } } $cohortids = optional_param_array('cohortlist', [], PARAM_SEQUENCE); $cohortid = optional_param('cohortid', 0, PARAM_INT); if ($cohortid) { $cohortids[] = $cohortid; } if ($cohortids) { foreach ($cohortids as $cohortid) { $cohort = $DB->get_record('cohort', array('id' => $cohortid), '*', MUST_EXIST); if (!cohort_can_view_cohort($cohort, $context)) { throw new enrol_ajax_exception('invalidenrolinstance'); // TODO error text! } $cohorts[] = $cohort; } } $roleid = optional_param('roletoassign', null, PARAM_INT); $duration = optional_param('duration', 0, PARAM_INT); $startdate = optional_param('startdate', 0, PARAM_INT); $recovergrades = optional_param('recovergrades', 0, PARAM_INT); $timeend = optional_param_array('timeend', [], PARAM_INT); if (empty($roleid)) { $roleid = null; } else { if (!has_capability('moodle/role:assign', $context)) { throw new enrol_ajax_exception('assignnotpermitted'); } if (!array_key_exists($roleid, get_assignable_roles($context, ROLENAME_ALIAS, false))) { throw new enrol_ajax_exception('invalidrole'); } } if (empty($startdate)) { if (!$startdate = get_config('enrol_manual', 'enrolstart')) { // Default to now if there is no system setting. $startdate = 4; } } switch($startdate) { case 2: $timestart = $course->startdate; break; case 4: // We mimic get_enrolled_sql round(time(), -2) but always floor as we want users to always access their // courses once they are enrolled. $timestart = intval(substr(time(), 0, 8) . '00') - 1; break; case 3: default: $today = time(); $today = make_timestamp(date('Y', $today), date('m', $today), date('d', $today), 0, 0, 0); $timestart = $today; break; } if ($timeend) { $timeend = make_timestamp($timeend['year'], $timeend['month'], $timeend['day'], $timeend['hour'], $timeend['minute']); } else if ($duration <= 0) { $timeend = 0; } else { $timeend = $timestart + $duration; } $mform = new enrol_manual_enrol_users_form(null, (object)["context" => $context]); $userenroldata = [ 'startdate' => $timestart, 'timeend' => $timeend, ]; $mform->set_data($userenroldata); $validationerrors = $mform->validation($userenroldata, null); if (!empty($validationerrors)) { throw new enrol_ajax_exception('invalidenrolduration'); } $instances = $manager->get_enrolment_instances(); $plugins = $manager->get_enrolment_plugins(true); // Do not allow actions on disabled plugins. if (!array_key_exists($enrolid, $instances)) { throw new enrol_ajax_exception('invalidenrolinstance'); } $instance = $instances[$enrolid]; if (!isset($plugins[$instance->enrol])) { throw new enrol_ajax_exception('enrolnotpermitted'); } $plugin = $plugins[$instance->enrol]; if ($plugin->allow_enrol($instance) && has_capability('enrol/'.$plugin->get_name().':enrol', $context)) { foreach ($users as $user) { $plugin->enrol_user($instance, $user->id, $roleid, $timestart, $timeend, null, $recovergrades); } $outcome->count += count($users); foreach ($cohorts as $cohort) { $totalenrolledusers = $plugin->enrol_cohort($instance, $cohort->id, $roleid, $timestart, $timeend, null, $recovergrades); $outcome->count += $totalenrolledusers; } } else { throw new enrol_ajax_exception('enrolnotpermitted'); } $outcome->success = true; break; default: throw new enrol_ajax_exception('unknowajaxaction'); } echo json_encode($outcome); PKԄ\amd/src/quickenrolment.jsnu[// 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 . /** * Quick enrolment AMD module. * * @module enrol_manual/quickenrolment * @copyright 2016 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ import * as DynamicTable from 'core_table/dynamic'; import * as Str from 'core/str'; import * as Toast from 'core/toast'; import Config from 'core/config'; import Fragment from 'core/fragment'; import ModalEvents from 'core/modal_events'; import Notification from 'core/notification'; import jQuery from 'jquery'; import Pending from 'core/pending'; import Prefetch from 'core/prefetch'; import ModalSaveCancel from 'core/modal_save_cancel'; const Selectors = { cohortSelector: "#id_cohortlist", triggerButtons: ".enrolusersbutton.enrol_manual_plugin [type='submit']", unwantedHiddenFields: "input[value='_qf__force_multiselect_submission']", buttonWrapper: '[data-region="wrapper"]', }; /** * Get the content of the body for the specified context. * * @param {Number} contextId * @returns {Promise} */ const getBodyForContext = contextId => { return Fragment.loadFragment('enrol_manual', 'enrol_users_form', contextId, {}); }; /** * Get the dynamic table for the button. * * @param {HTMLElement} element * @returns {HTMLElement} */ const getDynamicTableForElement = element => { const wrapper = element.closest(Selectors.buttonWrapper); return DynamicTable.getTableFromId(wrapper.dataset.tableUniqueid); }; /** * Register the event listeners for this contextid. * * @param {Number} contextId */ const registerEventListeners = contextId => { document.addEventListener('click', e => { if (e.target.closest(Selectors.triggerButtons)) { e.preventDefault(); showModal(getDynamicTableForElement(e.target), contextId); return; } }); }; /** * Display the modal for this contextId. * * @param {HTMLElement} dynamicTable The table to beb refreshed when changes are made * @param {Number} contextId * @returns {Promise} */ const showModal = (dynamicTable, contextId) => { const pendingPromise = new Pending('enrol_manual/quickenrolment:showModal'); return ModalSaveCancel.create({ large: true, title: Str.get_string('enrolusers', 'enrol_manual'), body: getBodyForContext(contextId), buttons: { save: Str.get_string('enrolusers', 'enrol_manual'), }, show: true, }) .then(modal => { modal.getRoot().on(ModalEvents.save, e => { // Trigger a form submission, so that any mform elements can do final tricks before the form submission // is processed. // The actual submit even tis captured in the next handler. e.preventDefault(); modal.getRoot().find('form').submit(); }); modal.getRoot().on('submit', 'form', e => { e.preventDefault(); submitFormAjax(dynamicTable, modal); }); modal.getRoot().on(ModalEvents.hidden, () => { modal.destroy(); }); return modal; }) .then(modal => Promise.all([modal, modal.getBodyPromise()])) .then(([modal, body]) => { if (body.get(0).querySelector(Selectors.cohortSelector)) { return modal.setSaveButtonText(Str.get_string('enroluserscohorts', 'enrol_manual')).then(() => modal); } return modal; }) .then(modal => { pendingPromise.resolve(); return modal; }) .catch(Notification.exception); }; /** * Submit the form via ajax. * * @param {HTMLElement} dynamicTable * @param {Object} modal */ const submitFormAjax = (dynamicTable, modal) => { // Note: We use a jQuery object here so that we can use its serialize functionality. const form = modal.getRoot().find('form'); // Before send the data through AJAX, we need to parse and remove some unwanted hidden fields. // This hidden fields are added automatically by mforms and when it reaches the AJAX we get an error. form.get(0).querySelectorAll(Selectors.unwantedHiddenFields).forEach(hiddenField => hiddenField.remove()); modal.hide(); modal.destroy(); jQuery.ajax( `${Config.wwwroot}/enrol/manual/ajax.php?${form.serialize()}`, { type: 'GET', processData: false, contentType: "application/json", } ) .then(response => { if (response.error) { throw new Error(response.error); } return response.count; }) .then(count => { return Promise.all([ Str.get_string('totalenrolledusers', 'enrol', count), DynamicTable.refreshTableContent(dynamicTable), ]); }) .then(([notificationBody]) => notificationBody) .then(notificationBody => Toast.add(notificationBody)) .catch(error => { Notification.addNotification({ message: error.message, type: 'error', }); }); }; /** * Set up quick enrolment for the manual enrolment plugin. * * @param {Number} contextid The context id to setup for */ export const init = ({contextid}) => { registerEventListeners(contextid); Prefetch.prefetchStrings('enrol_manual', [ 'enrolusers', 'enroluserscohorts', ]); Prefetch.prefetchString('enrol', 'totalenrolledusers'); }; PKԄ\4z'amd/src/form-potential-user-selector.jsnu[// 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 . /** * Potential user selector module. * * @module enrol_manual/form-potential-user-selector * @copyright 2016 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ define(['jquery', 'core/ajax', 'core/templates', 'core/str'], function($, Ajax, Templates, Str) { return /** @alias module:enrol_manual/form-potential-user-selector */ { processResults: function(selector, results) { var users = []; if ($.isArray(results)) { $.each(results, function(index, user) { users.push({ value: user.id, label: user._label }); }); return users; } else { return results; } }, transport: function(selector, query, success, failure) { var promise; var courseid = $(selector).attr('courseid'); var userfields = $(selector).attr('userfields').split(','); if (typeof courseid === "undefined") { courseid = '1'; } var enrolid = $(selector).attr('enrolid'); if (typeof enrolid === "undefined") { enrolid = ''; } var perpage = parseInt($(selector).attr('perpage')); if (isNaN(perpage)) { perpage = 100; } promise = Ajax.call([{ methodname: 'core_enrol_get_potential_users', args: { courseid: courseid, enrolid: enrolid, search: query, searchanywhere: true, page: 0, perpage: perpage + 1 } }]); promise[0].then(function(results) { var promises = [], i = 0; if (results.length <= perpage) { // Render the label. const profileRegex = /^profile_field_(.*)$/; $.each(results, function(index, user) { var ctx = user, identity = []; $.each(userfields, function(i, k) { const result = profileRegex.exec(k); if (result) { if (user.customfields) { user.customfields.forEach(function(customfield) { if (customfield.shortname === result[1]) { ctx.hasidentity = true; identity.push(customfield.value); } }); } } else { if (typeof user[k] !== 'undefined' && user[k] !== '') { ctx.hasidentity = true; identity.push(user[k]); } } }); ctx.identity = identity.join(', '); promises.push(Templates.render('enrol_manual/form-user-selector-suggestion', ctx)); }); // Apply the label to the results. return $.when.apply($.when, promises).then(function() { var args = arguments; $.each(results, function(index, user) { user._label = args[i]; i++; }); success(results); return; }); } else { return Str.get_string('toomanyuserstoshow', 'core', '>' + perpage).then(function(toomanyuserstoshow) { success(toomanyuserstoshow); return; }); } }).fail(failure); } }; }); PKԄ\4R1amd/build/form-potential-user-selector.min.js.mapnu[{"version":3,"file":"form-potential-user-selector.min.js","sources":["../src/form-potential-user-selector.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Potential user selector module.\n *\n * @module enrol_manual/form-potential-user-selector\n * @copyright 2016 Damyon Wiese\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/ajax', 'core/templates', 'core/str'], function($, Ajax, Templates, Str) {\n\n return /** @alias module:enrol_manual/form-potential-user-selector */ {\n\n processResults: function(selector, results) {\n var users = [];\n if ($.isArray(results)) {\n $.each(results, function(index, user) {\n users.push({\n value: user.id,\n label: user._label\n });\n });\n return users;\n\n } else {\n return results;\n }\n },\n\n transport: function(selector, query, success, failure) {\n var promise;\n var courseid = $(selector).attr('courseid');\n var userfields = $(selector).attr('userfields').split(',');\n if (typeof courseid === \"undefined\") {\n courseid = '1';\n }\n var enrolid = $(selector).attr('enrolid');\n if (typeof enrolid === \"undefined\") {\n enrolid = '';\n }\n var perpage = parseInt($(selector).attr('perpage'));\n if (isNaN(perpage)) {\n perpage = 100;\n }\n\n promise = Ajax.call([{\n methodname: 'core_enrol_get_potential_users',\n args: {\n courseid: courseid,\n enrolid: enrolid,\n search: query,\n searchanywhere: true,\n page: 0,\n perpage: perpage + 1\n }\n }]);\n\n promise[0].then(function(results) {\n var promises = [],\n i = 0;\n\n if (results.length <= perpage) {\n // Render the label.\n const profileRegex = /^profile_field_(.*)$/;\n $.each(results, function(index, user) {\n var ctx = user,\n identity = [];\n $.each(userfields, function(i, k) {\n const result = profileRegex.exec(k);\n if (result) {\n if (user.customfields) {\n user.customfields.forEach(function(customfield) {\n if (customfield.shortname === result[1]) {\n ctx.hasidentity = true;\n identity.push(customfield.value);\n }\n\n });\n }\n } else {\n if (typeof user[k] !== 'undefined' && user[k] !== '') {\n ctx.hasidentity = true;\n identity.push(user[k]);\n }\n }\n });\n ctx.identity = identity.join(', ');\n promises.push(Templates.render('enrol_manual/form-user-selector-suggestion', ctx));\n });\n\n // Apply the label to the results.\n return $.when.apply($.when, promises).then(function() {\n var args = arguments;\n $.each(results, function(index, user) {\n user._label = args[i];\n i++;\n });\n success(results);\n return;\n });\n\n } else {\n return Str.get_string('toomanyuserstoshow', 'core', '>' + perpage).then(function(toomanyuserstoshow) {\n success(toomanyuserstoshow);\n return;\n });\n }\n\n }).fail(failure);\n }\n\n };\n\n});\n"],"names":["define","$","Ajax","Templates","Str","processResults","selector","results","users","isArray","each","index","user","push","value","id","label","_label","transport","query","success","failure","courseid","attr","userfields","split","enrolid","perpage","parseInt","isNaN","call","methodname","args","search","searchanywhere","page","then","promises","i","length","profileRegex","ctx","identity","k","result","exec","customfields","forEach","customfield","shortname","hasidentity","join","render","when","apply","arguments","get_string","toomanyuserstoshow","fail"],"mappings":";;;;;;;AAuBAA,mDAAO,CAAC,SAAU,YAAa,iBAAkB,aAAa,SAASC,EAAGC,KAAMC,UAAWC,WAEjB,CAElEC,eAAgB,SAASC,SAAUC,aAC3BC,MAAQ,UACRP,EAAEQ,QAAQF,UACVN,EAAES,KAAKH,SAAS,SAASI,MAAOC,MAC5BJ,MAAMK,KAAK,CACPC,MAAOF,KAAKG,GACZC,MAAOJ,KAAKK,YAGbT,OAGAD,SAIfW,UAAW,SAASZ,SAAUa,MAAOC,QAASC,aAEtCC,SAAWrB,EAAEK,UAAUiB,KAAK,YAC5BC,WAAavB,EAAEK,UAAUiB,KAAK,cAAcE,MAAM,UAC9B,IAAbH,WACPA,SAAW,SAEXI,QAAUzB,EAAEK,UAAUiB,KAAK,gBACR,IAAZG,UACPA,QAAU,QAEVC,QAAUC,SAAS3B,EAAEK,UAAUiB,KAAK,YACpCM,MAAMF,WACNA,QAAU,KAGJzB,KAAK4B,KAAK,CAAC,CACjBC,WAAY,iCACZC,KAAM,CACFV,SAAUA,SACVI,QAASA,QACTO,OAAQd,MACRe,gBAAgB,EAChBC,KAAM,EACNR,QAASA,QAAU,MAInB,GAAGS,MAAK,SAAS7B,aACjB8B,SAAW,GACXC,EAAI,KAEJ/B,QAAQgC,QAAUZ,QAAS,OAErBa,aAAe,8BACrBvC,EAAES,KAAKH,SAAS,SAASI,MAAOC,UACxB6B,IAAM7B,KACN8B,SAAW,GACfzC,EAAES,KAAKc,YAAY,SAASc,EAAGK,SACrBC,OAASJ,aAAaK,KAAKF,GAC7BC,OACIhC,KAAKkC,cACLlC,KAAKkC,aAAaC,SAAQ,SAASC,aAC3BA,YAAYC,YAAcL,OAAO,KACjCH,IAAIS,aAAc,EAClBR,SAAS7B,KAAKmC,YAAYlC,gBAMf,IAAZF,KAAK+B,IAAkC,KAAZ/B,KAAK+B,KACvCF,IAAIS,aAAc,EAClBR,SAAS7B,KAAKD,KAAK+B,QAI/BF,IAAIC,SAAWA,SAASS,KAAK,MAC7Bd,SAASxB,KAAKV,UAAUiD,OAAO,6CAA8CX,SAI1ExC,EAAEoD,KAAKC,MAAMrD,EAAEoD,KAAMhB,UAAUD,MAAK,eACnCJ,KAAOuB,UACXtD,EAAES,KAAKH,SAAS,SAASI,MAAOC,MAC5BA,KAAKK,OAASe,KAAKM,GACnBA,OAEJlB,QAAQb,mBAKLH,IAAIoD,WAAW,qBAAsB,OAAQ,IAAM7B,SAASS,MAAK,SAASqB,oBAC7ErC,QAAQqC,0BAKjBC,KAAKrC"}PKԄ\7 ¾""#amd/build/quickenrolment.min.js.mapnu[{"version":3,"file":"quickenrolment.min.js","sources":["../src/quickenrolment.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Quick enrolment AMD module.\n *\n * @module enrol_manual/quickenrolment\n * @copyright 2016 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport * as DynamicTable from 'core_table/dynamic';\nimport * as Str from 'core/str';\nimport * as Toast from 'core/toast';\nimport Config from 'core/config';\nimport Fragment from 'core/fragment';\nimport ModalEvents from 'core/modal_events';\nimport Notification from 'core/notification';\nimport jQuery from 'jquery';\nimport Pending from 'core/pending';\nimport Prefetch from 'core/prefetch';\nimport ModalSaveCancel from 'core/modal_save_cancel';\n\nconst Selectors = {\n cohortSelector: \"#id_cohortlist\",\n triggerButtons: \".enrolusersbutton.enrol_manual_plugin [type='submit']\",\n unwantedHiddenFields: \"input[value='_qf__force_multiselect_submission']\",\n buttonWrapper: '[data-region=\"wrapper\"]',\n};\n\n/**\n * Get the content of the body for the specified context.\n *\n * @param {Number} contextId\n * @returns {Promise}\n */\nconst getBodyForContext = contextId => {\n return Fragment.loadFragment('enrol_manual', 'enrol_users_form', contextId, {});\n};\n\n/**\n * Get the dynamic table for the button.\n *\n * @param {HTMLElement} element\n * @returns {HTMLElement}\n */\nconst getDynamicTableForElement = element => {\n const wrapper = element.closest(Selectors.buttonWrapper);\n\n return DynamicTable.getTableFromId(wrapper.dataset.tableUniqueid);\n};\n\n/**\n * Register the event listeners for this contextid.\n *\n * @param {Number} contextId\n */\nconst registerEventListeners = contextId => {\n document.addEventListener('click', e => {\n if (e.target.closest(Selectors.triggerButtons)) {\n e.preventDefault();\n\n showModal(getDynamicTableForElement(e.target), contextId);\n\n return;\n }\n });\n};\n\n/**\n * Display the modal for this contextId.\n *\n * @param {HTMLElement} dynamicTable The table to beb refreshed when changes are made\n * @param {Number} contextId\n * @returns {Promise}\n */\nconst showModal = (dynamicTable, contextId) => {\n const pendingPromise = new Pending('enrol_manual/quickenrolment:showModal');\n\n return ModalSaveCancel.create({\n large: true,\n title: Str.get_string('enrolusers', 'enrol_manual'),\n body: getBodyForContext(contextId),\n buttons: {\n save: Str.get_string('enrolusers', 'enrol_manual'),\n },\n show: true,\n })\n .then(modal => {\n modal.getRoot().on(ModalEvents.save, e => {\n // Trigger a form submission, so that any mform elements can do final tricks before the form submission\n // is processed.\n // The actual submit even tis captured in the next handler.\n\n e.preventDefault();\n modal.getRoot().find('form').submit();\n });\n\n modal.getRoot().on('submit', 'form', e => {\n e.preventDefault();\n\n submitFormAjax(dynamicTable, modal);\n });\n\n modal.getRoot().on(ModalEvents.hidden, () => {\n modal.destroy();\n });\n\n return modal;\n })\n .then(modal => Promise.all([modal, modal.getBodyPromise()]))\n .then(([modal, body]) => {\n if (body.get(0).querySelector(Selectors.cohortSelector)) {\n return modal.setSaveButtonText(Str.get_string('enroluserscohorts', 'enrol_manual')).then(() => modal);\n }\n\n return modal;\n })\n .then(modal => {\n pendingPromise.resolve();\n\n return modal;\n })\n .catch(Notification.exception);\n};\n\n/**\n * Submit the form via ajax.\n *\n * @param {HTMLElement} dynamicTable\n * @param {Object} modal\n */\nconst submitFormAjax = (dynamicTable, modal) => {\n // Note: We use a jQuery object here so that we can use its serialize functionality.\n const form = modal.getRoot().find('form');\n\n // Before send the data through AJAX, we need to parse and remove some unwanted hidden fields.\n // This hidden fields are added automatically by mforms and when it reaches the AJAX we get an error.\n form.get(0).querySelectorAll(Selectors.unwantedHiddenFields).forEach(hiddenField => hiddenField.remove());\n\n modal.hide();\n modal.destroy();\n\n jQuery.ajax(\n `${Config.wwwroot}/enrol/manual/ajax.php?${form.serialize()}`,\n {\n type: 'GET',\n processData: false,\n contentType: \"application/json\",\n }\n )\n .then(response => {\n if (response.error) {\n throw new Error(response.error);\n }\n\n return response.count;\n })\n .then(count => {\n return Promise.all([\n Str.get_string('totalenrolledusers', 'enrol', count),\n DynamicTable.refreshTableContent(dynamicTable),\n ]);\n })\n .then(([notificationBody]) => notificationBody)\n .then(notificationBody => Toast.add(notificationBody))\n .catch(error => {\n Notification.addNotification({\n message: error.message,\n type: 'error',\n });\n });\n};\n\n/**\n * Set up quick enrolment for the manual enrolment plugin.\n *\n * @param {Number} contextid The context id to setup for\n */\nexport const init = ({contextid}) => {\n registerEventListeners(contextid);\n\n Prefetch.prefetchStrings('enrol_manual', [\n 'enrolusers',\n 'enroluserscohorts',\n ]);\n\n Prefetch.prefetchString('enrol', 'totalenrolledusers');\n};\n"],"names":["Selectors","getBodyForContext","contextId","Fragment","loadFragment","registerEventListeners","document","addEventListener","e","target","closest","preventDefault","showModal","element","wrapper","DynamicTable","getTableFromId","dataset","tableUniqueid","getDynamicTableForElement","dynamicTable","pendingPromise","Pending","ModalSaveCancel","create","large","title","Str","get_string","body","buttons","save","show","then","modal","getRoot","on","ModalEvents","find","submit","submitFormAjax","hidden","destroy","Promise","all","getBodyPromise","_ref","get","querySelector","setSaveButtonText","resolve","catch","Notification","exception","form","querySelectorAll","forEach","hiddenField","remove","hide","ajax","Config","wwwroot","serialize","type","processData","contentType","response","error","Error","count","refreshTableContent","_ref2","notificationBody","Toast","add","addNotification","message","_ref3","contextid","prefetchStrings","prefetchString"],"mappings":";;;;;;;ykBAkCMA,yBACc,iBADdA,yBAEc,wDAFdA,+BAGoB,mDAHpBA,wBAIa,0BASbC,kBAAoBC,WACfC,kBAASC,aAAa,eAAgB,mBAAoBF,UAAW,IAoB1EG,uBAAyBH,YAC3BI,SAASC,iBAAiB,SAASC,OAC3BA,EAAEC,OAAOC,QAAQV,iCACjBQ,EAAEG,sBAEFC,UAhBsBC,CAAAA,gBACxBC,QAAUD,QAAQH,QAAQV,gCAEzBe,aAAaC,eAAeF,QAAQG,QAAQC,gBAajCC,CAA0BX,EAAEC,QAASP,eAcrDU,UAAY,CAACQ,aAAclB,mBACvBmB,eAAiB,IAAIC,iBAAQ,gDAE5BC,2BAAgBC,OAAO,CAC1BC,OAAO,EACPC,MAAOC,IAAIC,WAAW,aAAc,gBACpCC,KAAM5B,kBAAkBC,WACxB4B,QAAS,CACLC,KAAMJ,IAAIC,WAAW,aAAc,iBAEvCI,MAAM,IAETC,MAAKC,QACFA,MAAMC,UAAUC,GAAGC,sBAAYN,MAAMvB,IAKjCA,EAAEG,iBACFuB,MAAMC,UAAUG,KAAK,QAAQC,YAGjCL,MAAMC,UAAUC,GAAG,SAAU,QAAQ5B,IACjCA,EAAEG,iBAEF6B,eAAepB,aAAcc,UAGjCA,MAAMC,UAAUC,GAAGC,sBAAYI,QAAQ,KACnCP,MAAMQ,aAGHR,SAEVD,MAAKC,OAASS,QAAQC,IAAI,CAACV,MAAOA,MAAMW,qBACxCZ,MAAKa,WAAEZ,MAAOL,kBACPA,KAAKkB,IAAI,GAAGC,cAAchD,0BACnBkC,MAAMe,kBAAkBtB,IAAIC,WAAW,oBAAqB,iBAAiBK,MAAK,IAAMC,QAG5FA,SAEVD,MAAKC,QACFb,eAAe6B,UAERhB,SAEViB,MAAMC,sBAAaC,YASlBb,eAAiB,CAACpB,aAAcc,eAE5BoB,KAAOpB,MAAMC,UAAUG,KAAK,QAIlCgB,KAAKP,IAAI,GAAGQ,iBAAiBvD,gCAAgCwD,SAAQC,aAAeA,YAAYC,WAEhGxB,MAAMyB,OACNzB,MAAMQ,0BAECkB,eACAC,gBAAOC,0CAAiCR,KAAKS,aAChD,CACIC,KAAM,MACNC,aAAa,EACbC,YAAa,qBAGpBjC,MAAKkC,cACEA,SAASC,YACH,IAAIC,MAAMF,SAASC,cAGtBD,SAASG,SAEnBrC,MAAKqC,OACK3B,QAAQC,IAAI,CACfjB,IAAIC,WAAW,qBAAsB,QAAS0C,OAC9CvD,aAAawD,oBAAoBnD,kBAGxCa,MAAKuC,YAAEC,+BAAsBA,oBAC7BxC,MAAKwC,kBAAoBC,MAAMC,IAAIF,oBACnCtB,OAAMiB,8BACUQ,gBAAgB,CACzBC,QAAST,MAAMS,QACfb,KAAM,4BAUEc,YAACC,UAACA,iBAClB1E,uBAAuB0E,6BAEdC,gBAAgB,eAAgB,CACrC,aACA,wCAGKC,eAAe,QAAS"}PKԄ\.-^^amd/build/quickenrolment.min.jsnu[define("enrol_manual/quickenrolment",["exports","core_table/dynamic","core/str","core/toast","core/config","core/fragment","core/modal_events","core/notification","jquery","core/pending","core/prefetch","core/modal_save_cancel"],(function(_exports,DynamicTable,Str,Toast,_config,_fragment,_modal_events,_notification,_jquery,_pending,_prefetch,_modal_save_cancel){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj} /** * Quick enrolment AMD module. * * @module enrol_manual/quickenrolment * @copyright 2016 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,DynamicTable=_interopRequireWildcard(DynamicTable),Str=_interopRequireWildcard(Str),Toast=_interopRequireWildcard(Toast),_config=_interopRequireDefault(_config),_fragment=_interopRequireDefault(_fragment),_modal_events=_interopRequireDefault(_modal_events),_notification=_interopRequireDefault(_notification),_jquery=_interopRequireDefault(_jquery),_pending=_interopRequireDefault(_pending),_prefetch=_interopRequireDefault(_prefetch),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel);const Selectors_cohortSelector="#id_cohortlist",Selectors_triggerButtons=".enrolusersbutton.enrol_manual_plugin [type='submit']",Selectors_unwantedHiddenFields="input[value='_qf__force_multiselect_submission']",Selectors_buttonWrapper='[data-region="wrapper"]',getBodyForContext=contextId=>_fragment.default.loadFragment("enrol_manual","enrol_users_form",contextId,{}),registerEventListeners=contextId=>{document.addEventListener("click",(e=>{if(e.target.closest(Selectors_triggerButtons))return e.preventDefault(),void showModal((element=>{const wrapper=element.closest(Selectors_buttonWrapper);return DynamicTable.getTableFromId(wrapper.dataset.tableUniqueid)})(e.target),contextId)}))},showModal=(dynamicTable,contextId)=>{const pendingPromise=new _pending.default("enrol_manual/quickenrolment:showModal");return _modal_save_cancel.default.create({large:!0,title:Str.get_string("enrolusers","enrol_manual"),body:getBodyForContext(contextId),buttons:{save:Str.get_string("enrolusers","enrol_manual")},show:!0}).then((modal=>(modal.getRoot().on(_modal_events.default.save,(e=>{e.preventDefault(),modal.getRoot().find("form").submit()})),modal.getRoot().on("submit","form",(e=>{e.preventDefault(),submitFormAjax(dynamicTable,modal)})),modal.getRoot().on(_modal_events.default.hidden,(()=>{modal.destroy()})),modal))).then((modal=>Promise.all([modal,modal.getBodyPromise()]))).then((_ref=>{let[modal,body]=_ref;return body.get(0).querySelector(Selectors_cohortSelector)?modal.setSaveButtonText(Str.get_string("enroluserscohorts","enrol_manual")).then((()=>modal)):modal})).then((modal=>(pendingPromise.resolve(),modal))).catch(_notification.default.exception)},submitFormAjax=(dynamicTable,modal)=>{const form=modal.getRoot().find("form");form.get(0).querySelectorAll(Selectors_unwantedHiddenFields).forEach((hiddenField=>hiddenField.remove())),modal.hide(),modal.destroy(),_jquery.default.ajax("".concat(_config.default.wwwroot,"/enrol/manual/ajax.php?").concat(form.serialize()),{type:"GET",processData:!1,contentType:"application/json"}).then((response=>{if(response.error)throw new Error(response.error);return response.count})).then((count=>Promise.all([Str.get_string("totalenrolledusers","enrol",count),DynamicTable.refreshTableContent(dynamicTable)]))).then((_ref2=>{let[notificationBody]=_ref2;return notificationBody})).then((notificationBody=>Toast.add(notificationBody))).catch((error=>{_notification.default.addNotification({message:error.message,type:"error"})}))};_exports.init=_ref3=>{let{contextid:contextid}=_ref3;registerEventListeners(contextid),_prefetch.default.prefetchStrings("enrol_manual",["enrolusers","enroluserscohorts"]),_prefetch.default.prefetchString("enrol","totalenrolledusers")}})); //# sourceMappingURL=quickenrolment.min.js.mapPKԄ\i-amd/build/form-potential-user-selector.min.jsnu[/** * Potential user selector module. * * @module enrol_manual/form-potential-user-selector * @copyright 2016 Damyon Wiese * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ define("enrol_manual/form-potential-user-selector",["jquery","core/ajax","core/templates","core/str"],(function($,Ajax,Templates,Str){return{processResults:function(selector,results){var users=[];return $.isArray(results)?($.each(results,(function(index,user){users.push({value:user.id,label:user._label})})),users):results},transport:function(selector,query,success,failure){var courseid=$(selector).attr("courseid"),userfields=$(selector).attr("userfields").split(",");void 0===courseid&&(courseid="1");var enrolid=$(selector).attr("enrolid");void 0===enrolid&&(enrolid="");var perpage=parseInt($(selector).attr("perpage"));isNaN(perpage)&&(perpage=100),Ajax.call([{methodname:"core_enrol_get_potential_users",args:{courseid:courseid,enrolid:enrolid,search:query,searchanywhere:!0,page:0,perpage:perpage+1}}])[0].then((function(results){var promises=[],i=0;if(results.length<=perpage){const profileRegex=/^profile_field_(.*)$/;return $.each(results,(function(index,user){var ctx=user,identity=[];$.each(userfields,(function(i,k){const result=profileRegex.exec(k);result?user.customfields&&user.customfields.forEach((function(customfield){customfield.shortname===result[1]&&(ctx.hasidentity=!0,identity.push(customfield.value))})):void 0!==user[k]&&""!==user[k]&&(ctx.hasidentity=!0,identity.push(user[k]))})),ctx.identity=identity.join(", "),promises.push(Templates.render("enrol_manual/form-user-selector-suggestion",ctx))})),$.when.apply($.when,promises).then((function(){var args=arguments;$.each(results,(function(index,user){user._label=args[i],i++})),success(results)}))}return Str.get_string("toomanyuserstoshow","core",">"+perpage).then((function(toomanyuserstoshow){success(toomanyuserstoshow)}))})).fail(failure)}}})); //# sourceMappingURL=form-potential-user-selector.min.js.mapPKԄ\ 0 cli/sync.phpnu[. /** * CLI update for manual enrolments expiration. * * Notes: * - it is required to use the web server account when executing PHP CLI scripts * - you need to change the "www-data" to match the apache user account * - use "su" if "sudo" not available * * @package enrol_manual * @copyright 2012 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ define('CLI_SCRIPT', true); require(__DIR__.'/../../../config.php'); require_once("$CFG->libdir/clilib.php"); // Now get cli options. list($options, $unrecognized) = cli_get_params(array('verbose'=>false, 'help'=>false), array('v'=>'verbose', 'h'=>'help')); if ($unrecognized) { $unrecognized = implode("\n ", $unrecognized); cli_error(get_string('cliunknowoption', 'admin', $unrecognized)); } if ($options['help']) { $help = "Execute manual enrolments expiration sync and send notifications. Options: -v, --verbose Print verbose progress information -h, --help Print out this help Example: \$ sudo -u www-data /usr/bin/php enrol/self/manual/sync.php "; echo $help; die; } if (!enrol_is_enabled('manual')) { cli_error('enrol_manual plugin is disabled, synchronisation stopped', 2); } if (empty($options['verbose'])) { $trace = new null_progress_trace(); } else { $trace = new text_progress_trace(); } /** @var $plugin enrol_manual_plugin */ $plugin = enrol_get_plugin('manual'); $result = $plugin->sync($trace, null); $plugin->send_expiry_notifications($trace); exit($result); PKԄ\5\ڷtests/lib_test.phpnu[. /** * Manual enrolment tests. * * @package enrol_manual * @category phpunit * @copyright 2012 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace enrol_manual; use course_enrolment_manager; use stdClass; defined('MOODLE_INTERNAL') || die(); /** * Manual enrolment tests. * * @package enrol_manual * @category phpunit * @copyright 2012 Petr Skoda {@link http://skodak.org} * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ final class lib_test extends \advanced_testcase { /** * Test enrol migration function used when uninstalling enrol plugins. */ public function test_migrate_plugin_enrolments(): void { global $DB, $CFG; require_once($CFG->dirroot.'/enrol/manual/locallib.php'); $this->resetAfterTest(); /** @var $manplugin enrol_manual_plugin */ $manplugin = enrol_get_plugin('manual'); // Setup a few courses and users. $studentrole = $DB->get_record('role', array('shortname'=>'student')); $this->assertNotEmpty($studentrole); $teacherrole = $DB->get_record('role', array('shortname'=>'teacher')); $this->assertNotEmpty($teacherrole); $course1 = $this->getDataGenerator()->create_course(); $course2 = $this->getDataGenerator()->create_course(); $course3 = $this->getDataGenerator()->create_course(); $course4 = $this->getDataGenerator()->create_course(); $course5 = $this->getDataGenerator()->create_course(); $context1 = \context_course::instance($course1->id); $context2 = \context_course::instance($course2->id); $context3 = \context_course::instance($course3->id); $context4 = \context_course::instance($course4->id); $user1 = $this->getDataGenerator()->create_user(); $user2 = $this->getDataGenerator()->create_user(); $user3 = $this->getDataGenerator()->create_user(); $user4 = $this->getDataGenerator()->create_user(); // We expect manual, self and guest instances to be created by default. $this->assertEquals(5, $DB->count_records('enrol', array('enrol'=>'manual'))); $this->assertEquals(5, $DB->count_records('enrol', array('enrol'=>'self'))); $this->assertEquals(5, $DB->count_records('enrol', array('enrol'=>'guest'))); $this->assertEquals(15, $DB->count_records('enrol', array())); $this->assertEquals(0, $DB->count_records('user_enrolments', array())); // Enrol some users to manual instances. $maninstance1 = $DB->get_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'manual'), '*', MUST_EXIST); $DB->set_field('enrol', 'status', ENROL_INSTANCE_DISABLED, array('id'=>$maninstance1->id)); $maninstance1 = $DB->get_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'manual'), '*', MUST_EXIST); $maninstance2 = $DB->get_record('enrol', array('courseid'=>$course2->id, 'enrol'=>'manual'), '*', MUST_EXIST); $DB->delete_records('enrol', array('courseid'=>$course3->id, 'enrol'=>'manual')); $DB->delete_records('enrol', array('courseid'=>$course4->id, 'enrol'=>'manual')); $DB->delete_records('enrol', array('courseid'=>$course5->id, 'enrol'=>'manual')); $manplugin->enrol_user($maninstance1, $user1->id, $studentrole->id); $manplugin->enrol_user($maninstance1, $user2->id, $studentrole->id); $manplugin->enrol_user($maninstance1, $user3->id, $teacherrole->id); $manplugin->enrol_user($maninstance2, $user3->id, $teacherrole->id); $this->assertEquals(4, $DB->count_records('user_enrolments', array())); // Set up some bogus enrol plugin instances and enrolments. $xxxinstance1 = $DB->insert_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'xxx', 'status'=>ENROL_INSTANCE_ENABLED)); $xxxinstance1 = $DB->get_record('enrol', array('id'=>$xxxinstance1)); $xxxinstance3 = $DB->insert_record('enrol', array('courseid'=>$course3->id, 'enrol'=>'xxx', 'status'=>ENROL_INSTANCE_DISABLED)); $xxxinstance3 = $DB->get_record('enrol', array('id'=>$xxxinstance3)); $xxxinstance4 = $DB->insert_record('enrol', array('courseid'=>$course4->id, 'enrol'=>'xxx', 'status'=>ENROL_INSTANCE_ENABLED)); $xxxinstance4 = $DB->get_record('enrol', array('id'=>$xxxinstance4)); $xxxinstance4b = $DB->insert_record('enrol', array('courseid'=>$course4->id, 'enrol'=>'xxx', 'status'=>ENROL_INSTANCE_DISABLED)); $xxxinstance4b = $DB->get_record('enrol', array('id'=>$xxxinstance4b)); $DB->insert_record('user_enrolments', array('enrolid'=>$xxxinstance1->id, 'userid'=>$user1->id, 'status'=>ENROL_USER_SUSPENDED)); role_assign($studentrole->id, $user1->id, $context1->id, 'enrol_xxx', $xxxinstance1->id); role_assign($teacherrole->id, $user1->id, $context1->id, 'enrol_xxx', $xxxinstance1->id); $DB->insert_record('user_enrolments', array('enrolid'=>$xxxinstance1->id, 'userid'=>$user4->id, 'status'=>ENROL_USER_ACTIVE)); role_assign($studentrole->id, $user4->id, $context1->id, 'enrol_xxx', $xxxinstance1->id); $this->assertEquals(2, $DB->count_records('user_enrolments', array('enrolid'=>$xxxinstance1->id))); $this->assertEquals(6, $DB->count_records('role_assignments', array('contextid'=>$context1->id))); $DB->insert_record('user_enrolments', array('enrolid'=>$xxxinstance3->id, 'userid'=>$user1->id, 'status'=>ENROL_USER_ACTIVE)); role_assign($studentrole->id, $user1->id, $context3->id, 'enrol_xxx', $xxxinstance3->id); $DB->insert_record('user_enrolments', array('enrolid'=>$xxxinstance3->id, 'userid'=>$user2->id, 'status'=>ENROL_USER_SUSPENDED)); $this->assertEquals(2, $DB->count_records('user_enrolments', array('enrolid'=>$xxxinstance3->id))); $this->assertEquals(1, $DB->count_records('role_assignments', array('contextid'=>$context3->id))); $DB->insert_record('user_enrolments', array('enrolid'=>$xxxinstance4->id, 'userid'=>$user1->id, 'status'=>ENROL_USER_ACTIVE)); role_assign($studentrole->id, $user1->id, $context4->id, 'enrol_xxx', $xxxinstance4->id); $DB->insert_record('user_enrolments', array('enrolid'=>$xxxinstance4->id, 'userid'=>$user2->id, 'status'=>ENROL_USER_ACTIVE)); role_assign($studentrole->id, $user2->id, $context4->id, 'enrol_xxx', $xxxinstance4->id); $DB->insert_record('user_enrolments', array('enrolid'=>$xxxinstance4b->id, 'userid'=>$user1->id, 'status'=>ENROL_USER_SUSPENDED)); role_assign($teacherrole->id, $user1->id, $context4->id, 'enrol_xxx', $xxxinstance4b->id); $DB->insert_record('user_enrolments', array('enrolid'=>$xxxinstance4b->id, 'userid'=>$user4->id, 'status'=>ENROL_USER_ACTIVE)); role_assign($teacherrole->id, $user4->id, $context4->id, 'enrol_xxx', $xxxinstance4b->id); $this->assertEquals(2, $DB->count_records('user_enrolments', array('enrolid'=>$xxxinstance4->id))); $this->assertEquals(2, $DB->count_records('user_enrolments', array('enrolid'=>$xxxinstance4b->id))); $this->assertEquals(4, $DB->count_records('role_assignments', array('contextid'=>$context4->id))); // Finally do the migration. enrol_manual_migrate_plugin_enrolments('xxx'); // Verify results. $this->assertEquals(1, $DB->count_records('enrol', array('courseid'=>$course1->id, 'enrol'=>'manual'))); $this->assertEquals(1, $DB->count_records('enrol', array('courseid'=>$course1->id, 'enrol'=>'xxx'))); $maninstance1 = $DB->get_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'manual'), '*', MUST_EXIST); $this->assertEquals(ENROL_INSTANCE_DISABLED, $maninstance1->status); $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance1->id, 'userid'=>$user1->id, 'status'=>ENROL_USER_ACTIVE))); $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance1->id, 'userid'=>$user2->id, 'status'=>ENROL_USER_ACTIVE))); $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance1->id, 'userid'=>$user3->id, 'status'=>ENROL_USER_ACTIVE))); $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance1->id, 'userid'=>$user4->id, 'status'=>ENROL_USER_ACTIVE))); $this->assertEquals(4, $DB->count_records('user_enrolments', array('enrolid'=>$maninstance1->id))); $this->assertEquals(0, $DB->count_records('user_enrolments', array('enrolid'=>$xxxinstance1->id))); $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>'', 'userid'=>$user1->id, 'roleid'=>$studentrole->id, 'contextid'=>$context1->id))); $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>'', 'userid'=>$user1->id, 'roleid'=>$teacherrole->id, 'contextid'=>$context1->id))); $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>'', 'userid'=>$user2->id, 'roleid'=>$studentrole->id, 'contextid'=>$context1->id))); $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>'', 'userid'=>$user3->id, 'roleid'=>$teacherrole->id, 'contextid'=>$context1->id))); $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>'', 'userid'=>$user4->id, 'roleid'=>$studentrole->id, 'contextid'=>$context1->id))); $this->assertEquals(5, $DB->count_records('role_assignments', array('contextid'=>$context1->id))); $this->assertEquals(1, $DB->count_records('enrol', array('courseid'=>$course2->id, 'enrol'=>'manual'))); $this->assertEquals(0, $DB->count_records('enrol', array('courseid'=>$course2->id, 'enrol'=>'xxx'))); $maninstance2 = $DB->get_record('enrol', array('courseid'=>$course2->id, 'enrol'=>'manual'), '*', MUST_EXIST); $this->assertEquals(ENROL_INSTANCE_ENABLED, $maninstance2->status); $this->assertEquals(1, $DB->count_records('enrol', array('courseid'=>$course3->id, 'enrol'=>'manual'))); $this->assertEquals(1, $DB->count_records('enrol', array('courseid'=>$course3->id, 'enrol'=>'xxx'))); $maninstance3 = $DB->get_record('enrol', array('courseid'=>$course3->id, 'enrol'=>'manual'), '*', MUST_EXIST); $this->assertEquals(ENROL_INSTANCE_DISABLED, $maninstance3->status); $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance3->id, 'userid'=>$user1->id, 'status'=>ENROL_USER_ACTIVE))); $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance3->id, 'userid'=>$user2->id, 'status'=>ENROL_USER_SUSPENDED))); $this->assertEquals(2, $DB->count_records('user_enrolments', array('enrolid'=>$maninstance3->id))); $this->assertEquals(0, $DB->count_records('user_enrolments', array('enrolid'=>$xxxinstance3->id))); $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>'', 'userid'=>$user1->id, 'roleid'=>$studentrole->id, 'contextid'=>$context3->id))); $this->assertEquals(1, $DB->count_records('role_assignments', array('contextid'=>$context3->id))); $this->assertEquals(1, $DB->count_records('enrol', array('courseid'=>$course4->id, 'enrol'=>'manual'))); $this->assertEquals(2, $DB->count_records('enrol', array('courseid'=>$course4->id, 'enrol'=>'xxx'))); $maninstance4 = $DB->get_record('enrol', array('courseid'=>$course4->id, 'enrol'=>'manual'), '*', MUST_EXIST); $this->assertEquals(ENROL_INSTANCE_ENABLED, $maninstance4->status); $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance4->id, 'userid'=>$user1->id, 'status'=>ENROL_USER_ACTIVE))); $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance4->id, 'userid'=>$user2->id, 'status'=>ENROL_USER_ACTIVE))); $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance4->id, 'userid'=>$user4->id, 'status'=>ENROL_USER_SUSPENDED))); $this->assertEquals(3, $DB->count_records('user_enrolments', array('enrolid'=>$maninstance4->id))); $this->assertEquals(0, $DB->count_records('user_enrolments', array('enrolid'=>$xxxinstance4->id))); $this->assertEquals(0, $DB->count_records('user_enrolments', array('enrolid'=>$xxxinstance4b->id))); $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>'', 'userid'=>$user1->id, 'roleid'=>$studentrole->id, 'contextid'=>$context4->id))); $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>'', 'userid'=>$user1->id, 'roleid'=>$teacherrole->id, 'contextid'=>$context4->id))); $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>'', 'userid'=>$user2->id, 'roleid'=>$studentrole->id, 'contextid'=>$context4->id))); $this->assertTrue($DB->record_exists('role_assignments', array('itemid'=>0, 'component'=>'', 'userid'=>$user4->id, 'roleid'=>$teacherrole->id, 'contextid'=>$context4->id))); $this->assertEquals(4, $DB->count_records('role_assignments', array('contextid'=>$context4->id))); $this->assertEquals(0, $DB->count_records('enrol', array('courseid'=>$course5->id, 'enrol'=>'manual'))); $this->assertEquals(0, $DB->count_records('enrol', array('courseid'=>$course5->id, 'enrol'=>'xxx'))); // Make sure wrong params do not produce errors or notices. enrol_manual_migrate_plugin_enrolments('manual'); enrol_manual_migrate_plugin_enrolments('yyyy'); } public function test_expired(): void { global $DB; $this->resetAfterTest(); /** @var $manualplugin enrol_manual_plugin */ $manualplugin = enrol_get_plugin('manual'); $trace = new \null_progress_trace(); $now = time(); // Prepare some data. $studentrole = $DB->get_record('role', array('shortname'=>'student')); $this->assertNotEmpty($studentrole); $teacherrole = $DB->get_record('role', array('shortname'=>'teacher')); $this->assertNotEmpty($teacherrole); $managerrole = $DB->get_record('role', array('shortname'=>'manager')); $this->assertNotEmpty($managerrole); $user1 = $this->getDataGenerator()->create_user(); $user2 = $this->getDataGenerator()->create_user(); $user3 = $this->getDataGenerator()->create_user(); $user4 = $this->getDataGenerator()->create_user(); $course1 = $this->getDataGenerator()->create_course(); $course2 = $this->getDataGenerator()->create_course(); $course3 = $this->getDataGenerator()->create_course(); $context1 = \context_course::instance($course1->id); $context2 = \context_course::instance($course2->id); $context3 = \context_course::instance($course3->id); $this->assertEquals(3, $DB->count_records('enrol', array('enrol'=>'manual'))); $instance1 = $DB->get_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'manual'), '*', MUST_EXIST); $this->assertEquals($studentrole->id, $instance1->roleid); $instance2 = $DB->get_record('enrol', array('courseid'=>$course2->id, 'enrol'=>'manual'), '*', MUST_EXIST); $this->assertEquals($studentrole->id, $instance2->roleid); $instance3 = $DB->get_record('enrol', array('courseid'=>$course3->id, 'enrol'=>'manual'), '*', MUST_EXIST); $this->assertEquals($studentrole->id, $instance3->roleid); $this->assertEquals(0, $DB->count_records('user_enrolments')); $this->assertEquals(0, $DB->count_records('role_assignments')); $manualplugin->enrol_user($instance1, $user1->id, $studentrole->id); $manualplugin->enrol_user($instance1, $user2->id, $studentrole->id); $manualplugin->enrol_user($instance1, $user3->id, $studentrole->id, 0, $now-60); $manualplugin->enrol_user($instance3, $user1->id, $studentrole->id, 0, 0); $manualplugin->enrol_user($instance3, $user2->id, $studentrole->id, 0, $now+60*60); $manualplugin->enrol_user($instance3, $user3->id, $teacherrole->id, 0, $now-60*60); role_assign($managerrole->id, $user4->id, $context1->id); $this->assertEquals(6, $DB->count_records('user_enrolments')); $this->assertEquals(7, $DB->count_records('role_assignments')); $this->assertEquals(5, $DB->count_records('role_assignments', array('roleid'=>$studentrole->id))); $this->assertEquals(1, $DB->count_records('role_assignments', array('roleid'=>$teacherrole->id))); $this->assertEquals(1, $DB->count_records('role_assignments', array('roleid'=>$managerrole->id))); // Execute tests. $this->assertEquals(ENROL_EXT_REMOVED_KEEP, $manualplugin->get_config('expiredaction')); $manualplugin->sync($trace, null); $this->assertEquals(6, $DB->count_records('user_enrolments')); $this->assertEquals(7, $DB->count_records('role_assignments')); $manualplugin->set_config('expiredaction', ENROL_EXT_REMOVED_SUSPENDNOROLES); $manualplugin->sync($trace, $course2->id); $this->assertEquals(6, $DB->count_records('user_enrolments')); $this->assertEquals(7, $DB->count_records('role_assignments')); $this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>$context1->id, 'userid'=>$user3->id, 'roleid'=>$studentrole->id))); $this->assertTrue($DB->record_exists('role_assignments', array('contextid'=>$context3->id, 'userid'=>$user3->id, 'roleid'=>$teacherrole->id))); $manualplugin->sync($trace, null); $this->assertEquals(6, $DB->count_records('user_enrolments')); $this->assertEquals(5, $DB->count_records('role_assignments')); $this->assertEquals(4, $DB->count_records('role_assignments', array('roleid'=>$studentrole->id))); $this->assertEquals(0, $DB->count_records('role_assignments', array('roleid'=>$teacherrole->id))); $this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>$context1->id, 'userid'=>$user3->id, 'roleid'=>$studentrole->id))); $this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>$context3->id, 'userid'=>$user3->id, 'roleid'=>$teacherrole->id))); $manualplugin->set_config('expiredaction', ENROL_EXT_REMOVED_UNENROL); role_assign($studentrole->id, $user3->id, $context1->id); role_assign($teacherrole->id, $user3->id, $context3->id); $this->assertEquals(6, $DB->count_records('user_enrolments')); $this->assertEquals(7, $DB->count_records('role_assignments')); $this->assertEquals(5, $DB->count_records('role_assignments', array('roleid'=>$studentrole->id))); $this->assertEquals(1, $DB->count_records('role_assignments', array('roleid'=>$teacherrole->id))); $this->assertEquals(1, $DB->count_records('role_assignments', array('roleid'=>$managerrole->id))); $manualplugin->sync($trace, null); $this->assertEquals(4, $DB->count_records('user_enrolments')); $this->assertFalse($DB->record_exists('user_enrolments', array('enrolid'=>$instance1->id, 'userid'=>$user3->id))); $this->assertFalse($DB->record_exists('user_enrolments', array('enrolid'=>$instance3->id, 'userid'=>$user3->id))); $this->assertEquals(5, $DB->count_records('role_assignments')); $this->assertEquals(4, $DB->count_records('role_assignments', array('roleid'=>$studentrole->id))); $this->assertEquals(0, $DB->count_records('role_assignments', array('roleid'=>$teacherrole->id))); $this->assertEquals(1, $DB->count_records('role_assignments', array('roleid'=>$managerrole->id))); $manualplugin->set_config('expiredaction', ENROL_EXT_REMOVED_SUSPEND); $manualplugin->enrol_user($instance1, $user3->id, $studentrole->id, 0, $now-60); $manualplugin->enrol_user($instance3, $user3->id, $teacherrole->id, 0, $now-60*60); $maninstance1 = $DB->get_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'manual'), '*', MUST_EXIST); $maninstance2 = $DB->get_record('enrol', array('courseid'=>$course3->id, 'enrol'=>'manual'), '*', MUST_EXIST); $this->assertEquals(6, $DB->count_records('user_enrolments')); $this->assertEquals(7, $DB->count_records('role_assignments')); $this->assertEquals(5, $DB->count_records('role_assignments', array('roleid'=>$studentrole->id))); $this->assertEquals(1, $DB->count_records('role_assignments', array('roleid'=>$teacherrole->id))); $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance1->id, 'userid'=>$user3->id, 'status'=>ENROL_USER_ACTIVE))); $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance2->id, 'userid'=>$user3->id, 'status'=>ENROL_USER_ACTIVE))); $manualplugin->sync($trace, null); $this->assertEquals(6, $DB->count_records('user_enrolments')); $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$instance1->id, 'userid'=>$user3->id))); $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$instance3->id, 'userid'=>$user3->id))); $this->assertEquals(7, $DB->count_records('role_assignments')); $this->assertEquals(5, $DB->count_records('role_assignments', array('roleid'=>$studentrole->id))); $this->assertEquals(1, $DB->count_records('role_assignments', array('roleid'=>$teacherrole->id))); $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance1->id, 'userid'=>$user3->id, 'status'=>ENROL_USER_SUSPENDED))); $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$maninstance2->id, 'userid'=>$user3->id, 'status'=>ENROL_USER_SUSPENDED))); } public function test_send_expiry_notifications(): void { global $DB, $CFG; $this->resetAfterTest(); $this->preventResetByRollback(); // Messaging does not like transactions... $trace = new \null_progress_trace(); /** @var $manualplugin enrol_manual_plugin */ $manualplugin = enrol_get_plugin('manual'); $now = time(); $admin = get_admin(); // Note: hopefully nobody executes the unit tests the last second before midnight... $manualplugin->set_config('expirynotifylast', $now - 60*60*24); $manualplugin->set_config('expirynotifyhour', 0); $studentrole = $DB->get_record('role', array('shortname'=>'student')); $this->assertNotEmpty($studentrole); $editingteacherrole = $DB->get_record('role', array('shortname'=>'editingteacher')); $this->assertNotEmpty($editingteacherrole); $managerrole = $DB->get_record('role', array('shortname'=>'manager')); $this->assertNotEmpty($managerrole); $user1 = $this->getDataGenerator()->create_user(array('lastname'=>'xuser1')); $user2 = $this->getDataGenerator()->create_user(array('lastname'=>'xuser2')); $user3 = $this->getDataGenerator()->create_user(array('lastname'=>'xuser3')); $user4 = $this->getDataGenerator()->create_user(array('lastname'=>'xuser4')); $user5 = $this->getDataGenerator()->create_user(array('lastname'=>'xuser5')); $user6 = $this->getDataGenerator()->create_user(array('lastname'=>'xuser6')); $user7 = $this->getDataGenerator()->create_user(array('lastname'=>'xuser6')); $user8 = $this->getDataGenerator()->create_user(array('lastname'=>'xuser6')); $course1 = $this->getDataGenerator()->create_course(array('fullname'=>'xcourse1')); $course2 = $this->getDataGenerator()->create_course(array('fullname'=>'xcourse2')); $course3 = $this->getDataGenerator()->create_course(array('fullname'=>'xcourse3')); $course4 = $this->getDataGenerator()->create_course(array('fullname'=>'xcourse4')); $this->assertEquals(4, $DB->count_records('enrol', array('enrol'=>'manual'))); $instance1 = $DB->get_record('enrol', array('courseid'=>$course1->id, 'enrol'=>'manual'), '*', MUST_EXIST); $instance1->expirythreshold = 60*60*24*4; $instance1->expirynotify = 1; $instance1->notifyall = 1; $DB->update_record('enrol', $instance1); $instance2 = $DB->get_record('enrol', array('courseid'=>$course2->id, 'enrol'=>'manual'), '*', MUST_EXIST); $instance2->expirythreshold = 60*60*24*1; $instance2->expirynotify = 1; $instance2->notifyall = 1; $DB->update_record('enrol', $instance2); $instance3 = $DB->get_record('enrol', array('courseid'=>$course3->id, 'enrol'=>'manual'), '*', MUST_EXIST); $instance3->expirythreshold = 60*60*24*1; $instance3->expirynotify = 1; $instance3->notifyall = 0; $DB->update_record('enrol', $instance3); $instance4 = $DB->get_record('enrol', array('courseid'=>$course4->id, 'enrol'=>'manual'), '*', MUST_EXIST); $instance4->expirythreshold = 60*60*24*1; $instance4->expirynotify = 0; $instance4->notifyall = 0; $DB->update_record('enrol', $instance4); $manualplugin->enrol_user($instance1, $user1->id, $editingteacherrole->id, 0, $now + 60*60*24*1, ENROL_USER_SUSPENDED); // Suspended users are not notified. $manualplugin->enrol_user($instance1, $user2->id, $studentrole->id, 0, $now + 60*60*24*5); // Above threshold are not notified. $manualplugin->enrol_user($instance1, $user3->id, $studentrole->id, 0, $now + 60*60*24*3 + 60*60); // Less than one day after threshold - should be notified. $manualplugin->enrol_user($instance1, $user4->id, $studentrole->id, 0, $now + 60*60*24*4 - 60*3); // Less than one day after threshold - should be notified. $manualplugin->enrol_user($instance1, $user5->id, $studentrole->id, 0, $now + 60*60); // Should have been already notified. $manualplugin->enrol_user($instance1, $user6->id, $studentrole->id, 0, $now - 60); // Already expired. $manualplugin->enrol_user($instance1, $user7->id, $editingteacherrole->id); $manualplugin->enrol_user($instance1, $user8->id, $managerrole->id); // Highest role --> enroller. $manualplugin->enrol_user($instance2, $user1->id, $studentrole->id); $manualplugin->enrol_user($instance2, $user2->id, $studentrole->id, 0, $now + 60*60*24*1 + 60*3); // Above threshold are not notified. $manualplugin->enrol_user($instance2, $user3->id, $studentrole->id, 0, $now + 60*60*24*1 - 60*60); // Less than one day after threshold - should be notified. $manualplugin->enrol_user($instance3, $user1->id, $editingteacherrole->id); $manualplugin->enrol_user($instance3, $user2->id, $studentrole->id, 0, $now + 60*60*24*1 + 60); // Above threshold are not notified. $manualplugin->enrol_user($instance3, $user3->id, $studentrole->id, 0, $now + 60*60*24*1 - 60*60); // Less than one day after threshold - should be notified. $manualplugin->enrol_user($instance4, $user4->id, $editingteacherrole->id); $manualplugin->enrol_user($instance4, $user5->id, $studentrole->id, 0, $now + 60*60*24*1 + 60); $manualplugin->enrol_user($instance4, $user6->id, $studentrole->id, 0, $now + 60*60*24*1 - 60*60); // The notification is sent out in fixed order first individual users, // then summary per course by enrolid, user lastname, etc. $this->assertGreaterThan($instance1->id, $instance2->id); $this->assertGreaterThan($instance2->id, $instance3->id); $sink = $this->redirectMessages(); $manualplugin->send_expiry_notifications($trace); $messages = $sink->get_messages(); $this->assertEquals(2+1 + 1+1 + 1 + 0, count($messages)); // First individual notifications from course1. $this->assertEquals($user3->id, $messages[0]->useridto); $this->assertEquals($user8->id, $messages[0]->useridfrom); $this->assertStringContainsString('xcourse1', $messages[0]->fullmessagehtml); $this->assertEquals($user4->id, $messages[1]->useridto); $this->assertEquals($user8->id, $messages[1]->useridfrom); $this->assertStringContainsString('xcourse1', $messages[1]->fullmessagehtml); // Then summary for course1. $this->assertEquals($user8->id, $messages[2]->useridto); $this->assertEquals($admin->id, $messages[2]->useridfrom); $this->assertStringContainsString('xcourse1', $messages[2]->fullmessagehtml); $this->assertStringNotContainsString('xuser1', $messages[2]->fullmessagehtml); $this->assertStringNotContainsString('xuser2', $messages[2]->fullmessagehtml); $this->assertStringContainsString('xuser3', $messages[2]->fullmessagehtml); $this->assertStringContainsString('xuser4', $messages[2]->fullmessagehtml); $this->assertStringContainsString('xuser5', $messages[2]->fullmessagehtml); $this->assertStringNotContainsString('xuser6', $messages[2]->fullmessagehtml); // First individual notifications from course2. $this->assertEquals($user3->id, $messages[3]->useridto); $this->assertEquals($admin->id, $messages[3]->useridfrom); $this->assertStringContainsString('xcourse2', $messages[3]->fullmessagehtml); // Then summary for course2. $this->assertEquals($admin->id, $messages[4]->useridto); $this->assertEquals($admin->id, $messages[4]->useridfrom); $this->assertStringContainsString('xcourse2', $messages[4]->fullmessagehtml); $this->assertStringNotContainsString('xuser1', $messages[4]->fullmessagehtml); $this->assertStringNotContainsString('xuser2', $messages[4]->fullmessagehtml); $this->assertStringContainsString('xuser3', $messages[4]->fullmessagehtml); $this->assertStringNotContainsString('xuser4', $messages[4]->fullmessagehtml); $this->assertStringNotContainsString('xuser5', $messages[4]->fullmessagehtml); $this->assertStringNotContainsString('xuser6', $messages[4]->fullmessagehtml); // Only summary in course3. $this->assertEquals($user1->id, $messages[5]->useridto); $this->assertEquals($admin->id, $messages[5]->useridfrom); $this->assertStringContainsString('xcourse3', $messages[5]->fullmessagehtml); $this->assertStringNotContainsString('xuser1', $messages[5]->fullmessagehtml); $this->assertStringNotContainsString('xuser2', $messages[5]->fullmessagehtml); $this->assertStringContainsString('xuser3', $messages[5]->fullmessagehtml); $this->assertStringNotContainsString('xuser4', $messages[5]->fullmessagehtml); $this->assertStringNotContainsString('xuser5', $messages[5]->fullmessagehtml); $this->assertStringNotContainsString('xuser6', $messages[5]->fullmessagehtml); // Make sure that notifications are not repeated. $sink->clear(); $manualplugin->send_expiry_notifications($trace); $this->assertEquals(0, $sink->count()); // use invalid notification hour to verify that before the hour the notifications are not sent. $manualplugin->set_config('expirynotifylast', time() - 60*60*24); $manualplugin->set_config('expirynotifyhour', '24'); $manualplugin->send_expiry_notifications($trace); $this->assertEquals(0, $sink->count()); $manualplugin->set_config('expirynotifyhour', '0'); $manualplugin->send_expiry_notifications($trace); $this->assertEquals(6, $sink->count()); } /** * Test for getting user enrolment actions. */ public function test_get_user_enrolment_actions(): void { global $CFG, $PAGE; $this->resetAfterTest(); // Set page URL to prevent debugging messages. $PAGE->set_url('/enrol/editinstance.php'); $pluginname = 'manual'; // Only enable the manual enrol plugin. $CFG->enrol_plugins_enabled = $pluginname; $generator = $this->getDataGenerator(); // Get the enrol plugin. $plugin = enrol_get_plugin($pluginname); // Create a course. $course = $generator->create_course(); // Enable this enrol plugin for the course. $plugin->add_instance($course); // Create a teacher. $teacher = $generator->create_user(); // Enrol the teacher to the course. $generator->enrol_user($teacher->id, $course->id, 'editingteacher', $pluginname); // Create a student. $student = $generator->create_user(); // Enrol the student to the course. $generator->enrol_user($student->id, $course->id, 'student', $pluginname); // Login as the teacher. $this->setUser($teacher); require_once($CFG->dirroot . '/enrol/locallib.php'); $manager = new course_enrolment_manager($PAGE, $course); $userenrolments = $manager->get_user_enrolments($student->id); $this->assertCount(1, $userenrolments); $ue = reset($userenrolments); $actions = $plugin->get_user_enrolment_actions($manager, $ue); // Manual enrol has 2 enrol actions -- edit and unenrol. $this->assertCount(2, $actions); } /** * Test how the default enrolment instance inherits its settings from the global plugin settings. * * @dataProvider default_enrolment_instance_data_provider * @param stdClass $expectation * @param stdClass $globalsettings * @covers \enrol_manual::add_default_instance */ public function test_default_enrolment_instance_acquires_correct_settings(stdClass $expectation, stdClass $globalsettings): void { global $DB; $this->resetAfterTest(); $generator = $this->getDataGenerator(); // Given the plugin is globally configured with the following settings. $plugin = enrol_get_plugin('manual'); $plugin->set_config('status', $globalsettings->status); $plugin->set_config('roleid', $globalsettings->roleid); $plugin->set_config('enrolperiod', $globalsettings->enrolperiod); $plugin->set_config('expirynotify', $globalsettings->expirynotify); $plugin->set_config('expirythreshold', $globalsettings->expirythreshold); // When creating a course. $course = $generator->create_course(); // Then the default manual enrolment instance being created is properly configured. $enrolinstance = $DB->get_record('enrol', ['courseid' => $course->id, 'enrol' => 'manual']); $this->assertEquals($expectation->status, $enrolinstance->status); $this->assertEquals($expectation->roleid, $enrolinstance->roleid); $this->assertEquals($expectation->enrolperiod, $enrolinstance->enrolperiod); $this->assertEquals($expectation->expirynotify, $enrolinstance->expirynotify); $this->assertEquals($expectation->notifyall, $enrolinstance->notifyall); $this->assertEquals($expectation->expirythreshold, $enrolinstance->expirythreshold); } /** * Data provider for test_default_enrolment_instance_acquires_correct_settings(). * * @return array */ public static function default_enrolment_instance_data_provider(): array { $studentroles = get_archetype_roles('student'); $studentrole = array_shift($studentroles); $teacherroles = get_archetype_roles('teacher'); $teacherrole = array_shift($teacherroles); return [ 'enabled, student role, no duration set, notify no one on expiry, 12 hours notification threshold' => [ 'expectation' => (object) [ 'status' => ENROL_INSTANCE_ENABLED, 'roleid' => $studentrole->id, 'enrolperiod' => 0, 'expirynotify' => 0, 'notifyall' => 0, 'expirythreshold' => 12 * HOURSECS, ], 'globalsettings' => (object) [ 'status' => ENROL_INSTANCE_ENABLED, 'roleid' => $studentrole->id, 'enrolperiod' => 0, 'expirynotify' => 0, 'expirythreshold' => 12 * HOURSECS, ], ], 'enabled, student role, 72 hours duration, notify enroller only on expiry, 1 day notification threshold' => [ 'expectation' => (object) [ 'status' => ENROL_INSTANCE_ENABLED, 'roleid' => $studentrole->id, 'enrolperiod' => 72 * HOURSECS, 'expirynotify' => 1, 'notifyall' => 0, 'expirythreshold' => DAYSECS, ], 'globalsettings' => (object) [ 'status' => ENROL_INSTANCE_ENABLED, 'roleid' => $studentrole->id, 'enrolperiod' => 72 * HOURSECS, 'expirynotify' => 1, 'expirythreshold' => DAYSECS, ], ], 'disabled, teacher role, no duration set, notify enroller and enrolled on expiry, 0 notification threshold' => [ 'expectation' => (object) [ 'status' => ENROL_INSTANCE_DISABLED, 'roleid' => $teacherrole->id, 'enrolperiod' => 0, 'expirynotify' => 2, 'notifyall' => 1, 'expirythreshold' => 0 ], 'globalsettings' => (object) [ 'status' => ENROL_INSTANCE_DISABLED, 'roleid' => $teacherrole->id, 'enrolperiod' => 0, 'expirynotify' => 2, 'expirythreshold' => 0, ], ], ]; } /** * Tests an enrolment instance is updated properly. * * @covers \enrol_manual::update_instance * @dataProvider update_enrolment_instance_data_provider * * @param stdClass $expectation * @param stdClass $updatedata */ public function test_enrolment_instance_is_updated(stdClass $expectation, stdClass $updatedata): void { global $DB; $this->resetAfterTest(); $generator = $this->getDataGenerator(); $studentroles = get_archetype_roles('student'); $studentrole = array_shift($studentroles); // Given the plugin is globally configured with the following settings. $plugin = enrol_get_plugin('manual'); $plugin->set_config('status', ENROL_INSTANCE_ENABLED); $plugin->set_config('roleid', $studentrole->id); $plugin->set_config('enrolperiod', 30 * DAYSECS); $plugin->set_config('expirynotify', 1); $plugin->set_config('expirythreshold', 2 * DAYSECS); // And a course is created with the default enrolment instance. $course = $generator->create_course(); // When the enrolment instance is being updated. $enrolinstance = $DB->get_record('enrol', ['courseid' => $course->id, 'enrol' => 'manual']); $successfullyupdated = $plugin->update_instance($enrolinstance, $updatedata); // Then the update is successful. $this->assertTrue($successfullyupdated); // And the updated enrolment instance contains the expected values. $enrolinstance = $DB->get_record('enrol', ['id' => $enrolinstance->id]); $this->assertEquals($expectation->status, $enrolinstance->status); $this->assertEquals($expectation->roleid, $enrolinstance->roleid); $this->assertEquals($expectation->enrolperiod, $enrolinstance->enrolperiod); $this->assertEquals($expectation->expirynotify, $enrolinstance->expirynotify); $this->assertEquals($expectation->notifyall, $enrolinstance->notifyall); $this->assertEquals($expectation->expirythreshold, $enrolinstance->expirythreshold); } /** * Data provider for test_enrolment_instance_is_updated(). * * @return array */ public static function update_enrolment_instance_data_provider(): array { $studentroles = get_archetype_roles('student'); $studentrole = array_shift($studentroles); $teacherroles = get_archetype_roles('teacher'); $teacherrole = array_shift($teacherroles); return [ 'disabled, all the others are default' => [ 'expectation' => (object) [ 'status' => ENROL_INSTANCE_DISABLED, 'roleid' => $studentrole->id, 'enrolperiod' => 30 * DAYSECS, 'expirynotify' => 1, 'notifyall' => 0, 'expirythreshold' => 2 * DAYSECS, ], 'updatedata' => (object) [ 'status' => ENROL_INSTANCE_DISABLED, 'roleid' => $studentrole->id, 'enrolperiod' => 30 * DAYSECS, 'expirynotify' => 1, 'expirythreshold' => 2 * DAYSECS, ], ], 'enabled, teacher role, no duration set, notify no one on expiry, 0 notification threshold' => [ 'expectation' => (object) [ 'status' => ENROL_INSTANCE_ENABLED, 'roleid' => $teacherrole->id, 'enrolperiod' => 0, 'expirynotify' => 0, 'notifyall' => 0, 'expirythreshold' => 0, ], 'updatedata' => (object) [ 'status' => ENROL_INSTANCE_ENABLED, 'roleid' => $teacherrole->id, 'enrolperiod' => 0, 'expirynotify' => 0, 'expirythreshold' => 0, ], ], 'notify enroller and enrolled on expiry, all the others are default' => [ 'expectation' => (object) [ 'status' => ENROL_INSTANCE_ENABLED, 'roleid' => $studentrole->id, 'enrolperiod' => 30 * DAYSECS, 'expirynotify' => 2, 'notifyall' => 1, 'expirythreshold' => 2 * DAYSECS, ], 'updatedata' => (object) [ 'status' => ENROL_INSTANCE_ENABLED, 'roleid' => $studentrole->id, 'enrolperiod' => 30 * DAYSECS, 'expirynotify' => 2, 'expirythreshold' => 2 * DAYSECS, ], ], ]; } /** * Test the behaviour of find_instance(). * * @covers ::find_instance */ public function test_find_instance(): void { global $DB; $this->resetAfterTest(); $cat = $this->getDataGenerator()->create_category(); // When we create a course, a manual enrolment instance is also created. $course = $this->getDataGenerator()->create_course(['category' => $cat->id, 'shortname' => 'ANON']); $teacherrole = $DB->get_record('role', ['shortname' => 'teacher']); $manualplugin = enrol_get_plugin('manual'); $expected = $DB->get_record('enrol', ['courseid' => $course->id, 'enrol' => 'manual']); // Let's try to add second instance - only 1 manual instance is possible. $instanceid2 = $manualplugin->add_instance($course, ['roleid' => $teacherrole->id]); $this->assertNull($instanceid2); $enrolmentdata = []; $actual = $manualplugin->find_instance($enrolmentdata, $course->id); $this->assertEquals($expected->id, $actual->id); } /** * Test send_course_welcome_message_to_user() method. * * @covers \enrol_plugin::send_course_welcome_message_to_user */ public function test_send_course_welcome_message(): void { global $DB; $this->resetAfterTest(); // Create course. $course = $this->getDataGenerator()->create_course([ 'fullname' => 'Course 1 & 2', 'shortname' => 'C1', ]); $courseurl = course_get_url($course)->out(); // Create users. $student = $this->getDataGenerator()->create_user(); $teacher1 = $this->getDataGenerator()->create_user(); $teacher2 = $this->getDataGenerator()->create_user(); $noreplyuser = \core_user::get_noreply_user(); // Enrol users. $this->getDataGenerator()->enrol_user($teacher1->id, $course->id, 'editingteacher'); $this->getDataGenerator()->enrol_user($teacher2->id, $course->id, 'editingteacher'); // Get manual plugin. $manualplugin = enrol_get_plugin('manual'); $maninstance = $DB->get_record( 'enrol', ['courseid' => $course->id, 'enrol' => 'manual'], '*', MUST_EXIST, ); // Test 1: Send welcome message to user from course contact with default message. // Redirect messages. $messagesink = $this->redirectMessages(); $manualplugin->send_course_welcome_message_to_user( instance: $maninstance, userid: $student->id, sendoption: ENROL_SEND_EMAIL_FROM_COURSE_CONTACT, message: '', ); $messages = $messagesink->get_messages_by_component_and_type( 'moodle', 'enrolcoursewelcomemessage', ); $this->assertNotEmpty($messages); $message = reset($messages); // The message should be sent from the first teacher. $this->assertEquals($teacher1->id, $message->useridfrom); $this->assertStringContainsString($course->fullname, $message->subject); $this->assertEquals( get_string( 'customwelcomemessageplaceholder', 'core_enrol', ['firstname' => $student->firstname, 'coursename' => $course->fullname], ), $message->fullmessage, ); // Clear sink. $messagesink->clear(); // Test 2: Send welcome message to user from course contact with a custom message. // Unenrol the first teacher from course. $manualplugin->unenrol_user($maninstance, $teacher1->id); // Redirect messages. $messagesink = $this->redirectMessages(); $manualplugin->send_course_welcome_message_to_user( instance: $maninstance, userid: $student->id, sendoption: ENROL_SEND_EMAIL_FROM_COURSE_CONTACT, message: 'Your email address: {$a->email}, your first name: {$a->firstname}, your last name: {$a->lastname}, ' . 'your course: {$a->coursename}', ); $messages = $messagesink->get_messages_by_component_and_type( 'moodle', 'enrolcoursewelcomemessage', ); $this->assertNotEmpty($messages); $message = reset($messages); // The message should be sent from the second teacher. $this->assertEquals($teacher2->id, $message->useridfrom); $this->assertStringContainsString($course->fullname, $message->subject); $this->assertEquals( 'Your email address: ' . $student->email . ', your first name: ' . $student->firstname . ', your last name: ' . $student->lastname . ', your course: ' . $course->fullname, $message->fullmessage, ); // Clear sink. $messagesink->clear(); // Test 3: Send welcome message to user from no-reply user with a custom message. // Redirect messages. $messagesink = $this->redirectMessages(); $manualplugin->send_course_welcome_message_to_user( instance: $maninstance, userid: $student->id, sendoption: ENROL_SEND_EMAIL_FROM_NOREPLY, message: 'Welcome to {$a->coursename}', ); $messages = $messagesink->get_messages_by_component_and_type( 'moodle', 'enrolcoursewelcomemessage', ); $this->assertNotEmpty($messages); $message = reset($messages); // The message should be sent from the noreply user. $this->assertEquals($noreplyuser->id, $message->useridfrom); $this->assertStringContainsString($course->fullname, $message->subject); $this->assertEquals( "Welcome to " . htmlentities($course->fullname) . "", $message->fullmessagehtml, ); // Clear sink. $messagesink->clear(); } /** * Test send_course_welcome_message_to_user() method via hook. * * @covers \enrol_plugin::send_course_welcome_message_to_user */ public function test_send_course_welcome_message_via_hook(): void { global $DB; $this->resetAfterTest(); $messagesink = $this->redirectMessages(); $course = $this->getDataGenerator()->create_course([ 'fullname' => 'Course 1', 'shortname' => 'C1', ]); $maninstance = $DB->get_record( 'enrol', ['courseid' => $course->id, 'enrol' => 'manual'], '*', MUST_EXIST, ); $maninstance->customint1 = ENROL_SEND_EMAIL_FROM_NOREPLY; $DB->update_record('enrol', $maninstance); $student = $this->getDataGenerator()->create_user(); $this->getDataGenerator()->enrol_user($student->id, $course->id); $messages = $messagesink->get_messages_by_component_and_type( 'moodle', 'enrolcoursewelcomemessage', ); $this->assertNotEmpty($messages); $message = reset($messages); $this->assertStringContainsString($course->fullname, $message->subject); } } PKԄ\zDe88tests/externallib_test.phpnu[. namespace enrol_manual; use enrol_manual_external; use externallib_advanced_testcase; defined('MOODLE_INTERNAL') || die(); global $CFG; require_once($CFG->dirroot . '/webservice/tests/helpers.php'); require_once($CFG->dirroot . '/enrol/manual/externallib.php'); /** * Enrol manual external PHPunit tests * * @package enrol_manual * @category phpunit * @copyright 2012 Jerome Mouneyrac * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @covers \enrol_manual_external * @since Moodle 2.4 */ final class externallib_test extends externallib_advanced_testcase { /** * Test get_enrolled_users */ public function test_enrol_users(): void { global $DB; $this->resetAfterTest(true); $user = self::getDataGenerator()->create_user(); $this->setUser($user); $course1 = self::getDataGenerator()->create_course(); $course2 = self::getDataGenerator()->create_course(); $user1 = self::getDataGenerator()->create_user(); $user2 = self::getDataGenerator()->create_user(); $context1 = \context_course::instance($course1->id); $context2 = \context_course::instance($course2->id); $instance1 = $DB->get_record('enrol', array('courseid' => $course1->id, 'enrol' => 'manual'), '*', MUST_EXIST); $instance2 = $DB->get_record('enrol', array('courseid' => $course2->id, 'enrol' => 'manual'), '*', MUST_EXIST); // Set the required capabilities by the external function. $roleid = $this->assignUserCapability('enrol/manual:enrol', $context1->id); $this->assignUserCapability('moodle/course:view', $context1->id, $roleid); $this->assignUserCapability('moodle/role:assign', $context1->id, $roleid); $this->assignUserCapability('enrol/manual:enrol', $context2->id, $roleid); $this->assignUserCapability('moodle/course:view', $context2->id, $roleid); $this->assignUserCapability('moodle/role:assign', $context2->id, $roleid); core_role_set_assign_allowed($roleid, 3); // Call the external function. enrol_manual_external::enrol_users(array( array('roleid' => 3, 'userid' => $user1->id, 'courseid' => $course1->id), array('roleid' => 3, 'userid' => $user2->id, 'courseid' => $course1->id), )); $this->assertEquals(2, $DB->count_records('user_enrolments', array('enrolid' => $instance1->id))); $this->assertEquals(0, $DB->count_records('user_enrolments', array('enrolid' => $instance2->id))); $this->assertTrue(is_enrolled($context1, $user1)); $this->assertTrue(is_enrolled($context1, $user2)); // Call without required capability. $DB->delete_records('user_enrolments'); $this->unassignUserCapability('enrol/manual:enrol', $context1->id, $roleid); try { enrol_manual_external::enrol_users(array( array('roleid' => 3, 'userid' => $user1->id, 'courseid' => $course1->id), )); $this->fail('Exception expected if not having capability to enrol'); } catch (\moodle_exception $e) { $this->assertInstanceOf('required_capability_exception', $e); $this->assertSame('nopermissions', $e->errorcode); } $this->assignUserCapability('enrol/manual:enrol', $context1->id, $roleid); $this->assertEquals(0, $DB->count_records('user_enrolments')); // Call with invalid user. try { enrol_manual_external::enrol_users([ ['roleid' => 1, 'userid' => 654321, 'courseid' => $course1->id], ]); $this->fail('Exception expected for invalid user.'); } catch (\moodle_exception $e) { $this->assertSame('invaliduser', $e->errorcode); } // Call with forbidden role. try { enrol_manual_external::enrol_users(array( array('roleid' => 1, 'userid' => $user1->id, 'courseid' => $course1->id), )); $this->fail('Exception expected if not allowed to assign role.'); } catch (\moodle_exception $e) { $this->assertSame('wsusercannotassign', $e->errorcode); } $this->assertEquals(0, $DB->count_records('user_enrolments')); // Call for course without manual instance. $DB->delete_records('user_enrolments'); $DB->delete_records('enrol', array('courseid' => $course2->id)); try { enrol_manual_external::enrol_users(array( array('roleid' => 3, 'userid' => $user1->id, 'courseid' => $course1->id), array('roleid' => 3, 'userid' => $user1->id, 'courseid' => $course2->id), )); $this->fail('Exception expected if course does not have manual instance'); } catch (\moodle_exception $e) { $this->assertSame('wsnoinstance', $e->errorcode); $this->assertSame( "Manual enrolment plugin instance doesn't exist or is disabled for the course (id = {$course2->id})", $e->getMessage() ); } } /** * Test for unerolling a single user. * @throws coding_exception * @throws invalid_parameter_exception * @throws moodle_exception */ public function test_unenrol_user_single(): void { global $CFG, $DB; require_once($CFG->libdir . '/enrollib.php'); $this->resetAfterTest(true); // The user who perform the action. $user = $this->getDataGenerator()->create_user(); $this->setUser($user); // Log this user in. $enrol = enrol_get_plugin('manual'); // Create a course. $course = self::getDataGenerator()->create_course(); $coursecontext = \context_course::instance($course->id); $enrolinstance = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'manual'), '*', MUST_EXIST); // Set the capability for the user. $roleid = $this->assignUserCapability('enrol/manual:enrol', $coursecontext); $this->assignUserCapability('enrol/manual:unenrol', $coursecontext, $roleid); $this->assignUserCapability('moodle/course:view', $coursecontext, $roleid); $this->assignUserCapability('moodle/role:assign', $coursecontext, $roleid); // Create a student and enrol them into the course. $student = $this->getDataGenerator()->create_user(); $enrol->enrol_user($enrolinstance, $student->id); $this->assertTrue(is_enrolled($coursecontext, $student)); // Call the web service to unenrol. enrol_manual_external::unenrol_users(array( array('userid' => $student->id, 'courseid' => $course->id), )); $this->assertFalse(is_enrolled($coursecontext, $student)); } /** * Test for unenrolling multiple users. * @throws coding_exception * @throws invalid_parameter_exception * @throws moodle_exception */ public function test_unenrol_user_multiple(): void { global $CFG, $DB; require_once($CFG->libdir . '/enrollib.php'); $this->resetAfterTest(true); // The user who perform the action. $user = $this->getDataGenerator()->create_user(); $this->setUser($user); // Log this user in. // Create a course. $course = self::getDataGenerator()->create_course(); $coursecontext = \context_course::instance($course->id); $enrolinstance = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'manual'), '*', MUST_EXIST); // Set the capability for the user. $roleid = $this->assignUserCapability('enrol/manual:enrol', $coursecontext); $this->assignUserCapability('enrol/manual:unenrol', $coursecontext, $roleid); $this->assignUserCapability('moodle/course:view', $coursecontext, $roleid); $this->assignUserCapability('moodle/role:assign', $coursecontext, $roleid); $enrol = enrol_get_plugin('manual'); // Create a student and enrol them into the course. $student1 = $this->getDataGenerator()->create_user(); $enrol->enrol_user($enrolinstance, $student1->id); $this->assertTrue(is_enrolled($coursecontext, $student1)); $student2 = $this->getDataGenerator()->create_user(); $enrol->enrol_user($enrolinstance, $student2->id); $this->assertTrue(is_enrolled($coursecontext, $student2)); // Call the web service to unenrol. enrol_manual_external::unenrol_users(array( array('userid' => $student1->id, 'courseid' => $course->id), array('userid' => $student2->id, 'courseid' => $course->id), )); $this->assertFalse(is_enrolled($coursecontext, $student1)); $this->assertFalse(is_enrolled($coursecontext, $student2)); } /** * Test for unenrol capability. * @throws coding_exception * @throws invalid_parameter_exception * @throws moodle_exception */ public function test_unenrol_user_error_no_capability(): void { global $CFG, $DB; require_once($CFG->libdir . '/enrollib.php'); $this->resetAfterTest(true); // The user who perform the action. $user = $this->getDataGenerator()->create_user(); $this->setUser($user); // Log this user in. // Create a course. $course = self::getDataGenerator()->create_course(); $coursecontext = \context_course::instance($course->id); $enrolinstance = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'manual'), '*', MUST_EXIST); $enrol = enrol_get_plugin('manual'); // Create a student and enrol them into the course. $student = $this->getDataGenerator()->create_user(); $enrol->enrol_user($enrolinstance, $student->id); $this->assertTrue(is_enrolled($coursecontext, $student)); // Call the web service to unenrol. try { enrol_manual_external::unenrol_users(array( array('userid' => $student->id, 'courseid' => $course->id), )); $this->fail('Exception expected: User cannot log in to the course'); } catch (\Exception $ex) { $this->assertTrue($ex instanceof \require_login_exception); } // Set the capability for the course, then try again. $roleid = $this->assignUserCapability('moodle/course:view', $coursecontext); try { enrol_manual_external::unenrol_users(array( array('userid' => $student->id, 'courseid' => $course->id), )); $this->fail('Exception expected: User cannot log in to the course'); } catch (\Exception $ex) { $this->assertTrue($ex instanceof \required_capability_exception); } // Assign unenrol capability. $this->assignUserCapability('enrol/manual:unenrol', $coursecontext, $roleid); enrol_manual_external::unenrol_users(array( array('userid' => $student->id, 'courseid' => $course->id), )); $this->assertFalse(is_enrolled($coursecontext, $student)); } /** * Test for unenrol if user does not exist. * @throws coding_exception */ public function test_unenrol_user_error_not_exist(): void { global $CFG, $DB; require_once($CFG->libdir . '/enrollib.php'); $this->resetAfterTest(true); // The user who perform the action. $user = $this->getDataGenerator()->create_user(); $this->setUser($user); // Log this user in. $enrol = enrol_get_plugin('manual'); // Create a course. $course = self::getDataGenerator()->create_course(); $coursecontext = \context_course::instance($course->id); $enrolinstance = $DB->get_record('enrol', array('courseid' => $course->id, 'enrol' => 'manual'), '*', MUST_EXIST); // Set the capability for the user. $roleid = $this->assignUserCapability('enrol/manual:enrol', $coursecontext); $this->assignUserCapability('enrol/manual:unenrol', $coursecontext, $roleid); $this->assignUserCapability('moodle/course:view', $coursecontext, $roleid); $this->assignUserCapability('moodle/role:assign', $coursecontext, $roleid); // Create a student and enrol them into the course. $student = $this->getDataGenerator()->create_user(); $enrol->enrol_user($enrolinstance, $student->id); $this->assertTrue(is_enrolled($coursecontext, $student)); try { enrol_manual_external::unenrol_users(array( array('userid' => $student->id + 1, 'courseid' => $course->id), )); $this->fail('Exception expected: invalid student id'); } catch (\Exception $ex) { $this->assertSame('invaliduser', $ex->errorcode); } // Call for course without manual instance. $DB->delete_records('user_enrolments'); $DB->delete_records('enrol', ['courseid' => $course->id]); try { enrol_manual_external::unenrol_users(array( array('userid' => $student->id + 1, 'courseid' => $course->id), )); $this->fail('Exception expected if course does not have manual instance'); } catch (\moodle_exception $e) { $this->assertSame('wsnoinstance', $e->errorcode); $this->assertSame( "Manual enrolment plugin instance doesn't exist or is disabled for the course (id = {$course->id})", $e->getMessage() ); } } } PKԄ\vBvB"tests/behat/quickenrolment.featurenu[@enrol @enrol_manual Feature: Teacher can search and enrol users one by one into the course In order to quickly enrol particular students into my course As a teacher I can search for the students and enrol them into the course Background: Given the following "custom profile fields" exist: | datatype | shortname | name | | text | customid | Custom user id | And the following "users" exist: | username | firstname | lastname | email | profile_field_customid | | teacher001 | Teacher | 001 | teacher001@example.com | | | student001 | Student | 001 | student001@example.com | Q994 | | student002 | Student | 002 | student002@example.com | Q008 | | student003 | Student | 003 | student003@example.com | Z442 | | student004 | Student | 004 | student004@example.com | | | student005 | Student | 005 | student005@example.com | | | student006 | Student | 006 | student006@example.com | | | student007 | Student | 007 | student007@example.com | | | student008 | Student | 008 | student008@example.com | | | student009 | Student | 009 | student009@example.com | | | student010 | Student | 010 | student010@example.com | | | student011 | Student | 011 | student011@example.com | | | student012 | Student | 012 | student012@example.com | | | student013 | Student | 013 | student013@example.com | | | student014 | Student | 014 | student014@example.com | | | student015 | Student | 015 | student015@example.com | | | student016 | Student | 016 | student016@example.com | | | student017 | Student | 017 | student017@example.com | | | student018 | Student | 018 | student018@example.com | | | student019 | Student | 019 | student019@example.com | | | student020 | Student | 020 | student020@example.com | | | student021 | Student | 021 | student021@example.com | | | student022 | Student | 022 | student022@example.com | | | student023 | Student | 023 | student023@example.com | | | student024 | Student | 024 | student024@example.com | | | student025 | Student | 025 | student025@example.com | | | student026 | Student | 026 | student026@example.com | | | student027 | Student | 027 | student027@example.com | | | student028 | Student | 028 | student028@example.com | | | student029 | Student | 029 | student029@example.com | | | student030 | Student | 030 | student030@example.com | | | student031 | Student | 031 | student031@example.com | | | student032 | Student | 032 | student032@example.com | | | student033 | Student | 033 | student033@example.com | | | student034 | Student | 034 | student034@example.com | | | student035 | Student | 035 | student035@example.com | | | student036 | Student | 036 | student036@example.com | | | student037 | Student | 037 | student037@example.com | | | student038 | Student | 038 | student038@example.com | | | student039 | Student | 039 | student039@example.com | | | student040 | Student | 040 | student040@example.com | | | student041 | Student | 041 | student041@example.com | | | student042 | Student | 042 | student042@example.com | | | student043 | Student | 043 | student043@example.com | | | student044 | Student | 044 | student044@example.com | | | student045 | Student | 045 | student045@example.com | | | student046 | Student | 046 | student046@example.com | | | student047 | Student | 047 | student047@example.com | | | student048 | Student | 048 | student048@example.com | | | student049 | Student | 049 | student049@example.com | | | student050 | Student | 050 | student050@example.com | | | student051 | Student | 051 | student051@example.com | | | student052 | Student | 052 | student052@example.com | | | student053 | Student | 053 | student053@example.com | | | student054 | Student | 054 | student054@example.com | | | student055 | Student | 055 | student055@example.com | | | student056 | Student | 056 | student056@example.com | | | student057 | Student | 057 | student057@example.com | | | student058 | Student | 058 | student058@example.com | | | student059 | Student | 059 | student059@example.com | | | student060 | Student | 060 | student060@example.com | | | student061 | Student | 061 | student061@example.com | | | student062 | Student | 062 | student062@example.com | | | student063 | Student | 063 | student063@example.com | | | student064 | Student | 064 | student064@example.com | | | student065 | Student | 065 | student065@example.com | | | student066 | Student | 066 | student066@example.com | | | student067 | Student | 067 | student067@example.com | | | student068 | Student | 068 | student068@example.com | | | student069 | Student | 069 | student069@example.com | | | student070 | Student | 070 | student070@example.com | | | student071 | Student | 071 | student071@example.com | | | student072 | Student | 072 | student072@example.com | | | student073 | Student | 073 | student073@example.com | | | student074 | Student | 074 | student074@example.com | | | student075 | Student | 075 | student075@example.com | | | student076 | Student | 076 | student076@example.com | | | student077 | Student | 077 | student077@example.com | | | student078 | Student | 078 | student078@example.com | | | student079 | Student | 079 | student079@example.com | | | student080 | Student | 080 | student080@example.com | | | student081 | Student | 081 | student081@example.com | | | student082 | Student | 082 | student082@example.com | | | student083 | Student | 083 | student083@example.com | | | student084 | Student | 084 | student084@example.com | | | student085 | Student | 085 | student085@example.com | | | student086 | Student | 086 | student086@example.com | | | student087 | Student | 087 | student087@example.com | | | student088 | Student | 088 | student088@example.com | | | student089 | Student | 089 | student089@example.com | | | student090 | Student | 090 | student090@example.com | | | student091 | Student | 091 | student091@example.com | | | student092 | Student | 092 | student092@example.com | | | student093 | Student | 093 | student093@example.com | | | student094 | Student | 094 | student094@example.com | | | student095 | Student | 095 | student095@example.com | | | student096 | Student | 096 | student096@example.com | | | student097 | Student | 097 | student097@example.com | | | student098 | Student | 098 | student098@example.com | | | student099 | Student | 099 | student099@example.com | | And the following "courses" exist: | fullname | shortname | format | startdate | | Course 001 | C001 | weeks | ##1 month ago## | And the following "course enrolments" exist: | user | course | role | timestart | | teacher001 | C001 | editingteacher | ##1 month ago## | And I log in as "teacher001" And I am on "Course 001" course homepage @javascript Scenario: Teacher can search and enrol one particular student Given I navigate to course participants And I press "Enrol users" When I set the field "Select users" to "student001" And I should see "Student 001" And I click on "Enrol users" "button" in the "Enrol users" "dialogue" Then I should see "Active" in the "Student 001" "table_row" And I should see "1 enrolled users" @javascript Scenario: Searching for a non-existing user Given I navigate to course participants And I press "Enrol users" And I click on "Select users" "field" And I type "qwertyuiop" Then I should see "No suggestions" @javascript Scenario: If there are less than 100 matching users, all are displayed for selection Given I navigate to course participants And I press "Enrol users" When I click on "Select users" "field" And I type "example.com" Then "Student 099" "autocomplete_suggestions" should exist @javascript Scenario: If there are more than 100 matching users, inform there are too many. Given the following "users" exist: | username | firstname | lastname | email | | student100 | Student | 100 | student100@example.com | | student101 | Student | 101 | student101@example.com | And I navigate to course participants And I press "Enrol users" When I click on "Select users" "field" And I type "example.com" Then I should see "Too many users (>100) to show" @javascript Scenario: Changing the Maximum users per page setting affects the enrolment pop-up. Given the following config values are set as admin: | maxusersperpage | 5 | And I navigate to course participants And I press "Enrol users" When I click on "Select users" "field" And I type "student00" Then I should see "Too many users (>5) to show" @javascript Scenario: Change the Show user identity setting affects the enrolment pop-up. Given I log out When I log in as "admin" Then the following "users" exist: | username | firstname | lastname | email | phone1 | phone2 | department | institution | city | country | | student100 | Student | 100 | student100@example.com | 1234567892 | 1234567893 | ABC1 | ABC2 | CITY1 | GB | And the following config values are set as admin: | showuseridentity | idnumber,email,city,country,phone1,phone2,department,institution | When I am on "Course 001" course homepage Then I navigate to course participants And I press "Enrol users" And I click on "Select users" "field" And I type "student100@example.com" Then I should see "student100@example.com, CITY1, GB, 1234567892, 1234567893, ABC1, ABC2" # Remove identity field in setting User policies And the following config values are set as admin: | showuseridentity | idnumber,email,phone1,phone2,department,institution | And I am on "Course 001" course homepage And I navigate to course participants And I press "Enrol users" And I click on "Select users" "field" And I type "student100@example.com" And I should see "student100@example.com, 1234567892, 1234567893, ABC1, ABC2" @javascript Scenario: Custom user profile fields work for search and display, if user has permission Given the following config values are set as admin: | showuseridentity | email,profile_field_customid | And I navigate to course participants And I press "Enrol users" When I set the field "Select users" to "Q994" Then I should see "student001@example.com, Q994" And I click on "Cancel" "button" in the "Enrol users" "dialogue" And the following "permission overrides" exist: | capability | permission | role | contextlevel | reference | | moodle/site:viewuseridentity | Prevent | editingteacher | Course | C001 | And I press "Enrol users" # Do this by keyboard because the 'I set the field' step doesn't let you set it to a missing value. And I press tab And I press tab And I press tab And I type "Q994" And I should see "No suggestions" # The following tests are commented out as a result of MDL-66339. # @javascript # Scenario: Enrol user from participants page # Given I navigate to course participants # # Enrol user to course # And I press "Enrol users" # And I set the field "Select users" to "example.com" # And I expand the "Select users" autocomplete # When I click on "Student 099" item in the autocomplete list # Then I should see "Student 099" in the list of options for the "Select users" autocomplete # And I click on "Show more" "button" # # Fill data to input duration # And "input[name='timeend[enabled]'][checked=checked]" "css_element" should not exist # And the "Enrolment duration" "select" should be enabled # And I set the field "duration" to "2" # # Fill data to input end time # And I set the field "Starting from" to "2" # And I set the field "timeend[enabled]" to "1" # And I set the field "timeend[day]" to "10" # And the "Enrolment duration" "select" should be disabled # And I click on "Enrol users" "button" in the "Enrol users" "dialogue" # And I am on "Course 001" course homepage # And I navigate to course participants # And I should see "Student 099" in the "participants" "table" # And I click on "Edit enrolment" "icon" in the "Student 099" "table_row" # And the field "timeend[day]" matches value "10" # # @javascript # Scenario: Update Enrol user # Given I am on "Course 001" course homepage # And I navigate to course participants # When I click on "Edit enrolment" "icon" in the "Teacher 001" "table_row" # Then the "Enrolment duration" "select" should be enabled # # Fill duration # And "input[name='timeend[enabled]'][checked=checked]" "css_element" should not exist # And the "Enrolment duration" "select" should be enabled # And I set the field "duration" to "2" # # Fill end time # And I set the field "timeend[enabled]" to "1" # And I set the field "timeend[day]" to "28" # And the "Enrolment duration" "select" should be disabled # And I press "Save changes" # And I click on "Edit enrolment" "icon" in the "Teacher 001" "table_row" # And the field "timeend[day]" matches value "28" PKԄ\]]tests/behat/manage.featurenu[@enrol @enrol_manual Feature: A teacher can manage manually enrolled users in their course In order to manage manually enrolled students in my course As a teacher I can manually add and remove users in my course Background: Given the following "custom profile fields" exist: | datatype | shortname | name | | text | fruit | Fruit | And the following "users" exist: | username | firstname | middlename | lastname | email | profile_field_fruit | | teacher | Teacher | | User | teacher@example.com | | | user1 | First | Alice | User | first@example.com | Apple | | user2 | Second | Bob | User | second@example.com | Banana | And the following "courses" exist: | fullname | shortname | category | | Course 1 | C1 | 0 | And the following "course enrolments" exist: | user | course | role | | teacher | C1 | editingteacher | @javascript Scenario Outline: Manually enrolling users should observe alternative fullname format Given the following config values are set as admin: | alternativefullnameformat | firstname middlename lastname | And the following "permission overrides" exist: | capability | permission | role | contextlevel | reference | | moodle/site:viewfullnames | | editingteacher | Course | C1 | When I am on the "Course 1" "enrolment methods" page logged in as "teacher" And I click on "Enrol users" "link" in the "Manual enrolments" "table_row" And I set the field "addselect_searchtext" to "First" And I wait "1" seconds And I set the field "Not enrolled users" to " (first@example.com)" And I press "Add" Then the "Enrolled users" select box should contain " (first@example.com)" Examples: | permission | expectedfullname | | Allow | First Alice User | | Prohibit | First User | @javascript Scenario Outline: Manually unenrolling users should observe alternative fullname format Given the following config values are set as admin: | alternativefullnameformat | firstname middlename lastname | And the following "permission overrides" exist: | capability | permission | role | contextlevel | reference | | moodle/site:viewfullnames | | editingteacher | Course | C1 | And the following "course enrolments" exist: | user | course | role | | user1 | C1 | student | | user2 | C1 | student | When I am on the "Course 1" "enrolment methods" page logged in as "teacher" And I click on "Enrol users" "link" in the "Manual enrolments" "table_row" And I set the field "removeselect_searchtext" to "First" And I wait "1" seconds And I set the field "Enrolled users" to " (first@example.com)" And I press "Remove" Then the "Not enrolled users" select box should contain " (first@example.com)" Examples: | permission | expectedfullname | | Allow | First Alice User | | Prohibit | First User | @javascript Scenario: Manually enrol users in course using custom user profile fields Given the following config values are set as admin: | showuseridentity | email,profile_field_fruit | When I am on the "Course 1" "enrolment methods" page logged in as "teacher" And I click on "Enrol users" "link" in the "Manual enrolments" "table_row" Then the "Not enrolled users" select box should contain "Second User (second@example.com\, Banana)" And I set the field "addselect_searchtext" to "Apple" And I wait "1" seconds And the "Not enrolled users" select box should not contain "Second User (second@example.com\, Banana)" And I set the field "Not enrolled users" to "First User (first@example.com\, Apple)" And I press "Add" And the "Enrolled users" select box should contain "First User (first@example.com\, Apple)" @javascript Scenario: Manually unenrol users in course using custom user profile fields Given the following config values are set as admin: | showuseridentity | email,profile_field_fruit | And the following "course enrolments" exist: | user | course | role | | user1 | C1 | student | | user2 | C1 | student | When I am on the "Course 1" "enrolment methods" page logged in as "teacher" And I click on "Enrol users" "link" in the "Manual enrolments" "table_row" Then the "Enrolled users" select box should contain "Second User (second@example.com\, Banana)" And I set the field "removeselect_searchtext" to "Apple" And I wait "1" seconds And the "Enrolled users" select box should not contain "Second User (second@example.com\, Banana)" And I set the field "Enrolled users" to "First User (first@example.com\, Apple)" And I press "Remove" And the "Not enrolled users" select box should contain "First User (first@example.com\, Apple)" PKԄ\8"tests/behat/welcomemessage.featurenu[@enrol @enrol_manual Feature: A course welcome message will be sent to the user when they are enrolled in a course In order to let the user know they have been enrolled in a course As a teacher I want the user to receive a welcome message when they are enrolled in a course Background: Given the following "users" exist: | username | firstname | lastname | email | | manager | Manager | User | manager@example.com | | teacher | Teacher | User | teacher@example.com | | user1 | First | User | first@example.com | | user2 | Second | User | second@example.com | And the following "courses" exist: | fullname | shortname | category | | Course 1 | C1 | 0 | | Course 2 | C2 | 0 | And the following "course enrolments" exist: | user | course | role | | manager | C1 | manager | | teacher | C1 | editingteacher | | teacher | C2 | editingteacher | @javascript Scenario: Manager should see the new settings for course welcome message Given I am on the "C1" "Enrolled users" page logged in as manager And I set the field "Participants tertiary navigation" to "Enrolment methods" When I click on "Edit" "link" in the "Manual enrolments" "table_row" Then I should see "Send course welcome message" And the field "Send course welcome message" matches value "From the course contact" And I should see "Custom welcome message" And the field "Custom welcome message" matches value "Hi {$a->firstname}, you are enrolled in the course {$a->coursename}." And I should see "Accepted formats: Plain text or Moodle-auto format. HTML tags and multi-lang tags are also accepted, as well as the following placeholders:" And I set the field "Send course welcome message" to "No" And I should not see "Custom welcome message" And I should not see "Accepted formats: Plain text or Moodle-auto format. HTML tags and multi-lang tags are also accepted, as well as the following placeholders:" @javascript Scenario: Teacher can edit the course welcome message Given I am on the "C1" "Enrolled users" page logged in as teacher And I set the field "Participants tertiary navigation" to "Enrolment methods" When I click on "Edit" "link" in the "Manual enrolments" "table_row" Then I should see "Send course welcome message" And I should not see "Enable manual enrolments" And I should not see "Default role" And I should not see "Default enrolment duration" And I should not see "Notify before enrolment expires" And I should not see "Notification threshold" And I set the field "Custom welcome message" to "Hello {$a->fullname}, welcome to the course {$a->coursename}" And I press "Save changes" And I click on "Edit" "link" in the "Manual enrolments" "table_row" And I should see "Hello {$a->fullname}, welcome to the course {$a->coursename}" @javascript Scenario: Student should not receive a welcome message if the setting is disabled Given I am on the "C1" "Enrolled users" page logged in as manager And I set the field "Participants tertiary navigation" to "Enrolment methods" And I click on "Edit" "link" in the "Manual enrolments" "table_row" And I set the field "Send course welcome message" to "No" And I press "Save changes" And I am on the "C1" "Enrolled users" page logged in as teacher And I press "Enrol users" And I set the field "Select users" to "First User" And I should see "First User" And I click on "Enrol users" "button" in the "Enrol users" "dialogue" And I should see "Active" in the "First User" "table_row" When I am on the "C1" "course" page logged in as user1 Then I should not see "1" in the "#nav-notification-popover-container [data-region='count-container']" "css_element" @javascript Scenario: Students should receive a welcome message if the setting is enabled - Default message Given I am on the "C1" "Enrolled users" page logged in as teacher # Enrol first user to Course 1. And I press "Enrol users" And I set the field "Select users" to "First User" And I should see "First User" And I click on "Enrol users" "button" in the "Enrol users" "dialogue" # Enrol second user to Course 2. And I am on the "C2" "Enrolled users" page And I press "Enrol users" And I set the field "Select users" to "Second User" And I should see "Second User" And I click on "Enrol users" "button" in the "Enrol users" "dialogue" # Login as first user and check the notification. When I am on the "C1" "course" page logged in as user1 Then I should see "1" in the "#nav-notification-popover-container [data-region='count-container']" "css_element" And I open the notification popover And I should see "Welcome to Course 1" And I click on "View full notification" "link" in the ".popover-region-notifications" "css_element" And I should see "Hi First, you are enrolled in the course Course 1." # Login as second user and check the notification. And I am on the "C1" "course" page logged in as user2 And I should see "1" in the "#nav-notification-popover-container [data-region='count-container']" "css_element" And I open the notification popover And I should see "Welcome to Course 2" And I click on "View full notification" "link" in the ".popover-region-notifications" "css_element" And I should see "Hi Second, you are enrolled in the course Course 2." @javascript Scenario: Students should receive a welcome message if the setting is enabled - Custom message Given I am on the "C1" "Enrolled users" page logged in as manager And I set the field "Participants tertiary navigation" to "Enrolment methods" And I click on "Edit" "link" in the "Manual enrolments" "table_row" And I set the field "Custom welcome message" to multiline: """ Dear {$a->fullname}, you are enrolled in the course {$a->coursename}. Your email address: {$a->email} Your first name: {$a->firstname} Your last name: {$a->lastname} Your course role: {$a->courserole} """ And I press "Save changes" # Enrol first user and second user to Course 1. And the following "course enrolments" exist: | user | course | role | | user1 | C1 | student | | user2 | C1 | student | # Login as first user and check the notification. When I am on the "C1" "course" page logged in as user1 Then I should see "1" in the "#nav-notification-popover-container [data-region='count-container']" "css_element" And I open the notification popover And I should see "Welcome to Course 1" And I click on "View full notification" "link" in the ".popover-region-notifications" "css_element" And I should see "Dear First User, you are enrolled in the course Course 1." And I should see "Your email address: first@example.com" And I should see "Your first name: First" And I should see "Your last name: User" And I should see "Your course role: Student" # Login as second user and check the notification. When I am on the "C1" "course" page logged in as user2 Then I should see "1" in the "#nav-notification-popover-container [data-region='count-container']" "css_element" And I open the notification popover And I should see "Welcome to Course 1" And I click on "View full notification" "link" in the ".popover-region-notifications" "css_element" And I should see "Dear Second User, you are enrolled in the course Course 1." And I should see "Your email address: second@example.com" And I should see "Your first name: Second" And I should see "Your last name: User" And I should see "Your course role: Student" PK\ c: styles.cssnu[/** * Manual allocator */ .path-mod-workshop .manual-allocator .allocations { margin: 0 auto; width: 100%; } .path-mod-workshop .manual-allocator .allocations tbody tr:nth-of-type(odd) { background-color: #eee; } .path-mod-workshop .manual-allocator .allocations tbody tr:nth-of-type(odd).highlightreviewerof, .path-mod-workshop .manual-allocator .allocations tbody tr:nth-of-type(odd).highlightreviewedby { background-color: inherit; } .path-mod-workshop .manual-allocator .allocations .peer .image { margin-right: 5px; vertical-align: middle; } .path-mod-workshop .manual-allocator .allocations .reviewedby .image, .path-mod-workshop .manual-allocator .allocations .reviewerof .image { margin-right: 3px; vertical-align: middle; } .path-mod-workshop .manual-allocator .allocations .highlightreviewedby .reviewedby, .path-mod-workshop .manual-allocator .allocations .highlightreviewerof .reviewerof { background-color: #fff3d2; } .path-mod-workshop .manual-allocator .allocations tr td { vertical-align: top; padding: 5px; } .path-mod-workshop .manual-allocator .allocations tr td ul { margin: 0; } .path-mod-workshop .manual-allocator .allocations tr td ul li { list-style: none; } .path-mod-workshop .manual-allocator .allocations tr td.peer { border-left: 1px solid #ccc; border-right: 1px solid #ccc; } .path-mod-workshop .manual-allocator .allocations .reviewedby .info, .path-mod-workshop .manual-allocator .allocations .peer .info, .path-mod-workshop .manual-allocator .allocations .reviewerof .info { font-size: 80%; color: #888; font-style: italic; } .path-mod-workshop .manual-allocator .allocations .peer .submission { font-size: 90%; margin-top: 1em; } PK\MAo&o& renderer.phpnu[. /** * Renderer class for the manual allocation UI is defined here * * @package workshopallocation * @subpackage manual * @copyright 2009 David Mudrak * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); /** * Manual allocation renderer class */ class workshopallocation_manual_renderer extends mod_workshop_renderer { /** @var workshop module instance */ protected $workshop; //////////////////////////////////////////////////////////////////////////// // External rendering API //////////////////////////////////////////////////////////////////////////// /** * Display the table of all current allocations and widgets to modify them * * @param workshopallocation_manual_allocations $data to be displayed * @return string html code */ protected function render_workshopallocation_manual_allocations(workshopallocation_manual_allocations $data) { $this->workshop = $data->workshop; $allocations = $data->allocations; // array prepared array of all allocations data $userinfo = $data->userinfo; // names and pictures of all required users $authors = $data->authors; // array potential reviewees $reviewers = $data->reviewers; // array potential submission reviewers $hlauthorid = $data->hlauthorid; // int id of the author to highlight $hlreviewerid = $data->hlreviewerid; // int id of the reviewer to highlight $selfassessment = $data->selfassessment; // bool is the self-assessment allowed in this workshop? if (empty($allocations)) { return ''; } // convert user collections into drop down menus $authors = array_map('fullname', $authors); $reviewers = array_map('fullname', $reviewers); $table = new html_table(); $table->attributes['class'] = 'allocations table-reboot'; $table->head = array(get_string('participantreviewedby', 'workshop'), get_string('participant', 'workshop'), get_string('participantrevierof', 'workshop')); $table->rowclasses = array(); $table->colclasses = array('reviewedby', 'peer', 'reviewerof'); $table->data = array(); foreach ($allocations as $allocation) { $row = array(); $row[] = $this->helper_reviewers_of_participant($allocation, $userinfo, $reviewers, $selfassessment); $row[] = $this->helper_participant($allocation, $userinfo); $row[] = $this->helper_reviewees_of_participant($allocation, $userinfo, $authors, $selfassessment); $thisrowclasses = array(); if ($allocation->userid == $hlauthorid) { $thisrowclasses[] = 'highlightreviewedby'; } if ($allocation->userid == $hlreviewerid) { $thisrowclasses[] = 'highlightreviewerof'; } $table->rowclasses[] = implode(' ', $thisrowclasses); $table->data[] = $row; } return $this->output->container(html_writer::table($table), 'manual-allocator'); } //////////////////////////////////////////////////////////////////////////// // Internal helper methods //////////////////////////////////////////////////////////////////////////// /** * Returns information about the workshop participant * * @return string HTML code */ protected function helper_participant(stdclass $allocation, array $userinfo) { $o = $this->output->user_picture($userinfo[$allocation->userid], array('courseid' => $this->page->course->id)); $o .= fullname($userinfo[$allocation->userid]); $o .= $this->output->container_start(array('submission')); if (is_null($allocation->submissionid)) { $o .= $this->output->container(get_string('nosubmissionfound', 'workshop'), 'info'); } else { $link = $this->workshop->submission_url($allocation->submissionid); $o .= $this->output->container(html_writer::link($link, format_string($allocation->submissiontitle)), 'title'); if (is_null($allocation->submissiongrade)) { $o .= $this->output->container(get_string('nogradeyet', 'workshop'), array('grade', 'missing')); } else { $o .= $this->output->container(get_string('alreadygraded', 'workshop'), array('grade', 'missing')); } } $o .= $this->output->container_end(); return $o; } /** * Returns information about the current reviewers of the given participant and a selector do add new one * * @return string html code */ protected function helper_reviewers_of_participant(stdclass $allocation, array $userinfo, array $reviewers, $selfassessment) { $o = ''; if (is_null($allocation->submissionid)) { $o .= $this->output->container(get_string('nothingtoreview', 'workshop'), 'info'); } else { $exclude = array(); if (! $selfassessment) { $exclude[$allocation->userid] = true; } // todo add an option to exclude users without own submission $options = array_diff_key($reviewers, $exclude); if ($options) { $handler = new moodle_url($this->page->url, array('mode' => 'new', 'of' => $allocation->userid, 'sesskey' => sesskey())); $select = new single_select($handler, 'by', $options, '', array(''=>get_string('chooseuser', 'workshop')), 'addreviewof' . $allocation->userid); $select->set_label(get_string('addreviewer', 'workshopallocation_manual')); $o .= $this->output->render($select); } } $o .= html_writer::start_tag('ul', array()); foreach ($allocation->reviewedby as $reviewerid => $assessmentid) { $o .= html_writer::start_tag('li', array()); $o .= $this->output->user_picture($userinfo[$reviewerid], array('courseid' => $this->page->course->id, 'size' => 16)); $o .= fullname($userinfo[$reviewerid]); // delete icon $handler = new moodle_url($this->page->url, array('mode' => 'del', 'what' => $assessmentid, 'sesskey' => sesskey())); $o .= $this->helper_remove_allocation_icon($handler); $o .= html_writer::end_tag('li'); } $o .= html_writer::end_tag('ul'); return $o; } /** * Returns information about the current reviewees of the given participant and a selector do add new one * * @return string html code */ protected function helper_reviewees_of_participant(stdclass $allocation, array $userinfo, array $authors, $selfassessment) { $o = ''; if (is_null($allocation->submissionid)) { $o .= $this->output->container(get_string('withoutsubmission', 'workshop'), 'info'); } $exclude = array(); if (! $selfassessment) { $exclude[$allocation->userid] = true; $o .= $this->output->container(get_string('selfassessmentdisabled', 'workshop'), 'info'); } // todo add an option to exclude users without own submission $options = array_diff_key($authors, $exclude); if ($options) { $handler = new moodle_url($this->page->url, array('mode' => 'new', 'by' => $allocation->userid, 'sesskey' => sesskey())); $select = new single_select($handler, 'of', $options, '', array(''=>get_string('chooseuser', 'workshop')), 'addreviewby' . $allocation->userid); $select->set_label(get_string('addreviewee', 'workshopallocation_manual')); $o .= $this->output->render($select); } else { $o .= $this->output->container(get_string('nothingtoreview', 'workshop'), 'info'); } $o .= html_writer::start_tag('ul', array()); foreach ($allocation->reviewerof as $authorid => $assessmentid) { $o .= html_writer::start_tag('li', array()); $o .= $this->output->user_picture($userinfo[$authorid], array('courseid' => $this->page->course->id, 'size' => 16)); $o .= fullname($userinfo[$authorid]); // delete icon $handler = new moodle_url($this->page->url, array('mode' => 'del', 'what' => $assessmentid, 'sesskey' => sesskey())); $o .= $this->helper_remove_allocation_icon($handler); $o .= html_writer::end_tag('li'); } $o .= html_writer::end_tag('ul'); return $o; } /** * Generates an icon link to remove the allocation * * @param moodle_url $link to the action * @return html code to be displayed */ protected function helper_remove_allocation_icon($link) { return $this->output->action_icon($link, new pix_icon('t/delete', 'X')); } } PK\Vi%lang/en/workshopallocation_manual.phpnu[. /** * Strings for component 'workshopallocation_manual', language 'en', branch 'MOODLE_20_STABLE' * * @package workshopallocation * @subpackage manual * @copyright 2009 David Mudrak * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ $string['addreviewee'] = 'Add reviewee'; $string['addreviewer'] = 'Add reviewer'; $string['allocationadded'] = 'The submission has been successfully allocated'; $string['allocationexists'] = 'The allocation already exists'; $string['areyousuretodeallocate'] = 'Are you sure you want to deallocate the selected assessment?'; $string['areyousuretodeallocategraded'] = 'You are going to remove the assessment that has already been graded. Are you really sure you want to do it?'; $string['pluginname'] = 'Manual allocation'; $string['privacy:metadata:preference:perpage'] = 'Number of allocated assessments the user prefers to see on one page.'; $string['showallparticipants'] = 'Show all participants'; PK\b**/tests/behat/behat_workshopallocation_manual.phpnu[. /** * Steps definitions related to workshopallocation_manual. * * @package workshopallocation_manual * @category test * @copyright 2014 Marina Glancy * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ // NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php. require_once(__DIR__ . '/../../../../../../lib/behat/behat_base.php'); require_once(__DIR__ . '/../../../../../../lib/behat/behat_field_manager.php'); use Behat\Gherkin\Node\TableNode as TableNode, Behat\Mink\Exception\ElementTextException as ElementTextException; /** * Steps definitions related to workshopallocation_manual. * * @package workshopallocation_manual * @category test * @copyright 2014 Marina Glancy * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class behat_workshopallocation_manual extends behat_base { /** * Manually adds a reviewer for workshop participant. * * This step should start on manual allocation page. * * @When /^I add a reviewer "(?P(?:[^"]|\\")*)" for workshop participant "(?P(?:[^"]|\\")*)"$/ * @param string $reviewername * @param string $participantname */ public function i_add_a_reviewer_for_workshop_participant($reviewername, $participantname) { $participantnameliteral = behat_context_helper::escape($participantname); $xpathtd = "//table[contains(concat(' ', normalize-space(@class), ' '), ' allocations ')]/". "tbody/tr[./td[contains(concat(' ', normalize-space(@class), ' '), ' peer ')]". "[contains(.,$participantnameliteral)]]/". "td[contains(concat(' ', normalize-space(@class), ' '), ' reviewedby ')]"; $xpathselect = $xpathtd . "/descendant::select"; try { $selectnode = $this->find('xpath', $xpathselect); } catch (Exception $ex) { $this->find_button(get_string('showallparticipants', 'workshopallocation_manual'))->press(); $selectnode = $this->find('xpath', $xpathselect); } $this->execute('behat_forms::set_field_node_value', [ $selectnode, $reviewername, ]); if (!$this->running_javascript()) { // Without Javascript we need to press the "Go" button. $go = behat_context_helper::escape(get_string('go')); $this->find('xpath', $xpathtd."/descendant::input[@value=$go]")->click(); } // Check the success string to appear. $allocatedtext = behat_context_helper::escape(get_string('allocationadded', 'workshopallocation_manual')); $this->find('xpath', "//*[contains(.,$allocatedtext)]"); } /** * Manually allocates multiple reviewers in workshop. * * @When /^I allocate submissions in workshop "(?P(?:[^"]|\\")*)" as:$/ * @When /^I allocate submissions in workshop "(?P(?:[^"]|\\")*)" as:"$/ * @param string $workshopname * @param TableNode $table should have one column with title 'Reviewer' and another with title 'Participant' (or 'Reviewee') */ public function i_allocate_submissions_in_workshop_as($workshopname, TableNode $table) { $this->execute("behat_navigation::go_to_breadcrumb_location", $workshopname); $this->execute('behat_navigation::i_navigate_to_in_current_page_administration', get_string('submissionsallocation', 'workshop')); $rows = $table->getRows(); $reviewer = $participant = null; for ($i = 0; $i < count($rows[0]); $i++) { if (strtolower($rows[0][$i]) === 'reviewer') { $reviewer = $i; } else if (strtolower($rows[0][$i]) === 'reviewee' || strtolower($rows[0][$i]) === 'participant') { $participant = $i; } else { throw new ElementTextException('Unrecognised column "'.$rows[0][$i].'"', $this->getSession()); } } if ($reviewer === null) { throw new ElementTextException('Column "Reviewer" could not be located', $this->getSession()); } if ($participant === null) { throw new ElementTextException('Neither "Participant" nor "Reviewee" column could be located', $this->getSession()); } for ($i = 1; $i < count($rows); $i++) { $this->execute( 'behat_workshopallocation_manual::i_add_a_reviewer_for_workshop_participant', [ $rows[$i][$reviewer], $rows[$i][$participant], ] ); } } } PKB\g$g$hexternallib.phpnu[PKԄ\$xmmdb/services.phpnu[PKԄ\-bdb/install.phpnu[PKԄ\G db/tasks.phpnu[PKԄ\T[[ db/hooks.phpnu[PKԄ\F&( ,db/access.phpnu[PKԄ\\lOEdb/messages.phpnu[PKԄ\`zCClang/en/enrol_manual.phpnu[PKԄ\Y[+CClib.phpnu[PKԄ\*u!!  manage.phpnu[PKԄ\>e+unenrolself.phpnu[PKԄ\O  k4bulkchangeforms.phpnu[PKԄ\>$:classes/user_enrolment_callbacks.phpnu[PKԄ\ *Aclasses/task/send_expiry_notifications.phpnu[PKԄ\oo  Iclasses/task/sync_enrolments.phpnu[PKԄ\eqOclasses/enrol_users_form.phpnu[PKԄ\Gfޣhh0ltemplates/form-user-selector-suggestion.mustachenu[PKԄ\6I6I tlocallib.phpnu[PKԄ\G#ajax.phpnu[PKԄ\amd/src/quickenrolment.jsnu[PKԄ\4z' amd/src/form-potential-user-selector.jsnu[PKԄ\4R1tamd/build/form-potential-user-selector.min.js.mapnu[PKԄ\7 ¾""#l!amd/build/quickenrolment.min.js.mapnu[PKԄ\.-^^Damd/build/quickenrolment.min.jsnu[PKԄ\i-0Xamd/build/form-potential-user-selector.min.jsnu[PKԄ\ 0 ]`cli/sync.phpnu[PKԄ\5\ڷsitests/lib_test.phpnu[PKԄ\zDe88l1tests/externallib_test.phpnu[PKԄ\vBvB"itests/behat/quickenrolment.featurenu[PKԄ\]]tests/behat/manage.featurenu[PKԄ\8")tests/behat/welcomemessage.featurenu[PK\ c: Hstyles.cssnu[PK\MAo&o& brenderer.phpnu[PK\Vi% lang/en/workshopallocation_manual.phpnu[PK\b**/tests/behat/behat_workshopallocation_manual.phpnu[PK--Wz*