Compare commits
309 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b69c11978 | |||
| 2d628ba8d6 | |||
| 3528887bb6 | |||
| 217a32f033 | |||
| d24c201b6d | |||
| 4d9960ace2 | |||
| 4e1c4f4483 | |||
| b4e7d5300a | |||
| 313d44f83a | |||
| 6a6f5b79dd | |||
| f3c335b376 | |||
| 5eb91a54c3 | |||
| 1e72ef978a | |||
| 28ccae3b7a | |||
| f06b222aa8 | |||
| a4aeb20a30 | |||
| b50964d2e2 | |||
| 2ea89b98a7 | |||
| 833bc53bbb | |||
| 5123d2bf53 | |||
| acd82d3a86 | |||
| 152bbd76c5 | |||
| 02df248fbd | |||
| f5844e80fd | |||
| a789bf5b24 | |||
| ff5fc3820a | |||
| 66dcb1683b | |||
| 9cdafb6cad | |||
| 89c59f32cf | |||
| 8280476728 | |||
| 4fc9c3bba1 | |||
| 74f1a285a1 | |||
| d9ae9e51cd | |||
| cd42aa554f | |||
| e8ea4916e9 | |||
| d1f75e4dce | |||
| 1b54b120ad | |||
| 3c5413f02a | |||
| 4ea254faf2 | |||
| d71ada1952 | |||
| 29efbedc8b | |||
| 8ea2421cd4 | |||
| 1361e6ad92 | |||
| 3387060fe1 | |||
| 5d27f595d7 | |||
| 6f2d09dc5a | |||
| e101b6f195 | |||
| 934d8a2866 | |||
| 6a7668e350 | |||
| d2bacb04ff | |||
| 77a0f1b129 | |||
| ca841d1489 | |||
| c2216eefa6 | |||
| 241da816db | |||
| 18302e1244 | |||
| 2080ba7c83 | |||
| b99d68087d | |||
| 93d95cf1fe | |||
| 13a6018b6d | |||
| ee507d22ec | |||
| 145317b2b3 | |||
| c3fdfc996c | |||
| 6ee2865fcc | |||
| 4943d24d50 | |||
| 64b8061180 | |||
| b24414554f | |||
| 325ca6568b | |||
| cd072308e5 | |||
| 3e0ad5831a | |||
| a5430f1022 | |||
| 46fca91bc4 | |||
| f8b463e394 | |||
| 717e049627 | |||
| 6e286481e0 | |||
| 1cf0f78b9a | |||
| d95ab775ac | |||
| 56a3828cf7 | |||
| 4ecd652189 | |||
| 9c4b8bf7e8 | |||
| c380811bb2 | |||
| 3e175706ff | |||
| db3bc708c4 | |||
| 87b3be343e | |||
| a25f2d0d7e | |||
| 0eb65f27ba | |||
| a4dfd67aed | |||
| 3a5820f3c4 | |||
| a9abcf1af7 | |||
| 72f4f8ec7c | |||
| 9fd77db89a | |||
| c6a1770682 | |||
| 3e4ba8af5c | |||
| dcb0de7c92 | |||
| e2c8f3db7c | |||
| b7b2097f50 | |||
| 0dbc7187ff | |||
| 7e1aa845c7 | |||
| 810cffb702 | |||
| 5271bd9237 | |||
| f84d8e3c37 | |||
| a3a3fd451c | |||
| 52d5ebbb21 | |||
| c72b7b7edc | |||
| c4960a2adf | |||
| 510bf00643 | |||
| d3fca5e689 | |||
| 1be732e913 | |||
| d2f55155a8 | |||
| 430c7ff4e7 | |||
| 181cab2fbd | |||
| f4f0207693 | |||
| ad8a10e7b6 | |||
| 9d822d41d8 | |||
| e89ed4b463 | |||
| 5db1ac4e98 | |||
| c22b39a650 | |||
| 9d64ad1416 | |||
| 11816b9e41 | |||
| 178aa05227 | |||
| 9f2e81004c | |||
| 0b7102abcc | |||
| fe16fea153 | |||
| 86f9ffc274 | |||
| a439461f03 | |||
| 5c15c1f211 | |||
| 86d02a9239 | |||
| 68706da86a | |||
| 296fd70a2c | |||
| 0c448af668 | |||
| 1f7bb3a50d | |||
| 744e1dbaea | |||
| 5b47024f13 | |||
| dcf4a4629b | |||
| bb7cce1e56 | |||
| 27cc23ca7a | |||
| e8123832fb | |||
| 6b47dc3b29 | |||
| a77b23cdb7 | |||
| 7708594e89 | |||
| 1dda343f05 | |||
| 4f19d454ea | |||
| 24c009f222 | |||
| 5556693437 | |||
| b874e20b55 | |||
| 06383b0b8a | |||
| 4979a5e988 | |||
| 76ce30ca98 | |||
| 1e54868139 | |||
| a33d1c8903 | |||
| c5213c103b | |||
| 4ce5c57547 | |||
| 7496e9df75 | |||
| fe094a3705 | |||
| 93bd773ee9 | |||
| 0d20d1e8aa | |||
| 361ebe960b | |||
| 0d5d3a0c49 | |||
| 4d94f7e677 | |||
| 6f68abf715 | |||
| ac21fa9a1c | |||
| f954848667 | |||
| a42c52bf4e | |||
| 987542f609 | |||
| 959b58e132 | |||
| be5cf43aa4 | |||
| de182b9a22 | |||
| c48f443c32 | |||
| fc67298a27 | |||
| c3db3298dd | |||
| 6bb2b9aa69 | |||
| 187a7f7ac5 | |||
| 6b24f59302 | |||
| 797a20a597 | |||
| 7a1e9ad82c | |||
| bd40bc0341 | |||
| 398fc9b0d3 | |||
| 2a69a46a71 | |||
| 41b48aeea5 | |||
| 3a544f384b | |||
| d8f2ead786 | |||
| 50f06cb2be | |||
| d4abc697b8 | |||
| 10c1a7de42 | |||
| dc444f4634 | |||
| 151de4586d | |||
| 52890694bf | |||
| 95f073453b | |||
| 6661a3eb45 | |||
| 0f8f164a07 | |||
| 4cc96c42fa | |||
| 208c9fb5af | |||
| 8884da3d8b | |||
| e0f34d5049 | |||
| 09e171c522 | |||
| 4426e21204 | |||
| fb7d1b34ef | |||
| 32bb535cb6 | |||
| dd3b92545e | |||
| e572943d21 | |||
| 2d2b2e3244 | |||
| e03c52acbe | |||
| c503238978 | |||
| bc5c812db3 | |||
| d7734740b2 | |||
| ba38d916be | |||
| e7c70fd2c0 | |||
| fa309d0650 | |||
| 1efd88b2c8 | |||
| 20ac0d1626 | |||
| 40ee69ea8b | |||
| 87d5f8eb0f | |||
| 05c53f2c44 | |||
| 1b9bcbf19d | |||
| a3ed6cea5f | |||
| f2dd9867ab | |||
| bbd49c9533 | |||
| bc82295f6a | |||
| 2e1118fa62 | |||
| 272a458099 | |||
| 2370255e4c | |||
| 9cf6e7128e | |||
| ed29852851 | |||
| a7e9836173 | |||
| add1d29339 | |||
| 68dc955abe | |||
| 258fe36e10 | |||
| 76fde950c1 | |||
| d4681259bc | |||
| e6dec9c0fb | |||
| 8b153fb67d | |||
| 1145b8f597 | |||
| 743fc68b4f | |||
| 9d801ab8a0 | |||
| b7f08d7ecc | |||
| 715ec40ad6 | |||
| 1d98d162a1 | |||
| d42189d71a | |||
| 7f5f04dbc2 | |||
| 82d90a8950 | |||
| e581bd6cde | |||
| 68cd224ef3 | |||
| afef05b07c | |||
| d9cd25c2fb | |||
| 960bc4e3c5 | |||
| cf06e4207f | |||
| 9f06111278 | |||
| a8bcb23134 | |||
| 5c96f74a06 | |||
| 7cc3d06276 | |||
| b00f1464a8 | |||
| 88393518f5 | |||
| 57e5cc1588 | |||
| a0efc27d31 | |||
| 0be376fdc0 | |||
| 8bf788a1bd | |||
| 864a2ea990 | |||
| 73f10723b3 | |||
| be11ee3383 | |||
| fbb0fd952d | |||
| 0cc4492303 | |||
| dcc2d22a74 | |||
| 1c2503a6de | |||
| c86d6510ce | |||
| a8f889acbf | |||
| 5a07bcf871 | |||
| 12ac6ce86b | |||
| 2db281b197 | |||
| 805559cb48 | |||
| 19ed64f2d9 | |||
| dcc115786d | |||
| 2ef01c0fdf | |||
| 2696f446ac | |||
| c45c98f6f2 | |||
| 181ecedb11 | |||
| 7e8efa870d | |||
| b363723f9a | |||
| e0ef5d0638 | |||
| 38250aec8e | |||
| 5475fb8a15 | |||
| 5384f872bb | |||
| eb3ee12852 | |||
| dded69ad15 | |||
| 2cfaa895aa | |||
| 60e176b50d | |||
| 530e06da6d | |||
| 4d9c6785ff | |||
| 1b8456dbb1 | |||
| 3e26150eae | |||
| 5cf31d7445 | |||
| 62238568a2 | |||
| 6e9e2be8f4 | |||
| 34f8f82288 | |||
| ac8b7e9f0a | |||
| b50c240ab5 | |||
| 189c4ce4de | |||
| e3840909f5 | |||
| 523b590711 | |||
| b391ed9941 | |||
| a5f92c041e | |||
| cfe0945ffb | |||
| 6989097a3c | |||
| 847f08dcd4 | |||
| 02430ee562 | |||
| a8e90aaae1 | |||
| fac0c27efb | |||
| a7e7cd71cb | |||
| 37be6bfa4d | |||
| d311cab0c0 | |||
| fb5a3dd643 |
@@ -87,6 +87,7 @@
|
||||
'no-octal-escape': 'error',
|
||||
'no-proto': 'error',
|
||||
'no-redeclare': 'warn',
|
||||
'no-restricted-globals': ['error', { 'name': 'Notification' }],
|
||||
'no-return-assign': 'error',
|
||||
'no-script-url': 'error',
|
||||
'no-self-assign': 'error',
|
||||
|
||||
+1
-1
@@ -54,5 +54,5 @@ moodle-plugin-ci.phar
|
||||
/admin/tool/componentlibrary/docs
|
||||
/admin/tool/componentlibrary/hugo/site/data/my-index.json
|
||||
.hugo_build.lock
|
||||
.phpcs.xml
|
||||
phpcs.xml
|
||||
jsconfig.json
|
||||
|
||||
@@ -39,7 +39,7 @@ module.exports = grunt => {
|
||||
{
|
||||
rule: {
|
||||
_attrs: {
|
||||
ref: './.phpcs.xml.dist',
|
||||
ref: './phpcs.xml.dist',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -52,7 +52,7 @@ module.exports = grunt => {
|
||||
});
|
||||
});
|
||||
|
||||
grunt.file.write('.phpcs.xml', toXML(config, {
|
||||
grunt.file.write('phpcs.xml', toXML(config, {
|
||||
header: true,
|
||||
indent: ' ',
|
||||
}) + "\n");
|
||||
|
||||
@@ -3371,6 +3371,7 @@
|
||||
<VENDOR name="oracle" version="11.2" />
|
||||
</DATABASE>
|
||||
<PHP version="7.3.0" level="required">
|
||||
<RESTRICT function="restrict_php_version_81" message="unsupportedphpversion81" />
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
@@ -3557,6 +3558,7 @@
|
||||
<VENDOR name="oracle" version="11.2" />
|
||||
</DATABASE>
|
||||
<PHP version="7.3.0" level="required">
|
||||
<RESTRICT function="restrict_php_version_81" message="unsupportedphpversion81" />
|
||||
</PHP>
|
||||
<PCREUNICODE level="optional">
|
||||
<FEEDBACK>
|
||||
|
||||
+18
-3
@@ -46,11 +46,23 @@
|
||||
|
||||
$formcontinue = new single_button(new moodle_url('index.php', array('confirm' => md5($mnet->public_key))), get_string('yes'));
|
||||
$formcancel = new single_button(new moodle_url('index.php', array()), get_string('no'));
|
||||
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->confirm(get_string("deletekeycheck", "mnet"), $formcontinue, $formcancel);
|
||||
echo $OUTPUT->footer();
|
||||
exit;
|
||||
} else {
|
||||
// We're deleting
|
||||
|
||||
// If no/cancel then redirect back to the network setting page.
|
||||
if (!isset($form->confirm)) {
|
||||
redirect(
|
||||
new moodle_url('/admin/mnet/index.php'),
|
||||
get_string('keydeletedcancelled', 'mnet'),
|
||||
null,
|
||||
\core\output\notification::NOTIFY_SUCCESS
|
||||
);
|
||||
}
|
||||
|
||||
if (!isset($SESSION->mnet_confirm_delete_key)) {
|
||||
// fail - you're being attacked?
|
||||
@@ -63,8 +75,12 @@
|
||||
|
||||
if($time < time() - 60) {
|
||||
// fail - you're out of time.
|
||||
throw new \moodle_exception ('deleteoutoftime', 'mnet', 'index.php');
|
||||
exit;
|
||||
redirect(
|
||||
new moodle_url('/admin/mnet/index.php'),
|
||||
get_string('deleteoutoftime', 'mnet'),
|
||||
null,
|
||||
\core\output\notification::NOTIFY_WARNING
|
||||
);
|
||||
}
|
||||
|
||||
if ($key != md5(sha1($mnet->keypair['keypair_PEM']))) {
|
||||
@@ -75,7 +91,6 @@
|
||||
|
||||
$mnet->replace_keys();
|
||||
redirect('index.php', get_string('keydeleted','mnet'));
|
||||
exit;
|
||||
}
|
||||
}
|
||||
$hosts = $DB->get_records_select('mnet_host', "id <> ? AND deleted = 0", array($CFG->mnet_localhost_id), 'wwwroot ASC');
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
namespace core_adminpresets\local\setting;
|
||||
|
||||
use admin_setting_configcheckbox;
|
||||
|
||||
/**
|
||||
* Checkbox setting.
|
||||
*
|
||||
@@ -26,15 +28,15 @@ namespace core_adminpresets\local\setting;
|
||||
*/
|
||||
class adminpresets_admin_setting_configcheckbox extends adminpresets_setting {
|
||||
|
||||
protected function set_value($value) {
|
||||
$this->value = clean_param($value, PARAM_BOOL);
|
||||
$this->set_visiblevalue();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the visible name for the setting selected value
|
||||
*/
|
||||
protected function set_visiblevalue() {
|
||||
if ($this->value) {
|
||||
/** @var admin_setting_configcheckbox $settingdata */
|
||||
$settingdata = $this->get_settingdata();
|
||||
|
||||
// We need to compare the "yes" value of the inner checkbox, which isn't necessarily boolean.
|
||||
if ($this->value == $settingdata->yes) {
|
||||
$str = get_string('yes');
|
||||
} else {
|
||||
$str = get_string('no');
|
||||
|
||||
@@ -533,7 +533,7 @@ class core_role_define_role_table_advanced extends core_role_capability_table_wi
|
||||
|
||||
protected function get_description_field($id) {
|
||||
return '<textarea class="form-textarea form-control" id="'. s($id) .'" name="description" rows="10" cols="50">' .
|
||||
htmlspecialchars($this->role->description) .
|
||||
htmlspecialchars($this->role->description, ENT_COMPAT) .
|
||||
'</textarea>';
|
||||
}
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ class provider_test extends provider_testcase {
|
||||
$writer = writer::with_context($context);
|
||||
$this->assertTrue($writer->has_any_data());
|
||||
if ($context->contextlevel == CONTEXT_MODULE) {
|
||||
if ($data = $writer->get_data($subcontextstudent)) {
|
||||
if ($data = (array)$writer->get_data($subcontextstudent)) {
|
||||
$this->assertEquals($user->id, reset($data)->userid);
|
||||
}
|
||||
if ($data = (array)$writer->get_data($subcontextrc)) {
|
||||
|
||||
@@ -101,7 +101,7 @@ if ($hassiteconfig) {
|
||||
new lang_string('limitconcurrentlogins', 'core_auth'),
|
||||
new lang_string('limitconcurrentlogins_desc', 'core_auth'), 0, $options));
|
||||
$temp->add(new admin_setting_configtext('alternateloginurl', new lang_string('alternateloginurl', 'auth'),
|
||||
new lang_string('alternatelogin', 'auth', htmlspecialchars(get_login_url())), ''));
|
||||
new lang_string('alternatelogin', 'auth', htmlspecialchars(get_login_url(), ENT_COMPAT)), ''));
|
||||
$temp->add(new admin_setting_configtext('forgottenpasswordurl', new lang_string('forgottenpasswordurl', 'auth'),
|
||||
new lang_string('forgottenpassword', 'auth'), '', PARAM_URL));
|
||||
$temp->add(new admin_setting_confightmleditor('auth_instructions', new lang_string('instructions', 'auth'),
|
||||
|
||||
@@ -51,12 +51,12 @@
|
||||
<div class="form-password">
|
||||
<span data-passwordunmask="wrapper" data-passwordunmaskid="{{ id }}">
|
||||
<span data-passwordunmask="editor">
|
||||
<input type="hidden"
|
||||
<input type="password"
|
||||
name="{{ name }}"
|
||||
id="{{ id }}"
|
||||
value="{{ value }}"
|
||||
data-size="{{ size }}"
|
||||
class="form-control d-inline-block"
|
||||
class="form-control d-none"
|
||||
>
|
||||
</span>
|
||||
<a href="#" data-passwordunmask="edit" title="{{ edithint }}">
|
||||
|
||||
@@ -18,7 +18,7 @@ Feature: An administrator can browse user accounts
|
||||
When I navigate to "Users > Accounts > Browse list of users" in site administration
|
||||
# Name field always present, email field is default for showidentity.
|
||||
Then the following should exist in the "users" table:
|
||||
| First name / Surname | Email address |
|
||||
| First name / Last name | Email address |
|
||||
| User One | one@example.com |
|
||||
| User Two | two@example.com |
|
||||
# Should not see other identity fields or non-default name fields.
|
||||
@@ -34,7 +34,7 @@ Feature: An administrator can browse user accounts
|
||||
| alternativefullnameformat | firstnamephonetic lastname |
|
||||
When I navigate to "Users > Accounts > Browse list of users" in site administration
|
||||
Then the following should exist in the "users" table:
|
||||
| First name - phonetic / Surname | Email address |
|
||||
| First name - phonetic / Last name | Email address |
|
||||
| Yewzer One | one@example.com |
|
||||
| Yoozare Two | two@example.com |
|
||||
|
||||
@@ -43,7 +43,7 @@ Feature: An administrator can browse user accounts
|
||||
| showuseridentity | department,profile_field_frog |
|
||||
When I navigate to "Users > Accounts > Browse list of users" in site administration
|
||||
Then the following should exist in the "users" table:
|
||||
| First name / Surname | Favourite frog | Department |
|
||||
| First name / Last name | Favourite frog | Department |
|
||||
| User One | Kermit | Attack |
|
||||
| User Two | Tree | Defence |
|
||||
And I should not see "Email address" in the "table" "css_element"
|
||||
|
||||
@@ -15,7 +15,7 @@ Feature: Allowing multiple accounts to have the same email address
|
||||
And I set the following fields to these values:
|
||||
| Username | s2 |
|
||||
| First name | Jane |
|
||||
| Surname | Doe |
|
||||
| Last name | Doe |
|
||||
| Email address | <email> |
|
||||
| New password | test |
|
||||
And I press "Create user"
|
||||
|
||||
@@ -8,7 +8,7 @@ Feature: An administrator can configure the available user list filters
|
||||
When I log in as "admin"
|
||||
And I navigate to "Users > Accounts > Browse list of users" in site administration
|
||||
Then I should see "User full name"
|
||||
And I should not see "Surname" in the "New filter" "fieldset"
|
||||
And I should not see "Last name" in the "New filter" "fieldset"
|
||||
And I should not see "Firstname" in the "New filter" "fieldset"
|
||||
And I should not see "Username" in the "New filter" "fieldset"
|
||||
And I should not see "Email address" in the "New filter" "fieldset"
|
||||
@@ -30,7 +30,7 @@ Feature: An administrator can configure the available user list filters
|
||||
And I should not see "MNet ID provider" in the "New filter" "fieldset"
|
||||
And I navigate to "Users > Accounts > Bulk user actions" in site administration
|
||||
Then I should see "User full name"
|
||||
And I should not see "Surname" in the "New filter" "fieldset"
|
||||
And I should not see "Last name" in the "New filter" "fieldset"
|
||||
And I should not see "Firstname" in the "New filter" "fieldset"
|
||||
And I should not see "Username" in the "New filter" "fieldset"
|
||||
And I should not see "Email address" in the "New filter" "fieldset"
|
||||
@@ -57,7 +57,7 @@ Feature: An administrator can configure the available user list filters
|
||||
And I log in as "admin"
|
||||
And I navigate to "Users > Accounts > Browse list of users" in site administration
|
||||
Then I should see "User full name"
|
||||
And I should not see "Surname" in the "New filter" "fieldset"
|
||||
And I should not see "Last name" in the "New filter" "fieldset"
|
||||
And I should not see "Firstname" in the "New filter" "fieldset"
|
||||
And I should see "Username" in the "New filter" "fieldset"
|
||||
And I should see "Email address" in the "New filter" "fieldset"
|
||||
@@ -79,7 +79,7 @@ Feature: An administrator can configure the available user list filters
|
||||
And I should not see "MNet ID provider" in the "New filter" "fieldset"
|
||||
And I navigate to "Users > Accounts > Bulk user actions" in site administration
|
||||
Then I should see "User full name"
|
||||
And I should not see "Surname" in the "New filter" "fieldset"
|
||||
And I should not see "Last name" in the "New filter" "fieldset"
|
||||
And I should not see "Firstname" in the "New filter" "fieldset"
|
||||
And I should see "Username" in the "New filter" "fieldset"
|
||||
And I should see "Email address" in the "New filter" "fieldset"
|
||||
@@ -106,7 +106,7 @@ Feature: An administrator can configure the available user list filters
|
||||
And I log in as "admin"
|
||||
And I navigate to "Users > Accounts > Browse list of users" in site administration
|
||||
Then I should see "User full name"
|
||||
And I should not see "Surname" in the "New filter" "fieldset"
|
||||
And I should not see "Last name" in the "New filter" "fieldset"
|
||||
And I should not see "Firstname" in the "New filter" "fieldset"
|
||||
And I should not see "Username" in the "New filter" "fieldset"
|
||||
And I should not see "Email address" in the "New filter" "fieldset"
|
||||
@@ -128,7 +128,7 @@ Feature: An administrator can configure the available user list filters
|
||||
And I should not see "MNet ID provider" in the "New filter" "fieldset"
|
||||
And I navigate to "Users > Accounts > Bulk user actions" in site administration
|
||||
Then I should see "User full name"
|
||||
And I should not see "Surname" in the "New filter" "fieldset"
|
||||
And I should not see "Last name" in the "New filter" "fieldset"
|
||||
And I should not see "Firstname" in the "New filter" "fieldset"
|
||||
And I should not see "Username" in the "New filter" "fieldset"
|
||||
And I should not see "Email address" in the "New filter" "fieldset"
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
define("tool_behat/steps",["exports","core/ajax","core/templates","core/pending"],(function(_exports,_ajax,_templates,_pending){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
|
||||
/**
|
||||
* Enhancements for the step definitions page.
|
||||
*
|
||||
* @module tool_behat/steps
|
||||
* @copyright 2022 Catalyst IT EU
|
||||
* @author Mark Johnson <mark.johnson@catalyst-eu.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_ajax=_interopRequireDefault(_ajax),_templates=_interopRequireDefault(_templates),_pending=_interopRequireDefault(_pending);_exports.init=()=>{document.addEventListener("change",(async e=>{const entityElement=e.target.closest(".entities"),stepElement=e.target.closest(".stepcontent");if(!entityElement||!stepElement)return;const pendingPromise=new _pending.default("tool_behat/steps:change"),entityData=await(entityType=e.target.value,_ajax.default.call([{methodname:"tool_behat_get_entity_generator",args:{entitytype:entityType}}])[0]);var entityType;const{html:html,js:js}=await(entityData=>{var _entityData$required;return null!==(_entityData$required=entityData.required)&&void 0!==_entityData$required&&_entityData$required.length?_templates.default.renderForPromise("tool_behat/steprequiredfields",{fields:entityData.required}):Promise.resolve({html:"",js:""})})(entityData),stepRequiredFields=stepElement.querySelector(".steprequiredfields");stepRequiredFields?await _templates.default.replaceNode(stepRequiredFields,html,js):await _templates.default.appendNodeContents(stepElement,html,js),pendingPromise.resolve()}))}}));
|
||||
|
||||
//# sourceMappingURL=steps.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"steps.min.js","sources":["../src/steps.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 <http://www.gnu.org/licenses/>.\n\nimport Ajax from 'core/ajax';\nimport Templates from 'core/templates';\nimport PendingJS from 'core/pending';\n\n/**\n * Enhancements for the step definitions page.\n *\n * @module tool_behat/steps\n * @copyright 2022 Catalyst IT EU\n * @author Mark Johnson <mark.johnson@catalyst-eu.net>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * Call the get_entity_generator web service function\n *\n * Takes the name of an entity generator and returns an object containing a list of the required fields.\n *\n * @param {String} entityType\n * @returns {Promise}\n */\nconst getGeneratorEntities = (entityType) => Ajax.call([{\n methodname: 'tool_behat_get_entity_generator',\n args: {entitytype: entityType}\n}])[0];\n\n/**\n * Render HTML for required fields\n *\n * Takes the entity data returned from getGeneratorEntities and renders the HTML to display the required fields.\n *\n * @param {String} entityData\n * @return {Promise}\n */\nconst getRequiredFieldsContent = (entityData) => {\n if (!entityData.required?.length) {\n return Promise.resolve({\n html: '',\n js: ''\n });\n }\n return Templates.renderForPromise('tool_behat/steprequiredfields', {fields: entityData.required});\n};\n\nexport const init = () => {\n // When an entity is selected in the \"the following exist\" step, fetch and display the required fields.\n document.addEventListener('change', async(e) => {\n const entityElement = e.target.closest('.entities');\n const stepElement = e.target.closest('.stepcontent');\n if (!entityElement || !stepElement) {\n return;\n }\n\n const pendingPromise = new PendingJS('tool_behat/steps:change');\n\n const entityData = await getGeneratorEntities(e.target.value);\n const {html, js} = await getRequiredFieldsContent(entityData);\n\n const stepRequiredFields = stepElement.querySelector('.steprequiredfields');\n if (stepRequiredFields) {\n await Templates.replaceNode(stepRequiredFields, html, js);\n } else {\n await Templates.appendNodeContents(stepElement, html, js);\n }\n pendingPromise.resolve();\n });\n};\n"],"names":["document","addEventListener","async","entityElement","e","target","closest","stepElement","pendingPromise","PendingJS","entityData","entityType","value","Ajax","call","methodname","args","entitytype","html","js","required","_entityData$required","length","Templates","renderForPromise","fields","Promise","resolve","getRequiredFieldsContent","stepRequiredFields","querySelector","replaceNode","appendNodeContents"],"mappings":";;;;;;;;4NA2DoB,KAEhBA,SAASC,iBAAiB,UAAUC,MAAAA,UAC1BC,cAAgBC,EAAEC,OAAOC,QAAQ,aACjCC,YAAcH,EAAEC,OAAOC,QAAQ,oBAChCH,gBAAkBI,yBAIjBC,eAAiB,IAAIC,iBAAU,2BAE/BC,iBAlCgBC,WAkCwBP,EAAEC,OAAOO,MAlClBC,cAAKC,KAAK,CAAC,CACpDC,WAAY,kCACZC,KAAM,CAACC,WAAYN,eACnB,IAH0BA,IAAAA,iBAmChBO,KAACA,KAADC,GAAOA,SAtBaT,CAAAA,0EACzBA,WAAWU,0CAAXC,qBAAqBC,OAMnBC,mBAAUC,iBAAiB,gCAAiC,CAACC,OAAQf,WAAWU,WAL5EM,QAAQC,QAAQ,CACnBT,KAAM,GACNC,GAAI,MAkBiBS,CAAyBlB,YAE5CmB,mBAAqBtB,YAAYuB,cAAc,uBACjDD,yBACMN,mBAAUQ,YAAYF,mBAAoBX,KAAMC,UAEhDI,mBAAUS,mBAAmBzB,YAAaW,KAAMC,IAE1DX,eAAemB"}
|
||||
@@ -0,0 +1,82 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import Ajax from 'core/ajax';
|
||||
import Templates from 'core/templates';
|
||||
import PendingJS from 'core/pending';
|
||||
|
||||
/**
|
||||
* Enhancements for the step definitions page.
|
||||
*
|
||||
* @module tool_behat/steps
|
||||
* @copyright 2022 Catalyst IT EU
|
||||
* @author Mark Johnson <mark.johnson@catalyst-eu.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Call the get_entity_generator web service function
|
||||
*
|
||||
* Takes the name of an entity generator and returns an object containing a list of the required fields.
|
||||
*
|
||||
* @param {String} entityType
|
||||
* @returns {Promise}
|
||||
*/
|
||||
const getGeneratorEntities = (entityType) => Ajax.call([{
|
||||
methodname: 'tool_behat_get_entity_generator',
|
||||
args: {entitytype: entityType}
|
||||
}])[0];
|
||||
|
||||
/**
|
||||
* Render HTML for required fields
|
||||
*
|
||||
* Takes the entity data returned from getGeneratorEntities and renders the HTML to display the required fields.
|
||||
*
|
||||
* @param {String} entityData
|
||||
* @return {Promise}
|
||||
*/
|
||||
const getRequiredFieldsContent = (entityData) => {
|
||||
if (!entityData.required?.length) {
|
||||
return Promise.resolve({
|
||||
html: '',
|
||||
js: ''
|
||||
});
|
||||
}
|
||||
return Templates.renderForPromise('tool_behat/steprequiredfields', {fields: entityData.required});
|
||||
};
|
||||
|
||||
export const init = () => {
|
||||
// When an entity is selected in the "the following exist" step, fetch and display the required fields.
|
||||
document.addEventListener('change', async(e) => {
|
||||
const entityElement = e.target.closest('.entities');
|
||||
const stepElement = e.target.closest('.stepcontent');
|
||||
if (!entityElement || !stepElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pendingPromise = new PendingJS('tool_behat/steps:change');
|
||||
|
||||
const entityData = await getGeneratorEntities(e.target.value);
|
||||
const {html, js} = await getRequiredFieldsContent(entityData);
|
||||
|
||||
const stepRequiredFields = stepElement.querySelector('.steprequiredfields');
|
||||
if (stepRequiredFields) {
|
||||
await Templates.replaceNode(stepRequiredFields, html, js);
|
||||
} else {
|
||||
await Templates.appendNodeContents(stepElement, html, js);
|
||||
}
|
||||
pendingPromise.resolve();
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Return data about an entity generator.
|
||||
*
|
||||
* @package tool_behat
|
||||
* @copyright 2022 onwards Catalyst IT EU {@link https://catalyst-eu.net}
|
||||
* @author Mark Johnson <mark.johnson@catalyst-eu.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_behat\external;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir . '/externallib.php');
|
||||
|
||||
/**
|
||||
* External function for getting properties of entity generators.
|
||||
*/
|
||||
class get_entity_generator extends \external_api {
|
||||
|
||||
/**
|
||||
* Define parameters for external function.
|
||||
*
|
||||
* The parameter is either in the format 'entity' or 'component_name > entity'. There is no appropriate param type for a
|
||||
* string like this containing angle brackets, so we will do PARAM_RAW. The value will be parsed by
|
||||
* behat_data_generators::parse_entity_type, which validates the format of the parameter and throws an exception if it is not
|
||||
* correct.
|
||||
*
|
||||
* @return \external_function_parameters
|
||||
*/
|
||||
public static function execute_parameters(): \external_function_parameters {
|
||||
return new \external_function_parameters([
|
||||
'entitytype' => new \external_value(PARAM_RAW, 'Entity type that can be created by a generator.'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of the required fields for a given entity type.
|
||||
*
|
||||
* @param string $entitytype
|
||||
* @return array
|
||||
*/
|
||||
public static function execute(string $entitytype): array {
|
||||
global $CFG;
|
||||
|
||||
// Ensure we can load Behat and Facebook namespaces in behat libraries.
|
||||
require_once("{$CFG->dirroot}/vendor/autoload.php");
|
||||
require_once("{$CFG->libdir}/tests/behat/behat_data_generators.php");
|
||||
|
||||
$params = self::validate_parameters(self::execute_parameters(), ['entitytype' => $entitytype]);
|
||||
$context = \context_system::instance();
|
||||
self::validate_context($context);
|
||||
require_capability('moodle/site:config', $context);
|
||||
|
||||
$generators = new \behat_data_generators();
|
||||
$entity = $generators->get_entity($params['entitytype']);
|
||||
return ['required' => $entity['required']];
|
||||
}
|
||||
|
||||
/**
|
||||
* Define return values.
|
||||
*
|
||||
* Return required fields
|
||||
*
|
||||
* @return \external_single_structure
|
||||
*/
|
||||
public static function execute_returns(): \external_single_structure {
|
||||
return new \external_single_structure([
|
||||
'required' => new \external_multiple_structure(
|
||||
new \external_value(PARAM_TEXT, 'Required field'),
|
||||
'Required fields',
|
||||
VALUE_OPTIONAL
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -278,7 +278,7 @@ if (empty($parallelrun)) {
|
||||
// Print combined run o/p from processes.
|
||||
$exitcodes = print_combined_run_output($processes, $stoponfail);
|
||||
// Time to finish run.
|
||||
$time = round(microtime(true) - $time, 1);
|
||||
$time = round(microtime(true) - $time, 0);
|
||||
echo "Finished in " . gmdate("G\h i\m s\s", $time) . PHP_EOL . PHP_EOL;
|
||||
ksort($exitcodes);
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Define web service functions for tool_behat
|
||||
*
|
||||
* @package tool_behat
|
||||
* @copyright 2022 onwards Catalyst IT EU {@link https://catalyst-eu.net}
|
||||
* @author Mark Johnson <mark.johnson@catalyst-eu.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$functions = [
|
||||
'tool_behat_get_entity_generator' => [
|
||||
'classname' => 'tool_behat\\external\\get_entity_generator',
|
||||
'methodname' => 'execute',
|
||||
'description' => 'Get the generator details for an entity',
|
||||
'type' => 'read',
|
||||
'ajax' => true,
|
||||
'capabilities' => 'moodle/site:config'
|
||||
]
|
||||
];
|
||||
@@ -56,6 +56,7 @@ if ($components) {
|
||||
$form = new steps_definitions_form(null, array('components' => $componentswithsteps));
|
||||
|
||||
// Output contents.
|
||||
$PAGE->requires->js_call_amd('tool_behat/steps', 'init');
|
||||
$renderer = $PAGE->get_renderer('tool_behat');
|
||||
echo $renderer->render_stepsdefinitions($steps, $form);
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir . '/behat/classes/behat_generator_base.php');
|
||||
|
||||
/**
|
||||
* Renderer for behat tool web features
|
||||
*
|
||||
@@ -106,6 +108,32 @@ class tool_behat_renderer extends plugin_renderer_base {
|
||||
},
|
||||
$stepsdefinitions
|
||||
);
|
||||
|
||||
$elementstrings = [];
|
||||
$count = 1;
|
||||
$stepsdefinitions = preg_replace_callback('/(the following ")ELEMENT\d?_STRING(" exist:)/',
|
||||
function($matches) use (&$elementstrings, &$count) {
|
||||
// Replace element type arguments with a user-friendly select.
|
||||
if (empty($elementstrings)) {
|
||||
$behatgenerators = new behat_data_generators();
|
||||
$componententities = $behatgenerators->get_all_entities();
|
||||
ksort($componententities);
|
||||
$elementstrings = [];
|
||||
foreach ($componententities as $component => $entities) {
|
||||
asort($entities);
|
||||
foreach ($entities as $entity) {
|
||||
$string = ($component === 'core') ? $entity : $component . ' > ' . $entity;
|
||||
$elementstrings[$string] = $string;
|
||||
}
|
||||
}
|
||||
}
|
||||
$select = html_writer::select($elementstrings, 'entities' . $count, '', ['' => 'choosedots'],
|
||||
['class' => 'entities']);
|
||||
$count++;
|
||||
return $matches[1] . $select . $matches[2];
|
||||
},
|
||||
$stepsdefinitions
|
||||
);
|
||||
}
|
||||
|
||||
// Steps definitions.
|
||||
|
||||
@@ -29,3 +29,9 @@
|
||||
#page-admin-tool-behat-index .steps-definitions .stepregex {
|
||||
color: #060;
|
||||
}
|
||||
|
||||
#page-admin-tool-behat-index .steprequiredfields {
|
||||
font-weight: bold;
|
||||
font-size: 1em;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
{{!
|
||||
This file is part of Moodle - https://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template tool_behat/steprequiredfields
|
||||
|
||||
Display a Gherkin-style table row showing required columns for a step.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"fields": [
|
||||
"user",
|
||||
"role",
|
||||
"context"
|
||||
]
|
||||
}
|
||||
}}
|
||||
<pre class="steprequiredfields">
|
||||
|{{#fields}} {{{.}}} |{{/fields}}
|
||||
</pre>
|
||||
@@ -13,6 +13,10 @@ Feature: List the system steps definitions
|
||||
Scenario: Accessing the list
|
||||
Then I should see "Step definitions"
|
||||
And I should not see "There aren't steps definitions matching this filter"
|
||||
And I should not see "the following \"ELEMENT_STRING\" exist:"
|
||||
And "entities1" "select" should exist
|
||||
And the "entities1" select box should contain "users"
|
||||
And the "entities1" select box should contain "mod_assign > submissions"
|
||||
|
||||
@javascript
|
||||
Scenario: Filtering by type
|
||||
@@ -34,3 +38,11 @@ Feature: List the system steps definitions
|
||||
Then I should not see "There aren't steps definitions matching this filter"
|
||||
And I should see "Checks the provided element and selector type exists in the current page."
|
||||
And I should see "Checks that an element and selector type exists in another element and selector type on the current page."
|
||||
|
||||
@javascript
|
||||
Scenario: Get required fields
|
||||
Given I set the field "entities1" to "users"
|
||||
And I should see "| username |"
|
||||
When I set the field "entities1" to "mod_quiz > user overrides"
|
||||
Then I should not see "| username |"
|
||||
And I should see "| quiz | user |"
|
||||
|
||||
@@ -13,24 +13,24 @@ Feature: Transform steps arguments
|
||||
And I open my profile in edit mode
|
||||
|
||||
Scenario: Use nasty strings on steps arguments
|
||||
When I set the field "Surname" to "$NASTYSTRING1"
|
||||
When I set the field "Last name" to "$NASTYSTRING1"
|
||||
And I set the field "Description" to "$NASTYSTRING2"
|
||||
And I set the field "City/town" to "$NASTYSTRING3"
|
||||
And I press "Update profile"
|
||||
And I click on "Edit profile" "link" in the "region-main" "region"
|
||||
Then I should not see "NASTYSTRING"
|
||||
And the field "Surname" matches value "$NASTYSTRING1"
|
||||
And the field "Last name" matches value "$NASTYSTRING1"
|
||||
And the field "City/town" matches value "$NASTYSTRING3"
|
||||
|
||||
Scenario: Use nasty strings on table nodes
|
||||
When I set the following fields to these values:
|
||||
| Surname | $NASTYSTRING1 |
|
||||
| Last name | $NASTYSTRING1 |
|
||||
| Description | $NASTYSTRING2 |
|
||||
| City/town | $NASTYSTRING3 |
|
||||
And I press "Update profile"
|
||||
And I click on "Edit profile" "link" in the "region-main" "region"
|
||||
Then I should not see "NASTYSTRING"
|
||||
And the field "Surname" matches value "$NASTYSTRING1"
|
||||
And the field "Last name" matches value "$NASTYSTRING1"
|
||||
And the field "City/town" matches value "$NASTYSTRING3"
|
||||
|
||||
Scenario: Use double quotes
|
||||
@@ -48,11 +48,11 @@ Feature: Transform steps arguments
|
||||
Scenario: Nasty strings with other contents
|
||||
When I set the field "First name" to "My Firstname $NASTYSTRING1"
|
||||
And I set the following fields to these values:
|
||||
| Surname | My Surname $NASTYSTRING2 |
|
||||
| Last name | My Last name $NASTYSTRING2 |
|
||||
And I press "Update profile"
|
||||
And I click on "Edit profile" "link" in the "region-main" "region"
|
||||
Then I should not see "NASTYSTRING"
|
||||
And I should see "My Firstname"
|
||||
And I should see "My Surname"
|
||||
And I should see "My Last name"
|
||||
And the field "First name" matches value "My Firstname $NASTYSTRING1"
|
||||
And the field "Surname" matches value "My Surname $NASTYSTRING2"
|
||||
And the field "Last name" matches value "My Last name $NASTYSTRING2"
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Unit tests for get_entity_generator web service
|
||||
*
|
||||
* @package tool_behat
|
||||
* @copyright 2022 onwards Catalyst IT EU {@link https://catalyst-eu.net}
|
||||
* @author Mark Johnson <mark.johnson@catalyst-eu.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_behat\external;
|
||||
|
||||
/**
|
||||
* Tests for get_entity_generator web service
|
||||
*
|
||||
* @covers \tool_behat\external\get_entity_generator
|
||||
*/
|
||||
class get_entity_generator_test extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* Log in as admin
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setUp(): void {
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the generator for a core entity.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_execute_core_entity(): void {
|
||||
$generator = get_entity_generator::execute('users');
|
||||
$this->assertEquals(['required' => ['username']], $generator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the generator for the plugin entity.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_execute_plugin_entity(): void {
|
||||
$generator = get_entity_generator::execute('mod_book > chapters');
|
||||
$this->assertEquals(['required' => ['book', 'title', 'content']], $generator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the generator for an entity with no required fields.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_execute_no_requried(): void {
|
||||
$generator = get_entity_generator::execute('mod_forum > posts');
|
||||
$this->assertEquals(['required' => []], $generator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to get the generator for a core entity that does not exist.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_execute_invalid_entity(): void {
|
||||
$this->expectException('coding_exception');
|
||||
get_entity_generator::execute('foo');
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to get a generator form a plugin that does not exist.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_execute_invalid_plugin(): void {
|
||||
$this->expectException('coding_exception');
|
||||
get_entity_generator::execute('foo > bar');
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to get a generator for an entity that does not exist, from a plugin that does.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_execute_invalid_plugin_entity(): void {
|
||||
$this->expectException('coding_exception');
|
||||
get_entity_generator::execute('mod_book > bar');
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2022112800; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->version = 2022112801; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2022111800; // Requires this Moodle version.
|
||||
$plugin->component = 'tool_behat'; // Full name of the plugin (used for diagnostics)
|
||||
|
||||
@@ -388,13 +388,13 @@ class accessibility {
|
||||
// Confirm if error is reported separately.
|
||||
if ($DB->record_exists_select(manager::DB_ERRORS,
|
||||
'resultid = ? AND ' . $DB->sql_compare_text('htmlcode', 255) . ' = ' . $DB->sql_compare_text('?', 255),
|
||||
[$resultid, html_entity_decode($tmp->html)])) {
|
||||
[$resultid, html_entity_decode($tmp->html, ENT_COMPAT)])) {
|
||||
continue;
|
||||
}
|
||||
$error = new stdClass();
|
||||
$error->resultid = $resultid;
|
||||
$error->linenumber = $tmp->line;
|
||||
$error->htmlcode = html_entity_decode($tmp->html);
|
||||
$error->htmlcode = html_entity_decode($tmp->html, ENT_COMPAT);
|
||||
$errors[] = $error;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ class report_array extends brickfield_accessibility_reporter {
|
||||
$output[$testname]['body'] = $translation['description'];
|
||||
foreach ($test as $k => $problem) {
|
||||
if (is_object($problem)) {
|
||||
$output[$testname]['problems'][$k]['element'] = htmlentities($problem->get_html());
|
||||
$output[$testname]['problems'][$k]['element'] = htmlentities($problem->get_html(), ENT_COMPAT);
|
||||
$output[$testname]['problems'][$k]['line'] = $problem->get_line();
|
||||
if ($problem->message) {
|
||||
$output[$testname]['problems']['message'] = $problem->message;
|
||||
|
||||
@@ -67,7 +67,7 @@ class report_codehighlight extends \tool_brickfield\local\htmlchecker\brickfield
|
||||
}
|
||||
}
|
||||
$this->dom->formatOutput = true;
|
||||
$html = htmlspecialchars($this->dom->saveHTML());
|
||||
$html = htmlspecialchars($this->dom->saveHTML(), ENT_COMPAT);
|
||||
$html = str_replace('"', '"', $html);
|
||||
foreach ($this->classnames as $name) {
|
||||
$html = preg_replace('/<'. $name .'([^&]+)+\>/', '<span \\1>', $html);
|
||||
|
||||
@@ -49,7 +49,7 @@ class report_xml extends brickfield_accessibility_reporter {
|
||||
$output .= "\n\t<htmlchecker:problems>";
|
||||
foreach ($test as $problem) {
|
||||
if (is_object($problem)) {
|
||||
$output .= "\n\t<htmlchecker:entities><![CDATA[" . htmlentities($problem->get_html()) .
|
||||
$output .= "\n\t<htmlchecker:entities><![CDATA[" . htmlentities($problem->get_html(), ENT_COMPAT) .
|
||||
"]]></htmlchecker:entities>";
|
||||
$output .= "\n\t<htmlchecker:line>". $problem->get_line() ."</htmlchecker:line>";
|
||||
if ($problem->message) {
|
||||
|
||||
@@ -180,7 +180,7 @@ class renderer extends \tool_brickfield\output\renderer {
|
||||
];
|
||||
$data->errordata = !empty($data->errordata) ? $data->errordata : [];
|
||||
foreach ($data->errordata as $err) {
|
||||
$err->htmlcode = htmlentities($err->htmlcode);
|
||||
$err->htmlcode = htmlentities($err->htmlcode, ENT_COMPAT);
|
||||
$row = [$data->tarlabels[$err->component], $err->shortname, $err->errline, $err->htmlcode];
|
||||
$table->data[] = $row;
|
||||
}
|
||||
|
||||
@@ -47,5 +47,8 @@ function xmldb_tool_cohortroles_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -81,6 +81,30 @@ $activity-icon-colors: (
|
||||
);
|
||||
{{</ highlight >}}
|
||||
|
||||
### Custom activity icons
|
||||
|
||||
Some activities allow icons to be customised. This can be done by implementing callback XXX_get_coursemodule_info() returning instance of object e.g. mod/lti/lib.php
|
||||
|
||||
{{< php >}}
|
||||
$info = new cached_cm_info();
|
||||
$info->iconurl = new moodle_url('https://moodle.org/theme/moodleorg/pix/moodle_logo_small.svg');
|
||||
{{< /php >}}
|
||||
|
||||
To get this customised icon, use:
|
||||
|
||||
{{< php >}}
|
||||
$iconurl = get_fast_modinfo($courseid)->get_cm($cmid)->get_icon_url()->out(false);
|
||||
{{< /php >}}
|
||||
|
||||
<div class="media mb-3">
|
||||
<div class="activityiconcontainer lti mr-3">
|
||||
<img alt="lti icon" title="lti icon" src="https://moodle.org/theme/moodleorg/pix/moodle_logo_small.svg" class="activityicon "> </div>
|
||||
<div class="media-body align-self-center">
|
||||
<div class="text-uppercase small">external</div>
|
||||
<div class="activityname"><a href="#">External tool module</a></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## Examples
|
||||
|
||||
<div class="media mb-3">
|
||||
|
||||
@@ -35,5 +35,8 @@ function xmldb_tool_customlang_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,6 +5,6 @@
|
||||
* @copyright 2018 Adrian Greeve
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
define("tool_dataprivacy/expand_contract",["jquery","core/url","core/str"],(function($,url,str){var expandedImage=$('<img alt="" src="'+url.imageUrl("t/expanded")+'"/>'),collapsedImage=$('<img alt="" src="'+url.imageUrl("t/collapsed")+'"/>'),CLASSES_EXPAND="fa-caret-right",CLASSES_COLLAPSE="fa-caret-down";return{expandCollapse:function(targetnode,thisnode){targetnode.hasClass("hide")?(targetnode.removeClass("hide"),targetnode.addClass("visible"),targetnode.attr("aria-expanded",!0),thisnode.find(":header i.fa").removeClass(CLASSES_EXPAND),thisnode.find(":header i.fa").addClass(CLASSES_COLLAPSE),thisnode.find(":header img.icon").attr("src",expandedImage.attr("src"))):(targetnode.removeClass("visible"),targetnode.addClass("hide"),targetnode.attr("aria-expanded",!1),thisnode.find(":header i.fa").removeClass(CLASSES_COLLAPSE),thisnode.find(":header i.fa").addClass(CLASSES_EXPAND),thisnode.find(":header img.icon").attr("src",collapsedImage.attr("src")))},expandCollapseAll:function(nextstate){var currentstate="visible"==nextstate?"hide":"visible",ariaexpandedstate="visible"==nextstate,iconclassnow="visible"==nextstate?CLASSES_EXPAND:CLASSES_COLLAPSE,iconclassnext="visible"==nextstate?CLASSES_COLLAPSE:CLASSES_EXPAND,imagenow="visible"==nextstate?expandedImage.attr("src"):collapsedImage.attr("src");$("."+currentstate).each((function(){$(this).removeClass(currentstate),$(this).addClass(nextstate),$(this).attr("aria-expanded",ariaexpandedstate)})),$(".tool_dataprivacy-expand-all").data("visibilityState",currentstate),str.get_string(currentstate,"tool_dataprivacy").then((function(langString){$(".tool_dataprivacy-expand-all").html(langString)})).catch(Notification.exception),$(":header i.fa").each((function(){$(this).removeClass(iconclassnow),$(this).addClass(iconclassnext)})),$(":header img.icon").each((function(){$(this).attr("src",imagenow)}))}}}));
|
||||
define("tool_dataprivacy/expand_contract",["jquery","core/url","core/str","core/notification"],(function($,url,str,Notification){var expandedImage=$('<img alt="" src="'+url.imageUrl("t/expanded")+'"/>'),collapsedImage=$('<img alt="" src="'+url.imageUrl("t/collapsed")+'"/>'),CLASSES_EXPAND="fa-caret-right",CLASSES_COLLAPSE="fa-caret-down";return{expandCollapse:function(targetnode,thisnode){targetnode.hasClass("hide")?(targetnode.removeClass("hide"),targetnode.addClass("visible"),targetnode.attr("aria-expanded",!0),thisnode.find(":header i.fa").removeClass(CLASSES_EXPAND),thisnode.find(":header i.fa").addClass(CLASSES_COLLAPSE),thisnode.find(":header img.icon").attr("src",expandedImage.attr("src"))):(targetnode.removeClass("visible"),targetnode.addClass("hide"),targetnode.attr("aria-expanded",!1),thisnode.find(":header i.fa").removeClass(CLASSES_COLLAPSE),thisnode.find(":header i.fa").addClass(CLASSES_EXPAND),thisnode.find(":header img.icon").attr("src",collapsedImage.attr("src")))},expandCollapseAll:function(nextstate){var currentstate="visible"==nextstate?"hide":"visible",ariaexpandedstate="visible"==nextstate,iconclassnow="visible"==nextstate?CLASSES_EXPAND:CLASSES_COLLAPSE,iconclassnext="visible"==nextstate?CLASSES_COLLAPSE:CLASSES_EXPAND,imagenow="visible"==nextstate?expandedImage.attr("src"):collapsedImage.attr("src");$("."+currentstate).each((function(){$(this).removeClass(currentstate),$(this).addClass(nextstate),$(this).attr("aria-expanded",ariaexpandedstate)})),$(".tool_dataprivacy-expand-all").data("visibilityState",currentstate),str.get_string(currentstate,"tool_dataprivacy").then((function(langString){$(".tool_dataprivacy-expand-all").html(langString)})).catch(Notification.exception),$(":header i.fa").each((function(){$(this).removeClass(iconclassnow),$(this).addClass(iconclassnext)})),$(":header img.icon").each((function(){$(this).attr("src",imagenow)}))}}}));
|
||||
|
||||
//# sourceMappingURL=expand_contract.min.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -21,7 +21,7 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
define(['jquery', 'core/url', 'core/str'], function($, url, str) {
|
||||
define(['jquery', 'core/url', 'core/str', 'core/notification'], function($, url, str, Notification) {
|
||||
|
||||
var expandedImage = $('<img alt="" src="' + url.imageUrl('t/expanded') + '"/>');
|
||||
var collapsedImage = $('<img alt="" src="' + url.imageUrl('t/collapsed') + '"/>');
|
||||
|
||||
@@ -84,5 +84,8 @@ function xmldb_tool_dataprivacy_upgrade($oldversion) {
|
||||
upgrade_plugin_savepoint(true, 2022053000, 'tool', 'dataprivacy');
|
||||
}
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -79,6 +79,19 @@ class locale {
|
||||
* @return string|false Returns the new current locale, or FALSE on error.
|
||||
*/
|
||||
protected function set_locale(int $category = LC_ALL, string $locale = '0') {
|
||||
return setlocale($category, $locale);
|
||||
if (strlen($locale) <= 255 || PHP_OS_FAMILY === 'BSD' || PHP_OS_FAMILY === 'Darwin') {
|
||||
// We can set the whole locale all together.
|
||||
return setlocale($category, $locale);
|
||||
}
|
||||
|
||||
// Too long locale with linux or windows, let's split it into known and supported categories.
|
||||
$split = explode(';', $locale);
|
||||
foreach ($split as $element) {
|
||||
[$category, $value] = explode('=', $element);
|
||||
if (defined($category)) { // Only if the category exists, there are OS differences.
|
||||
setlocale(constant($category), $value);
|
||||
}
|
||||
}
|
||||
return setlocale(LC_ALL, 0); // Finally, return the complete configured locale.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,6 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Tests for \tool_langimport\locale class.
|
||||
*
|
||||
* @package tool_langimport
|
||||
* @copyright 2018 Université Rennes 2 {@link https://www.univ-rennes2.fr}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace tool_langimport;
|
||||
|
||||
/**
|
||||
@@ -29,6 +21,7 @@ namespace tool_langimport;
|
||||
*
|
||||
* @package tool_langimport
|
||||
* @category test
|
||||
* @coversDefaultClass \tool_langimport\locale
|
||||
* @copyright 2018 Université Rennes 2 {@link https://www.univ-rennes2.fr}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
@@ -36,6 +29,7 @@ class locale_test extends \advanced_testcase {
|
||||
/**
|
||||
* Test that \tool_langimport\locale::check_locale_availability() works as expected.
|
||||
*
|
||||
* @covers ::check_locale_availability
|
||||
* @return void
|
||||
*/
|
||||
public function test_check_locale_availability() {
|
||||
@@ -43,7 +37,7 @@ class locale_test extends \advanced_testcase {
|
||||
// - first setlocale() call which backup current locale
|
||||
// - second setlocale() call which try to set new 'es' locale
|
||||
// - third setlocale() call which restore locale.
|
||||
$mock = $this->getMockBuilder(\tool_langimport\locale::class)
|
||||
$mock = $this->getMockBuilder(locale::class)
|
||||
->onlyMethods(['set_locale'])
|
||||
->getMock();
|
||||
$mock->method('set_locale')->will($this->onConsecutiveCalls('en', 'es', 'en'));
|
||||
@@ -56,7 +50,7 @@ class locale_test extends \advanced_testcase {
|
||||
// - first setlocale() call which backup current locale
|
||||
// - second setlocale() call which fail to set new locale
|
||||
// - third setlocale() call which restore locale.
|
||||
$mock = $this->getMockBuilder(\tool_langimport\locale::class)
|
||||
$mock = $this->getMockBuilder(locale::class)
|
||||
->onlyMethods(['set_locale'])
|
||||
->getMock();
|
||||
$mock->method('set_locale')->will($this->onConsecutiveCalls('en', false, 'en'));
|
||||
@@ -66,8 +60,104 @@ class locale_test extends \advanced_testcase {
|
||||
$this->assertFalse($result);
|
||||
|
||||
// Test an invalid parameter.
|
||||
$locale = new \tool_langimport\locale();
|
||||
$locale = new locale();
|
||||
$this->expectException(\coding_exception::class);
|
||||
$locale->check_locale_availability('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test \tool_langimport\locale::set_locale() own logic.
|
||||
*
|
||||
* We have to explicitly test set_locale() own logic and results,
|
||||
* that effectively sets the current locale, so we need to restore
|
||||
* the original locale after every test (ugly, from a purist unit test
|
||||
* point of view, but needed).
|
||||
*
|
||||
* @dataProvider set_locale_provider
|
||||
* @covers ::set_locale
|
||||
*
|
||||
* @param string $set locale string to be set.
|
||||
* @param string $ret expected results returned after setting the locale.
|
||||
*/
|
||||
public function test_set_locale(string $set, string $ret) {
|
||||
// Make set_locale() public.
|
||||
$loc = new locale();
|
||||
$rc = new \ReflectionClass(locale::class);
|
||||
$rm = $rc->getMethod('set_locale');
|
||||
$rm->setAccessible(true);
|
||||
|
||||
// Capture current locale for later restore (funnily, using the set_locale() method itself.
|
||||
$originallocale = $rm->invokeArgs($loc, [LC_ALL, 0]);
|
||||
|
||||
// Assert we get the locale defined as expected.
|
||||
$this->assertEquals($ret, $rm->invokeArgs($loc, [LC_ALL, $set]));
|
||||
|
||||
// We have finished, restore the original locale, so this doesn't affect other tests at distance.
|
||||
// (again, funnily, using the very same set_locale() method).
|
||||
$rm->invokeArgs($loc, [LC_ALL, $originallocale]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for test_set_locale().
|
||||
*
|
||||
* Provides a locale to be set (as 'set') and a expected return value (as 'ret'). Note that
|
||||
* some of the locales are OS dependent, so only the ones matching the OS will be provided.
|
||||
*
|
||||
* We make extensive use of the en_AU.UTF-8/English_Australia.1252 locale that is mandatory to
|
||||
* be installed in any system running PHPUnit tests.
|
||||
*/
|
||||
public function set_locale_provider(): array {
|
||||
// Let's list the allowed categories by OS.
|
||||
$bsdallowed = ['LC_COLLATE', 'LC_CTYPE', 'LC_MESSAGES', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME'];
|
||||
$winallowed = ['LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME'];
|
||||
$linuxallowed = [
|
||||
'LC_COLLATE', 'LC_CTYPE', 'LC_MESSAGES', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME',
|
||||
'LC_PAPER', 'LC_NAME', 'LC_ADDRESS', 'LC_TELEPHONE', 'LC_MEASUREMENT', 'LC_IDENTIFICATION'
|
||||
];
|
||||
|
||||
// The base locale name is also OS dependent.
|
||||
$baselocale = get_string('locale', 'langconfig');
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
$baselocale = get_string('localewin', 'langconfig');
|
||||
}
|
||||
|
||||
// Here we'll go accumulating cases to be provided.
|
||||
$cases = [];
|
||||
|
||||
// First, the simplest case, just pass a locale name, without categories.
|
||||
$cases['rawlocale'] = [
|
||||
'set' => $baselocale,
|
||||
'ret' => $baselocale,
|
||||
];
|
||||
|
||||
// Now, let's fill ALL LC categories, we should get back the locale name if all them are set with same value.
|
||||
// Note that this case is the one that, under Linux only, covers the changes performed to the set_locale() method.
|
||||
// Pick the correct categories depending on the OS.
|
||||
$oscategories = $bsdallowed; // Default to BSD/Dawrwin ones because they are the standard 6 supported by PHP.
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
$oscategories = $winallowed;
|
||||
} else if (PHP_OS_FAMILY === 'Linux') {
|
||||
$oscategories = $linuxallowed;
|
||||
}
|
||||
|
||||
$localestr = '';
|
||||
foreach ($oscategories as $category) {
|
||||
// Format is different by OS too, so let build the string conditionally.
|
||||
if (PHP_OS_FAMILY === 'BSD' || PHP_OS_FAMILY === 'Darwin') {
|
||||
// BSD uses slashes (/) separated list of the 6 values in exact order.
|
||||
$localestr .= '/' . $baselocale;
|
||||
} else {
|
||||
// Linux/Windows use semicolon (;) separated list of category=value pairs.
|
||||
$localestr .= ';' . $category . '=' . $baselocale;
|
||||
}
|
||||
}
|
||||
$cases['allcategories'] = [
|
||||
'set' => trim($localestr, ';/'),
|
||||
'ret' => $baselocale,
|
||||
];
|
||||
|
||||
// Return all the built cases.
|
||||
return $cases;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ trait store {
|
||||
throw new \coding_exception("Store $called doesn't define classes in correct namespaces.");
|
||||
}
|
||||
$this->component = $parts[0];
|
||||
$this->store = str_replace('logstore_', '', $this->store);
|
||||
$this->store = str_replace('logstore_', '', $this->component);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -39,5 +39,8 @@ function xmldb_tool_log_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -33,5 +33,8 @@ function xmldb_logstore_database_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -60,5 +60,8 @@ function xmldb_logstore_standard_upgrade($oldversion) {
|
||||
upgrade_plugin_savepoint(true, 2022053000, 'logstore', 'standard');
|
||||
}
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -41,5 +41,8 @@ function xmldb_tool_mobile_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -69,5 +69,8 @@ function xmldb_tool_monitor_upgrade($oldversion) {
|
||||
upgrade_plugin_savepoint(true, 2022053000, 'tool', 'monitor');
|
||||
}
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -149,5 +149,8 @@ function xmldb_tool_moodlenet_upgrade(int $oldversion) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -278,7 +278,7 @@ class page_agreedocs implements renderable, templatable {
|
||||
$cache = \cache::make('core', 'presignup');
|
||||
$cachekey = 'tool_policy_viewedpolicies';
|
||||
|
||||
$viewedpolicies = $cache->get($cachekey);
|
||||
$viewedpolicies = $cache->get($cachekey) ?: [];
|
||||
if (!empty($viewedpolicies)) {
|
||||
// Get the list of the policies docs which the user haven't viewed during this session.
|
||||
$pendingpolicies = array_diff($currentpolicyversionids, $viewedpolicies);
|
||||
|
||||
@@ -40,5 +40,8 @@ function xmldb_tool_policy_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ $string['nopermissiontoagreedocs'] = 'No permission to agree to the policies';
|
||||
$string['nopermissiontoagreedocs_desc'] = 'Sorry, you do not have the required permissions to agree to the policies.<br />You will not be able to use this site until the following policies are agreed:';
|
||||
$string['nopermissiontoagreedocsbehalf'] = 'No permission to agree to the policies on behalf of this user';
|
||||
$string['nopermissiontoagreedocsbehalf_desc'] = 'Sorry, you do not have the required permission to agree to the following policies on behalf of {$a}:';
|
||||
$string['nopermissiontoagreedocscontact'] = 'For further assistance:';
|
||||
$string['nopermissiontoagreedocscontact'] = 'For more help:';
|
||||
$string['nopermissiontoviewpolicyversion'] = 'You do not have permissions to view this policy version.';
|
||||
$string['nopolicies'] = 'There are no policies for registered users with an active version.';
|
||||
$string['selectpolicyandversion'] = 'Use the filter above to select policy and/or version';
|
||||
|
||||
@@ -19,7 +19,7 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
| Email address | user1@address.invalid |
|
||||
| Email (again) | user1@address.invalid |
|
||||
| First name | User1 |
|
||||
| Surname | L1 |
|
||||
| Last name | L1 |
|
||||
And I press "Create my new account"
|
||||
And I should see "Confirm your account"
|
||||
And I should see "An email should have been sent to your address at user1@address.invalid"
|
||||
@@ -53,7 +53,7 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
| Email address | user1@address.invalid |
|
||||
| Email (again) | user1@address.invalid |
|
||||
| First name | User1 |
|
||||
| Surname | L1 |
|
||||
| Last name | L1 |
|
||||
And I press "Create my new account"
|
||||
And I should see "Confirm your account"
|
||||
And I should see "An email should have been sent to your address at user1@address.invalid"
|
||||
@@ -98,7 +98,7 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
| Email address | user1@address.invalid |
|
||||
| Email (again) | user1@address.invalid |
|
||||
| First name | User1 |
|
||||
| Surname | L1 |
|
||||
| Last name | L1 |
|
||||
And I press "Create my new account"
|
||||
And I should see "Confirm your account"
|
||||
And I should see "An email should have been sent to your address at user1@address.invalid"
|
||||
@@ -157,7 +157,7 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
| Email address | user1@address.invalid |
|
||||
| Email (again) | user1@address.invalid |
|
||||
| First name | User1 |
|
||||
| Surname | L1 |
|
||||
| Last name | L1 |
|
||||
And I press "Create my new account"
|
||||
And I should see "Confirm your account"
|
||||
And I should see "An email should have been sent to your address at user1@address.invalid"
|
||||
@@ -210,7 +210,7 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
| Email address | user1@address.invalid |
|
||||
| Email (again) | user1@address.invalid |
|
||||
| First name | User1 |
|
||||
| Surname | L1 |
|
||||
| Last name | L1 |
|
||||
And I press "Create my new account"
|
||||
And I should see "Confirm your account"
|
||||
And I should see "An email should have been sent to your address at user1@address.invalid"
|
||||
@@ -522,7 +522,7 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
| Email address | user1@address.invalid |
|
||||
| Email (again) | user1@address.invalid |
|
||||
| First name | User1 |
|
||||
| Surname | L1 |
|
||||
| Last name | L1 |
|
||||
When I press "Create my new account"
|
||||
Then I should see "Confirm your account"
|
||||
And I should see "An email should have been sent to your address at user1@address.invalid"
|
||||
@@ -647,7 +647,7 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
| Email address | user1@address.invalid |
|
||||
| Email (again) | user1@address.invalid |
|
||||
| First name | User1 |
|
||||
| Surname | L1 |
|
||||
| Last name | L1 |
|
||||
And I press "Create my new account"
|
||||
And I should see "Confirm your account"
|
||||
And I should see "An email should have been sent to your address at user1@address.invalid"
|
||||
@@ -716,7 +716,7 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
| Email address | user1@address.invalid |
|
||||
| Email (again) | user1@address.invalid |
|
||||
| First name | User1 |
|
||||
| Surname | L1 |
|
||||
| Last name | L1 |
|
||||
And I press "Create my new account"
|
||||
And I should see "Confirm your account"
|
||||
And I should see "An email should have been sent to your address at user1@address.invalid"
|
||||
@@ -858,7 +858,7 @@ Feature: User must accept policy managed by this plugin when logging in and sign
|
||||
| Email address | user1@address.invalid |
|
||||
| Email (again) | user1@address.invalid |
|
||||
| First name | User1 |
|
||||
| Surname | L1 |
|
||||
| Last name | L1 |
|
||||
And I press "Create my new account"
|
||||
And I should see "Confirm your account"
|
||||
And I should see "An email should have been sent to your address at user1@address.invalid"
|
||||
|
||||
@@ -86,7 +86,7 @@ Feature: Optional policies
|
||||
| Email address | user3@address.invalid |
|
||||
| Email (again) | user3@address.invalid |
|
||||
| First name | User3 |
|
||||
| Surname | L3 |
|
||||
| Last name | L3 |
|
||||
And I press "Create my new account"
|
||||
And I should see "Confirm your account"
|
||||
And I should see "An email should have been sent to your address at user3@address.invalid"
|
||||
|
||||
@@ -51,6 +51,12 @@ if ($listurl) {
|
||||
$PAGE->navbar->add($listurl, $listurlnav);
|
||||
}
|
||||
|
||||
// Add a new nav item to make $listurl clickable for the Boost theme.
|
||||
if (isset($script)) {
|
||||
$lastrunnav = get_string('lastrun', 'tool_profiling');
|
||||
$PAGE->navbar->add($lastrunnav);
|
||||
}
|
||||
|
||||
// Header
|
||||
echo $OUTPUT->header();
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ $string['importok'] = 'File "{$a}" imported successfully.';
|
||||
$string['importprefix'] = 'Import prefix';
|
||||
$string['importproblem'] = 'Some problem happened importing the file "{$a}".';
|
||||
$string['lastrunof'] = 'Summary of last run of {$a}';
|
||||
$string['lastrun'] = 'Summary of last run';
|
||||
$string['markreferencerun'] = 'Mark as reference run/comment';
|
||||
$string['memory'] = 'Memory used';
|
||||
$string['pluginname'] = 'Profiling runs';
|
||||
|
||||
@@ -243,6 +243,16 @@ class category_bin extends base_bin {
|
||||
throw new \moodle_exception("Could not create course to restore into.");
|
||||
}
|
||||
|
||||
// As far as recycle bin is using MODE_AUTOMATED, it observes the General restore settings.
|
||||
// For recycle bin we want to ensure that backup files are always restore the users and groups information.
|
||||
// In order to achieve that, we hack the setting here via $CFG->forced_plugin_settings,
|
||||
// so it won't interfere other operations.
|
||||
// See MDL-65218 and MDL-35773 for more information.
|
||||
// This hack will be removed once recycle bin switches to use its own backup mode, with
|
||||
// own preferences and 100% separate from MOODLE_AUTOMATED.
|
||||
// TODO: Remove this as part of MDL-65228.
|
||||
$CFG->forced_plugin_settings['restore'] = ['restore_general_users' => 1, 'restore_general_groups' => 1];
|
||||
|
||||
// Define the import.
|
||||
$controller = new \restore_controller(
|
||||
$tempdir,
|
||||
@@ -277,6 +287,10 @@ class category_bin extends base_bin {
|
||||
// Run the import.
|
||||
$controller->execute_plan();
|
||||
|
||||
// We don't need the forced setting anymore, hence unsetting it.
|
||||
// TODO: Remove this as part of MDL-65228.
|
||||
unset($CFG->forced_plugin_settings['restore']);
|
||||
|
||||
// Have finished with the controller, let's destroy it, freeing mem and resources.
|
||||
$controller->destroy();
|
||||
|
||||
|
||||
@@ -234,6 +234,16 @@ class course_bin extends base_bin {
|
||||
$fb = get_file_packer('application/vnd.moodle.backup');
|
||||
$fb->extract_to_pathname($file, $fulltempdir);
|
||||
|
||||
// As far as recycle bin is using MODE_AUTOMATED, it observes the General restore settings.
|
||||
// For recycle bin we want to ensure that backup files are always restore the users and groups information.
|
||||
// In order to achieve that, we hack the setting here via $CFG->forced_plugin_settings,
|
||||
// so it won't interfere other operations.
|
||||
// See MDL-65218 and MDL-35773 for more information.
|
||||
// This hack will be removed once recycle bin switches to use its own backup mode, with
|
||||
// own preferences and 100% separate from MOODLE_AUTOMATED.
|
||||
// TODO: Remove this as part of MDL-65228.
|
||||
$CFG->forced_plugin_settings['restore'] = ['restore_general_users' => 1, 'restore_general_groups' => 1];
|
||||
|
||||
// Define the import.
|
||||
$controller = new \restore_controller(
|
||||
$tempdir,
|
||||
@@ -264,6 +274,10 @@ class course_bin extends base_bin {
|
||||
// Run the import.
|
||||
$controller->execute_plan();
|
||||
|
||||
// We don't need the forced setting anymore, hence unsetting it.
|
||||
// TODO: Remove this as part of MDL-65228.
|
||||
unset($CFG->forced_plugin_settings['restore']);
|
||||
|
||||
// Have finished with the controller, let's destroy it, freeing mem and resources.
|
||||
$controller->destroy();
|
||||
|
||||
|
||||
@@ -22,35 +22,30 @@ Feature: Backup user data
|
||||
And the following "activities" exist:
|
||||
| activity | course | section | name | intro |
|
||||
| quiz | C1 | 1 | Quiz 1 | Test quiz description |
|
||||
And the following "question categories" exist:
|
||||
| contextlevel | reference | name |
|
||||
| Course | C1 | Test questions |
|
||||
And the following "questions" exist:
|
||||
| questioncategory | qtype | name | questiontext |
|
||||
| Test questions | truefalse | TF1 | First question |
|
||||
| Test questions | truefalse | TF2 | Second question |
|
||||
And quiz "Quiz 1" contains the following questions:
|
||||
| question | page |
|
||||
| TF1 | 1 |
|
||||
| TF2 | 1 |
|
||||
|
||||
@javascript
|
||||
Scenario: Delete and restore a quiz with user data
|
||||
Given I am on the "Quiz 1" "quiz activity" page logged in as teacher1
|
||||
And I add a "True/False" question to the "Quiz 1" quiz with:
|
||||
| Question name | TF1 |
|
||||
| Question text | First question |
|
||||
| General feedback | Thank you, this is the general feedback |
|
||||
| Correct answer | False |
|
||||
| Feedback for the response 'True'. | So you think it is true |
|
||||
| Feedback for the response 'False'. | So you think it is false |
|
||||
And I add a "True/False" question to the "Quiz 1" quiz with:
|
||||
| Question name | TF2 |
|
||||
| Question text | Second question |
|
||||
| General feedback | Thank you, this is the general feedback |
|
||||
| Correct answer | False |
|
||||
| Feedback for the response 'True'. | So you think it is true |
|
||||
| Feedback for the response 'False'. | So you think it is false |
|
||||
And I set the field "maxgrade" to "10.0"
|
||||
And I press "savechanges"
|
||||
And I log out
|
||||
When I am on the "Quiz 1" "quiz activity" page logged in as student1
|
||||
Scenario Outline: Delete and restore a quiz with user data
|
||||
Given the following config values are set as admin:
|
||||
| restore_general_users | <include_user> | restore |
|
||||
And I am on the "Quiz 1" "quiz activity" page logged in as student1
|
||||
And I press "Attempt quiz"
|
||||
And I click on "True" "radio" in the "First question" "question"
|
||||
And I click on "False" "radio" in the "Second question" "question"
|
||||
And I press "Finish attempt"
|
||||
And I press "Submit all and finish"
|
||||
And I click on "Submit" "button" in the "Submit all your answers and finish?" "dialogue"
|
||||
And I should see "5.00 out of 10.00"
|
||||
And I should see "50.00 out of 100.00"
|
||||
And I log out
|
||||
And I log in as "teacher1"
|
||||
And I am on "Course 1" course homepage with editing mode on
|
||||
@@ -63,5 +58,10 @@ Feature: Backup user data
|
||||
And I log in as "student1"
|
||||
And I am on "Course 1" course homepage
|
||||
When I navigate to "User report" in the course gradebook
|
||||
Then "Quiz 1" row "Grade" column of "user-grade" table should contain "5"
|
||||
Then "Quiz 1" row "Grade" column of "user-grade" table should contain "50"
|
||||
And "Quiz 1" row "Percentage" column of "user-grade" table should contain "50"
|
||||
|
||||
Examples:
|
||||
| include_user | case_explanation |
|
||||
| 1 | Checked |
|
||||
| 1 | Unchecked |
|
||||
|
||||
@@ -190,6 +190,11 @@ class category_bin_test extends \advanced_testcase {
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 2],
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_destination', 'value' => true],
|
||||
]],
|
||||
|
||||
'restore/restore_general_users moodle' => [[
|
||||
(object)['plugin' => 'restore', 'name' => 'restore_general_users', 'value' => 0],
|
||||
(object)['plugin' => 'restore', 'name' => 'restore_general_groups', 'value' => 0],
|
||||
]],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -187,6 +187,11 @@ class course_bin_test extends \advanced_testcase {
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_storage', 'value' => 2],
|
||||
(object)['plugin' => 'backup', 'name' => 'backup_auto_destination', 'value' => true],
|
||||
]],
|
||||
|
||||
'restore/restore_general_users moodle' => [[
|
||||
(object)['plugin' => 'restore', 'name' => 'restore_general_users', 'value' => 0],
|
||||
(object)['plugin' => 'restore', 'name' => 'restore_general_groups', 'value' => 0],
|
||||
]],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -66,14 +66,10 @@ Feature: Upload users
|
||||
@javascript
|
||||
Scenario: Upload users with custom profile fields
|
||||
# Create user profile field.
|
||||
Given I log in as "admin"
|
||||
And I navigate to "Users > Accounts > User profile fields" in site administration
|
||||
And I click on "Create a new profile field" "link"
|
||||
And I click on "Text area" "link"
|
||||
And I set the following fields to these values:
|
||||
| Short name | superfield |
|
||||
| Name | Super field |
|
||||
And I click on "Save changes" "button"
|
||||
Given the following "custom profile fields" exist:
|
||||
| datatype | shortname | name |
|
||||
| text | superfield | Super field |
|
||||
And I log in as "admin"
|
||||
# Upload users.
|
||||
When I navigate to "Users > Accounts > Upload users" in site administration
|
||||
And I upload "lib/tests/fixtures/upload_users_profile.csv" file to "File" filemanager
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -44,7 +44,7 @@ import {prefetchStrings} from 'core/prefetch';
|
||||
* @constant
|
||||
* @type {number}
|
||||
*/
|
||||
const MINSPACING = 50;
|
||||
const MINSPACING = 10;
|
||||
|
||||
/**
|
||||
* A user tour.
|
||||
@@ -83,6 +83,12 @@ const Tour = class {
|
||||
// Apply configuration.
|
||||
this.configure.apply(this, arguments);
|
||||
|
||||
// Unset recalculate state.
|
||||
this.possitionNeedToBeRecalculated = false;
|
||||
|
||||
// Unset recalculate count.
|
||||
this.recalculatedNo = 0;
|
||||
|
||||
try {
|
||||
this.storage = window.sessionStorage;
|
||||
this.storageKey = 'tourstate_' + this.tourName;
|
||||
@@ -396,6 +402,11 @@ const Tour = class {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the CSS styles are allowed on the browser or not.
|
||||
if (!this.isCSSAllowed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let target = this.getStepTarget(stepConfig);
|
||||
if (target && target.length && target.is(':visible')) {
|
||||
// Without a target, there can be no step.
|
||||
@@ -405,6 +416,22 @@ const Tour = class {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the browser actually allow CSS styles?
|
||||
*
|
||||
* @returns {boolean} True if the browser is allowing CSS styles
|
||||
*/
|
||||
isCSSAllowed() {
|
||||
const testCSSElement = document.createElement('div');
|
||||
testCSSElement.classList.add('hide');
|
||||
document.body.appendChild(testCSSElement);
|
||||
const styles = window.getComputedStyle(testCSSElement);
|
||||
const isAllowed = styles.display === 'none';
|
||||
testCSSElement.remove();
|
||||
|
||||
return isAllowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to the next step in the tour.
|
||||
*
|
||||
@@ -924,6 +951,7 @@ const Tour = class {
|
||||
// Configure ARIA attributes on the target.
|
||||
let target = this.getStepTarget(stepConfig);
|
||||
if (target) {
|
||||
target.data('original-tabindex', target.attr('tabindex'));
|
||||
if (!target.attr('tabindex')) {
|
||||
target.attr('tabindex', 0);
|
||||
}
|
||||
@@ -1150,6 +1178,12 @@ const Tour = class {
|
||||
|
||||
if (target.data('original-tabindex')) {
|
||||
target.attr('tabindex', target.data('tabindex'));
|
||||
} else {
|
||||
// If the target does not have the tabindex attribute at the beginning. We need to remove it.
|
||||
// We should wait a little here before removing the attribute to prevent the browser from adding it again.
|
||||
window.setTimeout(() => {
|
||||
target.removeAttr('tabindex');
|
||||
}, 400);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1296,6 +1330,7 @@ const Tour = class {
|
||||
*/
|
||||
positionStep(stepConfig) {
|
||||
let content = this.currentStepNode;
|
||||
let thisT = this;
|
||||
if (!content || !content.length) {
|
||||
// Unable to find the step node.
|
||||
return this;
|
||||
@@ -1335,9 +1370,15 @@ const Tour = class {
|
||||
},
|
||||
onCreate: function(data) {
|
||||
recalculateArrowPosition(data);
|
||||
recalculateStepPosition(data);
|
||||
},
|
||||
onUpdate: function(data) {
|
||||
recalculateArrowPosition(data);
|
||||
if (thisT.possitionNeedToBeRecalculated) {
|
||||
thisT.recalculatedNo++;
|
||||
thisT.possitionNeedToBeRecalculated = false;
|
||||
recalculateStepPosition(data);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1387,6 +1428,90 @@ const Tour = class {
|
||||
}
|
||||
};
|
||||
|
||||
const recalculateStepPosition = function(data) {
|
||||
const placement = data.placement.split('-')[0];
|
||||
const isVertical = ['left', 'right'].indexOf(placement) !== -1;
|
||||
const popperElement = $(data.instance.popper);
|
||||
const targetElement = $(data.instance.reference);
|
||||
const arrowElement = popperElement.find('[data-role="arrow"]');
|
||||
const stepElement = popperElement.find('[data-role="flexitour-step"]');
|
||||
const viewportHeight = $(window).height();
|
||||
const viewportWidth = $(window).width();
|
||||
const arrowHeight = parseFloat(arrowElement.outerHeight(true));
|
||||
const popperHeight = parseFloat(popperElement.outerHeight(true));
|
||||
const targetHeight = parseFloat(targetElement.outerHeight(true));
|
||||
const arrowWidth = parseFloat(arrowElement.outerWidth(true));
|
||||
const popperWidth = parseFloat(popperElement.outerWidth(true));
|
||||
const targetWidth = parseFloat(targetElement.outerWidth(true));
|
||||
let maxHeight;
|
||||
|
||||
if (thisT.recalculatedNo > 1) {
|
||||
// The current screen is too small, and cannot fit with the original placement.
|
||||
// We should set the placement to auto so the PopperJS can calculate the perfect placement.
|
||||
thisT.currentStepPopper.options.placement = isVertical ? 'auto-left' : 'auto-bottom';
|
||||
}
|
||||
if (thisT.recalculatedNo > 2) {
|
||||
// Return here to prevent recursive calling.
|
||||
return;
|
||||
}
|
||||
|
||||
if (isVertical) {
|
||||
// Find the best place to put the tour: Left of right.
|
||||
const leftSpace = targetElement.offset().left > 0 ? targetElement.offset().left : 0;
|
||||
const rightSpace = viewportWidth - leftSpace - targetWidth;
|
||||
const remainingSpace = leftSpace >= rightSpace ? leftSpace : rightSpace;
|
||||
maxHeight = viewportHeight - MINSPACING * 2;
|
||||
if (remainingSpace < (popperWidth + arrowWidth)) {
|
||||
const maxWidth = remainingSpace - MINSPACING - arrowWidth;
|
||||
if (maxWidth > 0) {
|
||||
popperElement.css({
|
||||
'max-width': maxWidth + 'px',
|
||||
});
|
||||
// Not enough space, flag true to make Popper to recalculate the position.
|
||||
thisT.possitionNeedToBeRecalculated = true;
|
||||
}
|
||||
} else if (maxHeight < popperHeight) {
|
||||
// Check if the Popper's height can fit the viewport height or not.
|
||||
// If not, set the correct max-height value for the Popper element.
|
||||
popperElement.css({
|
||||
'max-height': maxHeight + 'px',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Find the best place to put the tour: Top of bottom.
|
||||
const topSpace = targetElement.offset().top > 0 ? targetElement.offset().top : 0;
|
||||
const bottomSpace = viewportHeight - topSpace - targetHeight;
|
||||
const remainingSpace = topSpace >= bottomSpace ? topSpace : bottomSpace;
|
||||
maxHeight = remainingSpace - MINSPACING - arrowHeight;
|
||||
if (remainingSpace < (popperHeight + arrowHeight)) {
|
||||
// Not enough space, flag true to make Popper to recalculate the position.
|
||||
thisT.possitionNeedToBeRecalculated = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the Popper's height can fit the viewport height or not.
|
||||
// If not, set the correct max-height value for the body.
|
||||
const currentStepBody = stepElement.find('[data-placeholder="body"]').first();
|
||||
const headerEle = stepElement.find('.modal-header').first();
|
||||
const footerEle = stepElement.find('.modal-footer').first();
|
||||
const headerHeight = headerEle.outerHeight(true) ?? 0;
|
||||
const footerHeight = footerEle.outerHeight(true) ?? 0;
|
||||
maxHeight = maxHeight - headerHeight - footerHeight;
|
||||
if (maxHeight > 0) {
|
||||
headerEle.removeClass('minimal');
|
||||
footerEle.removeClass('minimal');
|
||||
currentStepBody.css({
|
||||
'max-height': maxHeight + 'px',
|
||||
'overflow': 'auto',
|
||||
});
|
||||
} else {
|
||||
headerEle.addClass('minimal');
|
||||
footerEle.addClass('minimal');
|
||||
}
|
||||
// Call the Popper update method to update the position.
|
||||
thisT.currentStepPopper.update();
|
||||
};
|
||||
|
||||
let background = $('[data-flexitour="step-background"]');
|
||||
if (background.length) {
|
||||
target = background;
|
||||
@@ -1503,9 +1628,7 @@ const Tour = class {
|
||||
}
|
||||
|
||||
let targetPosition = this.calculatePosition(targetNode);
|
||||
if (targetPosition === 'fixed') {
|
||||
background.css('top', 0);
|
||||
} else if (targetPosition === 'absolute') {
|
||||
if (targetPosition === 'absolute') {
|
||||
background.css('position', 'fixed');
|
||||
}
|
||||
|
||||
|
||||
@@ -131,5 +131,8 @@ function xmldb_tool_usertours_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,12 @@ span[data-flexitour="container"] .modal-dialog .modal-content .modal-header {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
span[data-flexitour="container"] .modal-dialog .modal-content .modal-header.minimal,
|
||||
span[data-flexitour="container"] .modal-dialog .modal-content .modal-footer.minimal {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
span[data-flexitour="container"] .modal-dialog .modal-content .modal-footer {
|
||||
justify-content: flex-start;
|
||||
border-top: 0;
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
* @package core
|
||||
*/
|
||||
|
||||
use core_user\fields;
|
||||
|
||||
define('NO_OUTPUT_BUFFERING', true);
|
||||
require_once('../../config.php');
|
||||
require_once($CFG->libdir.'/adminlib.php');
|
||||
@@ -37,7 +39,7 @@ if (empty($SESSION->bulk_users)) {
|
||||
}
|
||||
|
||||
if ($dataformat) {
|
||||
$fields = array('id' => 'id',
|
||||
$originfields = array('id' => 'id',
|
||||
'username' => 'username',
|
||||
'email' => 'email',
|
||||
'firstname' => 'firstname',
|
||||
@@ -51,8 +53,10 @@ if ($dataformat) {
|
||||
'country' => 'country');
|
||||
|
||||
$extrafields = profile_get_user_fields_with_data(0);
|
||||
$profilefields = [];
|
||||
foreach ($extrafields as $formfield) {
|
||||
$fields['profile_field_'.$formfield->get_shortname()] = 'profile_field_'.$formfield->get_shortname();
|
||||
$profilefields[fields::PROFILE_FIELD_PREFIX . $formfield->get_shortname()] = fields::PROFILE_FIELD_PREFIX .
|
||||
$formfield->get_shortname();
|
||||
}
|
||||
|
||||
$filename = clean_filename(get_string('users'));
|
||||
@@ -60,17 +64,17 @@ if ($dataformat) {
|
||||
$downloadusers = new ArrayObject($SESSION->bulk_users);
|
||||
$iterator = $downloadusers->getIterator();
|
||||
|
||||
\core\dataformat::download_data($filename, $dataformat, $fields, $iterator, function($userid, $supportshtml)
|
||||
use ($extrafields, $fields) {
|
||||
\core\dataformat::download_data($filename, $dataformat, array_merge($originfields, $profilefields), $iterator,
|
||||
function($userid, $supportshtml) use ($originfields) {
|
||||
|
||||
global $DB;
|
||||
|
||||
if (!$user = $DB->get_record('user', array('id' => $userid))) {
|
||||
return null;
|
||||
}
|
||||
profile_load_data($user);
|
||||
|
||||
$userprofiledata = array();
|
||||
foreach ($fields as $field => $unused) {
|
||||
foreach ($originfields as $field) {
|
||||
// Custom user profile textarea fields come in an array
|
||||
// The first element is the text and the second is the format.
|
||||
// We only take the text.
|
||||
@@ -82,6 +86,19 @@ if ($dataformat) {
|
||||
$userprofiledata[$field] = $user->$field;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Formatting extra field if transform is true.
|
||||
$extrafields = profile_get_user_fields_with_data($userid);
|
||||
foreach ($extrafields as $field) {
|
||||
$fieldkey = fields::PROFILE_FIELD_PREFIX . $field->get_shortname();
|
||||
if ($field->is_transform_supported()) {
|
||||
$userprofiledata[$fieldkey] = $field->display_data();
|
||||
} else {
|
||||
$userprofiledata[$fieldkey] = $field->data;
|
||||
}
|
||||
}
|
||||
|
||||
return $userprofiledata;
|
||||
});
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ class calculation_info {
|
||||
$samplekey = self::get_sample_key($uniquesampleid);
|
||||
|
||||
// Update the cached data adding the new indicator data.
|
||||
$cacheddata = $cache->get($samplekey);
|
||||
$cacheddata = $cache->get($samplekey) ?: [];
|
||||
$cacheddata[$calculableclass] = $infokeys;
|
||||
$cache->set($samplekey, $cacheddata);
|
||||
}
|
||||
|
||||
@@ -46,5 +46,8 @@ function xmldb_auth_cas_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -38,5 +38,8 @@ function xmldb_auth_db_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -38,5 +38,8 @@ function xmldb_auth_email_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ Feature: User must accept policy when logging in and signing up
|
||||
| Email address | user1@address.invalid |
|
||||
| Email (again) | user1@address.invalid |
|
||||
| First name | User1 |
|
||||
| Surname | L1 |
|
||||
| Last name | L1 |
|
||||
And I press "Create my new account"
|
||||
And I should see "Confirm your account"
|
||||
And I should see "An email should have been sent to your address at user1@address.invalid"
|
||||
@@ -48,7 +48,7 @@ Feature: User must accept policy when logging in and signing up
|
||||
| Email address | user1@address.invalid |
|
||||
| Email (again) | user1@address.invalid |
|
||||
| First name | User1 |
|
||||
| Surname | L1 |
|
||||
| Last name | L1 |
|
||||
| I understand and agree | 1 |
|
||||
And I press "Create my new account"
|
||||
And I should see "Confirm your account"
|
||||
@@ -81,7 +81,7 @@ Feature: User must accept policy when logging in and signing up
|
||||
| Email address | <email1> |
|
||||
| Email (again) | <email2> |
|
||||
| First name | Jane |
|
||||
| Surname | Doe |
|
||||
| Last name | Doe |
|
||||
And I press "Create my new account"
|
||||
Then I should <expect> "This email address is already registered. Perhaps you created an account in the past?"
|
||||
And I should <expect2> "Invalid email address"
|
||||
|
||||
@@ -46,5 +46,8 @@ function xmldb_auth_ldap_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -88,5 +88,8 @@ function xmldb_auth_lti_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -38,5 +38,8 @@ function xmldb_auth_manual_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -38,5 +38,8 @@ function xmldb_auth_mnet_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -59,8 +59,8 @@ $string['privacy:metadata:mnet_external:institution'] = 'The institution that th
|
||||
$string['privacy:metadata:mnet_external:lang'] = 'A user preference for the language shown.';
|
||||
$string['privacy:metadata:mnet_external:lastaccess'] = 'The time that the user last accessed the site.';
|
||||
$string['privacy:metadata:mnet_external:lastlogin'] = 'The last login of this user.';
|
||||
$string['privacy:metadata:mnet_external:lastname'] = 'The surname of the user.';
|
||||
$string['privacy:metadata:mnet_external:lastnamephonetic'] = 'The phonetic details about the user\'s surname.';
|
||||
$string['privacy:metadata:mnet_external:lastname'] = 'The last name of the user.';
|
||||
$string['privacy:metadata:mnet_external:lastnamephonetic'] = 'The phonetic details of the user\'s last name.';
|
||||
$string['privacy:metadata:mnet_external:maildigest'] = 'A setting for the mail digest for this user.';
|
||||
$string['privacy:metadata:mnet_external:maildisplay'] = 'A preference for the user about displaying their email address to other users.';
|
||||
$string['privacy:metadata:mnet_external:middlename'] = 'The middle name of the user';
|
||||
|
||||
@@ -38,5 +38,8 @@ function xmldb_auth_none_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -39,5 +39,8 @@ function xmldb_auth_oauth2_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -60,5 +60,8 @@ function xmldb_auth_shibboleth_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -62,15 +62,10 @@ Feature: availability_profile
|
||||
|
||||
@javascript
|
||||
Scenario: Test with custom user profile field
|
||||
# Add custom field.
|
||||
Given I log in as "admin"
|
||||
And I navigate to "Users > Accounts > User profile fields" in site administration
|
||||
And I click on "Create a new profile field" "link"
|
||||
And I click on "Text input" "link"
|
||||
And I set the following fields to these values:
|
||||
| Short name | superfield |
|
||||
| Name | Super field |
|
||||
And I click on "Save changes" "button"
|
||||
Given the following "custom profile fields" exist:
|
||||
| datatype | shortname | name |
|
||||
| text | superfield | Super field |
|
||||
And I log in as "admin"
|
||||
|
||||
# Set field value for user.
|
||||
And I navigate to "Users > Accounts > Browse list of users" in site administration
|
||||
|
||||
@@ -48,7 +48,7 @@ class backup_files_edit_form extends moodleform {
|
||||
$mform->setType('component', PARAM_COMPONENT);
|
||||
|
||||
$mform->addElement('hidden', 'returnurl', $this->_customdata['returnurl']);
|
||||
$mform->setType('returnurl', PARAM_URL);
|
||||
$mform->setType('returnurl', PARAM_LOCALURL);
|
||||
|
||||
$this->add_action_buttons(true, get_string('savechanges'));
|
||||
$this->set_data($this->_customdata['data']);
|
||||
|
||||
@@ -89,7 +89,7 @@ function array_remove_by_key($arr,$key) {
|
||||
|
||||
|
||||
function cc_print_object($object) {
|
||||
echo '<pre>' . htmlspecialchars(print_r($object,true)) . '</pre>';
|
||||
echo '<pre>' . htmlspecialchars(print_r($object,true), ENT_COMPAT) . '</pre>';
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -5293,6 +5293,7 @@ class restore_move_module_questions_categories extends restore_execution_step {
|
||||
AND parentitemid = ?", array($this->get_restoreid(), $contextid));
|
||||
$top = question_get_top_category($newcontext->newitemid, true);
|
||||
$oldtopid = 0;
|
||||
$categoryids = [];
|
||||
foreach ($modulecats as $modulecat) {
|
||||
// Before 3.5, question categories could be created at top level.
|
||||
// From 3.5 onwards, all question categories should be a child of a special category called the "top" category.
|
||||
@@ -5308,6 +5309,7 @@ class restore_move_module_questions_categories extends restore_execution_step {
|
||||
$cat->parent = $top->id;
|
||||
}
|
||||
$DB->update_record('question_categories', $cat);
|
||||
$categoryids[] = (int)$cat->id;
|
||||
}
|
||||
|
||||
// And set new contextid (and maybe update newitemid) also in question_category mapping (will be
|
||||
@@ -5316,6 +5318,30 @@ class restore_move_module_questions_categories extends restore_execution_step {
|
||||
$modulecat->newitemid, $newcontext->newitemid);
|
||||
}
|
||||
|
||||
// Update the context id of any tags applied to any questions in these categories.
|
||||
if ($categoryids) {
|
||||
[$categorysql, $categoryidparams] = $DB->get_in_or_equal($categoryids, SQL_PARAMS_NAMED);
|
||||
$sqlupdate = "UPDATE {tag_instance}
|
||||
SET contextid = :newcontext
|
||||
WHERE component = :component
|
||||
AND itemtype = :itemtype
|
||||
AND itemid IN (SELECT DISTINCT bi.newitemid as questionid
|
||||
FROM {backup_ids_temp} bi
|
||||
JOIN {question} q ON q.id = bi.newitemid
|
||||
JOIN {question_versions} qv ON qv.questionid = q.id
|
||||
JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid
|
||||
WHERE bi.backupid = :backupid AND bi.itemname = 'question_created'
|
||||
AND qbe.questioncategoryid {$categorysql}) ";
|
||||
$params = [
|
||||
'newcontext' => $newcontext->newitemid,
|
||||
'component' => 'core_question',
|
||||
'itemtype' => 'question',
|
||||
'backupid' => $this->get_restoreid(),
|
||||
];
|
||||
$params += $categoryidparams;
|
||||
$DB->execute($sqlupdate, $params);
|
||||
}
|
||||
|
||||
// Now set the parent id for the question categories that were in the top category in the course context
|
||||
// and have been moved now.
|
||||
if ($oldtopid) {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
@core @core_backup
|
||||
Feature: Backup and restore of the question that was tagged
|
||||
|
||||
Background:
|
||||
Given the following "courses" exist:
|
||||
| fullname | shortname | category |
|
||||
| Course 1 | C1 | 0 |
|
||||
|
||||
@javascript @_file_upload
|
||||
Scenario: Restore the quiz containing the question that was tagged
|
||||
Given I am on the "Course 1" "restore" page logged in as "admin"
|
||||
And I press "Manage backup files"
|
||||
And I upload "backup/moodle2/tests/fixtures/test_tags_backup.mbz" file to "Files" filemanager
|
||||
And I press "Save changes"
|
||||
And I restore "test_tags_backup.mbz" backup into a new course using this options:
|
||||
| Schema | Course name | Course 2 |
|
||||
| Schema | Course short name | C2 |
|
||||
When I am on the "TF1" "core_question > edit" page logged in as admin
|
||||
And I expand all fieldsets
|
||||
Then I should see "Tag1-TF1"
|
||||
And I should see "Tag2-TF1"
|
||||
And I am on the "TF2" "core_question > edit" page logged in as admin
|
||||
And I expand all fieldsets
|
||||
And I should see "Tag1-TF2"
|
||||
Binary file not shown.
@@ -1811,10 +1811,9 @@ abstract class restore_dbops {
|
||||
public static function calculate_course_names($courseid, $fullname, $shortname) {
|
||||
global $CFG, $DB;
|
||||
|
||||
$currentfullname = '';
|
||||
$currentshortname = '';
|
||||
$counter = 0;
|
||||
// Iteratere while the name exists
|
||||
|
||||
// Iterate while fullname or shortname exist.
|
||||
do {
|
||||
if ($counter) {
|
||||
$suffixfull = ' ' . get_string('copyasnoun') . ' ' . $counter;
|
||||
@@ -1823,8 +1822,11 @@ abstract class restore_dbops {
|
||||
$suffixfull = '';
|
||||
$suffixshort = '';
|
||||
}
|
||||
$currentfullname = $fullname.$suffixfull;
|
||||
$currentshortname = substr($shortname, 0, 100 - strlen($suffixshort)).$suffixshort; // < 100cc
|
||||
|
||||
// Ensure we don't overflow maximum length of name fields, in multi-byte safe manner.
|
||||
$currentfullname = core_text::substr($fullname, 0, 254 - strlen($suffixfull)) . $suffixfull;
|
||||
$currentshortname = core_text::substr($shortname, 0, 100 - strlen($suffixshort)) . $suffixshort;
|
||||
|
||||
$coursefull = $DB->get_record_select('course', 'fullname = ? AND id != ?',
|
||||
array($currentfullname, $courseid), '*', IGNORE_MULTIPLE);
|
||||
$courseshort = $DB->get_record_select('course', 'shortname = ? AND id != ?', array($currentshortname, $courseid));
|
||||
|
||||
@@ -96,6 +96,21 @@ class async_helper {
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return appropriate description for current async operation {@see async_helper::type}
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_operation_description(): string {
|
||||
$operations = [
|
||||
'backup' => new lang_string('backup'),
|
||||
'copy' => new lang_string('copycourse'),
|
||||
'restore' => new lang_string('restore'),
|
||||
];
|
||||
|
||||
return (string) ($operations[$this->type] ?? $this->type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for preg_replace_callback.
|
||||
* Replaces message placeholders with real values.
|
||||
@@ -105,7 +120,7 @@ class async_helper {
|
||||
*/
|
||||
private function lookup_message_variables($matches) {
|
||||
$options = array(
|
||||
'operation' => $this->type,
|
||||
'operation' => $this->get_operation_description(),
|
||||
'backupid' => $this->backupid,
|
||||
'user_username' => $this->user->username,
|
||||
'user_email' => $this->user->email,
|
||||
|
||||
@@ -30,6 +30,7 @@ require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
|
||||
* Asyncronhous helper tests.
|
||||
*
|
||||
* @package core_backup
|
||||
* @covers \async_helper
|
||||
* @copyright 2018 Matt Porritt <mattp@catalyst-au.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
@@ -47,7 +48,7 @@ class async_helper_test extends \advanced_testcase {
|
||||
set_config('backup_async_message_users', '1', 'backup');
|
||||
set_config('backup_async_message_subject', 'Moodle {operation} completed sucessfully', 'backup');
|
||||
set_config('backup_async_message',
|
||||
'Dear {user_firstname} {user_lastname}, <br/> Your {operation} (ID: {backupid}) has completed successfully!',
|
||||
'Dear {user_firstname} {user_lastname}, your {operation} (ID: {backupid}) has completed successfully!',
|
||||
'backup');
|
||||
set_config('allowedemaildomains', 'example.com');
|
||||
|
||||
@@ -76,15 +77,17 @@ class async_helper_test extends \advanced_testcase {
|
||||
$this->assertCount(1, $emails);
|
||||
$email = reset($emails);
|
||||
|
||||
$this->assertSame($USER->email, $email->from);
|
||||
$this->assertSame($user2->email, $email->to);
|
||||
$this->assertSame('Moodle backup completed sucessfully', $email->subject);
|
||||
$this->assertNotEmpty($email->header);
|
||||
$this->assertNotEmpty($email->body);
|
||||
$this->assertMatchesRegularExpression("/$backupid/", $email->body);
|
||||
$this->assertThat($email->body, $this->logicalNot($this->stringContains('{')));
|
||||
$this->assertGreaterThan(0, $messageid);
|
||||
$sink->clear();
|
||||
|
||||
$this->assertSame($USER->email, $email->from);
|
||||
$this->assertSame($user2->email, $email->to);
|
||||
$this->assertSame('Moodle Backup completed sucessfully', $email->subject);
|
||||
|
||||
// Assert body placeholders have all been replaced.
|
||||
$this->assertStringContainsString('Dear test human, your Backup', $email->body);
|
||||
$this->assertStringContainsString("(ID: {$backupid})", $email->body);
|
||||
$this->assertStringNotContainsString('{', $email->body);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Open Badges Assertions specification 1.0 {@link https://github.com/mozilla/openbadges/wiki/Assertions}
|
||||
* Open Badges Assertions specification 1.0 {@link https://github.com/mozilla/openbadges-backpack/wiki/Assertions}
|
||||
*
|
||||
* Badge asserion is defined by three parts:
|
||||
* - Badge Assertion (information regarding a specific badge that was awarded to a badge earner)
|
||||
|
||||
@@ -72,16 +72,13 @@ class badges_test extends core_reportbuilder_testcase {
|
||||
$report = $generator->create_report(['name' => 'Badges', 'source' => badges::class, 'default' => 0]);
|
||||
|
||||
// Badge course.
|
||||
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:fullname'])
|
||||
->set_many(['sortenabled' => true, 'sortdirection' => SORT_ASC])->update();
|
||||
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:fullname', 'sortenabled' => 1]);
|
||||
|
||||
// Badge name.
|
||||
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'badge:name'])
|
||||
->set_many(['sortenabled' => true, 'sortdirection' => SORT_ASC])->update();
|
||||
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'badge:name', 'sortenabled' => 1]);
|
||||
|
||||
// User fullname.
|
||||
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:fullname'])
|
||||
->set_many(['sortenabled' => true, 'sortdirection' => SORT_ASC])->update();
|
||||
$generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:fullname', 'sortenabled' => 1]);
|
||||
|
||||
$content = $this->get_custom_report_content($report->get('id'));
|
||||
$this->assertCount(3, $content);
|
||||
|
||||
@@ -51,5 +51,8 @@ function xmldb_block_badges_upgrade($oldversion, $block) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -66,5 +66,8 @@ function xmldb_block_calendar_month_upgrade($oldversion, $block) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -51,5 +51,8 @@ function xmldb_block_calendar_upcoming_upgrade($oldversion, $block) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -54,5 +54,8 @@ function xmldb_block_completionstatus_upgrade($oldversion, $block) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -65,5 +65,8 @@ function xmldb_block_course_summary_upgrade($oldversion, $block) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -61,5 +61,8 @@ function xmldb_block_feedback_upgrade($oldversion, $block) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -39,5 +39,8 @@ function xmldb_block_html_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -132,12 +132,8 @@ class block_base {
|
||||
function name() {
|
||||
// Returns the block name, as present in the class name,
|
||||
// the database, the block directory, etc etc.
|
||||
static $myname;
|
||||
if ($myname === NULL) {
|
||||
$myname = strtolower(get_class($this));
|
||||
$myname = substr($myname, strpos($myname, '_') + 1);
|
||||
}
|
||||
return $myname;
|
||||
$myname = strtolower(get_class($this));
|
||||
return substr($myname, strpos($myname, '_') + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -78,5 +78,8 @@ function xmldb_block_myoverview_upgrade($oldversion) {
|
||||
// Automatically generated Moodle v4.0.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
// Automatically generated Moodle v4.1.0 release upgrade line.
|
||||
// Put any upgrade step following this.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user